Initial Chisel core implementation

This commit is contained in:
abnerhexu
2026-06-26 08:20:25 +00:00
commit 502803c37f
47 changed files with 2342 additions and 0 deletions

25
.gitignore vendored Normal file
View File

@@ -0,0 +1,25 @@
# SBT specific
target/
project/target/
project/project/
# IDE
.idea/
.vscode/
.bsp/
.metals/
.bloop/
metals.sbt
# Scala
*.class
*.log
# Generated files
generated/
sim/verilator/obj_dir/
sim/scripts/test_results.txt
riscv-tests/
# toolchain
toolchain/

466
ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,466 @@
# RISC-V RV64G 双发射乱序处理器架构设计
## 目标规格
- **ISA**: RV64IMAFD (RV64G without C extension)
- **Issue Width**: 双发射每周期2条指令
- **Physical Registers**: 64个物理寄存器
- **Branch Predictor**: Gshare
- **Memory**: Sv39分页分离的L1 ICache/DCache
- **Pipeline**: 11级流水线乱序执行
---
## 1. 流水线架构
### 流水线阶段
```
┌─────┐ ┌─────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌────┐
│ IF1 │ → │ IF2 │ → │ ID │ → │ RN │ → │ DP │ → │ IS │ → │ EX1 │ → │ EX2 │ → │ MEM │ → │ WB │ → │ CM │
└─────┘ └─────┘ └────┘ └────┘ └────┘ └────┘ └─────┘ └─────┘ └─────┘ └────┘
TLB ICache Decode Rename Dispatch Issue Execute Execute DCache Write Commit
BPU 2-way Access Back ROB
```
### 各阶段功能
1. **IF1 (Instruction Fetch 1)**
- PC生成与选择
- ITLB查询虚拟地址 → 物理地址)
- Gshare分支预测
- 取两条指令(双发射)
2. **IF2 (Instruction Fetch 2)**
- ICache访问64B缓存行
- 指令对齐与缓冲
3. **ID (Instruction Decode)**
- 双路译码器每周期译码2条指令
- 指令类型识别
- 立即数生成
- 依赖分析RAW检测
4. **RN (Rename)**
- 寄存器重命名32逻辑寄存器 → 64物理寄存器
- 空闲列表(Free List)管理
- 重命名表(Rename Map Table)更新
- ROB分配
5. **DP (Dispatch)**
- 分发到保留站(Reservation Station)
- 根据指令类型选择功能单元队列
6. **IS (Issue)**
- 从保留站唤醒就绪指令
- 乱序发射到执行单元
- 读取物理寄存器文件
7. **EX1 (Execute 1)**
- ALU运算
- 分支计算
- 地址生成load/store
- 浮点运算第一阶段
8. **EX2 (Execute 2)**
- 多周期运算完成
- 浮点运算完成
- 分支结果检查
9. **MEM (Memory)**
- DTLB查询
- DCache访问
- Load/Store执行
- Store Buffer管理
10. **WB (Write Back)**
- 写回物理寄存器
- 唤醒依赖指令
- 更新ROB状态
11. **CM (Commit)**
- ROB按序提交
- 架构状态更新
- 异常/中断处理
- 物理寄存器释放
---
## 2. 核心部件设计
### 2.1 前端 (Front-End)
#### Gshare 分支预测器
```
├── Global History Register (GHR): 12-bit
├── Pattern History Table (PHT): 4096 entries × 2-bit saturating counter
├── Branch Target Buffer (BTB): 512 entries
│ ├── Tag: 20-bit
│ ├── Target Address: 64-bit
│ └── Valid bit
└── Return Address Stack (RAS): 16 entries
```
**Gshare索引计算**: `index = (PC[13:2] XOR GHR[11:0])`
要求Branch Target Buffer大小可配置Return Address Stack大小可配置
#### ICache
```
├── Capacity: 32 KB
├── Associativity: 4-way set-associative
├── Line Size: 64 bytes
├── Sets: 128
└── Replacement: Pseudo-LRU
```
要求Capacity大小可配置Associativity大小可配置
#### ITLB
```
├── Entries: 32 (Fully associative)
├── Page Size: 4KB (Sv39)
├── Replacement: LRU
└── Support: Mega pages (2MB), Giga pages (1GB)
```
要求Entries大小可配置
### 2.2 乱序执行核心
#### 重命名逻辑
```
Rename Map Table (RMT)
├── Arch Regs: 32 (x0-x31)
├── Phys Regs: 64 (p0-p63)
└── Mapping: arch_reg → phys_reg
Free List
├── Available physical registers
└── FIFO allocation
Committed Map Table (CMT)
└── For precise exception recovery
```
#### Reorder Buffer (ROB)
```
├── Entries: 64
├── Fields per entry:
│ ├── Valid
│ ├── PC
│ ├── Instruction Type
│ ├── Destination Physical Register
│ ├── Old Physical Register (for freeing)
│ ├── Exception Info
│ ├── Completed
│ └── Branch Mispredict
├── Head Pointer (commit)
└── Tail Pointer (allocate)
```
要求Entries可配置
#### Reservation Stations
```
Integer RS
├── Entries: 16
└── ALU, Branch, Integer Multiply/Divide
Load/Store RS
├── Entries: 12
└── Address generation, Memory ops
FP RS
├── Entries: 16
└── FP Add, FP Multiply, FP Divide
```
#### Physical Register File
```
Integer PRF
├── Registers: 64 × 64-bit
└── Read Ports: 4 (dual-issue × 2 operands)
└── Write Ports: 2 (dual-issue)
FP PRF
├── Registers: 64 × 64-bit
└── Read Ports: 4
└── Write Ports: 2
```
### 2.3 执行单元
```
├── ALU0: Integer ALU (ADD, SUB, AND, OR, XOR, SLT, etc.)
├── ALU1: Integer ALU
├── Branch Unit: Branch comparison
├── AGU: Address Generation Unit (Load/Store)
├── MDU: Multiply/Divide Unit (3-cycle latency for MUL, iterative for DIV)
├── FPU0: FP Add/Sub (4-cycle latency)
├── FPU1: FP Multiply (5-cycle latency)
└── FPU2: FP Divide/Sqrt (iterative, variable latency)
```
### 2.4 后端 (Memory Subsystem)
#### DCache
```
├── Capacity: 32 KB
├── Associativity: 8-way set-associative
├── Line Size: 64 bytes
├── Sets: 64
├── Replacement: Pseudo-LRU
├── Write Policy: Write-back
└── MSHR: 4 entries (Miss Status Holding Registers)
```
要求Capacity大小可配置Associativity大小可配置
#### DTLB
```
├── Entries: 32 (Fully associative)
├── Page Size: 4KB (Sv39)
├── Replacement: LRU
└── Support: Mega pages (2MB), Giga pages (1GB)
```
要求Entries大小可配置
#### MMU (Sv39)
```
├── Virtual Address: 39-bit
├── Physical Address: 56-bit (RISC-V spec allows up to 56)
├── Page Table Walker:
│ ├── 3-level page table
│ ├── Hardware page table walk
│ └── PTE cache (8 entries per level)
└── CSR Support:
├── satp (Supervisor Address Translation and Protection)
├── sstatus, sie, sip, stvec
└── sepc, scause, stval, sscratch
```
#### Load/Store Queue
```
Load Queue (LQ)
├── Entries: 16
└── Track in-flight loads
Store Queue (SQ)
├── Entries: 16
├── Store-to-Load forwarding
└── Drain on fence/commit
```
要求Entries大小可配置
### 2.5 提交阶段
```
Commit Logic
├── Commit Width: 2 instructions per cycle
├── In-order commit from ROB head
├── Exception handling:
│ ├── Flush pipeline
│ ├── Restore architectural state from CMT
│ └── Free speculative physical registers
└── Branch mispredict recovery:
├── Flush younger instructions
├── Restore RMT from ROB checkpoint
└── Redirect fetch
```
---
## 3. 数据通路
### 3.1 指令流
```
PC → ITLB → ICache → Instruction Buffer → Decoder (×2) → Rename →
Dispatch → Reservation Stations → Issue → Execute → Write Back → ROB → Commit
```
### 3.2 数据流
```
Read PRF → Execute → Write Result → Broadcast (Bypass) → Write PRF → Update ROB
└→ Wakeup dependent instructions
```
### 3.3 控制流
```
Branch Predictor → Speculative Fetch → Execute Branch → Compare →
[Match: Continue] / [Mispredict: Flush + Redirect]
```
---
## 4. 关键设计决策
### 4.1 双发射策略
- **静态配对**: 在ID阶段检查指令间依赖
- **限制**: 不允许同时发射两条分支指令、两条访存指令
- **对齐**: 指令对必须来自连续的PC
### 4.2 物理寄存器分配
- **64个物理寄存器**: 32个用于映射当前架构状态32个用于投机执行
- **回收时机**: 指令提交时,释放旧的物理寄存器
### 4.3 分支预测恢复
- **检查点机制**: 每条分支在RN阶段创建RMT快照
- **恢复**: 分支错误预测时恢复到该分支的RMT快照
### 4.4 内存一致性
- **顺序模型**: RISC-V Weak Memory Order (RVWMO)
- **Load乱序**: 可以越过Store执行需要地址消歧
- **Store Buffer**: Store指令提交后写入按序释放到Cache
### 4.5 异常处理
- **精确异常**: 通过ROB实现
- **异常类型**:
- Page Fault (ITLB/DTLB miss)
- Illegal Instruction
- Misaligned Access
- Breakpoint
- System Call
---
## 5. 性能参数估算
| 指标 | 目标值 |
|------|--------|
| IPC (理想) | 1.8-2.0 |
| IPC (实际) | 1.2-1.5 (考虑分支错误、Cache miss) |
| 分支预测准确率 | >90% (Gshare) |
| ICache命中率 | >95% |
| DCache命中率 | >90% |
| TLB命中率 | >98% |
| 频率目标 | 100-200 MHz (FPGA) |
---
## 6. Linux启动需求
要成功启动LinuxCPU必须支持
### 6.1 特权级
- **M-mode** (Machine): 最高特权级,处理异常/中断
- **S-mode** (Supervisor): Linux内核运行在此
- **U-mode** (User): 用户程序
### 6.2 必需的CSR
```
Machine Mode:
- mstatus, misa, medeleg, mideleg
- mtvec, mepc, mcause, mtval
- mip, mie, mtime, mtimecmp
Supervisor Mode:
- sstatus, stvec, sepc, scause, stval
- satp (MMU配置)
- sip, sie
Counters:
- cycle, time, instret
```
### 6.3 中断/异常
- Timer interrupt (CLINT)
- External interrupt (PLIC)
- Software interrupt
- 所有RISC-V异常类型
### 6.4 原子指令 (A扩展)
- LR/SC (Load-Reserved/Store-Conditional)
- AMO (Atomic Memory Operations): AMOSWAP, AMOADD, AMOXOR, AMOAND, AMOOR, AMOMIN, AMOMAX
### 6.5 外设接口
- UART (串口输出)
- CLINT (核心本地中断器)
- PLIC (平台级中断控制器)
---
## 7. 验证策略
### 7.1 单元测试
- 每个模块的功能验证
- Chisel Unit Tests
### 7.2 ISA测试
- riscv-tests (官方ISA测试套件)
- rv64ui, rv64um, rv64ua, rv64uf, rv64ud
### 7.3 随机测试
- riscv-torture (随机指令生成)
### 7.4 系统测试
- CoreMark, Dhrystone (性能基准测试)
- Linux boot (最终目标)
---
## 8. Chisel实现模块划分
```
src/main/scala/
├── common/
│ ├── Consts.scala // 常量定义
│ ├── Parameters.scala // 参数配置
│ └── Bundles.scala // 数据结构定义
├── frontend/
│ ├── ICache.scala
│ ├── ITLB.scala
│ ├── BranchPredictor.scala // Gshare
│ ├── BTB.scala
│ ├── RAS.scala
│ └── Frontend.scala // 前端顶层
├── decode/
│ ├── Decoder.scala
│ └── IDStage.scala
├── rename/
│ ├── RenameTable.scala
│ ├── FreeList.scala
│ ├── ROB.scala
│ └── RenameStage.scala
├── issue/
│ ├── ReservationStation.scala
│ ├── IssueQueue.scala
│ └── IssueStage.scala
├── execute/
│ ├── ALU.scala
│ ├── BranchUnit.scala
│ ├── Multiplier.scala
│ ├── Divider.scala
│ ├── FPU.scala
│ └── ExecStage.scala
├── memory/
│ ├── DCache.scala
│ ├── DTLB.scala
│ ├── MMU.scala
│ ├── LSU.scala // Load/Store Unit
│ ├── LoadQueue.scala
│ ├── StoreQueue.scala
│ └── MemStage.scala
├── writeback/
│ └── WriteBackStage.scala
├── commit/
│ └── CommitStage.scala
├── regfile/
│ └── PhysicalRegFile.scala
├── csr/
│ ├── CSRFile.scala
│ └── PrivilegeControl.scala
└── Core.scala // 处理器顶层
```
---
## 9. 参考资料
- **RISC-V Spec**: https://riscv.org/technical/specifications/
- **Sv39 Paging**: RISC-V Privileged Spec Chapter 4
- **Gshare Predictor**: "Two-Level Adaptive Training Branch Prediction" (Yeh & Patt, 1991)
- **BOOM Processor**: Berkeley Out-of-Order Machine (参考设计)
- **Rocket Chip**: 顺序发射RISC-V处理器前端参考

