前言
上周定位某童鞋的环境代码,定位出一个感觉非常经典的验证环境时间问题,特此记录以兹鼓励~。~~
现象
代码示意如下,验证环境构建于module中,本意是希望对其rtl时序,在wait到mon.vld后@一拍时钟,之后从队列中取数进行处理,下面的代码直接变成打印:
initial begin
forever begin
wait (bus.mon.vld == 1'b1);
@bus.mon;
$display("%t, pop_front this cyc", $realtime);
end
end
当时出现的错误是,发现只有一拍的valid信号,但是却发生了两次取值行为,且发生在两拍:
打印结果为:
2010.000ns, pop_front this cyc
2020.000ns, pop_front this cyc
为了进一步确定行为,在wait和@之后分别加入打印:
initial begin
forever begin
wait (bus.mon.vld == 1'b1);
$display("%t, wait dao le!", $realtime);
@bus.mon;
$display("%t, pop_front this cyc", $realtime);
end
end
打印结果为:
2010.000ns, wait dao le!
2010.000ns, pop_front this cyc
2010.000ns, wait dao le!
2020.000ns, pop_front this cyc
可以确定在2010ns时刻wait生效了2次。
分析
首先明确下,这个现象是不符合代码本意的,本意应该是一拍有效取一个数据出来,那么接下来具体分析下为什么是这种现象;
由灵魂画手把波形画一下!
顺便在把time slot的图搬出来对着看,因为环境搭建并启动于于module中,只需要module regions的就够了:
那么在2010ns时间点,进程上在做哪些事呢?
1. 2010ns ts之前的1ps(interface中设置的skew)采样rtl vld信号为1,在active-observed之间bus.mon.vld值跳变为1;
2. 时钟bus.mon在observed域跳变为1;
对应的时序结构如下图,那么也就不难发现为什么wait会判定成功2次了:
因此时间线就理清楚了:
2010ns, bus.mon.vld跳变为1 -->
wait(bus.mon.vld)在observed之前生效 -->
@bus.mon成功(observed域)-->
执行环境语句,不消耗时间,forever回wait语句 -->
wait(bus.mon.vld)再次成功,此时仍为2010ns -->
@bus.mon不成功(关于@和wait的区别请查询绿皮书吧),该进程等待在这里,等下一次bus.mon上升沿-->
时间推进到2020ns,@bus.mon成功,再一次执行下面的语句。
结论
该波形是符合system verilog的规则的,但是不符合设计者意图,因此,就别这么搞了吧。
乖乖的@bus.mon (if bus,mon.vld) ......是最安全的。
ps.
在modelsim上未复现该波形,以后不用了。