如何解释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
首先解决阻塞和非阻塞任务之间的区别是非常困难的。 但没有恐惧 – 有一个方便的经验法则:
如果你想用
always
块推断组合逻辑,使用阻塞赋值(=
)。 如果您需要时序逻辑,请使用带有非阻塞赋值(<=
)的always
时钟块。 并且尽量不要混合两者。
上面的代码可能不是最好的例子。 不知道你想要build立什么加法器/触发器结构,有组合反馈path(这是不好的)的危险。 而且由于你没有input巴士,所以你基本上就是试图用空气来构builda
b
& c
!
但是要回答你的问题,任何分配给时钟always
块内的variables将推断一个触发器,除非它使用阻塞运算符( =
)分配,并用作一种局部variables。
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模拟器对待=
和<=
完全不同。 阻塞赋值意味着“立即将值分配给variables”。 非阻塞赋值意味着“找出要分配给该variables的内容,并将其存储在未来的某个时间分配”。 阅读以更好地理解这一点的好论文是:另见: http : //www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf
传统的Verilog智慧有一些错误。 对局部variables使用阻塞赋值没有任何问题。 但是,不应该使用阻塞分配进行同步通信,因为这是不确定的。
在时钟总是阻塞内的非阻塞分配将总是推断触发器,如语义所规定的。
一个时钟块总是阻塞一个触发器是否完全取决于它的使用方式。 如果可能在分配之前读取variables,则会推断出一个触发器。 否则,这就像一个临时variables,它会导致一些组合逻辑。
只是想添加Jan Decaluwe的答案。 看起来,野外的代码很less,实际上使用了Jan Decaluwe所描述的代码,即使它是绝对正确的。 混合阻塞和不阻塞的声明现在是一个禁忌,感谢卡明斯先生。
麻烦的是,大多数地方避免使用本地variables的阻塞语句,Google的直接search空间中的代码非常less,看起来就是一个例子。 Jan提到的编码风格的唯一地方就是本文中的获胜代码 。 而这个,我偶然遇到了
我也很难过。
但首先,你应该明白,非阻塞或阻塞实际上与是否创buildlatch / ff无关。
对于他们的区别,你可以简单地理解(开头)这一点:我。 如果使用阻塞,那么在阻塞语句LHS赋值之前,语句之后的句子不能执行,因为如果使用该variables,那么更改为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;
其中一个关键是要找出在你的代码中(总是阻塞)是否有任何情况variables没有赋值,但可能发生。 如果你没有把值传递给它,那么就会创buildlatch / 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.
以下也可以创buildlatch / ff:
always @(*) begin if(in) a = 1; else b = 1; end
– >为in = 1创build的latch / ffs,b没有赋值,in = 0没有赋值。
另外,当你always @(posedge clk)
感觉到clk的posedge always @(posedge clk)
,它肯定会以latch / ff结束。 因为,对于clk来说,肯定存在负面的边缘,而你什么都不做,latch / ffs被创build来保持所有的旧值!
请你总是可以解释在数字领域的verilog只是你必须明白如果你写的相同的代码将在门级转换将发生什么,我个人不要按照seq使用非阻塞的规则或使用阻塞组合这会限制你的想法。 坚持代码的数字方面只有这里是如果你的代码转换为门级只会看到你想要的只会发生什么
- 首先完整的加法器将被制造 – inputa和b
- 输出将进入触发器创build输出与clk同步
- 现在因为赋值被阻塞所以新的a将被应用到下一个完整的添加了这个新的a和c作为input,它的输出将会去dffcsync到clk创build新的b
- 现在因为b = c + a; 是在那里阻止统计所以b更新到这个新的b
- 现在它的c <= a + b现在发生了什么是一个完整的加法器被创build具有a和b作为input,这个dff同步到clk,现在是否会有其他条件再次说a = c;
- 那么将会创build一个dff,让旧的c不是由非阻塞语句创build的新的c,而是将这个dff同步到clk的输出转到a并且得到更新
感谢拉胡尔jain
我可以回答你的问题,但是我认为有一篇论文最适合这个,所以我build议你读出这篇克利福德·卡明斯的论文。 它会清除你所有的疑惑,除此之外还会加强你对verilog的理解。