22
build.sbt Normal file
View File

@@ -0,0 +1,22 @@
ThisBuild / scalaVersion := "2.13.18"
ThisBuild / version := "0.1.0"
ThisBuild / organization := "edu.riscv"
val chiselVersion = "7.7.0"
lazy val root = (project in file("."))
.settings(
name := "riscv-ooo-core",
libraryDependencies ++= Seq(
"org.chipsalliance" %% "chisel" % chiselVersion,
"org.scalatest" %% "scalatest" % "3.2.19" % "test",
),
scalacOptions ++= Seq(
"-language:reflectiveCalls",
"-deprecation",
"-feature",
"-Xcheckinit",
"-Ymacro-annotations",
),
addCompilerPlugin("org.chipsalliance" % "chisel-plugin" % chiselVersion cross CrossVersion.full),
)

1
project/build.properties Normal file
View File

@@ -0,0 +1 @@
sbt.version=1.9.7

98
sim/README.md Normal file
View File

@@ -0,0 +1,98 @@
# RISC-V Processor Simulation Environment
## 目录结构
```
sim/
├── verilator/ # Verilator仿真环境
│ ├── testbench.cpp # 主测试平台
│ ├── memory.cpp/h # 内存模型
│ └── Makefile # 构建脚本
├── scripts/ # 测试脚本
│ └── run_tests.sh # 批量测试运行器
└── waves/ # 波形文件输出目录
```
## 快速开始
### 1. 编译测试平台
```bash
cd sim/verilator
make compile
```
这会:
- 从Chisel生成Verilog代码
- 使用Verilator编译C++测试平台
### 2. 运行单个测试
```bash
make run TEST=../../riscv-tests/isa/rv64ui-p-add
```
或直接运行:
```bash
./obj_dir/VCore ../../riscv-tests/isa/rv64ui-p-add
```
### 3. 运行所有测试
```bash
cd ../scripts
./run_tests.sh
```
查看结果:
```bash
cat test_results.txt
```
## 测试结果说明
- **PASS**: 测试通过 (tohost写入1)
- **FAIL**: 测试失败 (tohost写入错误码)
- **TIMEOUT**: 测试超时 (超过100,000周期)
## tohost/fromhost 协议
- **tohost地址**: 0x80001000
- **fromhost地址**: 0x80001040
- **内存基址**: 0x80000000
- **内存大小**: 64MB
测试程序通过向tohost地址写入来报告结果
- `tohost = 1`: 测试通过
- `tohost = (error_code << 1) | 1`: 测试失败
## 依赖项
- Verilator (>= 4.0)
- Scala/SBT (Chisel编译)
- C++14编译器
- libelf-dev (ELF文件解析)
## 故障排查
### 编译错误
如果Verilator编译失败检查
1. Verilog是否正确生成在 `generated/` 目录
2. Core模块的IO接口定义
### 测试超时
如果测试一直超时:
1. 增加 `MAX_CYCLES` 在 testbench.cpp
2. 检查处理器是否正确执行指令
3. 验证内存接口握手逻辑
### 内存访问错误
检查:
1. ELF加载是否成功
2. 内存地址映射是否正确
3. 链接脚本中的地址与 `MEM_BASE` 一致

61
sim/scripts/run_tests.sh Executable file
View File

@@ -0,0 +1,61 @@
#!/bin/bash
TESTBENCH="../verilator/obj_dir/VCore"
TESTS_DIR="../../riscv-tests/isa"
RESULTS_FILE="test_results.txt"
if [ ! -x "$TESTBENCH" ]; then
echo "Error: Testbench not found. Run 'make compile' first."
exit 1
fi
echo "Running RISC-V tests..." > $RESULTS_FILE
echo "====================" >> $RESULTS_FILE
PASS=0
FAIL=0
TIMEOUT=0
for test in $TESTS_DIR/rv64ui-p-* $TESTS_DIR/rv64um-p-* $TESTS_DIR/rv64ua-p-*; do
# Skip dump files
[[ $test == *.dump ]] && continue
[ ! -f "$test" ] && continue
testname=$(basename $test)
echo -n "Running $testname... "
# Run with 5 second timeout
output=$(timeout 5s $TESTBENCH $test 2>&1)
exitcode=$?
if [ $exitcode -eq 0 ]; then
echo "PASS"
echo "$testname: PASS" >> $RESULTS_FILE
((PASS++))
elif [ $exitcode -eq 124 ]; then
echo "TIMEOUT"
echo "$testname: TIMEOUT" >> $RESULTS_FILE
((TIMEOUT++))
else
echo "FAIL (exit code $exitcode)"
echo "$testname: FAIL" >> $RESULTS_FILE
((FAIL++))
fi
done
echo "" >> $RESULTS_FILE
echo "====================" >> $RESULTS_FILE
echo "Summary:" >> $RESULTS_FILE
echo " PASS: $PASS" >> $RESULTS_FILE
echo " FAIL: $FAIL" >> $RESULTS_FILE
echo " TIMEOUT: $TIMEOUT" >> $RESULTS_FILE
echo " TOTAL: $((PASS + FAIL + TIMEOUT))" >> $RESULTS_FILE
echo ""
echo "Summary:"
echo " PASS: $PASS"
echo " FAIL: $FAIL"
echo " TIMEOUT: $TIMEOUT"
echo " TOTAL: $((PASS + FAIL + TIMEOUT))"
echo ""
echo "Results saved to $RESULTS_FILE"

43
sim/verilator/Makefile Normal file
View File

