Post-processing with VCD+ files
后处理基本概念
之前的仿真都是交互式的,每一步操作都能看到对应的结果。
后处理的意思是,保存仿真过程中的一些文件(如波形文件),然后在整个仿真流程结束之后,通过DVE、Verdi 等工具打开波形文件,分析仿真结果。
VCD+ 就是VCS产生的一种波形文件的格式。(VSC还能产生其他格式的波形文件,如用于 Verdi 的 fsdb 文件)
因为VCD文件不会自动产生,所以需要我们在源代码( testbench)中加入一些 VCD+ 相关的系统任务。这样在编译仿真后就可以得到 VCD+ 文件。
后处理的基本过程
后处理过程需要考虑的一些因素
- 速度。大型 SoC 的仿真时间是很长的,仅仅考虑计算机的运行速度是不够的,还要分析代码的质量、编译仿真过程中的开关等等。
比如,仿真过程打开波形是很耗时的。所以一般是在设计的初期或仿真平台搭建的初期,保存波形信息,因为这一阶段设计不稳定,可能存在各种问题。随着不断的优化完善,在设计收敛之后,仿真的时候是会关掉波形的,这样能大幅提高仿真速度。只有在出现问题的时候,才会打开波形进行调试。 - 可见性和可追踪性都是为了寻找仿真过程中存在的问题
- 可用性(Verdi,Windows版本叫Debussy,是最好用的 debug 工具)
后处理的应用时间
VCD+ 文件
VCD+只是一种说法,因为它是对VCD文件的拓展,真正产生的的波形文件的后缀名是 .vpd 。
VCD+ System Task
VCD+ 系统任务可以插入源文件中,或者是在仿真交互的时候使用。主要的系统函数如下:
$dumpvars # 创建一个VCD文件
$vcdplusfile("filename") # 指定一个VPD文件名,代替默认的 vcdplus.vpd
$vcdpluson (level,instance,...,net_or_reg )
level
:表示记录的层次。0或缺省表示记录指定模块的所有的层次,1是记录指定模块的顶层,n就是记录从指定模块顶层开始的n个层次。(当前的设计都是层次化设计,模块套模块,每一个嵌套就是一个层次。)
instance
:指定需要记录的模块,使用的是模块的实例化名称。(level
指定记录的层次就是以这里指定的模块为起点。)
net_or_reg
:记录net变量或reg变量,缺省时记录所有的 reg 和 net 变量。
$vcdplusoff(instance,...,net_or_reg)
停止记录波形。
示例:
$readmemb ("adder_ref.vec",testmem);
的意思是读取 adder_ref.vec文件中的内容,保存到数组 testmem 中。b表示二进制格式。(主要用于数组或存储器的初始化)
writememb
就是将数组中的信息写到文件中。
编译时的 VCD+ 选项
vcdplus_switches 就是和VCD+相关的一些选项,一些常见的具体选项如下:
-debug
:产生vpd文件的选项
+vpdfile+filename
:指定要写入的vpd文件名,而不用默认的 vcdplus.vpd
可以通过ifdef
命令来控制是否生成 vpd 文件,如下所示:
在源代码(testbench)中定义如下宏,
`ifdef dumpme
$vcdpluson();
`endif
然后在编译的时候,通过使用 +define+dumpme
选项来控制是否生成 VCD+ 文件。
Lab
测试用例还是之前的 fsm_moore。
为了产生VPD文件,需要在testbench源代码中加入如下系统函数:
(这里是通过宏实现的)
(可以通过在源代码头部定义宏来执行该部分代码,也可以通过 vcs 的编译命令来执行该部分代码。)
Makefile文件
仿真使用的makefile文件如下所示:
.PHONY: con cov clean debug
OUTPUT = simv_fsm_moore
ALL_DEFINE = +define+DUMP_VPD
#vdp file name
VPD_NAME = +vpdfile+$(OUTPUT).vpd
# Compile command
VCS = vcs -sverilog +v2k -timescale=1ns/1ns \
-debug_all \
+notimingcheck \
+nospecify \
+vcs+flush+all \
$(ALL_DEFINE) \
$(VPD_NAME) \
-o $(OUTPUT) \
-l compile.log
# Simulation command
SIM = ./$(OUTPUT) \
$(VPD_NAME) \
-l $(OUTPUT).log
# Start Compile
con:
$(VCS) -f file_list
# Start simulation
sim:
$(SIM)
debug:
dve -vpd $(OUTPUT).vpd &
# Start Clean
clean:
rm -rf ./csrc *.daidir *.log *.vpd *.vdb simv* *.key *tace.out*
其中filelist文件的内容如下, 这里是把所有的源文件的目录名称都放在了一个新的文件中。
基于makefile的编译和仿真
源文件添加了系统函数,编译指令也添加了 VCD+ 相关的选项之后,使用 make con
命令就可以执行比编译过程。
complie.log
文件中的内容如下:
编译前的文件内容:
执行 make con
命令后的文件内容:
(可以看出编译之后不会产生 vpd文件。)
执行 make sim
命令开始仿真:
仿真之后的文件内容:
这里可以看到,仿真之后才产生了 vpd 文件。
vpd 文件的使用
通过编译和仿真,得到了 vpd 文件,之后就可以在 DVE 中打开波形。
第一种打开方式是先使用 dev &
命令在后台启动 DVE 的图形化界面,然后在图形化界面打开 vpd 波形文件。
第二钟打开方式是使用dve -vpd $(OUTPUT).vpd &
, 使用make debug
其实就是执行makefile文件中的这一条命令。
图形界面打开波形文件的选项是 : File >> Open Database
打开之后如下:
选中要观察的信号,右键添加到波形窗口中,如下:
双击 dout 输出为 9 的地方之后,打开的界面如下:
源代码(tb文件)中不同的 vcdpluson 选项
$vcdpluson()
在不加任何参数的情况下, 会记录所有的波形文件。
$vcdpluson(1,fsm_top)
该选项只记录了 fsm_top 第一层的信号,如下,左侧层次化界面就不能展开了。(通过使用该选项就也可以记录指定模块的波形。)
记录数组(mem)中的信息
默认情况下, VCS 波形是不会记录数组中的信息的。
源文件中数组的定义:
数组初始化:
src.txt文件的内容如下:
编译仿真之后如下:
数组中保存了文件中定义的信息。
① 可以看出跳过的存储单元的值是 xx,即没有写入数据。
② $readmemh()
是将 src.txt 文件中的内容当作 16 进制来看待,并非是 10 进制。因为我们在$display()
中定义的输出文件格式是10进制,所以存储的 a,b 输出的是 10,11;存储的 11 - 13 输出的是 17 - 19 。如果更改dsisplay的输出格式为16进制,得到的输出如下:
③ $readmemb()
是读取二进制文件的信息,也就是说,输入文件的内容要是二进制格式的。
仿真结束之后,打开波形文件,可以看到默认情况是是不记录数组的波形的。
如果想要记录,需要在源文件(tb文件)中添加如下内容:
此时,编译仿真之后,就可以看到 数组中的内容:
显示$display()语句所在的位置
在源代码中的三个不同地方添加如下相同的输出信息:
fsm_moore.v中添加如下内容:
tb文件fsm_top.v中添加如下内容:
编译仿真后的输入如下,此时我们无法分辨相同的输出信息在文件中的位置。
通过对$display()
命令的修改,可以显示输出对应的位置,如下所示:
此时,就可以显示输出信息所在的源文件,和它在源文件中的位置,如下所示: