在Verilog HDL中,有两种过程性赋值方式,即阻塞式(blocking)和非阻塞式(non-blocking)。这两种赋值方式看似差不多,其实在某些情况下却有着根本的区别,如果使用不当,综合出来的结果和你所想得到的结果会相去甚远。
Tip:所谓过程性赋值就是指在initial或always语句内的赋值,它只能对寄存器数 据类型的变量赋值。
阻塞式(blocking)的操作符为 “ = ”
非阻塞式(non-blocking)的操作符为 “ <= ”
首先,我们通过两个例子来看看这两种赋值方式的区别,这里使用的综合工具为DC。
例1:非阻塞式赋值
module nonblock (clock,in1,in2,in3,in4,out);
input clock,in1,in2,in3,in4;
output out;
reg out;
reg f;
always @ (posedge clock)
begin
f <= in2 | in3; //语句1
if (in1)
out <= f & in4; //语句2
else
out <= in4;
end
endmodule
例1综合后的结果为(**注**):
module nonblock ( clock, in1, in2, in3, in4, out );
input clock, in1, in2, in3, in4;
output out;
wire f, n_6;
mfntnq out_reg ( .q(out), .da(n_6), .db(in4), .sa(in1), .cp(clock) );
mfntnq f_reg ( .q(f), .da(1'b1), .db(in2), .sa(in3), .cp(clock) );
an02d1 U10 ( .z(n_6), .a1(f), .a2(in4) );
endmodule
为了更直观,给出相应的schematic:
module block (clock,in1,in2,in3,in4,out);
input clock,in1,in2,in3,in4;
output out;
reg out;
reg f;
always @ (posedge clock)
begin
f = in2 | in3; //语句1
if (in1)
out = f & in4; //语句2
else
out = in4;
end
endmodule
例2综合后的结果为:
module block ( clock, in1, in2, in3, in4, out );
input clock, in1, in2, in3, in4;
output out;
wire n_6;
mfntnq out_reg ( .q(out), .da(n_6), .db(in4), .sa(in1), .cp(clock) );
oa14d0 U10 ( .z(n_6), .a1(in3), .a2(in2), .b(in4) );
endmodule
相应的schematic:
分析:
大家可以看到,例1和例2的code写法完全一样,只是在always语句块中使用了不同的赋值方式,就导致综合出来的结果不同。
在例1中,是非阻塞式赋值方式,非阻塞式赋值的赋值对象总是在当前仿真时刻结束时被赋值,所以,当在对语句2中的out赋值这一时刻, f 值还没有得到语句1中的新值,而是原来的值(即上一个时刻的值)。
而在例2中,使用了阻塞式赋值方式,就是在always语句块中是一句一句执行的。在执行语句2之前,语句1就已经执行完成,f被赋好了新值,所以语句2中的f值取的是新值。
建议:
1. 阻塞式赋值用于组合逻辑建模;
2. 非阻塞式赋值用于时序逻辑建模。
**注**
mfntnq q = rising(cp) ? (sa&da | !sa&db) : 'p'
an02d1 z = a1 & a2
oa14d0 z = (a1 | a2) & b
参考书目:
Verilog HDL Synthesis A Practical Primer J.Bhasker