@@ -0,0 +1,43 @@
VERILATOR = verilator
VERILATOR_FLAGS = --cc --exe --build -Wall --trace -Wno-fatal
VERILATOR_FLAGS += -CFLAGS "-std=c++14 -O2"
SBT = env SBT_OPTS="-Dsbt.boot.directory=/tmp/sbt-boot -Dsbt.ivy.home=/tmp/sbt-ivy" COURSIER_CACHE=/tmp/coursier-cache sbt
CHISEL_DIR = ../..
GENERATED_DIR = $(CHISEL_DIR)/generated
SRC_FILES = testbench.cpp memory.cpp
VERILOG_FILES = $(GENERATED_DIR)/Core.sv
TARGET = obj_dir/VCore
.PHONY: all verilog compile run clean
all: compile
verilog:
@echo "Generating Verilog from Chisel..."
cd $(CHISEL_DIR) && $(SBT) "runMain Core"
compile: verilog
@echo "Compiling with Verilator..."
$(VERILATOR) $(VERILATOR_FLAGS) \
-I$(GENERATED_DIR) \
--top-module Core \
-o VCore \
$(VERILOG_FILES) $(SRC_FILES)
run: compile
@if [ -z "$(TEST)" ]; then \
echo "Usage: make run TEST=<path/to/test>"; \
exit 1; \
fi
./$(TARGET) $(TEST)
test-simple: compile
@echo "Running simple test..."
./$(TARGET) ../../riscv-tests/isa/rv64ui-p-simple
clean:
rm -rf obj_dir
rm -rf $(GENERATED_DIR)

111
sim/verilator/memory.cpp Normal file
View File

@@ -0,0 +1,111 @@
#include "memory.h"
#include <cstring>
#include <cstdio>
#include <elf.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
Memory::Memory() : base_addr(MEM_BASE), entry_point(MEM_BASE) {
mem.resize(MEM_SIZE, 0);
}
uint64_t Memory::to_offset(uint64_t addr) {
if (addr < base_addr || addr >= base_addr + MEM_SIZE) {
fprintf(stderr, "Memory access out of bounds: 0x%lx\n", addr);
return 0;
}
return addr - base_addr;
}
uint8_t Memory::read8(uint64_t addr) {
return mem[to_offset(addr)];
}
uint16_t Memory::read16(uint64_t addr) {
uint64_t off = to_offset(addr);
return mem[off] | (mem[off + 1] << 8);
}
uint32_t Memory::read32(uint64_t addr) {
uint64_t off = to_offset(addr);
return mem[off] | (mem[off + 1] << 8) | (mem[off + 2] << 16) | (mem[off + 3] << 24);
}
uint64_t Memory::read64(uint64_t addr) {
uint64_t off = to_offset(addr);
uint64_t lo = read32(addr);
uint64_t hi = read32(addr + 4);
return lo | (hi << 32);
}
void Memory::write8(uint64_t addr, uint8_t data) {
mem[to_offset(addr)] = data;
}
void Memory::write16(uint64_t addr, uint16_t data) {
uint64_t off = to_offset(addr);
mem[off] = data & 0xFF;
mem[off + 1] = (data >> 8) & 0xFF;
}
void Memory::write32(uint64_t addr, uint32_t data) {
uint64_t off = to_offset(addr);
mem[off] = data & 0xFF;
mem[off + 1] = (data >> 8) & 0xFF;
mem[off + 2] = (data >> 16) & 0xFF;
mem[off + 3] = (data >> 24) & 0xFF;
}
void Memory::write64(uint64_t addr, uint64_t data) {
write32(addr, data & 0xFFFFFFFF);
write32(addr + 4, (data >> 32) & 0xFFFFFFFF);
}
bool Memory::load_elf(const std::string& filename) {
int fd = open(filename.c_str(), O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Failed to open ELF file: %s\n", filename.c_str());
return false;
}
Elf64_Ehdr ehdr;
if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
fprintf(stderr, "Failed to read ELF header\n");
close(fd);
return false;
}
if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) {
fprintf(stderr, "Not a valid ELF file\n");
close(fd);
return false;
}
entry_point = ehdr.e_entry;
// Load program headers
for (int i = 0; i < ehdr.e_phnum; i++) {
Elf64_Phdr phdr;
lseek(fd, ehdr.e_phoff + i * sizeof(phdr), SEEK_SET);
if (read(fd, &phdr, sizeof(phdr)) != sizeof(phdr)) {
fprintf(stderr, "Failed to read program header %d\n", i);
continue;
}
if (phdr.p_type == PT_LOAD) {
uint64_t offset = to_offset(phdr.p_paddr);
lseek(fd, phdr.p_offset, SEEK_SET);
if (read(fd, &mem[offset], phdr.p_filesz) != (ssize_t)phdr.p_filesz) {
fprintf(stderr, "Failed to load segment %d\n", i);
close(fd);
return false;
}
printf("Loaded segment: paddr=0x%lx size=%lu\n", phdr.p_paddr, phdr.p_filesz);
}
}
close(fd);
printf("ELF loaded: entry=0x%lx\n", entry_point);
return true;
}

40
sim/verilator/memory.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef MEMORY_H
#define MEMORY_H
#include <cstdint>
#include <vector>
#include <string>
#define TOHOST_ADDR 0x80001000ULL
#define FROMHOST_ADDR 0x80001040ULL
#define MEM_BASE 0x80000000ULL
#define MEM_SIZE (64 * 1024 * 1024) // 64MB
class Memory {
private:
std::vector<uint8_t> mem;
uint64_t base_addr;
public:
Memory();
bool load_elf(const std::string& filename);
uint8_t read8(uint64_t addr);
uint16_t read16(uint64_t addr);
uint32_t read32(uint64_t addr);
uint64_t read64(uint64_t addr);
void write8(uint64_t addr, uint8_t data);
void write16(uint64_t addr, uint16_t data);
void write32(uint64_t addr, uint32_t data);
void write64(uint64_t addr, uint64_t data);
uint64_t get_entry_point() const { return entry_point; }
private:
uint64_t entry_point;
uint64_t to_offset(uint64_t addr);
};
#endif

101
sim/verilator/testbench.cpp Normal file
View File

@@ -0,0 +1,101 @@
#include <verilated.h>
#include "VCore.h"
#include "memory.h"
#include <cstdio>
#include <cstdlib>
#define MAX_CYCLES 100000
int main(int argc, char** argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <test.elf>\n", argv[0]);
return 1;
}
Verilated::commandArgs(argc, argv);
VCore* core = new VCore;
Memory* mem = new Memory();
if (!mem->load_elf(argv[1])) {
fprintf(stderr, "Failed to load test binary\n");
return 1;
}
// Reset
core->reset = 1;
core->clock = 0;
core->eval();
core->clock = 1;
core->eval();
core->clock = 0;
core->reset = 0;
core->eval();
uint64_t cycle = 0;
bool test_done = false;
int exit_code = 0;
while (cycle < MAX_CYCLES && !test_done) {
// Handle instruction memory interface
if (core->io_imem_req_valid) {
uint64_t pc = core->io_imem_req_bits;
core->io_imem_resp_valid = 1;
core->io_imem_resp_bits_0 = mem->read32(pc);
core->io_imem_resp_bits_1 = mem->read32(pc + 4);
} else {
core->io_imem_resp_valid = 0;
}
// Handle data memory interface
if (core->io_dmem_req_valid) {
uint64_t addr = core->io_dmem_req_bits_addr;
// Check for tohost write
if (core->io_dmem_req_bits_isStore && addr == TOHOST_ADDR) {
uint64_t tohost = core->io_dmem_req_bits_data;
if (tohost == 1) {
printf("[%lu] TEST PASSED\n", cycle);
test_done = true;
exit_code = 0;
} else if (tohost & 1) {
printf("[%lu] TEST FAILED: error code %lu\n", cycle, tohost >> 1);
test_done = true;
exit_code = 1;
}
}
if (core->io_dmem_req_bits_isStore) {
switch (core->io_dmem_req_bits_size) {
case 0: mem->write8(addr, core->io_dmem_req_bits_data & 0xff); break;
case 1: mem->write16(addr, core->io_dmem_req_bits_data & 0xffff); break;
case 2: mem->write32(addr, core->io_dmem_req_bits_data & 0xffffffff); break;
default: mem->write64(addr, core->io_dmem_req_bits_data); break;
}
core->io_dmem_resp_valid = 0;
} else {
core->io_dmem_resp_bits = mem->read64(addr);
core->io_dmem_resp_valid = 1;
}
} else {
core->io_dmem_resp_valid = 0;
}
// Clock cycle
core->clock = 0;
core->eval();
core->clock = 1;
core->eval();
cycle++;
}
if (!test_done) {
printf("[%lu] TEST TIMEOUT\n", cycle);
exit_code = 2;
}
delete core;
delete mem;
return exit_code;
}

173
src/main/scala/Core.scala Normal file
View File

