标签:命令 无效 containe nbu display 功能 RoCE 必须 表达
参考 Clifford E. Cummings, Sunburst Design, Inc. "Nonblocking Assignments in Verilog Synthesis, CodingStyles That Kill!"
前段时间为了探究阻塞和非阻塞的进行过简单测试,当时觉得对阻塞与非阻塞的理解已经可以了,直到发现了Sunburst的这篇论文(这个机构真的很nb,很多verilog,SV的经典教程都出自它),才算真的明白了阻塞和非阻塞的原理。
RHS(right-hand-side): 指等式右边的表达式或者变量(RHS expression or RHS variable)
LHS(left-hand-side):指等式左边的表达式或者变量(RHS expression or RHS variable)
竞争条件:Verilog竞争条件发生在当两个或多个语句被安排在相同的仿真时间步长中执行时,当语句执行的顺序改变时,会产生不同的结果。
阻塞赋值:当没有其它的Verilog描述可以打断“阻塞赋值”时,操作将会估计RHS的值并完成赋值。“阻塞”即是说在当前的赋值完成前阻塞其它类型的赋值任务。
非阻塞赋值:在一个时间步(time step)的开始估计RHS expression的值并在这个时间步(time step)结束时用等式右边的值更新取代LHS。在估算RHS expression和更新LHS expression的中间时间段,其它的对LHS expression的非阻塞赋值可以被执行。即是说“非阻塞赋值”从估计RHS开始并不阻碍执行其它的Verilog描述。
层积事件列(stratified event queue)是一个概念模型,每个仿真器都有不同的实现方式。“层积事件列”逻辑上划分为四个不同的队列,分别用于当前的仿真时间和未来的仿真时间。
活跃事件列(Active Events)是(最多的被预备执行的Verilog事件)包括:
注意“非阻塞赋值”的LHS不在“活跃事件列”里更新。
非活跃事件列(Inactive event): #0延时的阻塞赋值。
非阻塞赋值更新事件列(Thenonblocking assign updates event queue)即是“非阻塞赋值”的LHS expression被安排更新赋值的那些事件。在一个仿真时间步(simulation time step)的开始,“RHS expression 的估值”与其它被激活事件是以任意的次序进行的。
monitor事件列是由那些被安排的“\(strobe”和“\)monitor”显示命令带来的。$strobe 和 $monitor 用于显示一个仿真时间步结束时变量更新后的值(这时该仿真时间步里所有的赋值分配都已经完成)
注意:事件可以被加到任意的事件列里(由IEEE标准强制约束的),但是只可能从“活跃事件列”里被移出。其它事件列里的事件最终总是要成为“激活事件”的(或者提升为“活跃事件)。
---- Non-Self Triggered ------
module osc1 (clk);
output clk;
reg clk;
initial #10 clk = 0;
always @(clk) #10 clk = ~clk;
endmodule
----------------------------------------
------- Self Triggered ------
module osc2 (clk);
output clk;
reg clk;
initial #10 clk = 0;
always @(clk) #10 clk <= ~clk;
endmodule
----------------------------------------
一般都建议使用阻塞逻辑描述组合逻辑,非阻塞逻辑描述时序逻辑。
但阻塞赋值就一定不能描述时序逻辑吗?答案是否定的,如果仔细安排阻塞赋值的顺序,也是可以进行时序电路的描述,并能通过仿真和综合(或者可以通过综合)。
以一个三级流水线为例:
首先我们直到,采用非阻塞赋值,仿真和综合肯定没问题。那么如果使用阻塞赋值呢?
always @(posedge clk) begin
q1 = d;
q2 = q1;
q3 = q2;
end
采用上面这种方式,肯定不是我们要的结果,而是将会只综合出一个寄存器。这肯定是不可行的。
而如果换一下顺序:
always @(posedge clk) begin
q3 = d2;
q2 = q1;
q1 = q;
end
阻塞赋值被仔细地排序,以使仿真能够像寄存器一样正确地工作。这样的写法无论是仿真还是综合都是正确的,但是不建议这样做。
另外,如果将一个always块拆分成三个写,也就是:
always @(posedge clk) q1=d;
always @(posedge clk) q2=q1;
always @(posedge clk) q3=q2;
这样Verilog标准允许以任意的次序来仿真执行3个always块,这也许会使得该流水线仿真结果产生错误,因为这产生了Verilog竞争条件。由不同的always块执行顺序会产生不同的结果。尽管这样,它的综合结果将是正确的!这就意味着综合前仿真和综合后仿真不匹配。
另外,在组合逻辑中,可以用非阻塞赋值来描述吗?
答案是肯定的,但是也需要一番波折,还是以一个例子来看:
always @(a or b or c or d) begin
tmp1 <= a & b;
tmp2 <= c & d;
y <= tmp1 | tmp2;
end
上面这段组合逻辑采用非阻塞赋值来描述,可以发现,y的值没法被正确更新,因为当abcd改变时tmp与y赋值的RHS同时估值,所以y的LHS更新时使用的还是旧的tmp,导致功能不正确。要解决这一问题也不麻烦:
always @(a or b or c or d or tmp1 or tmp2) begin
tmp1 <= a & b;
tmp2 <= c & d;
y <= tmp1 | tmp2;
end
在“非阻塞赋值更新事件队列”中当非阻塞赋值更新LHS变量时,always块将会“自触发”并使用最新的tmp1和tmp2来更新y输出。现在y输出值正确了,但时代价是因为增加了两条 passes贯穿整个always块。使用太多的pass来贯穿always块等于降低仿真器的性能。因此虽然能获得正确功能,但不建议使用。
非阻塞赋值在$display命令之后才被更新赋值,因此,display如果紧跟非阻塞赋值则无效。
initial $monitor("\$monitor: a = %b", a);
initial begin
$strobe ("\$strobe : a = %b", a);
a = 0;
a <= 1;
$display ("\$display: a = %b", a);
#1 $finish;
end
输出:
\(display: a = 0
\)monitor: a = 1
$strobe : a = 1
在层积事件列中可以发现,#0延时有一个专门的非活跃事件列,因此,零延迟#0 使得赋值事件处于“非激活事件列”,也就是非阻塞赋值LHS更新之前。
initial begin
a = 0;
b = 1;
a <= b;
b <= a;
$monitor ("%0dns: \$monitor: a=%b b=%b", $stime, a, b);
$display ("%0dns: \$display: a=%b b=%b", $stime, a, b);
$strobe ("%0dns: \$strobe : a=%b b=%b\n", $stime, a, b);
#0 $display ("%0dns: #0 : a=%b b=%b", $stime, a, b);
end
结果:
0ns: $display: a=0 b=1
0ns: #0 : a=0 b=1
0ns: $monitor: a=1 b=0
0ns: $strobe : a=1 b=0
从上面可以看到,使用#0延时后,display语句进入非活跃事件,在非阻塞赋值之前执行。
标签:命令 无效 containe nbu display 功能 RoCE 必须 表达
原文地址:https://www.cnblogs.com/lyc-seu/p/12701860.html