目录整理:RTL/上板/仿真分开存放,测试 ROM 镜像归入 testbench/rom

- rtl/:12 个可综合的处理器源文件(顶层 cpu21_riscv_redirect_int_bpb.v,其余为各功能单元)。
- board/:板级顶层 cpu21_board_top.v、引脚约束 cpu21_board_top.xdc、片内 ROM 初始化
  cpu21_board_rom.vh 与短延时版本 cpu21_board_rom_fast.vh。
- testbench/:保留三个测试平台;ROM 镜像统一放到 testbench/rom/,测试程序源码与期望值
  仍在 testbench/programs/(其中的 .hex 是 RARS 原始输出,与 rom/ 里改过显示方式的版本
  内容不同,两者都保留)。
- 同步更新:两个测试平台的 ROM 探测路径改为 testbench/rom/(并在头注释里补充说明:
  PowerShell 会把 -generic_top/--testplusarg 参数里的 "=" 拆开、Verilog 字符串中的
  路径要用正斜杠);tools/hex2vh.py 的用法示例指向 board/cpu21_board_top.v;
  说明文档新增“目录结构”一节并同步所有路径引用;.gitignore 补充 .Xil/。
- 清理:删除 *.hex.lst 中间文件与空的 .Xil/ 目录。
- 验证:isa_test 24/24、lui_test 4/4、mmio_test 5/5 三项回归通过;两个基线(5560 / 6003
  周期)与整理前一致;板级仿真(默认 ROM 首次写入 LED=0001、快速 ROM 跑马灯循环)正常;
  Vivado 工程源文件路径同步到 rtl/ 与 board/ 后综合通过(LUT 5276、23 个引脚,与整理前
  完全一致),因此已有 bit 文件仍然有效。