@@ -0,0 +1,173 @@
import chisel3._
import chisel3.util._
import _root_.circt.stage.ChiselStage
class Core(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val imem_req_valid = Output(Bool())
val imem_req_bits = Output(UInt(p.xlen.W))
val imem_resp_valid = Input(Bool())
val imem_resp_bits_0 = Input(UInt(32.W))
val imem_resp_bits_1 = Input(UInt(32.W))
val dmem_req_valid = Output(Bool())
val dmem_req_bits_addr = Output(UInt(p.xlen.W))
val dmem_req_bits_data = Output(UInt(p.xlen.W))
val dmem_req_bits_isStore = Output(Bool())
val dmem_req_bits_size = Output(UInt(3.W))
val dmem_resp_valid = Input(Bool())
val dmem_resp_bits = Input(UInt(p.xlen.W))
})
val sFetch :: sExec :: sLoadWait :: Nil = Enum(3)
val state = RegInit(sFetch)
val pc = RegInit(Consts.ResetVector)
val instReg = RegInit(0.U(32.W))
val pcReg = RegInit(Consts.ResetVector)
val regs = RegInit(VecInit(Seq.fill(32)(0.U(p.xlen.W))))
val decoder = Module(new Decoder(p))
decoder.io.pc := pcReg
decoder.io.inst := instReg
val dec = decoder.io.out
val alu = Module(new ALU(p))
val branch = Module(new BranchUnit(p))
val csr = Module(new CSRFile(p))
def regRead(addr: UInt): UInt = Mux(addr === 0.U, 0.U, regs(addr))
def lowLoad(data: UInt, size: UInt, signed: Bool): UInt = {
val b = data(7, 0)
val h = data(15, 0)
val w = data(31, 0)
MuxLookup(size, data)(Seq(
0.U -> Mux(signed, Consts.signExtend(b, 8), b),
1.U -> Mux(signed, Consts.signExtend(h, 16), h),
2.U -> Mux(signed, Consts.signExtend(w, 32), w),
3.U -> data
))
}
val src1 = regRead(dec.rs1)
val src2 = regRead(dec.rs2)
val aluB = Mux(dec.isOpImm || dec.isJalr || dec.isLoad, dec.immI, src2)
alu.io.fn := dec.aluFn
alu.io.a := src1
alu.io.b := aluB
alu.io.isWord := dec.isWord
branch.io.funct3 := dec.funct3
branch.io.a := src1
branch.io.b := src2
csr.io.cmd.valid := state === sExec && dec.isSystem && dec.funct3 =/= 0.U &&
!(dec.funct3(1) && dec.rs1 === 0.U)
csr.io.cmd.addr := instReg(31, 20)
csr.io.cmd.cmd := dec.funct3
csr.io.cmd.rs1 := src1
csr.io.cmd.zimm := dec.rs1
val isEcall = instReg === "h00000073".U
val isEbreak = instReg === "h00100073".U
val isMret = instReg === "h30200073".U
val takeTrap = state === sExec && (isEcall || isEbreak)
csr.io.trap := takeTrap
csr.io.trapPc := pcReg
csr.io.trapCause := Mux(isEbreak, 3.U, 11.U)
val loadAddr = src1 + dec.immI
val storeAddr = src1 + dec.immS
val pendingLoadAddr = Reg(UInt(p.xlen.W))
val pendingLoadRd = Reg(UInt(5.W))
val pendingLoadSize = Reg(UInt(3.W))
val pendingLoadSigned = Reg(Bool())
val isAmo = instReg(6, 0) === "b0101111".U
val amoOp = instReg(31, 27)
val amoLoadLike = isAmo && amoOp === "b00010".U
val amoStoreLike = isAmo && amoOp === "b00011".U
val memReqInExec = state === sExec && (dec.isLoad || dec.isStore)
io.imem_req_valid := state === sFetch
io.imem_req_bits := pc
io.dmem_req_valid := memReqInExec || state === sLoadWait
io.dmem_req_bits_addr := Mux(state === sLoadWait, pendingLoadAddr, Mux(dec.isStore, storeAddr, loadAddr))
io.dmem_req_bits_data := src2
io.dmem_req_bits_isStore := state === sExec && Mux(isAmo, !amoLoadLike, dec.isStore)
io.dmem_req_bits_size := Mux(state === sLoadWait, pendingLoadSize, dec.memWidth)
val branchTarget = pcReg + dec.immB
val jalTarget = pcReg + dec.immJ
val jalrTarget = (src1 + dec.immI) & (~1.U(p.xlen.W))
val branchTaken = dec.isBranch && branch.io.taken
val nextPc = Mux(dec.isJal, jalTarget,
Mux(dec.isJalr, jalrTarget,
Mux(branchTaken, branchTarget, pcReg + 4.U)))
val execWriteData = WireDefault(alu.io.out)
when(dec.isLui) {
execWriteData := dec.immU
}.elsewhen(dec.isAuipc) {
execWriteData := pcReg + dec.immU
}.elsewhen(dec.isJal || dec.isJalr) {
execWriteData := pcReg + 4.U
}.elsewhen(dec.isSystem && dec.funct3 =/= 0.U) {
execWriteData := csr.io.rdata
}
when(state === sFetch) {
when(io.imem_resp_valid) {
instReg := io.imem_resp_bits_0
pcReg := pc
state := sExec
}
}.elsewhen(state === sExec) {
when(takeTrap) {
pc := csr.io.mtvec
state := sFetch
}.elsewhen(isMret) {
pc := csr.io.mepc
state := sFetch
}.elsewhen(dec.isLoad && !amoStoreLike) {
when(io.dmem_resp_valid) {
when(dec.rd =/= 0.U) {
regs(dec.rd) := lowLoad(io.dmem_resp_bits, dec.memWidth, dec.memSigned)
}
pc := pcReg + 4.U
state := sFetch
}.otherwise {
pendingLoadAddr := loadAddr
pendingLoadRd := dec.rd
pendingLoadSize := dec.memWidth
pendingLoadSigned := dec.memSigned
state := sLoadWait
}
}.elsewhen(dec.isStore || amoStoreLike) {
pc := pcReg + 4.U
state := sFetch
}.otherwise {
when(dec.writesRd && dec.rd =/= 0.U) {
regs(dec.rd) := execWriteData
}
pc := nextPc
state := sFetch
}
}.elsewhen(state === sLoadWait) {
when(io.dmem_resp_valid) {
when(pendingLoadRd =/= 0.U) {
regs(pendingLoadRd) := lowLoad(io.dmem_resp_bits, pendingLoadSize, pendingLoadSigned)
}
pc := pcReg + 4.U
state := sFetch
}
}
}
object Core extends App {
ChiselStage.emitSystemVerilogFile(
new Core(),
args = Array("--target-dir", "generated"),
firtoolOpts = Array("-disable-all-randomization", "-strip-debug-info")
)
}

View File

@@ -0,0 +1,18 @@
import chisel3._
import chisel3.util._
class CommitStage(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val robValid = Input(Bool())
val robEntry = Input(new RobEntry(p))
val commitReady = Output(Bool())
val freeOldPhys = Output(Bool())
val oldPhys = Output(UInt(log2Ceil(p.physRegs).W))
val flush = Output(Bool())
})
io.commitReady := io.robValid
io.freeOldPhys := io.robValid && io.robEntry.oldDest =/= io.robEntry.dest
io.oldPhys := io.robEntry.oldDest
io.flush := io.robValid && (io.robEntry.exception || io.robEntry.branchMispredict)
}

View File

@@ -0,0 +1,76 @@
import chisel3._
import chisel3.util._
class FetchPacket(p: CoreParams = CoreParams()) extends Bundle {
val pc = UInt(p.xlen.W)
val inst = Vec(p.fetchWidth, UInt(32.W))
val predictedTaken = Bool()
val predictedTarget = UInt(p.xlen.W)
}
class BranchUpdate(p: CoreParams = CoreParams()) extends Bundle {
val valid = Bool()
val pc = UInt(p.xlen.W)
val taken = Bool()
val target = UInt(p.xlen.W)
val isCall = Bool()
val isReturn = Bool()
}
class DecodedInst(p: CoreParams = CoreParams()) extends Bundle {
val valid = Bool()
val pc = UInt(p.xlen.W)
val inst = UInt(32.W)
val rs1 = UInt(5.W)
val rs2 = UInt(5.W)
val rd = UInt(5.W)
val funct3 = UInt(3.W)
val funct7 = UInt(7.W)
val immI = UInt(p.xlen.W)
val immS = UInt(p.xlen.W)
val immB = UInt(p.xlen.W)
val immU = UInt(p.xlen.W)
val immJ = UInt(p.xlen.W)
val opClass = UInt(Consts.OpClassWidth.W)
val aluFn = UInt(5.W)
val memWidth = UInt(3.W)
val memSigned = Bool()
val isLoad = Bool()
val isStore = Bool()
val isBranch = Bool()
val isJal = Bool()
val isJalr = Bool()
val isLui = Bool()
val isAuipc = Bool()
val isOpImm = Bool()
val isOp = Bool()
val isWord = Bool()
val isSystem = Bool()
val writesRd = Bool()
val illegal = Bool()
}
class RenamePacket(p: CoreParams = CoreParams()) extends Bundle {
val valid = Bool()
val decoded = new DecodedInst(p)
val prs1 = UInt(log2Ceil(p.physRegs).W)
val prs2 = UInt(log2Ceil(p.physRegs).W)
val prd = UInt(log2Ceil(p.physRegs).W)
val oldPrd = UInt(log2Ceil(p.physRegs).W)
val robIdx = UInt(log2Ceil(p.robEntries).W)
}
class MemRequest(p: CoreParams = CoreParams()) extends Bundle {
val addr = UInt(p.xlen.W)
val data = UInt(p.xlen.W)
val isStore = Bool()
val size = UInt(3.W)
}
class CsrCommand(p: CoreParams = CoreParams()) extends Bundle {
val valid = Bool()
val addr = UInt(12.W)
val cmd = UInt(3.W)
val rs1 = UInt(p.xlen.W)
val zimm = UInt(5.W)
}

View File

