Verilog基础:避免混合使用阻塞和非阻塞赋值

张开发
2026/4/11 7:30:24 15 分钟阅读

分享文章

Verilog基础:避免混合使用阻塞和非阻塞赋值
相关阅读Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482“避免在一个always块中混杂阻塞赋值和非阻塞赋值”这一原则是著名Verilog专家Cliff Cummings在论文SUNG2000中提出的。这个观点在公开讨论中曾受到过广泛质疑。有人认为在时钟沿触发的 always 块中把用于组合逻辑的阻塞赋值和用于时序逻辑的非阻塞赋值写在一起也是安全可行的。从某种意义上说这种说法并不完全错误。确实在一些特定场景下这样写出来的代码既能够正确仿真也能够得到正确综合结果。但问题在于“能工作”并不等于“值得推荐”。这种编码风格虽然表面上没有问题但通常存在以下几个明显缺点1、always块内部的事件调度机制更难理解。2、非阻塞赋值在这种写法中通常必须放在最后否则容易出错。3、在零延迟RTL仿真模型中触发器输入对应的临时变量和触发器输出可能会在同一个时钟沿同时变化这种波形现象与真实硬件行为不一致容易让人困惑。换句话说这种风格不是绝对不能用而是不利于阅读、调试和维护尤其是在大型工程中会放大这些问题。先看下面这段代码。它采用了更规范、也更容易理解的写法组合逻辑和时序逻辑分开描述没有在同一个always块中混杂阻塞赋值和非阻塞赋值。module test( output reg q, output y, input a, b, c, clk, rst_n); wire d; always(posedge clk or negedge rst_n) if(!rst_n) q 0; else q d; assign d a b; assign y q c; endmodule这段代码的含义非常清晰d是一个组合逻辑信号由ab产生。q是一个时序寄存器在时钟上升沿采样dy则由寄存器输出q与输入c组合得到。这种写法的优点在于电路结构、仿真行为和工程师对硬件的直觉是一致的。阅读代码时很容易将其映射到实际电路先有组合逻辑产生d再由触发器采样最后输出y。以上Verilog代码的综合结果如图1所示。图1 对应的综合电路下面这段代码虽然从综合结果上看仍然可以正确构建与前面相同的电路但它在同一个always块中同时使用了阻塞赋值和非阻塞赋值。module test( output reg q, output y, input a, b, c, clk, rst_n); always(posedge clk or negedge rst_n) if(!rst_n) q 0; else begin:logic reg d; d a b; q d; end assign y q c; endmodule这种写法经常被具有VHDL背景的工程师采用。原因在于在VHDL设计中为了提升仿真效率有时会习惯在同一个process块中同时描述变量赋值和信号赋值因此会自然地把类似风格带到Verilog中来。但是在Verilog中这样做并不会带来明显的仿真性能提升。相反它往往会引入理解和调试上的额外负担。以上Verilog代码的综合结果如图2所示。图2 对应的综合电路需要强调的是这段混合写法的代码在仿真和综合上都可能是正确的。问题不在于它是否“功能错误”而在于它会产生一些很不直观的仿真现象。在这种编码风格中内部临时变量d并不是一个真正独立存在的组合逻辑节点。由于它被定义在时钟触发的always块内部所以它只有在以下时刻才会更新时钟有效沿到来或者复位触发时。这意味着当a或b在两个时钟沿之间发生变化时代码中的d并不会立刻变化。也就是说虽然从表达式上看dab像是组合逻辑但从仿真行为上看它并不表现为一个“随输入实时变化”的组合信号。这就会带来一个很别扭的现象触发器输入对应的临时变量d和触发器输出q可能在同一个时钟沿同时变化。而在真实硬件中触发器输入端的数据应当在时钟边沿到来之前就已经稳定输出则在边沿之后才更新。也就是说真实电路不会呈现出“输入和输出在同一个沿一起跳变”的直观波形。这个现象只是RTL编码方式导致的仿真副作用。对于小例子来说这种差异也许还不算严重但对于大型设计工程师往往需要花很多时间去判断这些波形究竟是设计本身的问题还是编码风格带来的假象。这会明显增加调试成本。此外这种写法还有一个小麻烦为了在always块内部定义临时变量通常还需要使用一个命名块例如上面代码中的logic。这虽然不算严重问题但确实让代码显得更绕了一层。有些工程师会提出一种折中做法既然这些内部临时变量容易让人误解那就在非阻塞赋值使用完之后立刻把它们赋值为x。这样一来查看波形时这些临时变量始终表现为未知值别人也就不会误把它们当作真实稳定的中间信号。例如下面这种写法module test( output reg q, output y, input a, b, c, clk, rst_n); always(posedge clk or negedge rst_n) if(!rst_n) q 0; else begin:logic reg d; d a b; q d; d 1bx; end assign y q c; endmodule以上Verilog代码的综合结果如图3所示。图3 对应的综合电路从结果上看这种写法依然可能综合出正确的电路但它实际上只是为了掩盖前一种混合风格带来的可读性问题而额外引入了更多技巧性处理。换句话说本来只是因为选用了一个不够好的编码风格结果又不得不通过“把内部变量设成x”这种方式去弥补其副作用。这不仅没有让代码更清晰反而使其变得更加怪异。而且这种做法还有一个明显问题内部变量在波形上几乎全程显示为x即使它们在某些时刻确实承载了中间值。这对于调试也未必真有帮助反而可能隐藏一些有价值的信息。

更多文章