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

发布于 2024-10-11 11:55:10 字数 341 浏览 4 评论 0 原文

在绘制硬件图时,我对如何解释阻塞和非阻塞分配感到有点困惑。我们是否必须推断非阻塞赋值给我们一个寄存器?那么根据这条语句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

I am a little confused about how blocking and non blocking assignments are interpreted when it comes to drawing a hardware diagram. Do we have to infer that a non blocking assignment gives us a register? Then according to this statement c <= a+b , c would be a register right, but not a and 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

眉目亦如画i 2024-10-18 11:55:10

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

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

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

The conventional Verilog wisdom has it all wrong. There is no problem with using blocking assignments for a local variable. However, you should never use blocking assignments for synchronous communication, as this is nondeterministic.

A non-blocking assignment within a clocked always block will always infer a flip-flop, as dictated by the semantics.

Whether a blocking assignment within a clocked always block infers a flip-flop or not depends entirely on how it is used. If it is possible that the variable is read before being assigned, a flip-flop will be inferred. Otherwise, this is like a temporary variable and it will result in some combinatorial logic.

沉默的熊 2024-10-18 11:55:10

最初了解阻塞和非阻塞分配之间的差异确实有点棘手。但不用担心 - 有一个方便的经验法则:

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

您上面的代码可能不是最好的例子。如果不知道您要构建什么加法器/触发器结构,则存在组合反馈路径的危险(这很糟糕)。由于您没有输入总线,因此您实际上是在尝试构造 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 模拟器对 =<= 的处理方式截然不同。阻塞赋值意味着“立即将值赋给变量”。非阻塞赋值意味着“找出要分配给该变量的内容,并将其存储起来以便在将来的某个时间分配”。为了更好地理解这一点,值得阅读的一篇好论文是:另请参阅:http://www.sunburst -design.com/papers/CummingsSNUG2000SJ_NBA.pdf

It's definitely a bit tricky to get your head around the differences between blocking and nonblocking assignments initially. But no fear - there's a handy rule of thumb:

If you want to infer combo logic with an always block, use blocking assignments (=). If you want sequential logic, use a clocked always block with nonblocking assignments (<=). And try not to mix the two.

Your code above is probably not the best example. Without knowing what adder/flipflop structure you were trying to build, there's the danger of having combo feedback paths (which are bad). And since you've no input buses, you're essentially trying to construct a, b & c out of thin air!

But to answer your question, any variable assigned to within a clocked always block will infer a flipflop, unless its assigned using the blocking operator (=) and used as a kind of a local variable.

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

A big reason for following the rule of thumb and not mixing blocking and nonblocking assignments within an always block, is that mixing your assignments can cause serious simulation mismatches between RTL sims and gate-sims/real hardware operation. The verilog simulator treats = and <= quite differently. Blocking assignments mean 'assign the value to the variable right away this instant'. Nonblocking assignments mean 'figure out what to assign to this variable, and store it away to assign at some future time'. A good paper to read to understand this better is: Also see: http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf

神经大条 2024-10-18 11:55:10

只是想补充 Jan Decaluwe 的答案。看起来,实际上很少有代码真正使用了 Jan Decaluwe 所描述的内容,尽管它是绝对正确的。感谢卡明斯先生,混合阻塞和非阻塞语句现在已成为一种禁忌。

问题是,大多数地方都避免对局部变量使用阻塞语句,并且在 Google 的直接搜索空间中几乎没有代码提供了如何完成此操作的示例。我找到 Jan 提到的编码风格的唯一地方是 本文中的获胜代码。还有这个,我无意中发现的

Just want to add to Jan Decaluwe's answer. It seems that there is very little code in the wild that actually actually uses what Jan Decaluwe's describes, even though it is absolutely right. Mixing blocking and non-blocking statements is now a taboo, thanks to Mr.Cummings.

The trouble is, most places avoid using blocking statements for local variables and there is very little code in the immediate search space of Google that looks gives an example of how it is done. The only place where I have found the coding style as mentioned by Jan is the winning code in this article. And this, I came across accidentally

雄赳赳气昂昂 2024-10-18 11:55:10

我也为这件事苦恼过。

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

-->为 in=1 创建闩锁/ffs,b 无分配,in=0 a 无分配。

另外,当你感知到clk的posegealways@(thoughtge clk)时,它必然以latch/ff结束。因为,对于clk来说,必须存在下降沿,而你什么都不做,就会创建latch/ffs来保留所有旧值!

I had a hard time over this too.

But firstly, you should understand that non-blocking or blocking is actually nothing to do with whether a latch/ff would be created!

For their difference you could understand it simply(at beginning) by this point: i. If use blocking, sentences after it could not be executed until block sentence LHS assigned value, since what changed to LHS of it could be updated and used if the variable is used. However, for non-blocking, it don't block following sentence like parallel with following sentence(actually RHS calculation should be done first, but it doesn't matter, ignore it when you confuse). The LHS don't change/updated for this time's execution (updated next time when always block trigged again). And following sentence use the old value, as it updated at the end of execution cycle.

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;

One key point is to find whether in you code (always block) there is any case variable not assigned value but could happen. If you don't pass value to it and that case occurs, then latch/ff is created to keep the value.

For example,

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. 

Following could also create latch/ff:

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

--> latch/ffs created for in=1, b no assignment, in=0 a no assignment.

In addition, when you sense posedge of clk always @(posedge clk), it is bound to end with latch/ff. Because, for clk, there must exist negative edge, and you don't do anything, latch/ffs are created to keep all the old value!

囍孤女 2024-10-18 11:55:10

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

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

谢谢
问候
拉胡尔·贾因

please you can always interpret the verilog in digital domain just you have to understand what will happen if the same code you wrote will be converted in gate level , i personally dont go by the rule that to use non blocking in seq or use blocking in combinational , this will limit your thinking . stick to the digital side of the code only
here is what will happen if your code is converted to gate level just see you want this only

  1. first the full adder wiil be made -- inputs a and b
    1. output will go to flip flop creating output a having sync with clk
    2. now since the assignment is blocking so the new a will be then applied to next full added having this new a and c as input , output of it will go to dffcsync to clk creating new b
    3. now since b = c + a; is there which is blocking statment so b is updated to this new b
    4. now its c<=a+b now what happens is a full adder is created having a and b as input which goes to dff sync to clk , now is there would be other condition like again say a=c;
    5. then a dff will be created having the old c not the new one just created by the non blocking statement and the output of this dff sync to clk goes to a and a gets updated

thanks
regards
Rahul jain

偏爱你一生 2024-10-18 11:55:10

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

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

I can answer your question, but I think one paper would be best for this so I recommend you to read out this paper of Clifford Cummings. It will clear all your doubts and in addition to it will strengthen your understanding of verilog.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文