@@ -0,0 +1,35 @@
import chisel3._
import chisel3.util._
object Consts {
val ResetVector = "h80000000".U(64.W)
val OpClassWidth = 4
val OP_NONE = 0.U(OpClassWidth.W)
val OP_ALU = 1.U(OpClassWidth.W)
val OP_BRANCH = 2.U(OpClassWidth.W)
val OP_LOAD = 3.U(OpClassWidth.W)
val OP_STORE = 4.U(OpClassWidth.W)
val OP_SYSTEM = 5.U(OpClassWidth.W)
val OP_FP = 6.U(OpClassWidth.W)
val ALU_ADD = 0.U(5.W)
val ALU_SUB = 1.U(5.W)
val ALU_SLL = 2.U(5.W)
val ALU_SLT = 3.U(5.W)
val ALU_SLTU = 4.U(5.W)
val ALU_XOR = 5.U(5.W)
val ALU_SRL = 6.U(5.W)
val ALU_SRA = 7.U(5.W)
val ALU_OR = 8.U(5.W)
val ALU_AND = 9.U(5.W)
val ALU_MUL = 10.U(5.W)
val ALU_DIV = 11.U(5.W)
val ALU_DIVU = 12.U(5.W)
val ALU_REM = 13.U(5.W)
val ALU_REMU = 14.U(5.W)
val ALU_COPY_B = 15.U(5.W)
def signExtend(value: UInt, from: Int, to: Int = 64): UInt =
Cat(Fill(to - from, value(from - 1)), value(from - 1, 0))
}

View File

@@ -0,0 +1,28 @@
case class CoreParams(
xlen: Int = 64,
fetchWidth: Int = 2,
issueWidth: Int = 2,
archRegs: Int = 32,
physRegs: Int = 64,
robEntries: Int = 64,
intRsEntries: Int = 16,
lsuRsEntries: Int = 12,
fpRsEntries: Int = 16,
loadQueueEntries: Int = 16,
storeQueueEntries: Int = 16,
btbEntries: Int = 512,
rasEntries: Int = 16,
ghrBits: Int = 12,
iCacheBytes: Int = 32 * 1024,
iCacheWays: Int = 4,
dCacheBytes: Int = 32 * 1024,
dCacheWays: Int = 8,
cacheLineBytes: Int = 64,
itlbEntries: Int = 32,
dtlbEntries: Int = 32
) {
require(xlen == 64, "this implementation targets RV64")
require(fetchWidth == 2, "frontend is parameterized around dual fetch")
require(issueWidth == 2, "backend structures are parameterized around dual issue")
}

View File

@@ -0,0 +1,99 @@
import chisel3._
import chisel3.util._
class CSRFile(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val cmd = Input(new CsrCommand(p))
val rdata = Output(UInt(p.xlen.W))
val trap = Input(Bool())
val trapPc = Input(UInt(p.xlen.W))
val trapCause = Input(UInt(p.xlen.W))
val satp = Output(UInt(p.xlen.W))
val mtvec = Output(UInt(p.xlen.W))
val mepc = Output(UInt(p.xlen.W))
})
val cycle = RegInit(0.U(p.xlen.W))
val instret = RegInit(0.U(p.xlen.W))
val mstatus = RegInit(0.U(p.xlen.W))
val misa = RegInit("h800000000014112d".U(p.xlen.W))
val mtvecReg = RegInit(0.U(p.xlen.W))
val mepcReg = RegInit(0.U(p.xlen.W))
val mcause = RegInit(0.U(p.xlen.W))
val mtval = RegInit(0.U(p.xlen.W))
val medeleg = RegInit(0.U(p.xlen.W))
val mideleg = RegInit(0.U(p.xlen.W))
val mie = RegInit(0.U(p.xlen.W))
val mip = RegInit(0.U(p.xlen.W))
val sstatus = RegInit(0.U(p.xlen.W))
val stvec = RegInit(0.U(p.xlen.W))
val sepc = RegInit(0.U(p.xlen.W))
val scause = RegInit(0.U(p.xlen.W))
val stval = RegInit(0.U(p.xlen.W))
val sscratch = RegInit(0.U(p.xlen.W))
val satpReg = RegInit(0.U(p.xlen.W))
cycle := cycle + 1.U
io.satp := satpReg
io.mtvec := mtvecReg
io.mepc := mepcReg
val r = WireDefault(0.U(p.xlen.W))
switch(io.cmd.addr) {
is("h300".U) { r := mstatus }
is("h301".U) { r := misa }
is("h302".U) { r := medeleg }
is("h303".U) { r := mideleg }
is("h304".U) { r := mie }
is("h305".U) { r := mtvecReg }
is("h341".U) { r := mepcReg }
is("h342".U) { r := mcause }
is("h343".U) { r := mtval }
is("h344".U) { r := mip }
is("h100".U) { r := sstatus }
is("h105".U) { r := stvec }
is("h140".U) { r := sscratch }
is("h141".U) { r := sepc }
is("h142".U) { r := scause }
is("h143".U) { r := stval }
is("h180".U) { r := satpReg }
is("hf14".U) { r := 0.U }
is("hc00".U) { r := cycle }
is("hc01".U) { r := 0.U }
is("hc02".U) { r := instret }
}
io.rdata := r
val operand = Mux(io.cmd.cmd(2), io.cmd.zimm, io.cmd.rs1)
val next = MuxLookup(io.cmd.cmd(1, 0), r)(Seq(
1.U -> operand,
2.U -> (r | operand),
3.U -> (r & ~operand)
))
when(io.cmd.valid && io.cmd.cmd =/= 0.U) {
switch(io.cmd.addr) {
is("h300".U) { mstatus := next }
is("h302".U) { medeleg := next }
is("h303".U) { mideleg := next }
is("h304".U) { mie := next }
is("h305".U) { mtvecReg := next }
is("h341".U) { mepcReg := next }
is("h342".U) { mcause := next }
is("h343".U) { mtval := next }
is("h344".U) { mip := next }
is("h100".U) { sstatus := next }
is("h105".U) { stvec := next }
is("h140".U) { sscratch := next }
is("h141".U) { sepc := next }
is("h142".U) { scause := next }
is("h143".U) { stval := next }
is("h180".U) { satpReg := next }
}
}
when(io.trap) {
mepcReg := io.trapPc
mcause := io.trapCause
}
}

View File

@@ -0,0 +1,21 @@
import chisel3._
class PrivilegeControl(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val trap = Input(Bool())
val mret = Input(Bool())
val sret = Input(Bool())
val privilege = Output(UInt(2.W))
})
val mode = RegInit(3.U(2.W))
when(io.trap) {
mode := 3.U
}.elsewhen(io.mret) {
mode := 0.U
}.elsewhen(io.sret) {
mode := 0.U
}
io.privilege := mode
}

View File

@@ -0,0 +1,150 @@
import chisel3._
import chisel3.util._
class Decoder(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val pc = Input(UInt(p.xlen.W))
val inst = Input(UInt(32.W))
val out = Output(new DecodedInst(p))
})
val opcode = io.inst(6, 0)
val rd = io.inst(11, 7)
val funct3 = io.inst(14, 12)
val rs1 = io.inst(19, 15)
val rs2 = io.inst(24, 20)
val funct7 = io.inst(31, 25)
val immI = Consts.signExtend(io.inst(31, 20), 12)
val immS = Consts.signExtend(Cat(io.inst(31, 25), io.inst(11, 7)), 12)
val immB = Consts.signExtend(Cat(io.inst(31), io.inst(7), io.inst(30, 25), io.inst(11, 8), 0.U(1.W)), 13)
val immU = Consts.signExtend(Cat(io.inst(31, 12), 0.U(12.W)), 32)
val immJ = Consts.signExtend(Cat(io.inst(31), io.inst(19, 12), io.inst(20), io.inst(30, 21), 0.U(1.W)), 21)
val d = WireDefault(0.U.asTypeOf(new DecodedInst(p)))
d.valid := true.B
d.pc := io.pc
d.inst := io.inst
d.rs1 := rs1
d.rs2 := rs2
d.rd := rd
d.funct3 := funct3
d.funct7 := funct7
d.immI := immI
d.immS := immS
d.immB := immB
d.immU := immU
d.immJ := immJ
d.memWidth := MuxLookup(funct3, 3.U)(Seq(
"b000".U -> 0.U,
"b001".U -> 1.U,
"b010".U -> 2.U,
"b011".U -> 3.U,
"b100".U -> 0.U,
"b101".U -> 1.U,
"b110".U -> 2.U
))
d.memSigned := !funct3(2)
d.illegal := false.B
switch(opcode) {
is("b0110111".U) {
d.isLui := true.B
d.writesRd := rd =/= 0.U
d.opClass := Consts.OP_ALU
d.aluFn := Consts.ALU_COPY_B
}
is("b0010111".U) {
d.isAuipc := true.B
d.writesRd := rd =/= 0.U
d.opClass := Consts.OP_ALU
d.aluFn := Consts.ALU_ADD
}
is("b1101111".U) {
d.isJal := true.B
d.writesRd := rd =/= 0.U
d.opClass := Consts.OP_BRANCH
}
is("b1100111".U) {
d.isJalr := true.B
d.writesRd := rd =/= 0.U
d.opClass := Consts.OP_BRANCH
}
is("b1100011".U) {
d.isBranch := true.B
d.opClass := Consts.OP_BRANCH
}
is("b0000011".U) {
d.isLoad := true.B
d.writesRd := rd =/= 0.U
d.opClass := Consts.OP_LOAD
}
is("b0100011".U) {
d.isStore := true.B
d.opClass := Consts.OP_STORE
}
is("b0010011".U, "b0011011".U) {
d.isOpImm := true.B
d.isWord := opcode === "b0011011".U
d.writesRd := rd =/= 0.U
d.opClass := Consts.OP_ALU
d.aluFn := MuxLookup(funct3, Consts.ALU_ADD)(Seq(
"b000".U -> Consts.ALU_ADD,
"b001".U -> Consts.ALU_SLL,
"b010".U -> Consts.ALU_SLT,
"b011".U -> Consts.ALU_SLTU,
"b100".U -> Consts.ALU_XOR,
"b101".U -> Mux(funct7(5), Consts.ALU_SRA, Consts.ALU_SRL),
"b110".U -> Consts.ALU_OR,
"b111".U -> Consts.ALU_AND
))
}
is("b0110011".U, "b0111011".U) {
d.isOp := true.B
d.isWord := opcode === "b0111011".U
d.writesRd := rd =/= 0.U
d.opClass := Consts.OP_ALU
d.aluFn := Mux(funct7 === "b0000001".U, MuxLookup(funct3, Consts.ALU_MUL)(Seq(
"b000".U -> Consts.ALU_MUL,
"b100".U -> Consts.ALU_DIV,
"b101".U -> Consts.ALU_DIVU,
"b110".U -> Consts.ALU_REM,
"b111".U -> Consts.ALU_REMU
)), MuxLookup(funct3, Consts.ALU_ADD)(Seq(
"b000".U -> Mux(funct7(5), Consts.ALU_SUB, Consts.ALU_ADD),
"b001".U -> Consts.ALU_SLL,
"b010".U -> Consts.ALU_SLT,
"b011".U -> Consts.ALU_SLTU,
"b100".U -> Consts.ALU_XOR,
"b101".U -> Mux(funct7(5), Consts.ALU_SRA, Consts.ALU_SRL),
"b110".U -> Consts.ALU_OR,
"b111".U -> Consts.ALU_AND
)))
}
is("b0001111".U) {
d.opClass := Consts.OP_SYSTEM
}
is("b1110011".U) {
d.isSystem := true.B
d.writesRd := rd =/= 0.U && funct3 =/= 0.U
d.opClass := Consts.OP_SYSTEM
}
is("b0101111".U) {
d.isLoad := true.B
d.isStore := true.B
d.writesRd := rd =/= 0.U
d.memWidth := Mux(funct3 === "b010".U, 2.U, 3.U)
d.opClass := Consts.OP_LOAD
}
}
when(opcode =/= "b0110111".U && opcode =/= "b0010111".U && opcode =/= "b1101111".U &&
opcode =/= "b1100111".U && opcode =/= "b1100011".U && opcode =/= "b0000011".U &&
opcode =/= "b0100011".U && opcode =/= "b0010011".U && opcode =/= "b0011011".U &&
opcode =/= "b0110011".U && opcode =/= "b0111011".U && opcode =/= "b0001111".U &&
opcode =/= "b1110011".U && opcode =/= "b0101111".U) {
d.illegal := true.B
}
io.out := d
}

View File

@@ -0,0 +1,20 @@
import chisel3._
class IDStage(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val inValid = Input(Bool())
val in = Input(new FetchPacket(p))
val outValid = Output(Vec(p.fetchWidth, Bool()))
val out = Output(Vec(p.fetchWidth, new DecodedInst(p)))
})
val decoders = Seq.fill(p.fetchWidth)(Module(new Decoder(p)))
for (i <- 0 until p.fetchWidth) {
decoders(i).io.pc := io.in.pc + (4 * i).U
decoders(i).io.inst := io.in.inst(i)
io.out(i) := decoders(i).io.out
io.out(i).valid := io.inValid
io.outValid(i) := io.inValid
}
}

View File

@@ -0,0 +1,36 @@
import chisel3._
import chisel3.util._
class ALU(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val fn = Input(UInt(5.W))
val a = Input(UInt(p.xlen.W))
val b = Input(UInt(p.xlen.W))
val isWord = Input(Bool())
val out = Output(UInt(p.xlen.W))
})
val shamt = Mux(io.isWord, io.b(4, 0), io.b(5, 0))
val raw = WireDefault(0.U(p.xlen.W))
switch(io.fn) {
is(Consts.ALU_ADD) { raw := io.a + io.b }
is(Consts.ALU_SUB) { raw := io.a - io.b }
is(Consts.ALU_SLL) { raw := io.a << shamt }
is(Consts.ALU_SLT) { raw := (io.a.asSInt < io.b.asSInt).asUInt }
is(Consts.ALU_SLTU) { raw := io.a < io.b }
is(Consts.ALU_XOR) { raw := io.a ^ io.b }
is(Consts.ALU_SRL) { raw := io.a >> shamt }
is(Consts.ALU_SRA) { raw := (io.a.asSInt >> shamt).asUInt }
is(Consts.ALU_OR) { raw := io.a | io.b }
is(Consts.ALU_AND) { raw := io.a & io.b }
is(Consts.ALU_MUL) { raw := (io.a * io.b)(p.xlen - 1, 0) }
is(Consts.ALU_DIV) { raw := Mux(io.b === 0.U, Fill(p.xlen, 1.U), (io.a.asSInt / io.b.asSInt).asUInt) }
is(Consts.ALU_DIVU) { raw := Mux(io.b === 0.U, Fill(p.xlen, 1.U), io.a / io.b) }
is(Consts.ALU_REM) { raw := Mux(io.b === 0.U, io.a, (io.a.asSInt % io.b.asSInt).asUInt) }
is(Consts.ALU_REMU) { raw := Mux(io.b === 0.U, io.a, io.a % io.b) }
is(Consts.ALU_COPY_B) { raw := io.b }
}
io.out := Mux(io.isWord, Consts.signExtend(raw(31, 0), 32), raw)
}

View File

@@ -0,0 +1,21 @@
import chisel3._
import chisel3.util._
class BranchUnit(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val funct3 = Input(UInt(3.W))
val a = Input(UInt(p.xlen.W))
val b = Input(UInt(p.xlen.W))
val taken = Output(Bool())
})
io.taken := MuxLookup(io.funct3, false.B)(Seq(
"b000".U -> (io.a === io.b),
"b001".U -> (io.a =/= io.b),
"b100".U -> (io.a.asSInt < io.b.asSInt),
"b101".U -> (io.a.asSInt >= io.b.asSInt),
"b110".U -> (io.a < io.b),
"b111".U -> (io.a >= io.b)
))
}

View File

@@ -0,0 +1,14 @@
import chisel3._
import chisel3.util._
class Divider(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val signed = Input(Bool())
val a = Input(UInt(p.xlen.W))
val b = Input(UInt(p.xlen.W))
val quotient = Output(UInt(p.xlen.W))
val remainder = Output(UInt(p.xlen.W))
})
io.quotient := Mux(io.b === 0.U, Fill(p.xlen, 1.U), Mux(io.signed, (io.a.asSInt / io.b.asSInt).asUInt, io.a / io.b))
io.remainder := Mux(io.b === 0.U, io.a, Mux(io.signed, (io.a.asSInt % io.b.asSInt).asUInt, io.a % io.b))
}

View File

@@ -0,0 +1,29 @@
import chisel3._
class ExecStage(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val inValid = Input(Bool())
val in = Input(new DecodedInst(p))
val src1 = Input(UInt(p.xlen.W))
val src2 = Input(UInt(p.xlen.W))
val outValid = Output(Bool())
val result = Output(UInt(p.xlen.W))
val branchTaken = Output(Bool())
})
val alu = Module(new ALU(p))
val branch = Module(new BranchUnit(p))
alu.io.fn := io.in.aluFn
alu.io.a := io.src1
alu.io.b := io.src2
alu.io.isWord := io.in.isWord
branch.io.funct3 := io.in.funct3
branch.io.a := io.src1
branch.io.b := io.src2
io.outValid := io.inValid
io.result := alu.io.out
io.branchTaken := branch.io.taken
}

View File

@@ -0,0 +1,16 @@
import chisel3._
class FPU(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val inValid = Input(Bool())
val op = Input(UInt(4.W))
val a = Input(UInt(p.xlen.W))
val b = Input(UInt(p.xlen.W))
val outValid = Output(Bool())
val out = Output(UInt(p.xlen.W))
})
io.outValid := io.inValid
io.out := 0.U
}

View File

@@ -0,0 +1,11 @@
import chisel3._
class Multiplier(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val a = Input(UInt(p.xlen.W))
val b = Input(UInt(p.xlen.W))
val out = Output(UInt(p.xlen.W))
})
io.out := (io.a * io.b)(p.xlen - 1, 0)
}

View File

@@ -0,0 +1,32 @@
import chisel3._
import chisel3.util._
class BTB(p: CoreParams = CoreParams()) extends Module {
private val idxBits = log2Ceil(p.btbEntries)
private val tagBits = 20
val io = IO(new Bundle {
val pc = Input(UInt(p.xlen.W))
val hit = Output(Bool())
val target = Output(UInt(p.xlen.W))
val update = Input(new BranchUpdate(p))
})
val valid = RegInit(VecInit(Seq.fill(p.btbEntries)(false.B)))
val tags = Reg(Vec(p.btbEntries, UInt(tagBits.W)))
val targets = Reg(Vec(p.btbEntries, UInt(p.xlen.W)))
val idx = io.pc(idxBits + 1, 2)
val tag = io.pc(idxBits + tagBits + 1, idxBits + 2)
io.hit := valid(idx) && tags(idx) === tag
io.target := targets(idx)
when(io.update.valid && io.update.taken) {
val uidx = io.update.pc(idxBits + 1, 2)
valid(uidx) := true.B
tags(uidx) := io.update.pc(idxBits + tagBits + 1, idxBits + 2)
targets(uidx) := io.update.target
}
}

View File

