如何使用香山之香山工具详解:difftest 应用及配合波形检验
在香山上仿真运行 coremark,开启 difftest 与 nemu 对比验证,观察对比正确时的输出结果
在一个有 bug 的香山上运行 coremark,开启 difftest 与 nemu 对比验证,观察对比出错时的输出结果
打印和观察波形(gtkwave)
引入 lightSSS
difftest 简介-copy
difftest(差分测试)difftest 是一个协同仿真框架,它在仿真运行时负责将香山核的输出与 NEMU 的输出进行对比,判断香山是否按照指令集定义的那样正确运行。difftest 是验证香山功能正确性的重要工具,也对我们定位 bug 和解决 bug 提供了极大帮助。difftest 与香山代码有较高的耦合,目前是作为一个子模块放在香山目录下,在编译仿真程序 emu 时将自动使用。在仿真运行(执行 emu 时),通过 --diff 参数指定 nemu 来开启对比功能,如果不需要对比可以使用 --no-diff 关闭该功能,这时将只进行香山核的仿真。编译香山核的仿真程序 EMU 有了前面部分的基础,这里我们尝试自行编译前面仿真演示中使用的香山仿真程序 EMU。这一过程中使用 mill 将香山的 chisel 语言代码编译为 Verilog 语言代码,然后通过 verilator 将 Verilog 代码编译为 c++ 语言的仿真模拟程序。这些步骤已经在香山的 Makefile 里预先配置好了,我们可以通过如下命令直接编译
对比正确时的 difftest
在开启 difftest 状态下运行 emu
对比正确时,输出结果为:
其中绿色: HIT GOOD TRAP at pc = 0x800026ba 即为代表 difftest 对比一致。
对比错误时的 difftest
使用特制的含有 bug 的 emu:
在开启 difftest 状态下运行 emu
对比出错时,输出结果为:
其中红色: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 计算有错误。
打印和观察波形
上述 difftest 结果帮我们定位到了 add 操作的错误,但 cpu 内部较为复杂,具体哪里出了问题,有时需要打印波形查看。我们可以使用 emu 的 --dump-wave 参数打印波形(前提,在编译 emu 时开启了 EMU_TRACE=1 参数)。-b 和 -e 参数可以选择打印波形的周期范围,根据上面 emu 出错结果可以看到出错时 cycleCnt=2271,只需要在这附近打印波形即可。
打印波形会重新跑一遍仿真,最后额外生成一个波形文件。在香山目录结构下,波形文件会放到 build 目录下,默认是 vcd 格式。你也可以手动指定波形放置的路径,具体参数请查看 emu 的 help。vcd 是一种无压缩的格式,如果打印的周期数较多,其体积会非常庞大;同时使用 gtkwave 等开源工具打开波形的速度较慢。emu 也支持生成另一种压缩格式波形——fst,但是需要在编译 emu 时使用 EMU_TRACE=fst 参数。fst 格式的波形大小不到 vcd 格式波形大小的 10%,但缺点是该格式的波形为 gtkwave 专属,只能由 gtkwave 打开。而 vcd 格式更通用,可以被更多查看波形的工具支持。为了调整波形格式重新编译 emu 多少有些麻烦,下面要讲到的 gtkwave 会自带将 vcd 转为 fst 格式的工具。
gtkwave 是一款开源的波形查看软件,在 ubuntu 下可以通过以下命令安装:
gtkwave 同时支持 vcd 和 fst 格式的波形文件,但 fst 加载速度更快。gtkwave 附带的组件 vcd2fst 则支持将 vcd 波形转为 fst 波形。
使用 gtkwave 打开波形:
以上面运行错误的 emu 为例,我们打开波形并筛选如下:其中,左上是硬件模块的层级结构,可以筛选需要关注的模块;左下是当前选择模块的信号;右边是具体的信号波形。需要注意一点,目前导出的波形只有每周期时钟上升区间的这一半,因此在波形里看不到时钟变化,但这并不影响观察其他信号的波形。另外,chisel 里的变量和波形里的变量名并不是完全对应的。chisel 的类成员和数组等会被展开,一些信号可能被优化消失,将 chisel 变量和波形信号关联起来需要仔细观察和一些经验。
简要描述一下定位 debug 的思路。首先在 rob 里找到出错 addi 指令的提交时刻;根据 robIdx,定位到写回的端口以及数据,发现此时数据已经出错。再往前到功能单元 ALU 里查找 addi 的执行过程,发现两个源操作数均正确,但结果出错,于是判断加法的代码存在问题。这时回过头来查看 ALU 里加法的代码,发现+号被错写成了-号,问题解决。当然,以上只是一个非常简单粗暴的例子,实际开发中错误可能发生在流水线的任何一个环节,有些时候可能需要按流水级一级一级地定位信号。
lightSSS
不难发现,以上 emu 仿真 -> difftest 报错 -> 重新仿真打印波形的过程有一个问题,那就是需要运行第二次仿真来打印波形。想象一下,如果错误不是发生在 2000 个周期,而是发生在 20000000 个周围处,重跑一次仿真的开销是非常大的。为了解决这个问题,我们设计了 lightSSS 工具。它在仿真时每隔一定时间 fork 一个子进程对仿真状态进行快照,如果父进程仿真出错,就会通知最新的子进程从快照处重新执行,并且这次执行时会打印出波形。这样我们就可以在仿真出错时,自动打印出错之前一段时间的波形。如今这一功能已经集成在 emu 中,仿真时通过 --enable-fork 参数即可开启。
由于运行两次,在出错时会打印两遍 difftest 的 log 信息,生成波形文件的位置会写在 log 里。
欢迎大家来开源芯片社区查看更多香山使用文档。
https://www.gitlink.org.cn/OSchip/HowtoUseXiangShan/about
同时,香山社区志愿者招募中,将个人简历发送至
anxu@bosc.ac.cn, 标题"香山开源生态建设志愿者报名"。
版权声明: 本文为 InfoQ 作者【源芯】的原创文章。
原文链接:【http://xie.infoq.cn/article/1c3bb9cd567d3dceb4e5c1f49】。文章转载请联系作者。
评论