一些设计师建议非阻塞赋值不应该只为编写时序逻辑,它也可以用来编写组合逻辑。当然对于简单的组合逻辑always块这是可以的,但是对于在一个always块里面含有多个赋值陈述,例如例19含有and-or的陈述,使用了不含延迟(delay)的非阻塞赋值会造成仿真不正确,或者要使仿真正确您需要另外的添加敏感事件列表(sensitivity list entries),和“多登入路径”(multiple passes)来贯穿always 块以使得仿真正确。接下来的问题是从仿真需要多长时间来看,这是低效率的(外注:即降低仿真的performance)。
例19的y输出建立在3个依次执行的陈述上(外注:tmp1 <= a & b; tmp2 <= c & d; y <= tmp1 | tmp2;)。由于非阻塞赋值的LHS变量值更新是在对RHS表达式估值之后,所以tmp1和tmp2的值仍然是该always块上一个登入口的值而不是在这一个仿真时间步(simulation time step)结束时被更新的值。因此y的值将受旧的tmp1和tmp2影响,而不是这次扫描过的always块内被更新的值。
module ao4 (y, a, b, c, d);
output y;
input a, b, c, d;
reg y, tmp1, tmp2;
always @(a or b or c or d) begin
tmp1 <= a & b;
tmp2 <= c & d;
y <= tmp1 | tmp2;
end
endmodule
Example 19 - Bad combinational logic coding style using nonblocking assignments
例20与例19是一样的,不同之处在于tmp1和tmp2被添加到事件列表中去了。如第7段(section 7.0)中所述,在“非阻塞赋值更新事件队列”中当非阻塞赋值更新LHS变量时,always块将会“自触发”并使用最新的tmp1和tmp2来更新y输出。现在y输出值正确了因为增加使用了两条“登入路径”(two passes)贯穿整个always块。使用更多的“登入路径”来贯穿always块等于降低仿真器的性能,因此如果可以有合理的一些代码变化可以取代这种用法的话,就尽量避免这种用法。
module ao5 (y, a, b, c, d);
output y;
input a, b, c, d;
reg y, tmp1, tmp2;
always @(a or b or c or d or tmp1 or tmp2) begin
tmp1 <= a & b;
tmp2 <= c & d;
y <= tmp1 | tmp2;
end
endmodule
Example 20 - Inefficient multi-pass combinational logic coding style with nonblocking assignments
发展一个好的习惯可以避免使用“多登入路径”(multiple passes)贯穿always块,即使用阻塞赋值为组合逻辑建模。
module ao2 (y, a, b, c, d);
output y;
input a, b, c, d;
reg y, tmp1, tmp2;
always @(a or b or c or d) begin
tmp1 = a & b;
tmp2 = c & d;
y = tmp1 | tmp2;
end
endmodule
Example 21 - Efficient combinational logic coding style using blocking assignments
例21与例19一样,不同之处只在于用阻塞赋值替代了非阻塞赋值。这保证了在一个“登入路径”贯穿always后y输出的正确(guarantee that the y-output assumes the correct value after only one pass through the always block?)。因此有下面的编码方针:
#3: 当用always块为组合逻辑建模,使用“阻塞赋值”
12.0 时序-组合混合逻辑建模:使用非阻塞赋值
很多时候为了方便我们把时序和一些简单的组合逻辑放在一起。当我们把时序和组合编码放在一个always块的时候,像编写时序逻辑一样使用非阻塞赋值为这种混合逻辑建模,如下面的例22:
module nbex2 (q, a, b, clk, rst_n);
output q;
input clk, rst_n;
input a, b;
reg q;
always @(posedge clk or negedge rst_n)
if (!rst_n) q <= 1'b0;
else q <= a ^ b;
endmodule
Example 22 - Combinational and sequential logic in a single always block
与例22相同的逻辑也可以使用两个分立的always块------一个是纯粹的时序逻辑(使用非阻塞赋值),另一个是纯粹的组合逻辑(使用阻塞赋值)------建模,例如下面的例23:
module nbex1 (q, a, b, clk, rst_n);
output q;
input clk, rst_n;
input a, b;
reg q, y;
always @(a or b)
y = a ^ b;
always @(posedge clk or negedge rst_n)
if (!rst_n) q <= 1'b0;
else q <= y;
endmodule
Example 23 - Combinational and sequential logic separated into two always blocks
#4: 当在同一个always块里面既为组合逻辑又为时序逻辑建模,使用“非阻塞赋值”。
13.0 其它混合“阻塞”与“非阻塞”赋值建模方针
Verilog允许在一个always块里面自由混合“阻塞”与“非阻塞”赋值。一般情况下在同一个always块里面混合“阻塞”与“非阻塞”赋值是“衰婆”风格(poor coding style,呵呵,借用电影《钢琴教师》里的翻译“衰婆”,刚好poor发音与“婆”有些相近。不过可能引起大家一阵反胃,女士们一阵痛恨------向导演,可不要向我!),尽管Verilog允许这样做。
下面的例24的仿真和综合都将是正确的,因为“阻塞”与“非阻塞”赋值不是针对同一个变量来的。尽管这可以“正常工作”,但是作者不推荐这种风格。
module ba_nba2 (q, a, b, clk, rst_n);
output q;
input a, b, rst_n;
input clk;
reg q;
always @(posedge clk or negedge rst_n) begin: ff
reg tmp;
if (!rst_n) q <= 1'b0;
else begin
tmp = a & b;
q <= tmp;
end
end
endmodule
Example 24 - Blocking and nonblocking assignment in the same always block - generally a bad idea!
下面的例25在大多数情况下仿真是正确的,但是新思(Synopsys)工具会报告语法错误因为针对同一个既进行了“阻塞赋值”又进行了“非阻塞赋值”。这样的编码必须进行修改才可以综合。(Error:Cannot mix blocking and non blocking assignments on signal .)
module ba_nba6 (q, a, b, clk, rst_n);
output q;
input a, b, rst_n;
input clk;
reg q, tmp;
always @(posedge clk or negedge rst_n)
if (!rst_n) q = 1'b0; // blocking assignment to "q"
else begin
tmp = a & b;
q <= tmp; // nonblocking assignment to "q"
end
endmodule
Example 25 - Synthesis syntax error - blocking and nonblocking assignment to the same variable
为了养成好的编写习惯,作者推荐始终坚持:
#5: 不要在同一个always块里面混合使用“阻塞赋值”和“非阻塞赋值”。
14.0 对同一变量多处赋值(Multiple assignments to the same variable)
对同一变量在二个以上(包括二个)always块里面进行赋值就是一种Verilog竞争生成环境------即使使用非阻塞赋值。 |