IEEE verilog标准中提供了阻塞赋值和非阻塞赋值两种赋值方式,二者的执行过程如下:
1.阻塞赋值(=):阻塞赋值操作实质上是一次性连续完成的,即计算等号右边变量(或表达式)的值(RHS)并立即赋值给等号左边的变量(LHS)。其中阻塞的含义为在同一个always块中,当前赋值语句正在执行时禁止其后的所有其他赋值语句的执行。只有当前赋值语句执行完成后,其后的赋值语句才能被执行。
2.非阻塞赋值(<=):非阻塞赋值操作实质上是分两步完成的,即第一步:在敏感事件开始时刻(如clk正跳变沿开始时刻)开始计算等号右边变量(或表达式)的值RHS;第二步:在敏感事件结束时刻(如clk正跳变沿结束时刻)将等号右边的值赋给等号左边的变量LHS。其中非阻塞的含义为在执行当前的非阻塞赋值语句的同时允许其他的语句执行。
由以上执行过程知,二者主要区别为:等号右边的变量值是否立即得到更新;赋值语句执行期间是否允许其他语句执行。
实验验证:
实验目的为验证在always块中分别采用阻塞赋值和非阻塞赋值对时序电路输出的影响。
阻塞赋值验证代码:
/*-----------------------------------------------------------
Filename: barrage_assign.v
Function: 说明阻塞赋值可能带来电路的竞争冒险从而使输出不确定
Author: Zhang Kaizhou
Date: 2019-10-12 11:58:43
------------------------------------------------------------*/
module barrage_assign(clk, reset, y1, y2);
//输入输出端口定义
output y1, y2;
input clk, reset;
//内部寄存器定义
reg y1, y2;
//功能块1
always@(posedge clk or posedge reset)
begin
if(reset) y1 = 0;
else y1 = y2;
end
//功能块2
always@(posedge clk or posedge reset)
begin
if(reset) y2 = 1;
else y2 = y1;
end
endmodule12345678910111213141516171819202122232425262728/*-----------------------------------------------------------
Filename: barrage_assign_t.v
Function: barrage_assign模块的测试程序
Author: Zhang Kaizhou
Date: 2019-10-12 21:04:06
------------------------------------------------------------*/
`timescale 1ns/1ns
`define PERIOD 10
module barrage_assign_t(y1, y2, clk, reset);
output y1, y2, clk, reset;
reg clk, reset;
barrage_assign m0(.clk(clk), .reset(reset), .y1(y1), .y2(y2));
initial
begin
clk = 0; reset = 0;
#100 reset = 1;
#100 reset = 0;
#300 reset = 1;
#500 reset = 0;
#500 $stop;
end
//产生时钟信号
always #`PERIOD clk = ~clk;
endmodule12345678910111213141516171819202122232425262728
阻塞赋值仿真结果:
非阻塞赋值验证代码:
/*----------------------------------
Filename: non_block_assign.v
Function: 说明非阻塞赋值语句的特性
Author: Zhang Kaizhou
Date: 2019-10-12 21:08:56
-----------------------------------*/
module non_block_assign(clk, reset, y1, y2);
//输入输出端口定义
input clk, reset;
output y1, y2;
//内部寄存器定义
reg y1, y2;
//功能块1
always@(posedge clk or posedge reset)
begin
if(reset) y1 <= 0;
else y1 <= y2;
end
//功能块2
always@(posedge clk or posedge reset)
begin
if(reset) y2 <= 1;
else y2 <= y1;
end
endmodule12345678910111213141516171819202122232425262728/*---------------------------------------
Filename: non_block_assign_t.v
Function: non_block_assign模块的测试程序
Author: Zhang Kaizhou
Date: 2019-10-12 21:09:05
---------------------------------------*/
`timescale 1ns/1ns
`define PERIOD 10
module non_block_assign_t(y1, y2, clk, reset);
output y1, y2, clk, reset;
reg clk, reset;
non_block_assign m0(.clk(clk), .reset(reset), .y1(y1), .y2(y2));
initial
begin
clk = 0; reset = 0;
#100 reset = 1;
#100 reset = 0;
#300 reset = 1;
#500 reset = 0;
#500 $stop;
end
//产生时钟信号
always #`PERIOD clk = ~clk;
endmodule12345678910111213141516171819202122232425262728
非阻塞赋值仿真结果:
实验总结:
对比上面两种赋值方式对应的仿真结果可知,对于阻塞赋值,因为IEEE Verilog标准规定always块的开始执行时刻的先后顺序是随机的(且always块是并行执行的),又由于在本次实验中两个always块中的变量变化相互依赖,再加上阻塞赋值是立即更新完成的,所以使得电路中存在竞争冒险现象,最终导致输出不确定。若功能块1先被执行,则输出为{y1, y2} = 2’b11;若功能块2先被执行,则输出为{y1, y2} = 2’b00。显然第一张仿真图显示的是第一种情况。
对于非阻塞赋值,虽然两个always块开始执行时刻的先后顺序是随机的,但是无论哪一个always块先提前几个ps开始执行,由于非阻塞赋值的执行过程是分两步进行的,在执行第一步(RHS计算)时,y1和y2的值仍然保持上一次更新后的值,在clk正跳变沿结束时刻,y1和y2会被同时更新为重新计算后的新值。所以在非阻塞赋值方式下,每个时钟周期y1和y2的值都会发生翻转。
附注:
用Verilog编写可综合模块时推荐遵循四条原则:
1.用always块描述时序逻辑时用非阻塞赋值(<=)。
2.用always块描述组合逻辑时用阻塞赋值(=)。
3.用always块描述时序和组合混合逻辑时用非阻塞赋值(<=)。
4.避免在多个always块中对同一变量赋值。