写在前面:
1、按照大家通常的思维,其实题目应该取“怎么分析一个0ps的毛刺”,即从结果分析原因。但是0ps的毛刺产生的原因实在太多了,我们不如反其道行之,自己尝试去构造一个0ps的毛刺,以一种正向的思维去理解,当你能够自己构造出一个0ps的毛刺时,基本上也能够分析其它0ps毛刺产生的原因了。
2、看本文之前需要先去了解verilog/systemverilog的调度机制,可以参考我以前写的一篇文章SystemVerilog调度机制与一些现象的思考。
3、我发现大家都喜欢白嫖啊,看完如果觉得文章对自己有帮助,一定要一键三连啊!!!
4、郫县犀浦这边有家王宝器椒麻鱼味道很不错,附近的同学可以尝试下。
正文:
Verdi相关知识补充
1、vcs编译完成的simv,在运行时是通过调用Verdi的相关函数来达到dump波形的效果的。如果想控制dump的过程,可以将相关参数附加在simv后面。比如:
+fsdb+glitch=num 控制glitch(毛刺)的dump,num取值0~254,0代表将所有的毛刺dump下来。
+fsdb+sequential 记录同一时刻下,信号变化的先后顺序。
+fsdb+region Enable region mode dumping。也就是记录信号的Event Regions(Active、Inactive、NBA...)信息。
2、为什么有些人使用的参数是+fsdb+delta,而不是+fsdb+glitch=num?
3、Verdi里面glich的调试方法
首先鼠标左键选择需要展开的时间点,然后按下图所示的选项展开这个时间点,可以看到在这个时间点上,信号变化的先后顺序。
Expand Delta
如果你还关注信号的Region信息,那么在此基础上点击Region Mode,就可以看到信号是在哪个区域发生变化的。
Expand Delta (Region Mode)
第一次尝试构造,直接连续两次赋值
dut.v
module dut();
logic a = 0;
logic b = 0;
initial begin
#10;
a = 1; b = 1;
a = 0;
#10;
end
endmodule
Expand Delta
写完代码还没跑仿真之前,我已经凭直觉知道这样肯定是失败的。果不其然,Verdi里面a的取值恒为0。可以看到,在10ns这个时间点上,a的取值是没有发生变化的。也就是说a的变化并没有被记录下来。联想到以前看到的文章说“begin...end之间的语句一定是按照先后顺序执行的”,难道因为这个原因,a的变化被仿真器优化掉了?所以我考虑换一种方式来生成0ps脉冲。
第二次尝试构造,利用竞争和冒险来产生毛刺
先复习下数字电路,当输入信号经过不同路径到达输出时,电路就会存在竞争,有发生冒险的可能,此时就会产生毛刺。
一个比较典型的竞争冒险电路如下,我尝试参考这种方式去生成0ps的毛刺。
dut.v
module dut();
logic a = 0;
logic a_inv;
logic b;
initial begin
#10 a = 1;
#10 a = 0;
end
assign a_inv = ~a;
assign b = a & a_inv;
endmodule
Expand Delta
Expand Delta (Region Mode)
然鹅,输出b还是一条水平的直线,到底是哪里不对呢?
难道是由于调度的先后顺序不对?先调度了assign a_inv = ~a;后调度assign b = a & a_inv;导致的?为此我也展开了部分许多实验,排除了这个可能,这里就不再展开了。
为什么?到底是为什么?难道出不出毛刺看人品?正当我苦思冥想之时,突然灵光一闪,我知道为什么了,是的,我知道为什么了。
因为vcs波形的dump本质上是通过PLI调用Verdi的相关函数来实现的,如果信号先发生变化,再调用dump函数,那么自然这些变化信息不能被及时的记录下来。如下图所示,在我们的例子中,信号的变化全部集中在Active Region,当Active Region执行完成后,再去调用PLI,这些变化信息自然被丢弃了。那么为了构建一个0ps的脉冲,我们就需要在NBA区域引入信号的变化,从而保证信号的变化完全地被PLI记录下来。(其实该思路存在误区)
第三次尝试构造,利用Active-NBA区域后的PLI来记录毛刺信息
dut.v
module dut();
logic a = 0;
logic a_inv;
logic b;
initial begin
#10 a = 1;
#10 a = 0;
end
//assign a_inv = ~a;
always @(*) begin
a_inv <= ~a;
end
assign b = a & a_inv;
endmodule
Expand Delta
Expand Delta (Region Mode)
果不其然,这样的确生成了一个0ps的毛刺。
可以看到,a的取值在Active区域发生变化后,会先让b的取值变为1;a_inv的取值在NBA区域发生变化后,会让b的取值变为0。二者的效果叠加,最终在b上输出的就是一个0ps的脉冲。
第四次尝试构造,利用双initial的生成glitch(2020/04/16添加,修正部分观点)
这里先感谢Holden_Liu同学在留言中指出了文中的一些问题。
这里贴出他的文章链接:
在前文中,我们可以通过Active-NBA区域的交互构造出glitch,导致我一度认为glitch必须由不同Region交互来生成。在Holden_Liu同学的文中,展示了使用双initial来生成glitch的例子。这里不妨借鉴一下。
dut.v
module dut();
logic a;
initial begin
a = 0;
#10 a = 1;
end
initial begin
#10 a = 0;
#10 a = 1;
end
endmodule
Expand Delta
Expand Delta (Region Mode)
可以看到,其实单在Active区域内的变化就可以生成glitch了,因此修正观点如下。
1、vcs仿真器应该是以调度单元process来存储信号变化的,process内部每个Region的变化不对外呈现。
2、verdi的PLI只提供了Dump波形的函数接口,具体要Dump什么样的数据,并存储到波形中,还是由vcs仿真器自己决定的。
再次感谢指出错误!
写在最后
1、在项目过程中,很多0ps的脉冲也可以通过以上介绍的方法来进行分析。
2、遇到0ps的毛刺,千万不要觉得这个不重要,在前仿中出现的0ps毛刺必须要全部解释清楚。