Verilog 基础知识 中已经介绍过了阻塞赋值和非阻塞赋值的区别,下面通过一个在Vivado中的简单例子来直观的反映两者的不同。
首先给出设计源代码如下。
module block(a,b,c,clk,x);
input x;
input clk;
output reg a,b,c;
always@(posedge clk)
begin
a = x; //阻塞赋值
b = a;
c = b;
// a <= x; //非阻塞赋值
// b <= a;
// c <= b;
end
endmodule
仿真测试源代码如下。
`timescale 1ns / 1ps
module sim_block();
reg x;
reg clk;
wire a,b,c;
initial
begin
x = 0;
clk = 1;
forever
begin
#({$random}%100)
x = ~x; //x的值在100ns内随机翻转一次
end
end
always #10 clk = ~clk;
// Instantiate the Unit Under Test (UUT) 例化待测设计
block uut_block(
.x(x),
.clk(clk),
.a(a),
.b(b),
.c(c)
);
endmodule
运行仿真后,阻塞赋值的结果如下图所示,其中仿真运行时间设置的是1us。
通过上图可以看到,阻塞赋值方式下,在时钟上升沿到来后,a,b,c的值是同时发生变化的,也就是在时钟上升沿到来后,x的值立刻赋给a,a又立刻赋值给b,b又立刻赋值给c。
非阻塞赋值的仿真结果如下图所示。
可以看到,如果是非阻塞赋值,在时钟上升沿到来后,x的值没有立刻赋给a,a的值没有立刻赋值给b,b的值也没有立刻赋值给c,b为a原来的值,同样,c为b原来的值。这就导致仿真一开始b和c的值都是未知的,因为上述仿真测试代码里并没有对a和b的值做初始化。非阻塞赋值情况下新的值会延迟一个时钟周期才会给到。
阻塞赋值的RTL图如下图所示。
非阻塞赋值的RTL图如下图所示。
通过阻塞赋值和非阻塞赋值的RTL图也可以看出明显的不同,非阻塞赋值情况下得到的都是D触发器的旧值。
所以一般情况下,在组合逻辑中使用阻塞赋值,执行赋值语句后立即改变,在assign语句中必须用阻塞赋值。在时序逻辑电路中使用非阻塞赋值,这样可以避免仿真时出现竞争冒险现象。