1VerilogHDL中阻塞赋值与非阻塞赋值的区别与应用张文波摘要:VerilogHDL语言是世界上应用最广泛的硬件描述语言之一。在VerilogHDL语言中最容易混淆的概念之一就是阻塞赋值与非阻塞赋值,甚至一些非常有经验的VerilogHDL设计师也不能完全理解为什么和什么时候使用阻塞赋值和非阻塞赋值。本文详细阐述了阻塞赋值与非阻塞赋值的概念和区别,通过仿真和举例进一步解释了阻塞赋值与非阻塞赋值在工程中的典型应用和技巧。本文中的所有程序代码,在ISE10.1应用软件中均测试通过,可以直接引用。关键词:阻塞赋值;非阻塞赋值;仿真中图分类号:TP368文献标识码:TheDifferenceandApplicationofBlockingassignmentandNonblockingassignmentinVerilogHDLZHANGWen-bo(ChinaAirborenMissileAcademy,HenanLuoyang471000,China)ABSTRACT:Intheworld,oneofthemostextensivehardwaredescriptionlanguageisVerilogHDL.OneofthemostmisunderstoodconstructsintheVeriloglanguageistheblockingassignmentandnonblockingassignment,evenveryexperiencedVerilogdesignersdonotunderstandwhenandwhyblockingassignmentandnonblockingassignmentshouldbeused.Thispaperdetailsthedifferenceandtheconstructsoftheblockingassignmentandnonblockingassignment,andmakesafurtherexplanationaboutthetypicalapplicationandskillswhitthesimulationandexamples.Inthispaper,allofthecodewerevalidatedbyISE10.1.KEYWORDS:blockingassignment;nonblockingassignment;simulation1引言VerilogHDL语言是目前世界上最流行的硬件描述语言之一,被广泛的应用于基于可编程逻辑器件的项目开发。在VerilogHDL语言中,信号有两种赋值方式:阻塞赋值方式和非阻塞赋值方式。2功能定义赋值语句的功能是用赋值语句右端的表达式所定义的逻辑来驱动该赋值语句左端表达式的变量。2.1阻塞赋值语句阻塞赋值用符号“=”表示。阻塞赋值表示在当前的赋值完成前阻塞其他的赋值任务。即在赋值时,先计算“=”右边的值,此时赋值语句不允许任何别的赋值任务的干扰,直到现行的赋值完成时,才允许别的赋值语句的执行。也就是说,在同一个块语句中,其后面的赋值语句是在前一句赋值语句结束后再开始赋值的。比如下面的程序:initialbegina=0;//阻塞赋值语句S1a=1;//阻塞赋值语句S2end中包含了两条阻塞赋值语句S1和S2,我们假设initial块是在仿真时刻0得到执行的。由于S1和S2都是阻塞赋值语句,所以在执行S1时,S2被“阻塞”而不能得到执行;只有在S1执行完毕,a被赋值为0之后,S2才开始执行。而S2的执行将使变量a重新被赋值为1。2.2非阻塞赋值语句非阻塞赋值用符号“<=”表示。非阻塞赋值表示在当前的赋值未完成前不阻塞其他的赋值任务。即在赋值操作开始时计算“<=”右边的表达式,在赋值操作结束时更新“<=”左边的变量,并且在赋值操作的过程中,允许其他赋值语句同时执行。也就是说,在同一个块语句中,其后面的赋值语句是在前一句非阻塞赋值语句开始时,同时开始赋值的,并且是在块语句结束时,同时更新左边的变量后一起结束的。比如下面的程序:initialbeginA=1;//非阻塞赋值语句S3B=1;//非阻塞赋值语句S42end中包含了两条非阻塞赋值语句S3和S4,我们假设initial块是在仿真时刻0得到执行的。语句S3首先得到执行,但是对被赋值对象A的赋值操作要等到当前时间步结束时(initial块结束时)才执行,同时因为S3是一条非阻塞赋值语句,所以S3不会阻塞S4的执行,于是S4也随即开始执行,但对被赋值变量B的赋值操作也要等到当前时间步结束时(initial块结束时)才执行。所以,在当前时间步结束时(initial块结束时)被赋值变量A和B同时被赋值为1。3.阻塞赋值与非阻塞赋值的仿真利用内部时间控制方式,可以对阻塞赋值和非阻塞赋值进行延时,这样就可以从程序执行的时间上清楚的看到阻塞赋值与非阻塞赋值的执行过程。3.1带有内延时的阻塞赋值语句执行过程moduleblock_test;rega;initialbegina=0;//语句1a=#51;//语句2a=#100;//语句3a=#151;//语句4endendmodule在这个程序中,使用了带有内部延时的阻塞赋值语句,该模块的仿真输出波形如图1所示。011ns5ns10ns15ns20ns25ns30ns35ns40ns45ns50nsa图1带内延时的阻塞赋值仿真输出波形该程序模块内的阻塞赋值语句将依次得到执行,并且在前一条语句所指定的赋值操作没有完成之前,下一条语句被阻塞而不能得到执行。如图1所示:语句1在t=0ns时刻开始执行,a被赋值为0;语句2也是在t=0ns时刻开始执行(前一条赋值语句执行时所需要的时间可以忽略不计),延时5ns后,即t=5ns时,a被赋值为1;语句3在t=5ns时刻开始执行,延时10ns后,即t=15ns时,a被赋值为0;语句4在t=15ns时刻开始执行,延时15ns后,即t=30ns时,a被赋值为1。由此得出,阻塞赋值语句的执行,是从前一条阻塞赋值语句执行完成后才开始的。3.1带有内延时的非阻塞赋值语句执行过程moduleblock_test;rega;initialbegina=0;//语句1a=#51;//语句2a=#100;//语句3a=#151;//语句4endendmodule在这个程序中,使用了带有内部延时的非阻塞赋值语句,该模块的仿真输出波形如图2所示。011ns5ns10ns15ns20ns25ns30ns35ns40ns45ns50nsa图2带内延时的非阻塞赋值仿真输出波形该程序模块内的非阻塞赋值语句将并行同时得到执行,即在前一条语句所指定的赋值操作没有完成之前,下一条语句不会被阻塞而能得到执行。如图2所示:语句1在t=0ns时刻开始执行,a被赋值为0;语句2在t=0ns时刻开始执行(前一条语句为非阻塞赋值语句),延时5ns后,即t=5ns时,a被赋值为1;语句3在t=0ns时刻开始执行(前两条语句均为非阻塞赋值语句),延时10ns后,即t=10ns时,a被赋值为0;语句4在t=0ns时刻开始执行(前三条语句均为非阻塞赋值语句),延时15ns后,即t=15ns时,a被赋值为1。由此得出,非阻塞赋值语句的执行,是从所在的块程序开始执行时就开始执行的,没有执行的次序可言。4.阻塞赋值与非阻塞赋值的典型应用4.1组合逻辑中的阻塞与非阻塞设计一个“两与门相或”的组合逻辑电路。modulezuhe(out,a,b,c,d)inputa,b,c,d;outputout;regtemp1,temp2;always@(aorborcord)begintemp1=a&b;//temp1=a&b;temp2=c&d;//temp2=c&d;out=temp1|temp2;//out=temp1|temp2;endendmoudle3在这个实现“两与门相或”的组合逻辑的程序中分别采用阻塞赋值(“//”前的语句)和非阻塞赋值(“//”后的语句)两种方法。如果输入值a,b,c,d的值发生变化,都从0→1,则采用阻塞赋值语句所得到的结果是:temp1变为1,temp2变为1,out变为1;采用非阻塞赋值语句所得到的结果是:temp1变为1,temp2变为1,out仍为0。这是因为阻塞赋值是一步完成的,而且前一条语句执行的同时会阻止其他阻塞赋值语句的执行,所以执行“out=temp1|temp2”时,所用的temp1,temp2的值是更新过的值;非阻塞赋值是两步完成的,而且前一条语句执行的同时不会阻止其他非阻塞赋值语句的执行,也就是说,当a,b,c,d发生变化时,“out=temp1|temp2”语句同时开始执行,而此时的temp1,temp2的值并没有得到更新,所以out的值仍是0。由此我们看出在组合逻辑的建模中,应该使用阻塞赋值语句来实现。4.2时序逻辑中的阻塞与非阻塞设计一个由两个D触发器构成的时序逻辑电路。moduleshixu(clk,d,q1,q2)inputclk,d;outputq1,q1;regq1,q2;always@(posedgeclk)beginq1=d;//q1=d;q2=q1;//q2=q1;endendmodule在这个实现两个D触发器串联的电路中,分别采用了阻塞(“//”前的语句)和非阻塞(“//”后的语句)两种方法。如果clk信号发生一次跳变,根据阻塞赋值语句的执行过程,先把d的值赋于q1,再把q1的值赋于q2,可以得到执行后的结果是:q1=q2=d,显然这不是两个D触发器串联后应该得到的结果。我们试着把always块中的两个阻塞赋值语句的次序颠倒后再分析:先把q1的值赋于q2,再把d的值赋于q1,这样q1,q2的值就不再都是d,完成了两个D触发器串联的功能。如果用非阻塞赋值来实现,当clk信号发生一次跳变后,就会同时把d的值赋于q1,把q1的值赋于q2,这样得到的结果就是q1=d,q2=q1。而且无论两条非阻塞赋值语句的次序如何,执行结果都是如此。如果把寄存器从2个变为3个甚至n个,不同的次序对阻塞赋值会有不同的结果,但是非阻塞赋值语句的结果都是一样的。由此我们看出在时序逻辑的建模中,应该使用非阻塞赋值语句来实现。5阻塞赋值和非阻塞赋值的混合使用在组合逻辑建模中使用阻塞赋值,在时序逻辑建模中使用非阻塞赋值,这是阻塞和非阻塞赋值在建模中最典型的应用。但是,在实际的工程应用中,有时候混合使用两种赋值语句会使得建模更加容易。我们通过几个例子来深入理解阻塞赋值与非阻塞赋值的区别,并充分掌握混合使用时的特点。首先给出一个程序实例,程序代码如下:ex1:modulehunhe(clk,din,a,b,c);inputclk,din;outputa,b,c;rega,b,c;always@(posedgeclk)begina=a^din;//语句1,阻塞赋值b=a|din;//语句2,非阻塞赋值c=b&din;//语句3,非阻塞赋值endendmodule在ex1程序实例的always顺序语句块中,混合使用了阻塞和非阻塞赋值。程序综合后,逻辑电路原理图如图3所示。DQDQDQdinclkcbaa^dina|dinbb&dinca图3ex1逻辑电路原理图由于所有赋值对时钟边沿敏感,所以寄存器变量a、b、c都被综合成触发器。因为对寄存器变量a进行的是阻塞赋值(语句1),其值立即更新,所以后面的语句引用的是a的新值。但是对变量b进行的是非阻塞赋值(语句2),它的新值与其后的非阻塞赋值语句(语句3)同时更新。所以语句3对寄存器变量c赋值的语句引用的是b前一个时钟的值。4在ex1的基础上,稍微修改赋值语句形成ex2程序实例,程序代码如下:ex2:modulehunhe(clk,din,a,b,c);inputclk,din;outputa,b,c;rega,b,c;always@(posedgeclk)beginb=a|din;//语句1,非阻塞赋值a=a^din;//语句2,阻塞赋值c=a&di