龙空技术网

如何使用香山之香山工具详解:difftest应用及配合波形检验

源芯 31

前言:

眼前我们对“波形图软件”大概比较珍视,看官们都需要剖析一些“波形图软件”的相关知识。那么小编也在网摘上收集了一些对于“波形图软件””的相关资讯,希望各位老铁们能喜欢,姐妹们一起来学习一下吧!

在香山上仿真运行coremark,开启difftest与nemu对比验证,观察对比正确时的输出结果在一个有bug的香山上运行coremark,开启difftest与nemu对比验证,观察对比出错时的输出结果打印和观察波形(gtkwave)引入lightSSSdifftest简介-copy

difftest(差分测试)difftest 是一个协同仿真框架,它在仿真运行时负责将香山核的输出与 NEMU 的输出进行对比,判断香山是否按照指令集定义的那样正确运行。difftest 是验证香山功能正确性的重要工具,也对我们定位 bug 和解决 bug 提供了极大帮助。difftest 与香山代码有较高的耦合,目前是作为一个子模块放在香山目录下,在编译仿真程序 emu 时将自动使用。在仿真运行(执行 emu 时),通过 --diff 参数指定 nemu 来开启对比功能,如果不需要对比可以使用 --no-diff 关闭该功能,这时将只进行香山核的仿真。编译香山核的仿真程序 EMU有了前面部分的基础,这里我们尝试自行编译前面仿真演示中使用的香山仿真程序EMU。这一过程中使用 mill 将香山的 chisel 语言代码编译为 Verilog 语言代码,然后通过 verilator 将 Verilog 代码编译为 c++ 语言的仿真模拟程序。这些步骤已经在香山的 Makefile 里预先配置好了,我们可以通过如下命令直接编译

1. 对比正确时的difftest在开启 difftest 状态下运行 emu

./build/emu -i ready-to-run/coremark-2-iteration.bin --diff ready-to-run/riscv64-nemu-interpreter-so 2> perf.log

对比正确时,输出结果为:

其中绿色: HIT GOOD TRAP at pc = 0x800026ba 即为代表 difftest 对比一致。

2. 对比错误时的difftest

使用特制的含有 bug 的 emu:

/nfs-nvme/home/share/jiazhijie/emu_bad_trap/emu

在开启 difftest 状态下运行 emu

./build/emu -i ready-to-run/coremark-2-iteration.bin --diff ready-to-run/riscv64-nemu-interpreter-so 2> perf.log

对比出错时,输出结果为:

其中红色:ABORT at pc = ... 代表 difftest 对比出错。

上述出错时的 log 较为复杂,这里简要介绍一下。

香山每周期会提交一定数量的指令,difftest会将这些指令的结果与nemu提供的正确结果进行对比,ABORT上面两行就是对比出错时,那些有区别的寄存器。当然,某些出错情况比如跳转出错、长时间未提交等不涉及寄存器修改,这部分内容就不存在。

回到log开头,Commit Group Trace是出错周期附近的提交情况,每一行代表一个周期,cmtcnt是该周期提交的指令数量,pc是该周期第一条指令的pc。箭头指向的就是对比出错的周期。

Commit Instr Trace是出错周期附近的指令情况,给出了更详细的指令信息。箭头指向的是对比出错周期的最后一条指令。通过前面知道该周期提交了两条指令,从箭头处往前两条,就是 addi 和 lui。两条指令的目标寄存器是 sp 和 a0,与出错的寄存器也正好对应。

后面较长的 REF Regs,打印了出错时刻 nemu 提供的正确状态,可以用来帮助 debug。

addi 和 lui 指令在香山内部都是使用 ALU 里的 add 单元计算,这里我们推测是 add 计算有错误。

3. 打印和观察波形

上述 difftest 结果帮我们定位到了 add 操作的错误,但 cpu 内部较为复杂,具体哪里出了问题,有时需要打印波形查看。

我们可以使用 emu 的 --dump-wave 参数打印波形(前提,在编译 emu 时开启了 EMU_TRACE=1 参数)。

