Verilog中非阻塞赋值为何导致进程重入?阻/非阻赋值执行差异问询
Verilog阻塞与非阻塞赋值生成时钟的执行差异分析
两段Verilog代码在实现振荡时钟时存在明显执行差异:
always @(clk) begin #10 clk <= ~clk;end // 正常工作:进程重复进入,形成振荡器
always @(clk) begin #10 clk = ~clk;end // 仅执行一次,无法形成振荡
下面针对你的疑问逐一解答:
1. 时间步内的事件评估是逐句执行的吗?
不是逐句执行,Verilog的时间步是分阶段处理事件队列的,核心队列包括:
- 活跃事件队列:处理当前时间步触发的事件(比如always块敏感事件触发),队列内的语句按顺序执行,但阻塞赋值会立即更新变量。
- 非阻塞赋值更新队列(NBA):存储非阻塞赋值的更新操作,必须等活跃队列处理完毕后才执行变量更新。
- 未来事件队列:存储带延迟的时序控制事件(比如
#10),到指定时间才会被移到活跃队列执行。
整个时间步的处理流程是:先清空活跃队列,再执行NBA队列的更新,最后处理监控类操作,之后进入下一个时间步。
2. 你的理解存在什么错误?
你之前认为两种赋值都能生成振荡,错误点在于忽略了赋值更新时机的核心差异:
- 阻塞赋值在活跃队列阶段立即更新变量,同一时间步内的敏感事件检测已经完成,变量的变化无法触发同一个always块再次执行。
- 非阻塞赋值的更新延迟到NBA阶段,这个阶段完成后会重新检测敏感事件,变量变化能触发always块进入下一轮执行,形成循环。
3. 为何非阻塞赋值的更新事件能在同一时间步触发敏感事件?
非阻塞赋值的更新发生在活跃队列处理完成后的NBA阶段。当NBA队列执行完clk的更新后,Verilog会重新检查所有敏感列表,always @(clk)会因为clk的跳变被触发,这个新的触发事件会被加入当前时间步的活跃队列,于是always块会立即再次执行,不需要等到下一个时间步,从而形成连续振荡。
4. 评估与更新事件如何进入层级事件队列?
- 评估事件的入队逻辑:
- 当clk发生跳变触发敏感列表时,always块的进程会被加入活跃事件队列等待执行。
- 执行到
#10这类延迟控制语句时,进程的剩余部分(赋值操作)会被加入未来事件队列,等到10个时间单位后,再被移到活跃队列执行。
- 更新事件的入队逻辑:
执行非阻塞赋值语句clk <= ~clk时,不会立即更新clk,而是把“将clk更新为~clk”的操作加入NBA队列。只有当活跃队列中的所有事件都处理完毕后,才会依次执行NBA队列中的所有更新操作,完成变量的赋值。
内容的提问来源于stack exchange,提问作者chen zhang
相关产品推荐
相关产品推荐

