【问题标题】:How to interpret blocking vs non blocking assignments in Verilog?如何解释 Verilog 中的阻塞与非阻塞分配?
【发布时间】:2011-06-06 21:35:56
【问题描述】:

在绘制硬件图时,我对如何解释阻塞和非阻塞分配有点困惑。我们是否必须推断非阻塞赋值给我们一个寄存器?那么根据c <= a+b 这个说法,c会是一个寄存器对,而不是a和b?

module add (input logic clock,  
output logic[7:0] f);   

logic[7:0] a, b, c;  

always_ff @(posedge clock)  
begin   
  a = b + c;   
  b = c + a;   
  c <= a + b;  
end   

assign f = c;  

endmodule

【问题讨论】:

标签: verilog system-verilog


【解决方案1】:

传统的 Verilog 智慧完全错了。对 local 变量使用阻塞赋值没有问题。但是,您永远不应该将阻塞分配用于同步通信,因为这是不确定的。

时钟控制的 always 块中的非阻塞分配将始终推断出触发器,如语义所指示的那样。

时钟控制的 always 块中的块分配是否推断触发器完全取决于它的使用方式。如果在赋值之前读取变量是可能,则将推断出触发器。否则,这就像一个临时变量,会导致一些组合逻辑。

【讨论】:

    【解决方案2】:

    首先要弄清楚阻塞和非阻塞分配之间的差异肯定有点棘手。但不要害怕 - 有一个方便的经验法则:

    如果您想用 always 块推断组合逻辑,请使用块分配 (=)。如果您需要时序逻辑,请使用具有非阻塞分配的时钟 always 块 (&lt;=)。并且尽量不要将两者混为一谈。

    您上面的代码可能不是最好的例子。在不知道您尝试构建的加法器/触发器结构的情况下,存在组合反馈路径(这是不好的)的危险。而且由于您没有输入总线,因此您实际上是在尝试凭空构造abc

    但要回答您的问题,分配给时钟 always 块内的任何变量都会推断出触发器,除非它使用阻塞运算符 (=) 分配并用作一种局部变量。

    module add
      (
       input clock,
       input [7:0] in1,
       input [7:0] in2,
       output logic [7:0] f1, f2, f3, f4, f5
       );   
    
    
       // f1 will be a flipflop
       always_ff @(posedge clock) begin
          f1 = in1 + in2;
       end
    
    
       // f2 will be a flipflop
       always_ff @(posedge clock) begin
          f2 <= in1 + in2;
       end
    
    
       // f3 will be a flipflop
       // c1 will be a flipflop
       logic [7:0] c1;
       always_ff @(posedge clock) begin
          c1 <= in1 + in2;
          f3 <= c1 + in1;
       end
    
    
       // f4 will be a flipflop
       // c2 is used only within the always block and so is treated
       // as a tmp variable and won't be inferred as a flipflop
       logic [7:0] c2;
       always_ff @(posedge clock) begin
          c2 = in1 + in2;
          f4 = c2 + in1;
       end
    
    
       // c3 will be a flipflop, as it's used outside the always block
       logic [7:0] c3;
       always_ff @(posedge clock) begin
          c3 = in1 + in2;
       end
    
       assign f5 = c3 + in1;
    
    endmodule
    

    遵循经验法则并且不在always 块中混合阻塞和非阻塞分配的一个重要原因是,混合分配可能会导致 RTL 模拟和门模拟/真实硬件操作之间的模拟严重不匹配。 verilog 模拟器对=&lt;= 的处理完全不同。阻塞分配意味着“立即将值分配给变量”。非阻塞赋值意味着“弄清楚要分配给这个变量的内容,并将其存储起来以便在将来的某个时间分配”。阅读以更好地理解这一点的好论文是:另请参阅:http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf

    【讨论】:

      【解决方案3】:

      只是想补充一下 Jan Decaluwe 的答案。似乎实际上很少有代码实际上使用了 Jan Decaluwe 所描述的内容,尽管它是绝对正确的。多亏了卡明斯先生,混合阻塞和非阻塞语句现在是一种禁忌。

      问题是,大多数地方都避免对局部变量使用阻塞语句,而且在 Google 的即时搜索空间中几乎没有代码可以提供如何完成的示例。我发现 Jan 提到的编码风格的唯一地方是 the winning code in this article。而这个,是我不小心碰到的

      【讨论】:

        【解决方案4】:

        这件事我也很难过。

        但首先,您应该明白,非阻塞或阻塞实际上与是否创建latch/ff 无关!

        对于它们的区别,您可以简单地(在开始时)理解它:i。如果使用阻塞,它后面的语句直到阻塞语句 LHS 赋值后才能执行,因为如果使用变量,它的 LHS 变化可以更新和使用。但是,对于非阻塞,它不会像与后句并行那样阻塞后句(实际上应该先进行RHS计算,但没关系,混淆时忽略它)。 LHS 不会更改/更新这次执行(下次总是再次触发块时更新)。以下句子使用旧值,因为它在执行周期结束时更新。

        a = 0; b= 0;
        a = 1;
        b = a;
        --> output a = 1, b = 1;
        a = 0; b= 0;
        a <= 1;
        b = a;
        --> output a = 1, b = 0;
        

        一个关键点是找出在你的代码中(总是阻塞)是否有任何没有赋值但可能发生的 case 变量。如果您不向它传递值并且发生这种情况,则创建latch/ff 以保留该值。

        例如,

        always @(*) begin
            if(in) out = 1;
            else out = 0;
        end
        --> this end without latch/ff
        always @(*) begin
            if(in) out = 1;
        end
        --> this end with one latch/ff to keep value when in = 0, as it might happen and you didn't assign value to out as in=1 do. 
        

        以下也可以创建latch/ff:

        always @(*) begin
            if(in) a = 1;
            else b = 1;
        end
        

        --> 为 in=1,b 无赋值,in=0 a 无赋值而创建的锁存器/ffs。

        另外,当你感觉到clkalways @(posedge clk)的posege时,必然以latch/ff结尾。因为,对于clk来说,必然存在负边沿,你什么都不做,latch/ffs被创建来保留所有的旧值!

        【讨论】:

          【解决方案5】:

          请您始终可以在数字域中解释verilog,只是您必须了解如果您编写的相同代码将在门级转换会发生什么,我个人不遵循在seq中使用非阻塞或使用的规则组合阻塞,这会限制你的思维。只坚持代码的数字部分 如果您的代码转换为门级会发生什么,只是看到您只想要这个

          1. 首先将生成全加器 -- 输入 a 和 b
            1. 输出将转到触发器创建与 clk 同步的输出
            2. 现在由于分配是阻塞的,所以新的 a 将被应用到下一个完整添加的新 a 和 c 作为输入,它的输出将转到 dffcsync 到 clk 创建新 b
            3. 现在因为 b = c + a;是否存在阻塞语句,所以 b 更新为这个新的 b
            4. 现在它的 c
            5. 然后将创建一个 dff,其中旧 c 而不是刚刚由非阻塞语句创建的新 c,并且此 dff 同步到 clk 的输出转到 a 并且 a 得到更新

          谢谢 问候 拉胡尔耆那教

          【讨论】:

            【解决方案6】:

            我可以回答您的问题,但我认为一篇论文最适合此问题,因此我建议您阅读 Clifford Cummings 的这篇论文。它将消除您的所有疑虑,除此之外,还将加强您对 Verilog 的理解。

            http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA_rev1_2.pdf

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-05-18
              • 2014-04-29
              • 2015-04-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多