昨天经历了恶梦debug,中间排了很多坑,特来记录一番。
一、问题描述
和队友写了lenet神经网络推理的硬件实现,在modelsim已经跑通,且验证了功能,但需要移植到vivado,利用里面的dist_rom加载权重。
顺便插一句,vivado有两者存储IP:dist_ram和blk_ram,分别表示分布式存储和块存储。分布式存储利用lut单元,分布在电路周围,因此布线容易,但容量有限制。块存储是真实的ram,容量大,但有可能布线较长。
但将reg更换成rom和ram之后,运行结果和modelsim不一致。然后用源文件在vivado仿真,结果还是不一样。
第二天用vcs仿真,发现前半部分输出和vivado一致,后半部分输出不一样。用iverilog仿真,发现前半部分输出和modelsim一致,后半部分输出不一样。
这下彻底崩溃,四个EDA软件仿真相同的文件,出现四种不同结果!而且代码那么多,可谓海底捞针,真不知道自己当时哪来的勇气去debug。
二、漫长debug
Debug最基本的思想就是控制变量,要么从头开始,要么从尾部回溯。电路的调试还有个很好的切入点是“状态机”,查看状态跳转可以缩小搜索范围,相当好用。
我首先比较modelsim和iverilog后半部分输出,确实发现最后一个全连接层的状态跳转失败,因此回溯决定状态跳转的几个信号。iverilog的好处是一次仿真可以保存所有信号,这和vcs一样。modelsim每次添加信号需要重新仿真,但它能够查看中间memory的数据,这点和vivado一致。
回溯到最后,发现一个always模块的非阻塞赋值写成了阻塞赋值!modelsim默认always模块里都是非阻塞赋值,其他仿真器则不会,两者赋值的输出结果会不一样。这样modelsim和iverilog就对上了。
接下来找vcs和iverilog差别,它们是前半部分不同,因此向前回溯,最后发现TB顶层,打入Input map的时候,always模块没有用非阻塞,导致memory初始化数据不对。这么明显的错误,我当时竟然没看到,真是眼瞎了!这下vcs和iverilog也对上了。
最后权重替换为dist_rom和blk_ram,前半部分结果不对,而且数据延时了一拍才出来。还是用回溯的方法找信号,最后发现blk_ram的读使能应该由两个信号决定,我只写了一个信号,导致其中一个数据写错,影响最终结果。
从早上7点找到晚上8点,庆幸找到了这三个bug,真是伤神。
三、阻塞和非阻塞仿真的区别
这里做一个简单的测试模块:
module test(
input clk,rst_n,
output reg acc,
output reg [3:0] cnt);
reg enable;
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
acc <= 0;
else
//非阻塞赋值,cnt正常累加
acc <= ~acc;
//阻塞赋值,cnt输出一直为0
// acc = ~acc;
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
enable <= 0;
else
enable <= ~enable;
end
always @(posedge clk or negedge rst_n) begin
if (~rst_n)
cnt <= 0;
else if(enable)
cnt <= cnt + acc;
end
endmodule
仿真阻塞和非阻塞的结果,我们看到,非阻塞的acc正常累加,阻塞的acc一直保持0,为什么呢?
看图中的第一个↑,非阻塞赋值,acc从0→1,阻塞不变。
- 非阻塞:当上升沿到来时,会查看上升沿前信号的值,acc=1,enabel=1,因此cnt=cnt+1,更新为1;
- 阻塞:当上升沿到来时,查看enable(非阻塞)上升沿前的值,enabel=1,acc上升沿后(阻塞),acc=0,因此cnt=cnt+0,保持不变;
四、iverilog和vcs仿真
iverilog是开源软件,用于仿真并产生相应文件,再利用gtkwave查看。仿真前需要在tb文件加入保持波形文件的语句:
initial begin
$dumpfile("test2.vcd")
$dumpvars;
end
然后执行以下语句,注意tb文件放最后:
iverilog test2_rtl.v test2_tb.v
./a.out
gtkwave test2.vcd
vcs是Synopsys公司的仿真软件,工业界一般配合verdi查看波形debug,也可以利用自带的dve。同样,tb文件加入保存波形的语句:
initial begin
$vcdpluson();
end
依次执行
vcs -full64 -f verilog_file.f -debug_pp +vcd+vcdpluson
./simv
dve -vpd vcdplus.vpd
这里的verilog_file.f 文件是.v文件的列表,即
test2.v test2_tb.v