@@ -0,0 +1,38 @@
import chisel3._
import chisel3.util._
class BranchPredictor(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val pc = Input(UInt(p.xlen.W))
val taken = Output(Bool())
val target = Output(UInt(p.xlen.W))
val update = Input(new BranchUpdate(p))
})
val ghr = RegInit(0.U(p.ghrBits.W))
val pht = RegInit(VecInit(Seq.fill(1 << p.ghrBits)(1.U(2.W))))
val btb = Module(new BTB(p))
val ras = Module(new RAS(p))
val idx = io.pc(p.ghrBits + 1, 2) ^ ghr
btb.io.pc := io.pc
btb.io.update := io.update
ras.io.push := io.update.valid && io.update.isCall
ras.io.pop := io.update.valid && io.update.isReturn
ras.io.pushAddr := io.update.pc + 4.U
io.taken := pht(idx)(1) && btb.io.hit
io.target := Mux(io.update.isReturn && !ras.io.empty, ras.io.top, btb.io.target)
when(io.update.valid) {
val uidx = io.update.pc(p.ghrBits + 1, 2) ^ ghr
when(io.update.taken && pht(uidx) =/= 3.U) {
pht(uidx) := pht(uidx) + 1.U
}.elsewhen(!io.update.taken && pht(uidx) =/= 0.U) {
pht(uidx) := pht(uidx) - 1.U
}
ghr := Cat(ghr(p.ghrBits - 2, 0), io.update.taken)
}
}

View File

@@ -0,0 +1,43 @@
import chisel3._
class Frontend(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val redirectValid = Input(Bool())
val redirectPc = Input(UInt(p.xlen.W))
val imemReqValid = Output(Bool())
val imemReqAddr = Output(UInt(p.xlen.W))
val imemRespValid = Input(Bool())
val imemRespBits = Input(Vec(p.fetchWidth, UInt(32.W)))
val outValid = Output(Bool())
val out = Output(new FetchPacket(p))
val branchUpdate = Input(new BranchUpdate(p))
})
val pc = RegInit(Consts.ResetVector)
val predictor = Module(new BranchPredictor(p))
val itlb = Module(new ITLB(p))
val icache = Module(new ICache(p))
predictor.io.pc := pc
predictor.io.update := io.branchUpdate
itlb.io.vaddr := pc
icache.io.reqValid := true.B
icache.io.reqAddr := itlb.io.paddr
icache.io.memRespValid := io.imemRespValid
icache.io.memRespBits := io.imemRespBits
io.imemReqValid := icache.io.memReqValid
io.imemReqAddr := icache.io.memReqAddr
io.outValid := icache.io.respValid
io.out := icache.io.resp
io.out.predictedTaken := predictor.io.taken
io.out.predictedTarget := predictor.io.target
when(io.redirectValid) {
pc := io.redirectPc
}.elsewhen(icache.io.respValid) {
pc := Mux(predictor.io.taken, predictor.io.target, pc + (4 * p.fetchWidth).U)
}
}

View File

@@ -0,0 +1,23 @@
import chisel3._
class ICache(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val reqValid = Input(Bool())
val reqAddr = Input(UInt(p.xlen.W))
val memReqValid = Output(Bool())
val memReqAddr = Output(UInt(p.xlen.W))
val memRespValid = Input(Bool())
val memRespBits = Input(Vec(p.fetchWidth, UInt(32.W)))
val respValid = Output(Bool())
val resp = Output(new FetchPacket(p))
})
io.memReqValid := io.reqValid
io.memReqAddr := io.reqAddr
io.respValid := io.memRespValid
io.resp.pc := io.reqAddr
io.resp.inst := io.memRespBits
io.resp.predictedTaken := false.B
io.resp.predictedTarget := io.reqAddr + (4 * p.fetchWidth).U
}

View File

@@ -0,0 +1,16 @@
import chisel3._
import chisel3.util._
class ITLB(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val vaddr = Input(UInt(p.xlen.W))
val paddr = Output(UInt(p.xlen.W))
val hit = Output(Bool())
val miss = Output(Bool())
})
io.paddr := io.vaddr
io.hit := true.B
io.miss := false.B
}

View File

@@ -0,0 +1,31 @@
import chisel3._
import chisel3.util._
class RAS(p: CoreParams = CoreParams()) extends Module {
private val ptrBits = log2Ceil(p.rasEntries)
val io = IO(new Bundle {
val push = Input(Bool())
val pop = Input(Bool())
val pushAddr = Input(UInt(p.xlen.W))
val top = Output(UInt(p.xlen.W))
val empty = Output(Bool())
})
val stack = Reg(Vec(p.rasEntries, UInt(p.xlen.W)))
val sp = RegInit(0.U(ptrBits.W))
val count = RegInit(0.U(log2Ceil(p.rasEntries + 1).W))
io.empty := count === 0.U
io.top := stack(Mux(sp === 0.U, (p.rasEntries - 1).U, sp - 1.U))
when(io.push) {
stack(sp) := io.pushAddr
sp := Mux(sp === (p.rasEntries - 1).U, 0.U, sp + 1.U)
when(count =/= p.rasEntries.U) { count := count + 1.U }
}.elsewhen(io.pop && count =/= 0.U) {
sp := Mux(sp === 0.U, (p.rasEntries - 1).U, sp - 1.U)
count := count - 1.U
}
}

View File

@@ -0,0 +1,24 @@
import chisel3._
class IssueQueue(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val enqValid = Input(Bool())
val enq = Input(new RenamePacket(p))
val enqReady = Output(Bool())
val issueValid = Output(Bool())
val issue = Output(new RenamePacket(p))
val issueReady = Input(Bool())
val flush = Input(Bool())
})
val intRs = Module(new ReservationStation(p, p.intRsEntries))
intRs.io.enqValid := io.enqValid
intRs.io.enq := io.enq
intRs.io.issueReady := io.issueReady
intRs.io.flush := io.flush
io.enqReady := intRs.io.enqReady
io.issueValid := intRs.io.issueValid
io.issue := intRs.io.issue
}

View File

@@ -0,0 +1,24 @@
import chisel3._
class IssueStage(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val inValid = Input(Bool())
val in = Input(new RenamePacket(p))
val inReady = Output(Bool())
val outValid = Output(Bool())
val out = Output(new RenamePacket(p))
val outReady = Input(Bool())
val flush = Input(Bool())
})
val queue = Module(new IssueQueue(p))
queue.io.enqValid := io.inValid
queue.io.enq := io.in
queue.io.issueReady := io.outReady
queue.io.flush := io.flush
io.inReady := queue.io.enqReady
io.outValid := queue.io.issueValid
io.out := queue.io.issue
}

View File

@@ -0,0 +1,24 @@
import chisel3._
import chisel3.util._
class ReservationStation(p: CoreParams = CoreParams(), entries: Int = 16) extends Module {
val io = IO(new Bundle {
val enqValid = Input(Bool())
val enq = Input(new RenamePacket(p))
val enqReady = Output(Bool())
val issueValid = Output(Bool())
val issue = Output(new RenamePacket(p))
val issueReady = Input(Bool())
val flush = Input(Bool())
})
val q = Module(new Queue(new RenamePacket(p), entries, flow = false, pipe = true))
q.io.enq.valid := io.enqValid
q.io.enq.bits := io.enq
q.io.deq.ready := io.issueReady || io.flush
io.enqReady := q.io.enq.ready
io.issueValid := q.io.deq.valid && !io.flush
io.issue := q.io.deq.bits
}

View File

@@ -0,0 +1,20 @@
import chisel3._
class DCache(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val reqValid = Input(Bool())
val req = Input(new MemRequest(p))
val memReqValid = Output(Bool())
val memReq = Output(new MemRequest(p))
val memRespValid = Input(Bool())
val memRespData = Input(UInt(p.xlen.W))
val respValid = Output(Bool())
val respData = Output(UInt(p.xlen.W))
})
io.memReqValid := io.reqValid
io.memReq := io.req
io.respValid := io.memRespValid
io.respData := io.memRespData
}

View File

@@ -0,0 +1,15 @@
import chisel3._
class DTLB(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val vaddr = Input(UInt(p.xlen.W))
val paddr = Output(UInt(p.xlen.W))
val hit = Output(Bool())
val miss = Output(Bool())
})
io.paddr := io.vaddr
io.hit := true.B
io.miss := false.B
}

View File

@@ -0,0 +1,30 @@
import chisel3._
class LSU(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val reqValid = Input(Bool())
val req = Input(new MemRequest(p))
val dmemReqValid = Output(Bool())
val dmemReq = Output(new MemRequest(p))
val dmemRespValid = Input(Bool())
val dmemRespData = Input(UInt(p.xlen.W))
val respValid = Output(Bool())
val respData = Output(UInt(p.xlen.W))
})
val dtlb = Module(new DTLB(p))
val dcache = Module(new DCache(p))
dtlb.io.vaddr := io.req.addr
dcache.io.reqValid := io.reqValid
dcache.io.req := io.req
dcache.io.req.addr := dtlb.io.paddr
dcache.io.memRespValid := io.dmemRespValid
dcache.io.memRespData := io.dmemRespData
io.dmemReqValid := dcache.io.memReqValid
io.dmemReq := dcache.io.memReq
io.respValid := dcache.io.respValid
io.respData := dcache.io.respData
}

View File

@@ -0,0 +1,23 @@
import chisel3._
import chisel3.util._
class LoadQueue(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val enqValid = Input(Bool())
val enqAddr = Input(UInt(p.xlen.W))
val enqReady = Output(Bool())
val complete = Input(Bool())
val flush = Input(Bool())
})
val count = RegInit(0.U(log2Ceil(p.loadQueueEntries + 1).W))
io.enqReady := count =/= p.loadQueueEntries.U
when(io.flush) {
count := 0.U
}.otherwise {
when(io.enqValid && io.enqReady) { count := count + 1.U }
when(io.complete && count =/= 0.U) { count := count - 1.U }
}
}

View File

