Skip to content

Zxis233/EsCute-RV

Repository files navigation

EsCute 极简五级流水线RV32I实现

项目简介

  • 实现RV32I指令集的五级流水线CPU
  • 支持流水线停顿与数据前推
  • 支持 NONE / STATIC / DYNAMIC_1bit / GSHARE 分支预测
  • 支持 Zmmul 扩展
  • 支持基础 U/S/M-Mode 与常用特权 CSR
  • 支持 Zicfiss / Zicfilp 已实现子集
  • 支持基本的异常处理机制
  • 通过官方riscv-tests RV32I_Zicsr_Zmmul验证(不含ECALLEBRAK指令,不含非对齐访存相关指令,仅实现非对齐访存异常)
  • 通过Coremark基准测试

当前已实现的 Zicfi 相关功能:

  • ZicfissSSRDPSSPUSHSSPOPCHKssp CSR,以及 menvcfg/senvcfg 控制下的 shadow stack 使能
  • ZicfilpLPADELP 状态跟踪、间接 JALR 后的软件检查、software-check 异常上报
  • trap / xRET 对 ELP 的保存与恢复

当前未实现或仅做最小化建模的部分:

  • SSAMOSWAP.* 尚未实现
  • shadow stack 目前复用了普通 DRAM 路径,只实现了按字对齐检查与基本 fault 行为

关于 Zicfiss / Zicfilp 的详细实现分析、源码跳转和波形观察点,见 CFI_Instruction.md

目录层级

EsCute-RV
├── .vscode
│  └── property.json                  # DIDE配置文件
├── CFI_Instruction.md                # Zicfiss / Zicfilp 实现说明
├── filelist.f                        # 工程文件列表
├── README.md                         # 项目说明文件
├── Makefile                          # 自动化测试用 Makefile 文件   
├── prj
│  └── synlig                        # Synlig 综合输出目录
│     ├── CPU_TOP_synlig.json        # Synlig 导出的 JSON 网表
│     ├── CPU_TOP_synlig.v           # Synlig 导出的 Verilog 网表
│     ├── sythesis.log               # Synlig 综合日志
│     └── work                       # 每次综合的独立 Surelog 工作目录
├── coremark                          # CoreMark基准测试文件夹
│  ├── Makefile                       # CoreMark基准测试 Makefile 文件
│  └── escute
│     ├── soft_div.c                  # 软件除法函数实现文件
│     ├── core_portme.c               # CoreMark平台相关函数实现文件
│     ├── core_portme.h               # CoreMark平台相关函数声明文件
│     ├── ee_printf.h                 # CoreMark打印函数声明文件
│     └── core_portme.mak             # CoreMark平台相关Makefile文件
└── user
   ├── data
   │  └── isa                         # riscv-tests指令集测试
   │     ├── env                      # 测试用环境文件
   │     │  ├── encoding.h
   │     │  ├── link.ld               # 测试用链接脚本文件
   │     │  ├── riscv_test.h
   │     │  └── test_macros.h         # 自动化测试用 宏定义文件
   │     ├── generated                # 测试样例二进制文件与反编译文件
   │     ├── hex                      # 自动化测试用 机器码文件
   │     ├── Makefile                 # 自动化测试用 测试样例生成 Makefile
   │     ├── rv32ui                   # 自动化测试用 汇编代码文件
   │     ├── rv64ui                   # 自动化测试用 汇编代码文件
   │     ├── rv32mi                   # 自动化测试用 汇编代码文件 (机器模式指令测试,含 zicfi.S)
   │     ├── rv64mi                   # 自动化测试用 汇编代码文件 (机器模式指令测试)
   │     ├── test_tb.sv               # 自动化测试用 顶层仿真文件
   │     └── verilog                  # 自动化测试用 Verilog 反编译内存文件
   ├── sim
   │  ├── tb_CPU_TOP.sv               # 顶层CPU仿真文件
   │  ├── tb_Zicfi.sv                 # Zicfiss / Zicfilp 定向测试
   │  ├── simple_counter.sv           # 简单计数器模块
   │  └── tb_simple_counter.sv        # 简单计数器测试文件 用于测试环境是否正确
   ├── tools
   │  ├── run_synlig_synth.sh         # Synlig 综合脚本
   │  └── synlig_blackboxes
   │     ├── DRAM_blackbox.sv         # DRAM 黑盒 stub
   │     └── IROM_blackbox.sv         # IROM 黑盒 stub
   └── src
      ├── include
      │  └── defines.svh              # 全局宏定义文件
      │
      ├── IROM.sv                     # 指令存储器模块
      ├── CPU_TOP.sv                  # CPU顶层模块
      ├── PC.sv                       # 程序计数器模块
      │
      ├── Decoder.sv                  # 指令译码模块
      ├── imm_extender.sv             # 立即数扩展模块
      ├── BPU.sv                      # 分支预测单元模块
      ├── Static_Predictor.sv         # 静态分支预测模块
      ├── Dynamic_1bit_Predictor.sv   # 1-bit动态分支预测模块
      ├── Dynamic_Gshare_Predictor.sv # GShare动态分支预测模块
      │
      ├── ALU.sv                      # 算术逻辑单元模块
      ├── MUL.sv                      # 乘法运算模块
      ├── CSR.sv                      # 控制状态寄存器模块
      ├── NextPC_Generator.sv         # 下一条指令地址生成模块
      │
      ├── DRAM.sv                     # 同步数据存储器模块
      ├── LoadUnit.sv                 # 读取单元模块
      ├── StoreUnit.sv                # 存储单元模块
      │
      ├── HazardUnit.sv               # 数据冒险处理模块
      ├── PR_IF_ID.sv                 # IF/ID级  流水线寄存器模块
      ├── PR_ID_EX.sv                 # ID/EX级  流水线寄存器模块
      ├── PR_EX_MEM.sv                # EX/MEM级 流水线寄存器模块
      ├── PR_MEM_WB.sv                # MEM/WB级 流水线寄存器模块
      └── RegisterF.sv                # 寄存器堆模块
      