This commit is contained in:
2026-09-13 18:06:21 +08:00
parent 1ae6fe6ebe
commit 556fbe9530
29 changed files with 157 additions and 110 deletions
+1
View File
@@ -8,3 +8,4 @@ _vivado/
*.vcd
*.pb
*.hex.lst
.Xil/
@@ -165,7 +165,8 @@ module cpu21_board_top #(
always @(posedge clk_cpu or posedge arst) begin
if (arst) led_we_cnt <= 24'd0;
else if (led_valid) led_we_cnt <= 24'hFF_FFFF; // 约 2.7 s @6.25 MHz(分频后的 CPU 时钟)
else if (led_valid)
led_we_cnt <= 24'hFF_FFFF; // 约 2.7 s @6.25 MHz(分频后的 CPU 时钟)
else if (led_we_cnt != 24'd0) led_we_cnt <= led_we_cnt - 24'd1;
end
+69 -38
View File
@@ -1,11 +1,36 @@
# `cpu21-riscv-4.circ` 的 Verilog 实现
顶层文件:`cpu21_riscv_redirect_int_bpb.v`
处理器顶层文件:`rtl/cpu21_riscv_redirect_int_bpb.v`
该设计对应原电路中的“重定向流水线+中断+分支预测”部分,顶层模块为
`cpu21_riscv_redirect_int_bpb`。为便于单独仿真与调试,各功能单元已拆分为
独立文件,顶层的 ALU、BPB 也已移出,具体见文末“文件与模块划分”。
## 目录结构
```text
risc-v/
├─ rtl/ 处理器 RTL(13 个文件,cpu21_riscv_redirect_int_bpb.v 为顶层)
├─ board/ 上板(Nexys4DDR):板级顶层、引脚约束、片内 ROM 初始化
│ ├─ cpu21_board_top.v 板级顶层(时钟分频 + 片内 ROM/RAM + 引脚)
│ ├─ cpu21_board_top.xdc 引脚约束与分频时钟约束
│ ├─ cpu21_board_rom.vh 片内 ROM 内容(由 tools/hex2vh.py 生成,上板用)
│ └─ cpu21_board_rom_fast.vh 短延时的 ROM 内容(仅仿真,-d BOARD_ROM_FAST 选择)
├─ testbench/ 仿真
│ ├─ tb_cpu21_riscv_redirect_int_bpb.v 中断/分支预测测试平台
│ ├─ tb_no_intr.v 无中断测试平台(指令集测试用)
│ ├─ tb_board_top.v 板级顶层仿真
│ ├─ rom/ 仿真实际加载的 ROM 镜像(.hex)
│ └─ programs/ 汇编源程序、期望值、课程原始资产(RARS)
├─ tools/ rv_asm.py(迷你汇编器)、hex2vh.py(hex → ROM 初始化)
├─ pipelined-cpu/ Vivado 工程(本地目录,不入库)
└─ cpu21_riscv_redirect_int_bpb说明.md 本文档
```
约定:`rtl/` 只放可综合的处理器源码;板级/仿真相关的东西都在 `board/`、
`testbench/` 下;`testbench/rom/` 里的镜像是测试平台默认加载的那个版本
(已按 MMIO 写 LED 修改),`testbench/programs/` 保留课程原始程序与 RARS 输出。
## 已转换的功能
- 五级流水:IF、ID、EX、MEM、WB。
@@ -28,7 +53,7 @@
因此它与标准 RISC-V 指令的低 7 位 opcode 相差右移两位。
| 指令类别 | opcode | 说明 |
| ----------------------------------------------- | -----: | ---------------------------------------------------------------------------- |
| ----------------------------------------------------------- | -------: | -------------------------------------------------------------------------------- |
| R 型 | `0x0c` | add/sub/and/or/xor/slt/sltu/sll/srl/sra |
| M 扩展(R 型) | `0x0c` | `funct7=0000001`:mul/mulh/mulhsu/mulhu(另有课程约定的 remu,`funct3=111`) |
| I 型 ALU | `0x04` | addi/andi/ori/xori/slti/sltiu/slli/srli/srai |
@@ -45,12 +70,12 @@
## MMIO 与外设
外设寄存器映射在数据存储器地址空间,由 `cpu21_mmio.v` 在 MEM 级译码,
外设寄存器映射在数据存储器地址空间,由 `rtl/cpu21_mmio.v` 在 MEM 级译码,
顶层参数 `LED_ADDR`(默认 `0x0000_1000`)指定 LED 数据寄存器的地址。
该地址位于 4 KB 数据 RAM(`0x0000`–`0x0FFC`)之外,因此不会与 RAM 冲突。
| 地址 | 外设 | 写 | 读 |
| ------------- | -------------- | ---------------------------------------------------------------------------- | -------------------------- |
| --------------- | -------------- | ------------------------------------------------------------------------------------ | ---------------------------- |
| `0x0000_1000` | LED 数据寄存器 | `sw` 写全字;`sb` 按 `data_wstrb_o` 只改对应字节通道,`led_valid_o` 拉高一拍 | `lw` 返回 LED 寄存器当前值 |
地址译码按**字**对齐:同一 32 位寄存器内的任意字节偏移(如 `0x1001`)都算命中,
@@ -69,7 +94,7 @@
`testbench/programs/mmio_test.asm`(期望值见 `mmio_test.expected`)覆盖了
全字写、读回、字节写三种情况。加入新外设(拨码开关、UART 等)时只需在
`cpu21_mmio.v` 里扩展地址译码,处理器流水线无需改动。
`rtl/cpu21_mmio.v` 里扩展地址译码,处理器流水线无需改动。
## 顶层端口约定
@@ -96,23 +121,27 @@
## 文件与模块划分
| 文件 | 模块 | 职责 |
| -------------------------------- | ------------------------------ | --------------------------------------------------------------- |
| `cpu21_riscv_redirect_int_bpb.v` | `cpu21_riscv_redirect_int_bpb` | 顶层:五级流水寄存器、子模块例化、PC/IF-ID/ID-EX 控制、对外输出 |
| `cpu21_riscv_alu.v` | `cpu21_riscv_alu` | 组合 ALU(`result2` 用于 MUL 高位 / DIVU 余数) |
| `cpu21_bpb_8.v` | `cpu21_bpb_8` | 8 项全相联分支目标缓冲(2 位饱和计数器 + 近似 LRU) |
| `cpu21_riscv_decoder.v` | `cpu21_riscv_decoder` | ID 级组合译码器(操作码 `IR[6:2]`) |
| `cpu21_riscv_regfile.v` | `cpu21_riscv_regfile` | 寄存器堆,`x0` 恒为 0,同周期 WB 写优先旁路 |
| `cpu21_riscv_forward_unit.v` | `cpu21_riscv_forward_unit` | EX 级前递网络(EX/MEM 优先于 MEM/WB) |
| `cpu21_riscv_store_unit.v` | `cpu21_riscv_store_unit` | EX 级存储数据对齐:`sw`/`sb` 的写数据与字节写掩码 |
| `cpu21_riscv_branch_unit.v` | `cpu21_riscv_branch_unit` | EX 级控制流裁决:分支方向/目标、JAL/JALR 重定向 |
| `cpu21_riscv_hazard_unit.v` | `cpu21_riscv_hazard_unit` | load-use 冒险、停顿(stall)与冲刷(flush)判定 |
| `cpu21_riscv_irq_ctrl.v` | `cpu21_riscv_irq_ctrl` | 中断采样与优先级、`ustatus`/`uepc` CSR、嵌套返回栈 |
| `cpu21_riscv_perf_counters.v` | `cpu21_riscv_perf_counters` | 周期/停顿/气泡/分支/预测成功率等统计计数器 |
| `cpu21_mmio.v` | `cpu21_mmio` | MMIO 外设:LED 数据寄存器(可写、可读回,支持字节写) |
| `cpu21_board_top.v` | `cpu21_board_top` | 上板顶层:时钟分频、复位同步、片内 ROM/RAM、引脚外设连接 |
| `testbench/tb_board_top.v` | `tb_board_top` | 上板顶层的 RTL 仿真(观察 LED 跑马灯与写入指示) |
| ----------------------------------------------- | ----------------------------------- | --------------------------------------------------------------------- |
| `rtl/cpu21_riscv_redirect_int_bpb.v` | `cpu21_riscv_redirect_int_bpb` | 顶层:五级流水寄存器、子模块例化、PC/IF-ID/ID-EX 控制、对外输出 |
| `rtl/cpu21_riscv_alu.v` | `cpu21_riscv_alu` | 组合 ALU(`result2` 用于 MUL 高位 / DIVU 余数) |
| `rtl/cpu21_bpb_8.v` | `cpu21_bpb_8` | 8 项全相联分支目标缓冲(2 位饱和计数器 + 近似 LRU) |
| `rtl/cpu21_riscv_decoder.v` | `cpu21_riscv_decoder` | ID 级组合译码器(操作码`IR[6:2]`) |
| `rtl/cpu21_riscv_regfile.v` | `cpu21_riscv_regfile` | 寄存器堆,`x0` 恒为 0,同周期 WB 写优先旁路 |
| `rtl/cpu21_riscv_forward_unit.v` | `cpu21_riscv_forward_unit` | EX 级前递网络(EX/MEM 优先于 MEM/WB) |
| `rtl/cpu21_riscv_store_unit.v` | `cpu21_riscv_store_unit` | EX 级存储数据对齐:`sw`/`sb` 的写数据与字节写掩码 |
| `rtl/cpu21_riscv_branch_unit.v` | `cpu21_riscv_branch_unit` | EX 级控制流裁决:分支方向/目标、JAL/JALR 重定向 |
| `rtl/cpu21_riscv_hazard_unit.v` | `cpu21_riscv_hazard_unit` | load-use 冒险、停顿(stall)与冲刷(flush)判定 |
| `rtl/cpu21_riscv_irq_ctrl.v` | `cpu21_riscv_irq_ctrl` | 中断采样与优先级、`ustatus`/`uepc` CSR、嵌套返回栈 |
| `rtl/cpu21_riscv_perf_counters.v` | `cpu21_riscv_perf_counters` | 周期/停顿/气泡/分支/预测成功率等统计计数器 |
| `rtl/cpu21_mmio.v` | `cpu21_mmio` | MMIO 外设:LED 数据寄存器(可写、可读回,支持字节写) |
| `board/cpu21_board_top.v` | `cpu21_board_top` | 板级顶层:时钟分频、复位同步、片内 ROM/RAM、引脚外设连接 |
| `board/cpu21_board_top.xdc` | — | Nexys4DDR 引脚约束 + 分频时钟(`create_generated_clock`) |
| `board/cpu21_board_rom.vh` | — | 片内 ROM 内容(生成文件,`rom[i] = 32'h…;`) |
| `testbench/tb_board_top.v` | `tb_board_top` | 板级顶层的 RTL 仿真(观察 LED 跑马灯与写入指示) |
| `testbench/tb_no_intr.v` | `tb_no_intr` | 无中断测试平台(指令集/MMIO 测试,默认加载 benchmark 镜像) |
| `testbench/tb_cpu21_riscv_redirect_int_bpb.v` | `tb_cpu21_riscv_redirect_int_bpb` | 中断与分支预测测试平台(默认加载中断测试镜像) |
| `tools/rv_asm.py` | — | 迷你汇编器:`.asm` → 机器码 hex(`.lst` 带反汇编清单) |
| `tools/hex2vh.py` | — | hex → `cpu21_board_rom.vh`(`rom[i] = 32'h…;` 初始化语句) |
| `tools/hex2vh.py` | — | hex →`board/cpu21_board_rom.vh`(`rom[i] = 32'h…;` 初始化语句) |
拆分只调整了模块边界与文件组织,端口、时序与行为完全等价:使用同一 ROM 程序
在 xsim 下对拆分前后逐周期对比,波形输出与最终统计(周期、停顿、气泡、分支
@@ -129,7 +158,7 @@ BPB/中断观测值)。如果直接把它设为实现顶层,这些端口会
### 引脚分配(`cpu21_board_top.xdc`)
| 端口 | 引脚 | 板上元件 | 说明 |
| ------------- | --------- | ------------------------- | ----------------------------------------------- |
| --------------- | ----------- | ------------------------- | -------------------------------------------------- |
| `clk100` | E3 | 100 MHz 晶振 | 经`CLK_DIV` 分频后给 CPU |
| `rst_n` | C12 | 按键 CPU_RESETN(低有效) | 异步置位、与 CPU 时钟同步释放 |
| `go` | N17 | 按键 BTNC | 对应原电路“继续运行”(`ecall` 停机后恢复执行) |
@@ -164,7 +193,8 @@ create_generated_clock -name cpu_clk -source [get_ports { clk100 }] \
### 片内 ROM / RAM
- ROM:`ROM_WORDS = 1024` 字(4 KB),组合读,取指地址 `instr_addr[11:2]`;
内容由 `` `include "cpu21_board_rom.vh" `` 给出(文件里是 1024 条
内容由 `` `include "cpu21_board_rom.vh" `` 给出(与 `cpu21_board_top.v` 同目录,
文件里是 1024 条
`rom[i] = 32'h…;`,放在 `initial` 里,等价于 `$readmemh`,且不依赖文件路径)。
- RAM:`RAM_WORDS = 1024` 字(4 KB),字节写使能 + 组合读,只响应
`data_addr < 4 KB`,因此 MMIO 地址(默认 `0x1000`)不会落进 RAM。
@@ -176,10 +206,10 @@ create_generated_clock -name cpu_clk -source [get_ports { clk100 }] \
```powershell
# 1) 汇编 + 生成 ROM 初始化文件(在仓库根目录执行)
python tools/rv_asm.py testbench/programs/board_demo.asm _tmp/board_demo.hex
python tools/hex2vh.py _tmp/board_demo.hex cpu21_board_rom.vh 1024
python tools/hex2vh.py _tmp/board_demo.hex board/cpu21_board_rom.vh 1024
# 2) 上板顶层的 RTL 仿真(默认程序延时约 20 ms,只需确认第一次写入 LED=0001)
xvlog <RTL…> testbench/tb_board_top.v
xvlog rtl/*.v board/cpu21_board_top.v testbench/tb_board_top.v
xelab -top tb_board_top -snapshot board_sim
xsim board_sim -tclbatch run_quit.tcl
```
@@ -189,18 +219,19 @@ xsim board_sim -tclbatch run_quit.tcl
文件,由 `BOARD_ROM_FAST` 宏选择(综合流程不会定义该宏):
| 文件 | 用途 | 每步耗时 |
| ------------------------ | ------------------------------------- | -------- |
| `cpu21_board_rom.vh` | 综合/上板(默认) | 约 20 ms |
| `cpu21_board_rom_fast.vh` | 仅仿真:一次跑完整个跑马灯序列 | 4.8 µs |
| --------------------------------- | ------------------------------ | -------- |
| `board/cpu21_board_rom.vh` | 综合/上板(默认) | 约 20 ms |
| `board/cpu21_board_rom_fast.vh` | 仅仿真:一次跑完整个跑马灯序列 | 4.8 µs |
```powershell
# 快速版仿真:生成/重新生成 CPU 时加上 -d BOARD_ROM_FAST
xvlog -d BOARD_ROM_FAST <RTL…> testbench/tb_board_top.v
# 快速版仿真:编译时加上 -d BOARD_ROM_FAST(注意 xvlog/elaborate 都要带)
xvlog -d BOARD_ROM_FAST rtl/*.v board/cpu21_board_top.v testbench/tb_board_top.v
xelab -top tb_board_top -snapshot board_sim_fast -d BOARD_ROM_FAST
```
两份文件都由同样的两步生成,只有延时循环的初值不同(把 `board_demo.asm` 里的
`lui t2, 0xf` 换成 `addi t2, zero, 8` 再照上面两步走一遍,输出到
`cpu21_board_rom_fast.vh`)。
`board/cpu21_board_rom_fast.vh`)。
### Vivado 工程设置与实现结果
@@ -208,16 +239,17 @@ Vivado 工程 `pipelined-cpu/pipelined-cpu.xpr` 被 `.gitignore` 排除,需要
1. `sources_1` 顶层设为 `cpu21_board_top`(纯 RTL 仿真时也可切回
`cpu21_riscv_redirect_int_bpb`);
2. 加入 `cpu21_board_top.v`、`cpu21_board_top.xdc`,`cpu21_board_rom.vh` 的文件类型设成
`Verilog Header`(不能当普通源码,否则会被当成顶层候选);
3. 三个 testbench(`tb_cpu21_riscv_redirect_int_bpb.v`、`tb_no_intr.v`、`tb_board_top.v`)的
`USED_IN_SYNTHESIS` / `USED_IN_IMPLEMENTATION` 设为 `0`,否则综合可能把
testbench 当顶层,报 `[Place 30-494] The design is empty`。
2. 加入 `board/cpu21_board_top.v`、`board/cpu21_board_top.xdc`,
`board/cpu21_board_rom.vh` 的文件类型设成 `Verilog Header`(不能当普通源码,
否则会被当成顶层候选);
3. 三个 testbench(`testbench/tb_cpu21_riscv_redirect_int_bpb.v`、`tb_no_intr.v`、
`tb_board_top.v`)的 `USED_IN_SYNTHESIS` / `USED_IN_IMPLEMENTATION` 设为 `0`,
否则综合可能把 testbench 当顶层,报 `[Place 30-494] The design is empty`。
`xc7a100tcsg324-1` 上的实现结果(`CLK_DIV=16`):
| 项目 | 结果 |
| -------------- | ------------------------------------------------------------- |
| -------- | --------------------------------------------------------------------------- |
| 综合 | `synth_design Complete!` |
| 实现 | `route_design Complete!`,`WNS = +6.083 ns`(无 Failed Timing) |
| 资源 | LUT 5400 (8.52%)、LUTRAM 512、FF 2507 (1.98%)、DSP 12 (5%)、IOB 23 (10.95%) |
@@ -226,4 +258,3 @@ Vivado 工程 `pipelined-cpu/pipelined-cpu.xpr` 被 `.gitignore` 排除,需要
上板操作:用 Vivado 打开工程 → `Open Hardware Manager` → `Program Device`,选上述
`.bit`。复位后跑马灯立即从 `0001` 开始逐位左移(演示程序不执行 `ecall`,所以不用
按 `go`;`go` 只对执行了 `ecall` 停机、等待继续运行的程序有意义),LED16_B 每次写入时点亮。
View File
View File
+1 -1
View File
@@ -9,7 +9,7 @@
//
// 默认的 board_demo 程序带约 20 ms 软件延时,仿真只需确认第一次写入;
// 若要完整验证跑马灯序列,可生成一份短延时的 ROM 并用 -d BOARD_ROM_FAST 编译:
// xvlog -d BOARD_ROM_FAST <RTL...> cpu21_board_top.v testbench/tb_board_top.v
// xvlog -d BOARD_ROM_FAST board/cpu21_board_top.v <rtl/*.v> testbench/tb_board_top.v
// -----------------------------------------------------------------------------
module tb_board_top;
reg clk100 = 1'b0;
+11 -7
View File
@@ -1,6 +1,6 @@
// 与 RTL 文件(cpu21_riscv_*.v、cpu21_bpb_8.v)一起编译。
// 与 RTL 文件(rtl/cpu21_*.v)一起编译,仿真相关文件都在 testbench/ 下。
// 示例:
// iverilog -g2012 -o cpu21_sim cpu21_*.v tb_cpu21_riscv_redirect_int_bpb.v
// iverilog -g2012 -o cpu21_sim rtl/*.v testbench/tb_cpu21_riscv_redirect_int_bpb.v
// vvp cpu21_sim
// 仿真会写出 cpu21_riscv_redirect_int_bpb.vcd,可供 GTKWave 查看。
//
@@ -8,9 +8,13 @@
// 只会打印警告并保持内存原样,所以路径写错会让 ROM 看起来全是 NOP
// (0x00000013)。仿真器的工作目录并不固定(Vivado xsim 在
// <proj>.sim/sim_1/behav/xsim 下运行),因此本测试平台会依次探测若干候选
// 路径,并报告实际命中的那个。显式给出的路径始终优先:
// 路径(testbench/rom/、../testbench/rom/、…,相对仓库根目录),并报告实际
// 命中的那个。显式给出的路径始终优先:
// vvp cpu21_sim +ROM_FILE=<path> (iverilog/vvp)
// xelab ... -generic_top "ROM_FILE=<path>" (Vivado xsim)
// 注意:PowerShell 会把命令行参数里的 "=" 拆开(xelab/xsim 报
// "Expected a switch but found …"),此时直接改本文件的 ROM_FILE 参数即可;
// ROM 镜像都在 testbench/rom/ 下,路径请用正斜杠(Verilog 字符串里 "\" 是转义符)。
module tb_cpu21_riscv_redirect_int_bpb #(
parameter ROM_FILE = "cpu21_riscv_redirect_int_bpb_rom.hex"
// risc-v-benchmark_ccab.hex
@@ -227,10 +231,10 @@ module tb_cpu21_riscv_redirect_int_bpb #(
rom_loaded = 1'b0;
if ($value$plusargs("ROM_FILE=%s", rom_file_path)) load_rom_image(rom_file_path, rom_loaded);
if (!rom_loaded) load_rom_image(ROM_FILE, rom_loaded);
if (!rom_loaded) load_rom_image({"testbench/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../testbench/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../testbench/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../../../testbench/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"testbench/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../testbench/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../testbench/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../../../testbench/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded)
$display(
"WARNING: no ROM image found for \"%0s\" - the ROM stays filled with NOPs (0x00000013).",
+11 -7
View File
@@ -1,9 +1,9 @@
// 仿真顶层:tb_no_intr —— 与 tb_cpu21_riscv_redirect_int_bpb.v 相同的测试框架,
// 但不注入任何中断脉冲,因此主程序可以不受打扰地连续运行。
//
// 与 RTL 文件(cpu21_riscv_*.v、cpu21_bpb_8.v)一起编译。
// 与 RTL 文件(rtl/cpu21_*.v)一起编译,仿真相关文件都在 testbench/ 下。
// 示例:
// iverilog -g2012 -o cpu21_sim cpu21_*.v tb_no_intr.v
// iverilog -g2012 -o cpu21_sim rtl/*.v testbench/tb_no_intr.v
// vvp cpu21_sim
// 仿真会写出 tb_no_intr.vcd,可供 GTKWave 查看。
//
@@ -11,9 +11,13 @@
// 只会打印警告并保持内存原样,所以路径写错会让 ROM 看起来全是 NOP
// (0x00000013)。仿真器的工作目录并不固定(Vivado xsim 在
// <proj>.sim/sim_1/behav/xsim 下运行),因此本测试平台会依次探测若干候选
// 路径,并报告实际命中的那个。显式给出的路径始终优先:
// 路径(testbench/rom/、../testbench/rom/、…,相对仓库根目录),并报告实际
// 命中的那个。显式给出的路径始终优先:
// vvp cpu21_sim +ROM_FILE=<path> (iverilog/vvp)
// xelab ... -generic_top "ROM_FILE=<path>" (Vivado xsim)
// 注意:PowerShell 会把命令行参数里的 "=" 拆开(xelab/xsim 报
// "Expected a switch but found …"),此时直接改本文件的 ROM_FILE 参数即可;
// ROM 镜像都在 testbench/rom/ 下,路径请用正斜杠(Verilog 字符串里 "\" 是转义符)。
module tb_no_intr #(
parameter ROM_FILE = "risc-v-benchmark_ccab.hex"
// risc-v-benchmark_ccab.hex
@@ -230,10 +234,10 @@ module tb_no_intr #(
rom_loaded = 1'b0;
if ($value$plusargs("ROM_FILE=%s", rom_file_path)) load_rom_image(rom_file_path, rom_loaded);
if (!rom_loaded) load_rom_image(ROM_FILE, rom_loaded);
if (!rom_loaded) load_rom_image({"testbench/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../testbench/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../testbench/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../../../testbench/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"testbench/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../testbench/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../testbench/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded) load_rom_image({"../../../../../testbench/rom/", ROM_FILE}, rom_loaded);
if (!rom_loaded)
$display(
"WARNING: no ROM image found for \"%0s\" - the ROM stays filled with NOPs (0x00000013).",
+9 -3
View File
@@ -8,7 +8,7 @@
rom[0] = 32'h00001037;
rom[1] = 32'h00100093;
配套 RTL 用法(见 cpu21_board_top.v):
配套 RTL 用法(见 board/cpu21_board_top.v,两者同目录,所以 include 能直接解析):
initial begin
for (i = 0; i < ROM_WORDS; i = i + 1) rom[i] = 32'h0000_0013; // NOP 填充
@@ -18,6 +18,7 @@
这样 ROM 内容直接跟着 RTL 走,不依赖综合时的当前工作目录,
避免了 $readmemh 相对路径在 Vivado 综合流程里找不到文件(且只报 warning)的坑。
"""
import sys
NOP = 0x00000013
@@ -36,11 +37,16 @@ def main():
words.append(int(line, 16))
if len(words) > words_total:
raise SystemExit("程序 %d 个字,超过 ROM 容量 %d 个字" % (len(words), words_total))
raise SystemExit(
"程序 %d 个字,超过 ROM 容量 %d 个字" % (len(words), words_total)
)
with open(dst, "w", encoding="utf-8") as f:
f.write(" // generated by tools/hex2vh.py - do not edit by hand\n")
f.write(" // source: %s (%d words, remaining entries filled with NOP)\n" % (src, len(words)))
f.write(
" // source: %s (%d words, remaining entries filled with NOP)\n"
% (src, len(words))
)
for addr in range(words_total):
w = words[addr] if addr < len(words) else NOP
f.write(" rom[%d] = 32'h%08X;\n" % (addr, w))