写在前面的话
开发过程中,我们用的最多的恐怕就是赋值语句了,我们常用的赋值方式有两种:阻塞赋值和非阻塞赋值。梦翼师兄刚开始学的时候就被这两种赋值方式搞晕了,当时脑子里面有几个问题总是一团乱麻-什么是阻塞赋值?什么是非阻塞赋值?什么时候用阻塞赋值?什么时候用非阻塞赋值?这两种赋值方式到底有哪些不同?什么时候两种赋值方式结合起来用?当时由于好的教材比较少,因此梦翼师兄被这些简单的问题困扰了很久,今天通过本节课程的学习,梦翼师兄将通过一些实际的例子来说明这些问题。
非阻塞赋值语句
以赋值操作符“<=”来标识的赋值操作称为“非阻塞型过程赋值(Nonblocking Assignment)”。非阻塞型过程赋值语句的特点如下:
(1) 在begin-end串行语句块中,一条非阻塞过程语句的执行不会阻塞下一条语句的执行,也就是说在本条非阻塞型过程赋值语句对应的赋值操作执行完之前,下一条语句也可以开始执行。
(2) 仿真过程在遇到非阻塞型过程赋值语句后首先计算其右端赋值表达式的值,然后等到仿真时间结束时再将该计算结果赋值变量。也就是说,这种情况下的赋值操作是在同一仿真时刻上的其他普通操作结束后才得以执行。
如以下语句的程序1:
Initial
begin
A<=B;//语句S1
B<=A; //语句S2
end
上述语句中包含了两条非阻塞型过程赋值语句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的取值。接下来,梦翼师兄和大家一起看一个实例,代码如下
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * E_mail : zxopenwjf@126.com * The module function:非阻塞赋值模块 *****************************************************/ 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 |
编写测试代码如下
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * E_mail : zxopenwjf@126.com * The module function:非阻塞赋值测试模块 *****************************************************/ 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 |
查看仿真波形如下:
从仿真图我们可以看出,使用非阻塞型过程赋值语句,把a的初值给b,b的初值给a。因此可以证实我们前面的分析是正确的。非阻塞型过程赋值语句一般应用于时序逻辑。
阻塞赋值语句
以赋值操作符“=”来标识的赋值操作称为“阻塞型过程赋值(blocking Assignment)”。阻塞型过程赋值语句的特点是:
(1)串行块(begin-end)中的各条阻塞型过程赋值语句将以它们在顺序块中排列次序依次得到执行。
(2)阻塞型过程赋值语句的执行过程是:首先计算右端赋值表达式的值,然后立即将计算结果赋值给“=”左端的被赋值变量。
阻塞型过程赋值语句的这两个特点表明:仿真进程在遇到阻塞型过程赋值语句时将计算表达式的值并立即将其结果赋给等式左边的被赋值变量;在串行语句块中,下一条语句的执行会被本条阻塞型过程赋值语句所阻塞,只有在当前这条阻塞型过程赋值语句所对应的赋值操作执行完后下一条语句才能开始执行。
如以下语句程序2:
initial
begin
a=0;//语句S1
a=1;//语句s2
end
在这段语句中包含两条阻塞型过程赋值语句S1和S2,它们都是在仿真零时刻得到执行的,其对应的赋值操作也都是在0时刻进行的。但由于它们是阻塞型赋值语句,所以在执行S1语句是S2被阻塞而不能得到执行;只有在S1执行完,a被赋值0之后,S2才能开始执行。而S2的执行将使a被重新赋值1,所以上面这个过程块执行后变量a的值终取值为1。
接下来,梦翼师兄和大家一起看一个实例,代码如下
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * E_mail : zxopenwjf@126.com * The module function:阻塞赋值模块 *****************************************************/ 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 |
查看仿真波形如下:
可以看出,只是把赋值方式换成了阻塞型,结果就和非阻塞型的不同。阻塞型赋值语句一般用在组合逻辑中。