Initial Chisel core implementation
This commit is contained in:
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal 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
466
ARCHITECTURE.md
Normal 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启动需求
|
||||||
|
|
||||||
|
要成功启动Linux,CPU必须支持:
|
||||||
|
|
||||||
|
### 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
22
build.sbt
Normal 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
1
project/build.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
sbt.version=1.9.7
|
||||||
98
sim/README.md
Normal file
98
sim/README.md
Normal 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
61
sim/scripts/run_tests.sh
Executable 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
43
sim/verilator/Makefile
Normal 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
111
sim/verilator/memory.cpp
Normal 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
40
sim/verilator/memory.h
Normal 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
101
sim/verilator/testbench.cpp
Normal 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
173
src/main/scala/Core.scala
Normal 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")
|
||||||
|
)
|
||||||
|
}
|
||||||
18
src/main/scala/commit/CommitStage.scala
Normal file
18
src/main/scala/commit/CommitStage.scala
Normal 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)
|
||||||
|
}
|
||||||
76
src/main/scala/common/Bundles.scala
Normal file
76
src/main/scala/common/Bundles.scala
Normal 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)
|
||||||
|
}
|
||||||
35
src/main/scala/common/Consts.scala
Normal file
35
src/main/scala/common/Consts.scala
Normal 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))
|
||||||
|
}
|
||||||
28
src/main/scala/common/Parameters.scala
Normal file
28
src/main/scala/common/Parameters.scala
Normal 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")
|
||||||
|
}
|
||||||
|
|
||||||
99
src/main/scala/csr/CSRFile.scala
Normal file
99
src/main/scala/csr/CSRFile.scala
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/main/scala/csr/PrivilegeControl.scala
Normal file
21
src/main/scala/csr/PrivilegeControl.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
150
src/main/scala/decode/Decoder.scala
Normal file
150
src/main/scala/decode/Decoder.scala
Normal 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
|
||||||
|
}
|
||||||
20
src/main/scala/decode/IDStage.scala
Normal file
20
src/main/scala/decode/IDStage.scala
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
36
src/main/scala/execute/ALU.scala
Normal file
36
src/main/scala/execute/ALU.scala
Normal 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)
|
||||||
|
}
|
||||||
|
|
||||||
21
src/main/scala/execute/BranchUnit.scala
Normal file
21
src/main/scala/execute/BranchUnit.scala
Normal 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)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
14
src/main/scala/execute/Divider.scala
Normal file
14
src/main/scala/execute/Divider.scala
Normal 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))
|
||||||
|
}
|
||||||
29
src/main/scala/execute/ExecStage.scala
Normal file
29
src/main/scala/execute/ExecStage.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
16
src/main/scala/execute/FPU.scala
Normal file
16
src/main/scala/execute/FPU.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
11
src/main/scala/execute/Multiplier.scala
Normal file
11
src/main/scala/execute/Multiplier.scala
Normal 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)
|
||||||
|
}
|
||||||
|
|
||||||
32
src/main/scala/frontend/BTB.scala
Normal file
32
src/main/scala/frontend/BTB.scala
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
38
src/main/scala/frontend/BranchPredictor.scala
Normal file
38
src/main/scala/frontend/BranchPredictor.scala
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
43
src/main/scala/frontend/Frontend.scala
Normal file
43
src/main/scala/frontend/Frontend.scala
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
23
src/main/scala/frontend/ICache.scala
Normal file
23
src/main/scala/frontend/ICache.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
16
src/main/scala/frontend/ITLB.scala
Normal file
16
src/main/scala/frontend/ITLB.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
31
src/main/scala/frontend/RAS.scala
Normal file
31
src/main/scala/frontend/RAS.scala
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
24
src/main/scala/issue/IssueQueue.scala
Normal file
24
src/main/scala/issue/IssueQueue.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
24
src/main/scala/issue/IssueStage.scala
Normal file
24
src/main/scala/issue/IssueStage.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
24
src/main/scala/issue/ReservationStation.scala
Normal file
24
src/main/scala/issue/ReservationStation.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
20
src/main/scala/memory/DCache.scala
Normal file
20
src/main/scala/memory/DCache.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
15
src/main/scala/memory/DTLB.scala
Normal file
15
src/main/scala/memory/DTLB.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
30
src/main/scala/memory/LSU.scala
Normal file
30
src/main/scala/memory/LSU.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
23
src/main/scala/memory/LoadQueue.scala
Normal file
23
src/main/scala/memory/LoadQueue.scala
Normal 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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
15
src/main/scala/memory/MMU.scala
Normal file
15
src/main/scala/memory/MMU.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
26
src/main/scala/memory/MemStage.scala
Normal file
26
src/main/scala/memory/MemStage.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
24
src/main/scala/memory/StoreQueue.scala
Normal file
24
src/main/scala/memory/StoreQueue.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
23
src/main/scala/regfile/PhysicalRegFile.scala
Normal file
23
src/main/scala/regfile/PhysicalRegFile.scala
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
26
src/main/scala/rename/FreeList.scala
Normal file
26
src/main/scala/rename/FreeList.scala
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
71
src/main/scala/rename/ROB.scala
Normal file
71
src/main/scala/rename/ROB.scala
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
49
src/main/scala/rename/RenameStage.scala
Normal file
49
src/main/scala/rename/RenameStage.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
32
src/main/scala/rename/RenameTable.scala
Normal file
32
src/main/scala/rename/RenameTable.scala
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
18
src/main/scala/writeback/WriteBackStage.scala
Normal file
18
src/main/scala/writeback/WriteBackStage.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user