-b 和 -e 参数可以选择打印波形的周期范围,根据上面 emu 出错结果可以看到出错时 cycleCnt=2271,只需要在这附近打印波形即可。

./build/emu -i ready-to-run/coremark-2-iteration.bin --diff ready-to-run/riscv64-nemu-interpreter-so --dump-wave -b 2000 -e 2271 2> perf.log

打印波形会重新跑一遍仿真,最后额外生成一个波形文件。在香山目录结构下,波形文件会放到 build 目录下,默认是 vcd 格式。你也可以手动指定波形放置的路径,具体参数请查看 emu 的 help。

vcd 是一种无压缩的格式,如果打印的周期数较多,其体积会非常庞大;同时使用 gtkwave 等开源工具打开波形的速度较慢。

emu 也支持生成另一种压缩格式波形——fst,但是需要在编译 emu 时使用 EMU_TRACE=fst 参数。fst 格式的波形大小不到 vcd 格式波形大小的 10%,但缺点是该格式的波形为 gtkwave 专属,只能由 gtkwave 打开。而 vcd 格式更通用,可以被更多查看波形的工具支持。

为了调整波形格式重新编译 emu 多少有些麻烦,下面要讲到的 gtkwave 会自带将 vcd 转为 fst 格式的工具。

gtkwave 是一款开源的波形查看软件,在ubuntu下可以通过以下命令安装:

sudo apt install gtkwave

gtkwave 同时支持 vcd 和 fst 格式的波形文件,但 fst 加载速度更快。gtkwave 附带的组件 vcd2fst 则支持将vcd波形转为 fst 波形。

vcd2fst xxx.vcd xxx.fst

使用 gtkwave 打开波形:

gtkwave xxx.fst

以上面运行错误的 emu 为例,我们打开波形并筛选如下:

其中,左上是硬件模块的层级结构,可以筛选需要关注的模块;左下是当前选择模块的信号;右边是具体的信号波形。

需要注意一点,目前导出的波形只有每周期时钟上升区间的这一半,因此在波形里看不到时钟变化,但这并不影响观察其他信号的波形。

另外,chisel 里的变量和波形里的变量名并不是完全对应的。chisel 的类成员和数组等会被展开,一些信号可能被优化消失,将 chisel 变量和波形信号关联起来需要仔细观察和一些经验。

简要描述一下定位 debug 的思路。首先在 rob 里找到出错 addi 指令的提交时刻;根据 robIdx,定位到写回的端口以及数据,发现此时数据已经出错。再往前到功能单元 ALU 里查找 addi 的执行过程,发现两个源操作数均正确,但结果出错,于是判断加法的代码存在问题。

这时回过头来查看 ALU 里加法的代码,发现+号被错写成了-号,问题解决。

当然,以上只是一个非常简单粗暴的例子,实际开发中错误可能发生在流水线的任何一个环节,有些时候可能需要按流水级一级一级地定位信号。

4. lightSSS

不难发现,以上 emu 仿真 -> difftest 报错 -> 重新仿真打印波形的过程有一个问题,那就是需要运行第二次仿真来打印波形。想象一下,如果错误不是发生在2000个周期,而是发生在20000000个周围处,重跑一次仿真的开销是非常大的。

为了解决这个问题,我们设计了 lightSSS 工具。它在仿真时每隔一定时间 fork 一个子进程对仿真状态进行快照,如果父进程仿真出错,就会通知最新的子进程从快照处重新执行,并且这次执行时会打印出波形。这样我们就可以在仿真出错时,自动打印出错之前一段时间的波形。

如今这一功能已经集成在 emu 中,仿真时通过 --enable-fork 参数即可开启。

./build/emu -i ready-to-run/coremark-2-iteration.bin --diff ready-to-run/riscv64-nemu-interpreter-so --enable-fork 2> perf.log

由于运行两次,在出错时会打印两遍 difftest 的 log 信息,生成波形文件的位置会写在 log 里。

标签: #波形图软件