此文章为v3学院原创 www.v3edu.org
开发过程中,我们用的最多的恐怕就是赋值语句了,我们常用的赋值方式有两种:阻塞赋值和非阻塞赋值。我刚开始学的时候就被这两种赋值方式搞晕了,当时脑子里面有几个问题总是一团乱麻-什么是阻塞赋值?什么是非阻塞赋值?什么时候用阻塞赋值?什么时候用非阻塞赋值?这两种赋值方式到底有哪些不同?什么时候两种赋值方式结合起来用?
当时由于好的教材比较少,因此我被这些简单的问题困扰了很久,今天通过本节课程的学习,我们将通过一些实际的例子来说明这些问题。
一非阻塞赋值语句
以赋值操作符“<=”来标识的赋值操作称为“非阻塞型过程赋值(Nonblocking Assignment)”。非阻塞型过程赋值语句的特点如下:
1.在begin-end串行语句块中,一条非阻塞过程语句的执行不会阻塞下一条语句的执行,也就是说在本条非阻塞型过程赋值语句对应的赋值操作执行完之前,下一条语句也可以开始执行。
2.仿真过程在遇到非阻塞型过程赋值语句后首先计算其右端赋值表达式的值,然后等到仿真时间结束时再将该计算结果赋值变量。也就是说,这种情况下的赋值操作是在同一仿真时刻上的其他普通操作结束后才得以执行。
如有这样一段程序1:
initial
begin
A<=B;//语句S1
B<=A; //语句S2
end
上述initial语句中包含了两条非阻塞型过程赋值语句S1和S2,当仿真进程遇到initial过程块后(0时刻),语句S1首先开始执行,赋值表达式“B”的值得到计算(但是对被赋值变量A的赋值操作要等到当前时间步结束才执行),同时由于S1是一条非阻塞型赋值语句,所以S1的执行不会阻塞S2的执行;由于S2也随即开始执行,其对应的赋值表达式“A”的值得到计算,由于这时S1对A的赋值操作还没有执行,所以此时计算得到的赋值表达式取值是A的初值。由于S2也是一条非阻塞型赋值语句,它对应的为变量B进行赋值操作也要等到当前时间步结束时才会得到执行;所以在当前时间步结束时,S1、S2两条语句对应的赋值操作同时执行,分别将计算得到的A和B初值赋给变量B和A,这样就交换了A与B的取值。
1.实例1代码
接下来,大家一起来看一个实例,代码如下:
01 module Assignment1(clk,rst_n); 02 //系统输入 03 input clk;//系统时钟输入 04 input rst_n;//低电平复位信号 05 //内部寄存器定义 06 reg [1:0]a;//内部寄存器 07 reg [1:0]b;//内部寄存器 08 //赋值语句块 09 always@(posedge clk or negedge rst_n) 10 begin 11 if(!rst_n) 12 begin 13 a<=2;//寄存器赋初值 14 b<=1;//寄存器赋初值 15 end 16 else 17 begin 18 a<=b;//寄存器数据交换 19 b<=a;//寄存器数据交换 20 end 21 end 22 endmodule |
2.实例1测试代码
编写测试代码如下:
01 `timescale 1ns/1ps 02 module tb; 03 04 reg clk; 05 reg rst_n; 06 07 initial 08 begin 09 clk=0; 10 rst_n=0; 11 # 1000.1 rst_n=1; 12 end 13 14 always #10 clk=~clk; 15 16 Assignment1 Assignment1( 17 .clk(clk), 18 .rst_n(rst_n) 19 ); 20 endmodule |
3.实例1对应的仿真波形
查看该实例对应的仿真波形如图3-1所示。
图3-1 非阻塞赋值对应的仿真波形
从仿真图我们可以看出,使用非阻塞型过程赋值语句,把a的初值给b,b的初值给a。因此可以证实我们前面的分析是正确的。非阻塞型过程赋值语句一般应用于时序逻辑。
二阻塞赋值语句
以赋值操作符“=”来标识的赋值操作称为“阻塞型过程赋值(blocking Assignment)”。阻塞型过程赋值语句的特点是:
1.串行块(begin-end)中的各条阻塞型过程赋值语句将以它们在顺序块中排列次序依次得到执行。
2.阻塞型过程赋值语句的执行过程是:首先计算右端赋值表达式的值,然后立即将计算结果赋值给“=”左端的被赋值变量。
阻塞型过程赋值语句的这两个特点表明:仿真进程在遇到阻塞型过程赋值语句时将计算表达式的值并立即将其结果赋给等式左边的被赋值变量;在串行语句块中,下一条语句的执行会被本条阻塞型过程赋值语句所阻塞,只有在当前这条阻塞型过程赋值语句所对应的赋值操作执行完后下一条语句才能开始执行。
如有这样一段程序2:
initial
begin
a=0;//语句S1
a=1;//语句s2
end
在这段initial语句中包含两条阻塞型过程赋值语句S1和S2,它们都是在仿真零时刻得到执行的,其对应的赋值操作也都是在0时刻进行的。但由于它们是阻塞型赋值语句,所以在执行S1语句时S2被阻塞而不能得到执行;只有在S1执行完,a被赋值0之后,S2才能开始执行。而S2的执行将使a被重新赋值1,所以上面这个过程块执行后变量a的值最终取值为1。
1.实例2代码
接下来,我们一起下面的实例,代码如下:
01 module Assignment2(clk,rst_n); 02 //系统输入 03 input clk;//系统时钟输入 04 input rst_n;//低电平复位信号 05 //内部寄存器定义 06 reg [1:0]a;//内部寄存器 07 reg [1:0]b;//内部寄存器 08 //赋值语句块 09 always@(posedge clk or negedge rst_n) 10 begin 11 if(!rst_n) 12 begin 13 b=1;//寄存器赋初值 14 a=2;//寄存器赋初值 15 end 16 else 17 begin 18 b=a;//寄存器数据交换 19 a=b;//寄存器数据交换 20 end 21 end 22 endmodule |
2.实例2对应的仿真波形
查看实例2对应的仿真波形如图3-2所示。
图3-2 阻塞赋值对应的仿真波形
可以看出,只是把赋值方式换成了阻塞型,结果就和非阻塞型的不同。阻塞型赋值语句一般用在组合逻辑中。
此文章为v3学院原创 www.v3edu.org