如何解释Verilog中的阻塞与非阻塞分配?

时间:2011-01-11 01:19:22

标签: verilog system-verilog

在绘制硬件图时,我对如何解释阻塞和非阻塞分配感到有些困惑。我们是否必须推断非阻塞分配给我们一个寄存器?然后根据这个陈述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

6 个答案:

答案 0 :(得分:43)

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

时钟始终块内的非阻塞分配将始终推断触发器,如语义所示。

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

答案 1 :(得分:28)

最初了解阻塞和非阻塞分配之间的差异肯定有点棘手。但没有恐惧 - 有一个方便的经验法则:

  

如果要使用always块推断组合逻辑,请使用阻止分配(=)。如果您需要顺序逻辑,请使用带有非阻塞分配的时钟always块(<=)。并尽量不要混淆两者。

上面的代码可能不是最好的例子。在不知道你试图构建什么加法器/触发器结构的情况下,存在组合反馈路径的危险(这很糟糕)。由于您没有输入总线,因此您实际上是在尝试构建ab&amp;凭空而来c

但是要回答你的问题,分配给时钟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 sims与gate-sims /实际硬件操作之间严重的模拟不匹配。 verilog模拟器以完全不同的方式处理=<=。阻止分配意味着“立即将值赋值给变量”。非阻塞分配意味着“弄清楚要分配给这个变量的内容,并将其存储起来以便在将来某个时间分配”。阅读以便更好地理解这一点的好文章是:另请参阅:http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf

答案 2 :(得分:2)

只想添加Jan Decaluwe的回答。似乎实际上使用Jan Decaluwe所描述的内容的代码很少,即使它是绝对正确的。由于Mr.Cummings,混合阻塞和非阻塞语句现在是一个禁忌。

麻烦的是,大多数地方都避免对局部变量使用阻塞语句,并且Google的直接搜索空间中的代码很少,只是给出了一个如何完成它的示例。我发现Jan提到的编码风格的唯一地方是the winning code in this article。而且,我偶然遇到了

答案 3 :(得分:1)

我也很难过。

但首先,你应该明白,非阻塞或阻塞实际上与是否会创建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;

一个关键点是要找到你的代码(总是阻止)是否有任何案例变量未分配值但可能发生。如果您没有将值传递给它并且发生这种情况,则会创建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

- &GT;为in = 1创建的latch / ffs,b没有赋值,in = 0没有赋值。

此外,当你感觉到clk always @(posedge clk)的构成时,它必然以latch / ff结束。因为,对于clk,必须存在负边缘,并且你不做任何事情,创建latch / ffs以保留所有旧值!

答案 4 :(得分:0)

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

,那么会发生什么
  1. 首先要制作完整的加法器 - 输入a和b
    1. 输出将转到触发器,创建与clk同步的输出
    2. 现在,因为赋值是阻塞的,所以新的a将被应用到下一个完全添加的这个新的a和c作为输入,它的输出将转到dffcsync到clk创建新的b
    3. 现在因为b = c + a;是否存在阻止声明,因此b更新为新b
    4. 现在它的c&lt; = a + b现在发生的是一个完整的加法器被创建了一个和一个b作为输入进入dff同步到clk,现在是否会有其他条件再次说a = c;
    5. 然后将创建一个dff,其中旧的c而不是刚刚由非阻塞语句创建的新c,并且此dff同步到clk的输出转到a并且得到更新
  2. 感谢 问候 Rahul jain

答案 5 :(得分:0)

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

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

相关问题