@@ -0,0 +1,15 @@
import chisel3._
class MMU(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val satp = Input(UInt(p.xlen.W))
val vaddr = Input(UInt(p.xlen.W))
val isStore = Input(Bool())
val paddr = Output(UInt(p.xlen.W))
val pageFault = Output(Bool())
})
io.paddr := io.vaddr
io.pageFault := false.B
}

View File

@@ -0,0 +1,26 @@
import chisel3._
class MemStage(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val reqValid = Input(Bool())
val req = Input(new MemRequest(p))
val dmemReqValid = Output(Bool())
val dmemReq = Output(new MemRequest(p))
val dmemRespValid = Input(Bool())
val dmemRespData = Input(UInt(p.xlen.W))
val respValid = Output(Bool())
val respData = Output(UInt(p.xlen.W))
})
val lsu = Module(new LSU(p))
lsu.io.reqValid := io.reqValid
lsu.io.req := io.req
lsu.io.dmemRespValid := io.dmemRespValid
lsu.io.dmemRespData := io.dmemRespData
io.dmemReqValid := lsu.io.dmemReqValid
io.dmemReq := lsu.io.dmemReq
io.respValid := lsu.io.respValid
io.respData := lsu.io.respData
}

View File

@@ -0,0 +1,24 @@
import chisel3._
import chisel3.util._
class StoreQueue(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val enqValid = Input(Bool())
val enq = Input(new MemRequest(p))
val enqReady = Output(Bool())
val drainValid = Output(Bool())
val drain = Output(new MemRequest(p))
val drainReady = Input(Bool())
val flush = Input(Bool())
})
val q = Module(new Queue(new MemRequest(p), p.storeQueueEntries))
q.io.enq.valid := io.enqValid && !io.flush
q.io.enq.bits := io.enq
q.io.deq.ready := io.drainReady || io.flush
io.enqReady := q.io.enq.ready
io.drainValid := q.io.deq.valid && !io.flush
io.drain := q.io.deq.bits
}

View File

@@ -0,0 +1,23 @@
import chisel3._
import chisel3.util._
class PhysicalRegFile(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val raddr = Input(Vec(4, UInt(log2Ceil(p.physRegs).W)))
val rdata = Output(Vec(4, UInt(p.xlen.W)))
val wen = Input(Vec(2, Bool()))
val waddr = Input(Vec(2, UInt(log2Ceil(p.physRegs).W)))
val wdata = Input(Vec(2, UInt(p.xlen.W)))
})
val regs = RegInit(VecInit(Seq.fill(p.physRegs)(0.U(p.xlen.W))))
for (i <- 0 until 4) {
io.rdata(i) := Mux(io.raddr(i) === 0.U, 0.U, regs(io.raddr(i)))
}
for (i <- 0 until 2) {
when(io.wen(i) && io.waddr(i) =/= 0.U) {
regs(io.waddr(i)) := io.wdata(i)
}
}
}

View File

@@ -0,0 +1,26 @@
import chisel3._
import chisel3.util._
class FreeList(p: CoreParams = CoreParams()) extends Module {
private val physBits = log2Ceil(p.physRegs)
val io = IO(new Bundle {
val alloc = Input(Bool())
val allocPhys = Output(UInt(physBits.W))
val canAlloc = Output(Bool())
val free = Input(Bool())
val freePhys = Input(UInt(physBits.W))
})
val freeBits = RegInit(VecInit((0 until p.physRegs).map(i => (i >= p.archRegs).B)))
val chosen = PriorityEncoder(freeBits)
io.canAlloc := freeBits.asUInt.orR
io.allocPhys := chosen
when(io.alloc && io.canAlloc) {
freeBits(chosen) := false.B
}
when(io.free && io.freePhys >= p.archRegs.U) {
freeBits(io.freePhys) := true.B
}
}

View File

@@ -0,0 +1,71 @@
import chisel3._
import chisel3.util._
class RobEntry(p: CoreParams = CoreParams()) extends Bundle {
val valid = Bool()
val pc = UInt(p.xlen.W)
val opClass = UInt(Consts.OpClassWidth.W)
val dest = UInt(log2Ceil(p.physRegs).W)
val oldDest = UInt(log2Ceil(p.physRegs).W)
val completed = Bool()
val exception = Bool()
val branchMispredict = Bool()
}
class ROB(p: CoreParams = CoreParams()) extends Module {
private val idxBits = log2Ceil(p.robEntries)
val io = IO(new Bundle {
val allocate = Input(Bool())
val allocatePc = Input(UInt(p.xlen.W))
val allocateClass = Input(UInt(Consts.OpClassWidth.W))
val allocateDest = Input(UInt(log2Ceil(p.physRegs).W))
val allocateOldDest = Input(UInt(log2Ceil(p.physRegs).W))
val allocateIdx = Output(UInt(idxBits.W))
val canAllocate = Output(Bool())
val complete = Input(Bool())
val completeIdx = Input(UInt(idxBits.W))
val commitValid = Output(Bool())
val commit = Output(new RobEntry(p))
val commitReady = Input(Bool())
val flush = Input(Bool())
})
val entries = RegInit(VecInit(Seq.fill(p.robEntries)(0.U.asTypeOf(new RobEntry(p)))))
val head = RegInit(0.U(idxBits.W))
val tail = RegInit(0.U(idxBits.W))
val count = RegInit(0.U(log2Ceil(p.robEntries + 1).W))
io.canAllocate := count =/= p.robEntries.U
io.allocateIdx := tail
io.commit := entries(head)
io.commitValid := count =/= 0.U && entries(head).valid && entries(head).completed
when(io.flush) {
entries.foreach(_.valid := false.B)
head := 0.U
tail := 0.U
count := 0.U
}.otherwise {
when(io.allocate && io.canAllocate) {
entries(tail).valid := true.B
entries(tail).pc := io.allocatePc
entries(tail).opClass := io.allocateClass
entries(tail).dest := io.allocateDest
entries(tail).oldDest := io.allocateOldDest
entries(tail).completed := false.B
entries(tail).exception := false.B
entries(tail).branchMispredict := false.B
tail := tail + 1.U
count := count + 1.U
}
when(io.complete) {
entries(io.completeIdx).completed := true.B
}
when(io.commitValid && io.commitReady) {
entries(head).valid := false.B
head := head + 1.U
count := count - 1.U
}
}
}

View File

@@ -0,0 +1,49 @@
import chisel3._
import chisel3.util._
class RenameStage(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val inValid = Input(Bool())
val in = Input(new DecodedInst(p))
val outValid = Output(Bool())
val out = Output(new RenamePacket(p))
val commitFree = Input(Bool())
val commitOldPhys = Input(UInt(log2Ceil(p.physRegs).W))
val flush = Input(Bool())
})
val table = Module(new RenameTable(p))
val freeList = Module(new FreeList(p))
val rob = Module(new ROB(p))
table.io.rs1 := io.in.rs1
table.io.rs2 := io.in.rs2
table.io.rd := io.in.rd
table.io.newPhys := freeList.io.allocPhys
table.io.wen := io.inValid && io.in.writesRd && freeList.io.canAlloc && rob.io.canAllocate
table.io.recover := io.flush
freeList.io.alloc := table.io.wen
freeList.io.free := io.commitFree
freeList.io.freePhys := io.commitOldPhys
rob.io.allocate := io.inValid && rob.io.canAllocate && (!io.in.writesRd || freeList.io.canAlloc)
rob.io.allocatePc := io.in.pc
rob.io.allocateClass := io.in.opClass
rob.io.allocateDest := Mux(io.in.writesRd, freeList.io.allocPhys, table.io.oldPrd)
rob.io.allocateOldDest := table.io.oldPrd
rob.io.complete := false.B
rob.io.completeIdx := 0.U
rob.io.commitReady := false.B
rob.io.flush := io.flush
io.outValid := rob.io.allocate
io.out.valid := io.outValid
io.out.decoded := io.in
io.out.prs1 := table.io.prs1
io.out.prs2 := table.io.prs2
io.out.prd := Mux(io.in.writesRd, freeList.io.allocPhys, table.io.oldPrd)
io.out.oldPrd := table.io.oldPrd
io.out.robIdx := rob.io.allocateIdx
}

View File

@@ -0,0 +1,32 @@
import chisel3._
import chisel3.util._
class RenameTable(p: CoreParams = CoreParams()) extends Module {
private val physBits = log2Ceil(p.physRegs)
val io = IO(new Bundle {
val rs1 = Input(UInt(5.W))
val rs2 = Input(UInt(5.W))
val rd = Input(UInt(5.W))
val newPhys = Input(UInt(physBits.W))
val wen = Input(Bool())
val prs1 = Output(UInt(physBits.W))
val prs2 = Output(UInt(physBits.W))
val oldPrd = Output(UInt(physBits.W))
val recover = Input(Bool())
})
val init = VecInit((0 until p.archRegs).map(_.U(physBits.W)))
val speculative = RegInit(init)
val committed = RegInit(init)
io.prs1 := speculative(io.rs1)
io.prs2 := speculative(io.rs2)
io.oldPrd := speculative(io.rd)
when(io.recover) {
speculative := committed
}.elsewhen(io.wen && io.rd =/= 0.U) {
speculative(io.rd) := io.newPhys
}
}

View File

@@ -0,0 +1,18 @@
import chisel3._
import chisel3.util._
class WriteBackStage(p: CoreParams = CoreParams()) extends Module {
val io = IO(new Bundle {
val valid = Input(Bool())
val physDest = Input(UInt(log2Ceil(p.physRegs).W))
val data = Input(UInt(p.xlen.W))
val wen = Output(Bool())
val waddr = Output(UInt(log2Ceil(p.physRegs).W))
val wdata = Output(UInt(p.xlen.W))
})
io.wen := io.valid
io.waddr := io.physDest
io.wdata := io.data
}