27

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

6 回答 6

45

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

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

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

于 2011-01-23T14:56:43.300 回答
34

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

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

您上面的代码可能不是最好的例子。在不知道您尝试构建的加法器/触发器结构的情况下,存在组合反馈路径(这是不好的)的危险。而且由于您没有输入总线,因此您实际上是在尝试构建a&b无中生有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 模拟和门模拟/真实硬件操作之间的严重模拟不匹配。verilog 模拟器的处理=方式<=完全不同。阻塞分配意味着“立即将值分配给变量”。非阻塞赋值意味着“弄清楚要分配给这个变量的内容,并将其存储起来以便在将来的某个时间分配”。阅读以更好地理解这一点的好论文是:另请参阅:http ://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf

于 2011-01-11T12:27:39.760 回答
2

这件事我也很难过。

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

另外,当你感觉到 clk 的 posedge 时always @(posedge clk),它​​必然以latch/ff 结束。因为,对于 clk,必须存在负边沿,并且您什么都不做,因此创建了 latch/ffs 以保留所有旧值!

于 2014-06-25T17:33:20.670 回答
2

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

问题是,大多数地方都避免对局部变量使用阻塞语句,而且在谷歌的直接搜索空间中几乎没有代码可以给出一个例子来说明它是如何完成的。我发现 Jan 提到的编码风格的唯一地方是本文中的获胜代码。而这个,是我偶然发现的

于 2014-01-15T05:35:26.290 回答
0

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

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

谢谢问候拉胡尔耆那教

于 2014-09-29T13:36:28.590 回答
0

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

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

于 2014-12-09T03:13:59.133 回答