总结了一下本人项目中遇到的类似问题, 大家如果遇到可以从以下方面着手检查bug.
1.
寄存器未给初值;
一个良好的习惯就是每个寄存器变量都要在reset里面预先定义初值. 看下面一个例子:
reg [1:0] unini;
always@(posedge clk or negedge rst)
begin
if(~rst)
;//未给定初值
else
begin
unini<=2\'d1;
if(unini<2\'d1)
a<=b;
end
end
这个例子是笔者在做项目的时候真实经历的一个bug的一个简化描述,实际代码比这个要复杂得多,当时也是找了半天最后才发现是寄存器未给初值这个低级错误.
对于上段代码,仿真的时候,在rst之后的第一个clk上升沿,unini是未定态(即modelsim中的红线), 这个未定态是不满足下面unini<2\'d1这个条件的,所以这个上升沿后a不会被赋值b; 而在第二个时钟上升沿之后才会满足条件而进行a<=b赋值;
但是在硬件实测的时候,寄存器里面的值一定是要么是1要么是0(一般默认的初值都是0),所以在第一个时钟上升沿就会进行a<=b的赋值,这样就造成了一个仿真结果和实测不符合的bug.
2.阻塞和非阻塞赋值混用.
阻塞和非阻塞赋值在always里面混用是RTL设计的大忌,即便你很了解阻塞和非阻塞的原理,还是可能因为疏忽造成难以发现的bug.看下面一段代码:
always@(posedge clk or negedge rst)
begin
if(~rst)
...
else
begin
if(a)
c<=1\'b1;//很早就将c赋值为1了
else if(b)
c=1\'b0; //注释1
end
end
reg state;
always@(posedge clk or negedge rst)
begin
if(~rst)
state<=1\'b0;
else
begin
case(state)
1\'b0:
begin
b<=1\'b0;
if(c)
state<=1\'b1;
...
end
1\'b1:
begin
....
if(...)
begin
state<=1\'b0;
b<=1\'b1;
a<=1\'b0;
end
end
endcase
end
以上这段代码也是笔者在真实项目中的一个血的教训,在注释1处错误的使用了阻塞赋值,使得一个bug仿真的时候没有仿出来,实测的时候一个信号一个信号地查才最后定为到这个点.有兴趣的可以仿真一下,如果b在state=1状态时,clk的一个上升沿置1,state会立刻回到0状态.此时在state=0状态时如果a不为1,b为1,那么应该在下一个时钟上升沿之前c保持为1,所以state应该立刻转回1状态. 但是由于之前c=0用了阻塞赋值,在仿真的时候就state就不会转回1.而在实测的时候,虽然用了c=0阻塞赋值,但是仍然按照<=综合(综合软件在这种情况下会把=当做<=处理),
这样就导致了一个本该在仿真阶段暴露的bug未被及时发现.
3.时序收敛问题;
随着FPGA功能越来越强大,时序问题将变得越来越重要. 值得注意的是,以往时序问题往往因为setup time不满足,而随着fpga能跑的越来越快,hold time violation也会越来越多地出现.而hold violation主要解决方法有两种, 首先让信号跑在全局网络上,这样虽然慢,但是信号的skew也小. 其次可以通过插入LCELL等FPGA内延时原件来解决. 虽然时序是个大问题,不过一定要首先在确定功能正确后再着手动时序这快,你会发现绝大部分仿真通过但是实测不过的原因还是代码的功能有问题,而由于一些原因没有仿真到.
4.Multi-cornor Simulation
多种情况下仿真. 现在高端FPGA能做的事情已经很接近大规模的ASIC电路,而ASIC级别的复杂度的FPGA设计要求的是实测前要进行完备的功能验证.比如码流的长度\样式的多种变化,数据的不同输入速率等多种情况都要进行仿真.笔者极力推荐大家仿真时候尽量用system verilog这种高级仿真语言,其有条件随机激励和assertion等功能可以极大增强代码覆盖率,十分有助于发现那种普通定点看波形仿真发现不到的问题.记住一句话,复杂设计的仿真绝对不能局限在一点一点看波形,绝大多数的bug是要编程靠程序自动发现的!!