为何Verilog模拟器对不同赋值场景的处理存在差异?
Verilog模拟器对不同赋值场景的处理差异解析
核心疑问
为何Verilog模拟器对以下几种赋值场景的处理存在差异?
场景1
always #1 clk =! clk; initial begin @(posedge clk) start <= 1; end
场景2
initial begin clk <= 1; start <= 1; end
等效场景(与场景1效果一致)
initial begin clk = 1; start <= 1; end
资料显示,场景1中clk与start信号会被同时设置,但clk只能在下一个边沿捕获start信号;场景2却没有这种延迟效果;而第三种写法又能和场景1表现一致。明明非阻塞赋值应该同步完成,为何会出现这种差异?
示例验证模块
示例模块1(对应场景1)
module test(); reg clk=0; reg start=0; reg flag=0; always #1 clk =! clk; initial begin #5; @(posedge clk) start <= 1; end always@(posedge clk) if(start) flag<=1; endmodule
示例模块2(对应场景2)
module test(); reg clk=0; reg start=0; reg flag=0; initial begin #5; clk <= 1; start <= 1; end always@(posedge clk) if(start) flag<=1; endmodule
仿真结果


差异原因解析
一切差异源于Verilog的事件调度机制,重点要区分阻塞赋值、非阻塞赋值的执行时机,以及边沿敏感块的触发逻辑:
场景1的执行逻辑
always #1 clk = !clk用阻塞赋值周期性翻转clk,每1时间单位产生一次边沿。initial块等待posedge clk到来后,执行start <= 1:非阻塞赋值不会立即更新start,而是把更新操作放入非阻塞赋值更新队列。- 当前的clk上升沿已经触发了
always@(posedge clk)块,这个块会读取当前的start值(0),因此flag不会被置1。 - 直到当前时间步所有事件执行完毕,
start才会被更新为1;下一个clk上升沿到来时,always@(posedge clk)读取到start=1,才会置位flag。
场景2的执行逻辑
initial块中两个非阻塞赋值clk <=1和start <=1会被同时放入非阻塞更新队列。- 当前时间步所有顺序执行的代码完成后,队列中的赋值统一执行:
clk从0变为1(产生上升沿),start同时变为1。 - clk上升沿触发
always@(posedge clk)块时,start已经完成更新,因此读取到start=1,直接置位flag——这就是和场景1的核心差异。
等效场景的执行逻辑
clk =1是阻塞赋值,会立即更新clk的值,直接产生上升沿。- 此时
start <=1还在非阻塞更新队列中,未完成更新;always@(posedge clk)块触发时,读取到的start仍是旧值0,所以flag不置位。 - 当前时间步结束后
start才更新为1,下一个clk边沿到来时才会触发flag置位——效果和场景1完全一致。
内容的提问来源于stack exchange,提问作者den251
相关产品推荐
相关产品推荐

