昨天经历了恶梦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,为什么呢? wavedrom.png

看图中的第一个↑,非阻塞赋值,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

测试代码链接:test2

标签: none

已有 6 条评论

  1. IC小白 IC小白

  2. 是小甜甜呢 是小甜甜呢

    帆哥超棒!我想问您一下,这个博客开通起来麻烦吗,我也想开一个~

    1. fallenangel fallenangel

      这有建站笔记 http://www.icfgblog.com/index.php/Thinking/98.html

  3. 是小甜甜呢 是小甜甜呢

    开心鸭~谢谢

  4. baiwang baiwang

    遇到了同样的问题,十分感谢

  5. yuyuan yuyuan

    感谢分享,之后碰到类似问题不至于没头绪,不过我现在遇到的问题是除法器ip核输出的使能信号在vivado和modelsim里面仿真不一致,vivado里可以正常输出,但是在modelsim里就是x,不知道是ip核的问题还是啥问题

添加新评论