分支预测说明

当前前端支持 4 种 BPU 模式,枚举定义见 defines.svh

  • NONE (0):关闭分支预测,控制流统一在 EX 级解析并恢复
  • STATIC (1):静态预测,JAL 恒预测跳转,JALR 不预测,条件分支采用 BTFNT(Backward Taken, Forward Not Taken)
  • DYNAMIC_1bit (2):基于 PC 低位索引的 1-bit BHT,记录该分支最近一次真实结果
  • GSHARE (3):使用 PC xor GHR 索引的 GShare,PHT 为 2-bit 饱和计数器

相关实现文件:

当前默认配置:

  • CPU_TOP 默认 BPU_TYPE = STATIC
  • CPU_TOP 内部默认 BPU_INDEX_BITS = 8
  • CPU_TOP 内部默认 BPU_META_BITS = 2
  • GShare 当前采用“非投机式” GHR 更新:GHR 在 EX 级按真实分支结果更新,不做投机更新与回滚

切换不同预测器时,可以直接在仿真命令里覆盖 BPU_TYPE

make SIM=iverilog run TESTCASE=rv32ui-p-beq BPU_TYPE=0
make SIM=iverilog run TESTCASE=rv32ui-p-beq BPU_TYPE=1
make SIM=iverilog run TESTCASE=rv32ui-p-beq BPU_TYPE=2
make SIM=iverilog run TESTCASE=rv32ui-p-beq BPU_TYPE=3

make SIM=iverilog coremark_test BPU_TYPE=3
make SIM=verilator compile BPU_TYPE=3

如果需要 sweep INDEX_BITS / META_BITS,当前可以直接修改 CPU_TOP.sv 中的 BPU_INDEX_BITSBPU_META_BITS 两个 localparam

关于统计信息:

  • mispredict_counter 会在 test_tb.svcoremark.svtb_Verilator.sv 中输出
  • BPU_TYPE = NONE 时该计数器保持为 0
  • 启用 BPU 时,它可用于不同预测器之间的相对性能比较

Synlig 综合说明

当前仓库提供了基于 ChipsAlliance Synlig 的综合脚本:run_synlig_synth.sh

脚本行为:

  • 自动从 filelist.f 提取 -I 路径,并传给 read_systemverilog
  • 默认使用 read_systemverilog -defer,随后执行 read_systemverilog -link
  • 默认顶层模块为 CPU_TOP
  • 默认输出到 prj/synlig/
  • 每次综合都在 prj/synlig/work/run.* 下创建独立工作目录,避免 Surelog 的 slpp_all 缓存污染不同模式的结果

默认综合命令:

user/tools/run_synlig_synth.sh

默认模式下:

  • IROM 走黑盒 stub
  • DRAM 走黑盒 stub
  • 适合快速得到 CPU 顶层网表

常用输出文件:

  • prj/synlig/sythesis.log
  • prj/synlig/CPU_TOP_synlig.json
  • prj/synlig/CPU_TOP_synlig.v

让真实 DRAM 参与综合

如果想检查 DRAM.sv 是否为可综合 Verilog,可以关闭 DRAM 黑盒:

SYNLIG_USE_DRAM_BLACKBOX=0 user/tools/run_synlig_synth.sh

此时脚本会直接把真实的 DRAM.sv 加入综合,而不是使用 DRAM_blackbox.sv

建议关注两点:

  • 日志中应出现 Compile module "work@DRAM",说明综合读入的是真实 DRAM.sv
  • 不应再出现黑盒模式下的 spo ... has no driver 报告

DRAM.sv 中与仿真有关的 plusargs / readmemh 路径已经通过 ifndef YOSYS 隔离;综合时真正参与检查的是内存数组、同步写和同步读逻辑。

如果还想把真实 IROM 也拉进综合,可以再关闭 IROM 黑盒:

SYNLIG_USE_IROM_BLACKBOX=0 SYNLIG_USE_DRAM_BLACKBOX=0 user/tools/run_synlig_synth.sh

如果只想单独检查某个模块,也可以覆盖顶层:

TOP_MODULE=DRAM SYNLIG_USE_DRAM_BLACKBOX=0 user/tools/run_synlig_synth.sh

Zicfi 扩展说明

本仓库当前实现的 Zicfi 逻辑主要分布在以下文件:

  • defines.svh:Zicfiss / Zicfilp 指令匹配、CSR 编号、异常号与 software-check 子码
  • Decoder.svLPADSSRDPSSPUSHSSPOPCHK 的识别与控制信号生成
  • CPU_TOP.sv:LPAD 检查、shadow stack 地址生成、ssp / ELP 更新、异常优先级与流水线串行化
  • CSR.svsspmenvcfgsenvcfgmstatushmseccfg 以及 trap / xRET 对 ELP 的保存恢复
  • zicfi.S:基于 riscv-tests 风格的 Zicfi 正向汇编测试
  • tb_Zicfi.sv:无需工具链即可运行的定向仿真测试

当前实现的行为重点:

  • SSRDPssp 读出 shadow stack pointer
  • SSPUSH / SSPOPCHK 走专用 shadow stack 访存路径,并更新 ssp
  • 间接 JALR 会设置 ELP,后续落点必须通过 LPAD 检查
  • LPADSSPOPCHK 失败时,会上报 software-check 异常

建议阅读顺序:

  1. CFI_Instruction
  2. Decoder.sv
  3. CPU_TOP.sv
  4. CSR.sv
  5. zicfi.Stb_Zicfi.sv
  6. zicfi_rv32_test.S

Zicfi 测试入口

如果工具链支持 zicfiss / zicfilp,可以先生成并运行汇编测试:

make -C user/data/isa rv32mi-p-zicfi.dump
vvp prj/run/zicfi_check.vvp +TESTCASE=user/data/isa/hex/rv32mi-p-zicfi.hex +DUMPWAVE=1 +PRINT_INFO=0

如果只想快速验证 RTL,也可以直接跑定向测试:

iverilog -g2012 -Wall -s tb_Zicfi -I user/src -I user/src/include -o prj/run/tb_zicfi.vvp user/sim/tb_Zicfi.sv user/src/*.sv
vvp prj/run/tb_zicfi.vvp

CoreMark性能测试结果 (On Verilator)

分支预测类型 总Tick数 总周期数 错误预测数 基准速度比
无分支预测 5292624 53336415000 / 1.000x
静态分支预测 5011712 50501545000 163426 1.056x
1-bit(4I )动态分支预测 5035325 50730805000 167403 1.051x
1-bit(8I )动态分支预测 4998735 50371755000 142873 1.059x
GShare(8I1M)动态分支预测 4971079 50093755000 120647 1.065x
GShare(8I2M)动态分支预测 4975420 50139215000 124155 1.064x
GShare(8I4M)动态分支预测 4984823 50232025000 130558 1.062x
GShare(8I8M)动态分支预测 4993908 50323525000 138736 1.060x

后续工作

  • 支持 Zifenci 扩展
  • 补充 Zicfiss / Zicfilp 的负例测试(LPAD fault、SSPOPCHK mismatch 等)
  • 支持非对齐访存
  • 使用华莱士树乘法器优化乘法运算
  • 优化流水线结构以提升性能
  • 进行形式化验证