常见的对于非阻塞复制的误解
1,非阻塞赋值和$display
1.1,RTL案例
module display_cmds;
reg a;
initial $monitor("\$ monitor: a = %b", a);
initial begin
$strobe ("\$ strobe : a = %b", a);
a = 0;
a <= 1;
$display ("\$ display: a = %b", a);
#1 $finish;
end
endmodule
1.2,功能实现
1.3,解释误解
注意事项:
$display,在仿真期间会自动执行,但在综合到实际硬件时可能会被忽略,因此请仅将其用于仿真和调试目的。
2,#0延时赋值
2.1,RTL案例
// #0延时赋值
module nb_schedule;
reg a, b;
initial begin
a = 0;
b = 1;
a <= b;
b <= a;
$monitor("%0dns: \ $ monitor: a = %b, b = %b", $stime, a, b);
$display("%0dns: \ $ display: a = %b, b = %b", $stime, a, b);
$strobe ("%0dns: \ $ strobe: a = %b, b = %b\n", $stime, a, b);
#0 $display("%0dns: #0: a = %b, b = %b", $stime, a, b);
#1 $monitor("%0dns: \ $ monitor: a = %b, b = %b", $stime, a, b);
$display("%0dns: \ $ display: a = %b, b = %b", $stime, a, b);
$strobe ("%0dns: \ $ strobe : a = %b, b = %b\n", $stime, a, b);
$display("%0dns: #0: a = %b, b = %b", $stime, a, b);
#200 $finish;
end
endmodule
2.2,功能实现
以上8条语句是#0
延时赋值模块的仿真结果,这说明#0
延时命令在非阻塞赋值事件发生前,在停止运行事件队列中执行。
#0
延时赋值建议遵循以下的原则7:
2.3,解释误解
3,对同一变量进行多次非阻塞赋值
3.1,RTL案例
// 对同一变量进行多次非阻塞赋值
module multi_touch;
reg a;
initial begin
a <= 0;
a <= 1;
end
endmodule
3.2,功能实现
执行该模块时,有两个非阻塞赋值更新事件加入到非阻塞赋值更新队列。以前的规划要求将非阻塞赋值更新事件按照它们在源文件的顺序加入队列,这便要求按照事件在源文件中的顺序,将事件从队列中取出并执行。因此,在仿真第一步结束时刻,变量a被设置为0,然后是1。
结论:
最后一个非阻塞赋值决定了变量的值。
3.3,解释误解
4,小结
在夏宇闻的《Verilog数字系统设计教程》的第14章-深入理解阻塞和非阻塞赋值的不同中,其所有的原则归纳如下所示:
(1)原则1:时序电路建模时,用非阻塞赋值;
(2)原则2:锁存器电路建模时,用非阻塞赋值;
(3)原则3:用always块写组合逻辑时,采用阻塞赋值;
(4)原则4:在同一个always块中同时建立时序和组合逻辑电路时,用非阻塞赋值;
(5)原则5:在同一个always块中不要同时使用非阻塞赋值和阻塞赋值;
(6)原则6:不要在多个always块中为同一个变量赋值;
(7)原则7:用$strobe系统任务来显示用非阻塞赋值的变量值;
(8)原则8:在赋值时,不要使用 #0
延迟。
遵循以上原则,有助于正确的编写可综合硬件,并且可以清楚90%~100%在仿真时可能产生的竞争冒险现象。