基于TinyEMU的RISC-V指令集验证实战(一)
1. 从零搭建RISC-V指令验证环境第一次接触RISC-V指令集验证的朋友可能会觉得这是个高大上的技术活其实只要选对工具整个过程就像搭积木一样简单。我去年在开发一个RISC-V教学模拟器时就深深体会到了TinyEMUriscv-tests这套组合的便利性。相比QEMU等重型模拟器TinyEMU的代码量只有几千行特别适合快速验证指令集实现。riscv-tests测试套件就像是RISC-V世界的体检中心里面包含了各种体检项目测试用例。比如rv64ui-p-add这个测试就是专门检查64位用户模式下加法指令ADD是否正确实现的。想象一下你刚写完一个加法指令的模拟代码怎么知道它真的符合RISC-V标准直接跑这个测试就能见分晓。2. 准备编译工具链2.1 交叉编译器选择刚开始我图省事直接用了系统自带的gcc结果编译出来的测试程序根本跑不起来。后来才明白这就像用中文说明书去指导一个只会英文的人干活——必须用专门针对RISC-V架构的交叉编译器。这里推荐使用riscv64-elf-gcc它就像是专门为RISC-V定制的翻译官。安装过程比想象中简单wget https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2023.01.31/riscv64-elf-ubuntu-20.04-nightly-2023.01.31-nightly.tar.gz tar -xvzf riscv64-elf-*.tar.gz echo export PATH$PATH:/path/to/riscv/bin ~/.bashrc source ~/.bashrc2.2 验证安装装好后千万别急着下一步先做个简单验证riscv64-unknown-elf-gcc -v如果看到类似这样的输出说明你的翻译官就位了gcc version 12.1.0 (riscv64-elf)3. 获取并编译测试套件3.1 下载源码riscv-tests的仓库就像个百宝箱git clone https://github.com/riscv/riscv-tests cd riscv-tests git submodule update --init --recursive这里有个坑我踩过——忘记更新子模块结果编译时各种头文件找不到。所以务必记得执行submodule更新。3.2 编译配置编译前需要做些准备工作autoconf ./configure --prefix$RISCV/target这步会生成适合你系统的编译配置。有次我在ARM主机上忘记配置直接make结果编译出来的居然是ARM架构的测试程序闹了个大笑话。3.3 选择性编译全量编译要等很久其实可以按需编译cd isa make rv64ui-p-add编译完成后会在当前目录生成两个关键文件rv64ui-p-addELF格式可执行文件rv64ui-p-add.dump对应的汇编代码4. TinyEMU环境配置4.1 文件格式转换TinyEMU这个轻量级跑步机有个特点——它只认RAW格式的二进制文件。这就好比把Word文档转成纯文本riscv64-unknown-elf-objcopy -O binary rv64ui-p-add rv64ui-p-add.bin4.2 配置文件调整修改TinyEMU的配置文件就像调整跑步机的参数{ version: 1, machine: riscv64, memory_size: 128, bios: rv64ui-p-add.bin, // 替换原来的bbl64.bin // 其他配置保持不变... }这里有个细节要注意测试程序的入口地址必须和配置匹配。通过查看.dump文件可以确认80000000 _start: 80000000: 0500006f j 80000050 reset_vector4.3 模拟器源码修改要让TinyEMU能自动判断测试结果需要修改riscv_cpu.c文件。这就像给裁判装上自动计分器static void raise_exception2(RISCVCPUState *s, uint32_t cause, target_ulong tval) { if (s-reg[17] 0x5d) { // 检查a7寄存器 if (s-reg[10] 0) { // 检查a0寄存器 printf([\033[32mPASS\033[0m] Test passed successfully\n); } else { printf([\033[31mFAIL\033[0m] Test #%d failed\n, s-reg[10]/2); } } }这个修改增加了彩色输出测试通过显示绿色PASS失败显示红色FAIL视觉效果更直观。5. 运行与结果验证5.1 启动测试一切就绪后启动命令很简单./temu -ctrlc root-riscv64.cfg但要注意几个细节.bin文件必须和temu放在同一目录确保配置文件路径正确内存大小配置要足够128MB通常够用5.2 结果解读看到终端输出[PASS] Test passed successfully时那种成就感就像考试得了满分。如果显示FAIL也别慌这时可以检查.dump文件看测试逻辑用spike模拟器交叉验证在TinyEMU中加调试打印我在第一次实现时就遇到ADD指令没正确处理溢出标志的情况正是通过这个测试发现的。后来加了条简单的条件判断就修复了if (overflow) set_csr_bit(CSR_FFLAGS, 0x01);6. 进阶技巧与排错6.1 批量测试技巧单个测试没问题后可以尝试批量运行make isa find isa -name *.bin | xargs -I {} cp {} tests/ for test in tests/*.bin; do sed s/bios:.*/bios: \${test}\/ root-riscv64.cfg tmp.cfg ./temu -ctrlc tmp.cfg done这个脚本会自动运行isa目录下所有测试适合做回归测试。6.2 常见问题解决段错误问题检查.bin文件是否完整可以用hexdump查看头部寄存器值异常确认交叉编译器版本是否匹配测试卡住在TinyEMU源码中加入更多调试输出有次我遇到测试一直卡住后来发现是ecall指令实现不完整。通过在raise_exception2函数开头加打印很快就定位到了问题。这套验证方法最大的优势就是透明——所有环节都在你的掌控中。相比商业EDA工具动辄几个GB的体积TinyEMUriscv-tests的组合只有几十MB但验证效果毫不逊色。