Verilog阻塞与非阻塞赋值中延迟控制的行为差异问询
你在研究Verilog模拟器对#<delay>延迟控制的处理时,遇到两段代码输出差异的问题:
代码段(1)(非阻塞赋值版本)
module top (); reg a,b; initial begin $monitor("At time %d a=%b b=%b", $time,a,b); a = 1'b0; b = 1'b0; #10 a = 1'b1; #10 b = 1'b1; a <= #5 1'b0; b <= #5 1'b0; end endmodule
代码段(2)(阻塞赋值版本)
module top (); reg a,b; initial begin $monitor("At time %d a=%b b=%b", $time,a,b); a = 1'b0; b = 1'b0; #10 a = 1'b1; #10 b = 1'b1; a = #5 1'b0; b = #5 1'b0; end endmodule
输出差异
- 代码(1)中,
a和b均在t=25时被设为0; - 代码(2)中,
a在t=25时设为0,b却要到t=30才变为0。
核心疑问
原本以为执行到a <= #5 1'b0;时,会先计算右侧值,等待5个时间单位后在非阻塞赋值(NBA)区域更新a,且要到t=25才开始处理b <= #5 1'b0;,就像阻塞赋值的情况一样。为何非阻塞赋值时,即便前一条语句有#5,模拟器仍会直接处理下一条?之前认为阻塞与非阻塞赋值仅在分层Verilog队列的调度区域不同,是否还有其他差异?
原因解析
要搞懂这个差异,关键是区分阻塞赋值中的延迟和非阻塞赋值中的延迟的本质:
阻塞赋值中的
#<delay>:
阻塞赋值(=)会直接暂停当前进程的执行。当执行a = #5 1'b0;时,当前initial进程立刻暂停5个时间单位,这段时间内不会执行任何后续语句。等5个时间单位到了(t=20+5=25),才完成a的赋值,之后才继续执行下一条b = #5 1'b0;,此时进程又暂停5个时间单位(到t=25+5=30),再完成b的赋值。这就是代码(2)中b在t=30才变0的原因。非阻塞赋值中的
#<delay>:
非阻塞赋值(<=)不会暂停当前进程,而是把**“延迟5个时间单位后更新变量”这个动作打包,放到Verilog事件队列的非阻塞赋值更新区域**。当执行a <= #5 1'b0;时,模拟器会立即安排一个事件:在当前时间t=20的基础上加5,也就是t=25时,在NBA区域更新a为0。安排完这个事件后,当前进程不会暂停,立刻继续执行下一条b <= #5 1'b0;,同样安排一个t=25时更新b为0的事件。到t=25时,两个事件同时触发,所以a和b同时变为0。除了调度区域的差异,阻塞和非阻塞赋值对延迟的处理逻辑完全不同:
- 阻塞赋值的延迟是进程级暂停,会阻塞后续语句的执行;
- 非阻塞赋值的延迟是事件调度,仅影响赋值动作的触发时间,不会阻塞当前进程的执行流程。
内容的提问来源于stack exchange,提问作者EE18

