After modifying the Block Design in GUI:
- Save BD (Ctrl+S)
- Regenerate and re-import wrapper:
- In Vivado Tcl Console:
cd [get_property DIRECTORY [current_project]] source scripts/update_wrapper.tcl update_bd_wrapper
- Or in terminal:
vivado -mode batch -source scripts/update_wrapper.tcl -tclargs kcu105.xpr
- In Vivado Tcl Console:
- Commit the updated wrapper file:
risc-v.srcs/sources_1/imports/hdl/*_wrapper.v- and BD
.bdif it changed
本專案目前提供三種測試流程:
- Icarus Verilog(iverilog)+
.exp提交序列比對:用於你自己寫的program/source/*.S單元測試 - Verilator + Spike 對照(log + signature):用於跑
riscv-tests(ELF)並確保結果與 Spike 一模一樣 - Verilator + 自己寫的單元測試 + Spike
make run TEST=test-name其中 test-name 代表應該存在這兩個檔案: program/source/test-name.S program/exp/test-name.exp
常用目標:
make test-summary:跑program/hex/*_prog.hex所有測試,輸出每個測試 PASS/FAIL 與總結make test-all:跑全部測試(輸出較多)make run-quiet TEST=<name>:只輸出一行結果(方便寫腳本/CI)
exp 文件用于定义「執行期間每一次寄存器寫回(commit)」的期望值序列。
寄存器编号 期望值
- 每行一筆:按照實際執行的「提交(commit)順序」逐條列出
- 寄存器編號:1-31(x1–x31),不包含 x0(寫 x0 不會被記為提交)
- 期望值:32 位十六進位,8 碼,無 0x 前綴(如
00000001) - 支援兩種註解格式:
- 整行註解:以
#開頭的整行會被忽略 - 行尾註解:在數值後加上
#及註解內容
- 整行註解:以
- 同一個寄存器若被多次寫回,需多次列出,並維持實際發生的先後順序
- 基本格式:每行為
<rd> <value>或<rd> <value> # 註解
# 初始化测试
1 00000000 # x1 = 0
2 00000001 # x2 = 1
3 00000002 # x3 = 2
# 数据前递测试
6 0000000a # x6 = 10
7 0000000b # x7 = 11
8 0000000d # x8 = x6 + x7 = 13
# 同一暫存器連續寫入範例(如 hazard 測試)
6 000000F0 # 第一次寫入 x6
6 000000FF # 第二次寫入 x6
6 0000000F # 第三次寫入 x6
6 00000000 # 最後一次寫入 x6
- 必須嚴格依照實際提交順序撰寫,不可依寄存器編號排序
- 只需列出「會寫回暫存器」的指令;如 store/branch 等不寫回的指令不需出現
- 驗證器在匹配完 exp 全部條目後會提前結束;如出現多餘提交會回報錯誤
tb_riscv_cpu.sv的MAX_COMMITS = 1024,若測試更大需同步調整
- 於時鐘負緣觀察到一次合法的寄存器寫回事件(
rd != x0且寫回有效)即記為一次提交 - 允許連續對同一寄存器提交(即便值相同),因此 exp 中可以連續出現同一寄存器
- 8 位十六進位,不帶
0x前綴;大小寫皆可(如0000000a或0000000A) - 支援兩種註解格式:
- 整行註解:以
#開頭 - 行尾註解:
<rd> <value> # 註解內容
- 整行註解:以
- 允許空行與註解行,解析時會被忽略
- 建議加上註解,便於之後 debug
正確格式:
20 F8000000 # 沒有註解也可以
20 FC000000 # x20 左移
20 FE000000 # 再次左移
22 00000001 # x22 = 1
# 這是整行註解
23 00000002 # x23 = 2
錯誤格式(會導致解析錯誤):
22 20 F8000000 # 錯誤:三個欄位(第二個 20 會被誤解為值)
0x14 F8000000 # 錯誤:寄存器編號不能有 0x 前綴
20 0xF8000000 # 錯誤:值不能有 0x 前綴
make run TEST=<name>會自動傳入:+HEX=program/hex/<name>_prog.hex +EXP=program/exp/<name>.exp +CASE=<name>- 若 exp 檔存在:逐條比對提交序列;全部匹配後測試提前結束
- 若出現「未預期的提交」(超過 exp 條目),會報錯
以下是一個實際的 hazard 測試 exp 檔案範例,展示了註解的用法:
# Hazard 測試:驗證資料前遞(forwarding)和危害偵測
# 初始化暫存器
1 0000000a # x1 = 10
2 00000005 # x2 = 5
# RAW hazard 測試
3 0000000f # x3 = x1 + x2 = 15(需要前遞 x1, x2)
4 0000000a # x4 = x1 = 10
# 連續相依性測試
5 00000019 # x5 = x3 + x4 = 25(需要前遞 x3, x4)
6 00000008 # x6 = 8
# Load-use hazard 測試
7 40218233 # x7 = lw 結果
8 4021823b # x8 = lw 結果
- 從實際提交導出 exp(僅作為草稿,請自行審核):
vvp ./simv +HEX=program/hex/xor_prog.hex +CASE=xor \
+DUMP_EXP=program/exp/xor.auto.exp +QUIET執行後會於 program/exp/xor.auto.exp 輸出觀測到的提交序列,可據此整理成最終 .exp
-
注意:該方法輸出的內容未必正確(DUT 可能有 bug),使用時請審慎比對規格
-
禁止某些暫存器被寫回(例如禁止寫 x0/x1/x2):
vvp ./simv +HEX=... +CASE=... +EXP=... +FORBID_RD=0,1,2- 在比較提交序列之外,額外斷言若干暫存器的最終值:
vvp ./simv +HEX=... +CASE=... +EXP=... +EXPECT_REG=1:0x1,2:0x2,3:0x3- 輸出詳略控制:
+QUIET:簡化輸出;+SUMMARY:僅摘要;+VERBOSE:更多中間訊息
- exp 中的每一行對應一次「寫回事件」,非「某條指令的靜態期望」。若同一條指令未寫回(例如分支未被採用),就不會在 exp 中出現
- 允許連續多次寫回至同一個目的暫存器(如多步運算、hazard 測試)。請依實際觀察到的先後次序列出
- 對含 load 的場景,若資料尚未就緒,提交只會在真正寫回之那一個週期出現
- RV32 的移位類指令(SLL/SRL/SRA)之 shamt 寬度為 5(log2(XLEN)),動態移位使用 rs2 的低 5 位;RV64 為 6 位
- 例:在 RV32 中
sll x3, x1, x2的位移量為x2[4:0];若x2=32,實際位移為 0
- 例:在 RV32 中
- exp 必须对应"实际执行路径"的提交顺序,分支未执行到的指令不应写入 exp
- 同一寄存器多次写入要多次列出,顺序必须与执行一致
- 对含 load 的场景,若数据通路存在等待,提交只在真正写回的那个周期出现
- 註解功能 支援行尾註解
- 整行註解:
# 這是整行註解 - 行尾註解:
1 00000001 # x1 = 1 - 兩種格式都可以使用:
# 方法一:整行註解 # x1 = 1 1 00000001 # 方法二:行尾註解(推薦) 1 00000001 # x1 = 1
- 整行註解:
- RISC-V I-type 指令的立即數是 12-bit 有符號數,範圍是 -2048 到 +2047
- 超過此範圍的值需要用負數形式表示:
- 例如:
xori x1, x2, 0xAAA是錯誤的(0xAAA = 2730 > 2047) - 應寫成:
xori x1, x2, -1366(-1366 的 12-bit 表示就是 0xAAA)
- 例如:
- addi、xori、ori、andi 等 I-type 指令都會對 12-bit 立即數進行符號擴展到 32-bit
- lui 指令使用 20-bit 立即數,載入到高 20 位
前置需求:
spike:/opt/riscv/bin/spikeriscv64-unknown-elf-objcopy:/opt/riscv/bin/riscv64-unknown-elf-objcopy
(ELF 可以是 repo 內的檔案,也可以是絕對路徑,例如 riscv-tests 目錄下的 ELF)
make riscv-test ELF=rv32ui-p-add
# 或
make riscv-test ELF=/opt/riscv/target/share/riscv-tests/isa/rv32ui-p-add輸出檔會在 build/riscv-tests/:
<name>.spike.log/<name>.dut.log:Spike vs DUT 的 commit log(逐字比對)<name>.spike.sig/<name>.dut.sig:signature dump(若該測試 signature 區間為空,sig 會是空檔)<name>.runner.txt:完整比對過程(FAIL 時優先看這個)
make riscv-tests可用參數(環境變數):
PATTERN:要跑的檔名 glob(預設rv32ui-p-*)DIR:ELF 目錄(預設/opt/riscv/target/share/riscv-tests/isa)LIMIT:只跑前 N 個(smoke 用)FAIL_FAST=1:遇到第一個 FAIL 就停止ISA:Spike ISA 字串(預設rv32i)SPIKE_MAX_INSN:Spike 最多跑多少指令(預設200000)MAX_CYCLES:DUT 最多跑多少 cycle(預設200000)
範例:
make riscv-tests LIMIT=10 FAIL_FAST=1
make riscv-tests PATTERN='rv32ui-p-*'
make riscv-tests DIR=/opt/riscv/target/share/riscv-tests/isa PATTERN='rv32ui-p-*'執行完會在 build/riscv-tests/summary.txt 產生 PASS/FAIL 總表。
這條流程跟 2) 的目標一樣:把 DUT 的提交 log / signature 做成 Spike 相容格式並 逐字比對,
差別在於 DUT 不是 Verilator,而是 Vivado XSim,且指令記憶體使用 BRAM IP(blk_mem_gen_0)。
make xsim-test ELF=/opt/riscv/target/share/riscv-tests/isa/rv32ui-p-add輸出檔會在 build/xsim-tests/:
<name>.spike.log/<name>.xsim.log:Spike vs XSim 的 commit log(逐字比對)<name>.spike.sig/<name>.xsim.sig:signature dump(若該測試 signature 區間為空,sig 會是空檔)<name>.runner.txt:完整比對過程(FAIL 時優先看這個)summary.txt:suite 模式的 PASS/FAIL 總表
make xsim-test
# 或
make xsim-tests可用參數(環境變數,刻意對齊 make riscv-tests 的介面):
PATTERN:要跑的檔名 glob(預設rv32ui-p-*)DIR:ELF 目錄(預設/opt/riscv/target/share/riscv-tests/isa)LIMIT:只跑前 N 個(smoke 用)FAIL_FAST=1:遇到第一個 FAIL 就停止ISA:Spike ISA 字串(預設rv32i)SPIKE_MAX_INSN:Spike 最多跑多少指令(預設200000)MAX_CYCLES:DUT 最多跑多少 cycle(預設200000)
範例:
make xsim-tests LIMIT=10 FAIL_FAST=1
make xsim-tests PATTERN='rv32ui-p-*'
make xsim-tests DIR=/opt/riscv/target/share/riscv-tests/isa PATTERN='rv32ui-p-*'XSim 記憶體載入方式
tb_xsim.sv使用+HEX=<path>透過$readmemh同時載入 instruction memory 和 data memory- Instruction memory 使用
true_dual_port_bram(RTL 實作),testbench 直接存取dut.inst_mem_multicycle_0.imem.ram - 不再依賴 Vivado Block Memory IP 的 COE 初始化,每次測試可以快速切換不同的 ELF
補充:
make xsim-test-quick ELF=...使用--skip-setup,跳過 Vivado 設定步驟,適合快速迭代測試
make spike-selftests
make xsim-selftests單檔測試
make store-spike
make store-xsimmake check-bram會嘗試 synthesis 並列出報告看看 true_dual_port_bram.sv 是否合成出 BRAM
請先做:
make bootrom然後直接在 Vivado 執行 Synthesis -> Implementation -> Generate Bitstream