PLD - Verilog 阻塞和非阻塞

本文详细解析了Verilog HDL中阻塞与非阻塞赋值的区别及其执行过程,通过具体实例展示了两种赋值方式对逻辑电路设计的影响,并提出了良好的编程规范建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://blog.youkuaiyun.com/ywhfdl/article/details/7495615   

而用非阻塞赋值方式描述的移位寄存器,无论将其“always”过程块中四条赋值语句的顺序怎么变动,均不会影响其综合几个,其结果与第一个例子的结果相 同。对于时许逻辑描述和建模,应尽量使用非阻塞赋值方式。此外,若在同一个“always”过程块中描述时许和组合逻辑混合电路时,也最好使用非阻塞赋值 方式。

——

        非阻塞赋值不能用于“assign”持续赋值中,一般只出现在“initial”和“always”等过程块中,对reg型变量进行赋值。像assign out<=a+b;这样的语句是错误的。
        当用“always”块来描述组合逻辑时,既可以用阻塞赋值,也可以采用非阻塞赋值。但在同一个过程块中,最好不要同时用阻塞赋值和非阻塞赋值,虽然同时使用这两种赋值方式在综合时并不一定会出错。
在向函数(function)的返回值赋值时,应使用阻塞赋值“=”。
        不能在一个以上的“always”过程块中对同一个变量赋值,这样会引起冲突,在综合时会报错。
        在一个模块中,严禁对同一个变量既进行阻塞赋值,又进行非阻塞赋值,这样在综合时会报错。
        对时序逻辑描述和建模,应尽量使用非阻塞赋值方式,此外,若在同一个“always”过程块中描述时序和组合逻辑混合电路时,也最好使用非阻塞赋值方式。



http://blog.sina.com.cn/s/blog_7f72c4030100rny4.html




在对用verilog HDL编好的程序进行仿真和综合的过程中经常会遇到由于“=”和“<=”的使用不当产生不易察觉的问题,从而使仿真和综合的结果不一致,那是由于设计者对阻塞与非阻塞过程赋值的功能和执行过程没有深刻理解所造成的,本文将详细地阐述阻塞与非阻塞过程赋值的功能和执行过程,并通过一些具体的例子来分析他们之间的差异。

    过程赋值语句只能出现在always语句和initial语句中。有两种过程赋值语句:

(1)阻塞式过程赋值(blocking assignments)

    阻塞赋值由符号“=”来完成,“阻塞”即是说在当前的赋值完成前阻塞其他类型的赋值任务。但是如果右边表达式中含有延时语句,则在延时没结束前不会阻塞其他赋值任务。

(2)非阻塞式过程赋值(nonblocking assignments)

    非阻塞赋值由符号“<=”来完成,“非阻塞赋值”在一个时间步(time step)的开始估计右边表达式的值,并在这个时间步结束时用等式右边的值更新取代左边表达式的值。在估算右边表达式和更新左边表达式的中间时间段,其他的对左边表达式的非阻塞赋值可以被执行。即“非阻塞赋值”从估计右边表达式开始并不阻碍其他的赋值任务。

执行过程:

    在分析阻塞与非阻塞过程赋值语句的执行过程之前,必须了解IEEE Verilog标准中对层积事件列(stratified event queue)的划分,包括以下几种:

    活跃事件列(active events)发生在当前仿真时刻的事件,列中的事件可以在任意的顺序执行。从图中可见阻塞过程赋值和非阻塞过程赋值中的对右端表达式求值都属于活跃事件。

    非活跃事件(inactive events)发生在当前仿真时刻的事件,但是必须等所有的活跃事件执行完后才执行。从图可见#0延时的阻塞过程赋值属于非活跃事件。

FPGA/CPLD-浅析verilog阻塞赋值与非阻塞赋值

                                     图1  层积事件列表

    非阻塞事件列(nonblocking events)发生在当前仿真时刻,在所有的活跃事件和非活跃事件都执行完后执行,从图可见更新非阻塞赋值左边变量的值属于这种事件。

    侦测事件列(monitor events)发生在当前仿真时刻,当上述所有事件都执行完后才执行,包括系统任务$monitor $strobe。

从层积事件列表不难看出阻塞与非阻塞过程赋值在执行过程上的区别:

1、阻碍式赋值一步完成。

2、非阻塞式赋值分两步完成。

例子:

FPGA/CPLD-浅析verilog阻塞赋值与非阻塞赋值

在这个例子中,如果a,b,c,d的值发生如下变化:a,b,c,d都从0->1;

则采用阻塞赋值语句所得的结果是:

t1变为1,t2变为1,out变为1;

采用非阻塞赋值语句所得的结果是:

t1变为1,t2变为1,out仍为0;

从阻塞与非阻塞赋值语句的执行过程来看就是:

    阻塞赋值是一步完成,而且一条语句执行的同时会阻止其他阻塞赋值语句的执行,所以执行“out = t1 | t2;”语句时所用的t1,t2的值是更新过的值;

    非阻塞赋值是两步完成而且一条语句执行的同时不会阻碍其他非阻塞赋值语句的执行,首先执行右边表达式的估值,此时t1,t2都没有被更新,使用的都是没有更新的旧值,然后执行左边变量的更新,所以out的值不变。

    由上例可见“=”和“<=”的不同对逻辑的影响,很显然此例要实现的是两个与门然后或的一个组合逻辑电路,而使用“<=”的电路并不能满足要求,但可以通过在触发事件表中增加变量来满足功能。把@(a or b or c or d)改为@(a or b or c or d or t1 or t2),这样每当t1,t2发生变化时,always语句会被重新计算,并最终得到正确的out值,但是这样会降低仿真器性能,由此发现以下两个问题:

(1)非阻塞赋值不反映逻辑流;

(2)需要将所有赋值对象都列入事件列表中。

如果用阻塞式赋值就很容易避免这两个问题,所以一个好的编程风格就是:对组合逻辑建模采用阻塞式赋值。

例子:

FPGA/CPLD-浅析verilog阻塞赋值与非阻塞赋值

    ex2综合后能得到所希望的逻辑电路吗?答案是否定的,根据阻塞赋值语句的执行过程可以得到执行后的结果是q1 = d;q2 = d;实际只会综合出一个寄存器,而不是所期望的两个,那如何才能得到所需要的电路呢?把always块中的两个赋值语句的次序癫倒后在分析:先把q1的值赋于q2,然后再把d赋于q1,这样q1,q2的值就不再都是d,满足了要求。

    如果用非阻塞赋值来实现,就会发现不论两天语句的次序如何都能满足要求。如果把寄存器从2个变为3,4,···,n个,语句的次序就会更多,不同的次序对阻塞赋值会有不同的结果,但非阻塞赋值语句的结果都是一样的,所以一个好的编程风格是:对时序逻辑建模采用非阻塞式赋值。

 

 

引用《verilog HDL程序设计与实践》-云创工作室

//8位乘法器

module mult8(clk,a,b,q);

 parameter size = 8;

 input clk;

 input[7:0] a,b;

 output[15:0] q;

 reg[15:0] a_t;

 reg[7:0] b_t;

 reg[3:0] cnt;

 always @(posedge clk)

 begin

  q = 0;

  a_t = {8'd0,a};

  b_t = b;

  cnt = size;

  for(cnt=size;cnt>0;cnt=cnt-1)

  begin

   if(b_t[0])

    q = q + a_t;    //一定要使用阻塞赋值

   a_t = a_t << 1;  //一定要使用阻塞赋值

   b_t = b_t >> 1;  //一定要使用阻塞赋值

  end

 end

endmodule

    在循环语句中出项的变量都采用了阻塞赋值,这是因为在always语句中使用非阻塞赋值“<=”时,只有在always结束后才会把右端的值赋值给左边的寄存器。如果采用非阻塞赋值,则会造成循环语句只执行一次。

 

结语:

(1)对组合逻辑建模采用阻塞式赋值。

(2)对时序逻辑建模采用非阻塞是赋值。

(3)用两个always块分别对FSM的组合和时序逻辑建模。

(4)尽量不要在同一个always块里面混合使用“阻塞赋值”和“非阻塞赋值”,如果在同一个always块里面既有组合逻辑又有时序逻辑建模,应使用“非阻塞赋值”。







 http://www.cnblogs.com/Jezze/archive/2012/03/19/2406847.html

两个要点:

×在描述组合逻辑的always块中用阻塞赋值,则综合成组合逻辑的电路结构。
×在描述时序逻辑的always块中用非阻塞赋值,则综合成时序逻辑的电路结构。

为了更好地理解上述要点,我们需要对Verilog 语言中的阻塞赋值和非阻塞赋值的功能和执行时间上的差别有深入的了解。为了解释问题方便下面定义两个缩写字:
RHS – 方程式右手方向的表达式或变量可分别缩写为: RHS表达式或RHS变量。 LHS – 方程式左手方向的表达式或变量可分别缩写为: LHS表达式或LHS变量。 
IEEE Verilog标准定义了有些语句有确定的执行时间,有些语句没有确定的执行时间。若有两条或两条以上语句准备在同一时刻执行,但由于语句的排列次序不同(而这种排列次序的不同是IEEE Verilog标准所允许的), 却产生了不同的输出结果。这就是造成Verilog模块冒险和竞争现象的原因。为了避免产生竞争,理解阻塞和非阻塞赋值在执行时间上的差别是至关重要的。

阻塞赋值

阻塞赋值操作符用等号(即 = )表示。为什么称这种赋值为阻塞赋值呢?这是因为在赋值时先计算等号右手方向(RHS)部分的值,这时赋值语句不允许任何别的Verilog语句的干扰,直到现行的赋值完成时刻,即把RHS赋值给 LHS的时刻,它才允许别的赋值语句的执行。一般可综合的阻塞赋值操作在RHS不能设定有延迟,(即使是零延迟也不允许)。从理论上讲,它与后面的赋值语句只有概念上的先后,而无实质上的延迟。 若在RHS 加上延迟,则在延迟期间会阻止赋值语句的执行, 延迟后才执行赋值,这种赋值语句是不可综合的,在需要综合的模块设计中不可使用这种风格的代码。
  
阻塞赋值的执行可以认为是只有一个步骤的操作:

计算RHS并更新LHS,此时不能允许有来自任何其他Verilog语句的干扰。 所谓阻塞的概念是指在同一个always块中,其后面的赋值语句从概念上(即使不设定延迟)是在前一句赋值语句结束后再开始赋值的。

如果在一个过程块中阻塞赋值的RHS变量正好是另一个过程块中阻塞赋值的LHS变量,这两个过程块又用同一个时钟沿触发,这时阻塞赋值操作会出现问题,即如果阻塞赋值的次序安排不好,就会出现竞争。若这两个阻塞赋值操作用同一个时钟沿触发,则执行的次序是无法确定的。下面的例子可以说明这个问题。

[例1]. 用阻塞赋值的反馈振荡器
    module fbosc1 (y1, y2, clk, rst);
      output y1, y2;
      input  clk, rst;
      reg    y1, y2;

      always @(posedge clk or posedge rst)
        if (rst) y1 = 0;  // reset
        else     y1 = y2;

      always @(posedge clk or posedge rst)
        if (rst) y2 = 1;  // preset
        else     y2 = y1;
    endmodule
    
按照IEEE Verilog 的标准,上例中两个always块是并行执行的,与前后次序无关。如果前一个always块的复位信号先到0时刻,则y1 和y2都会取1,而如果后一个always块的复位信号先到0时刻,则y1 和y2都会取0。这清楚地说明这个Verilog模块是不稳定的会产生冒险和竞争的情况。

非阻塞赋值

非阻塞赋值操作符用小于等于号 (即 <= )表示。为什么称这种赋值为非阻塞赋值?这是因为在赋值操作时刻开始时计算非阻塞赋值符的RHS表达式,赋值操作时刻结束时更新LHS。在计算非阻塞赋值的RHS表达式和更新LHS期间,其他的Verilog语句,包括其他的Verilog非阻塞赋值语句都能同时计算RHS表达式和更新LHS。非阻塞赋值允许其他的Verilog语句同时进行操作。非阻塞赋值的操作可以看作为两个步骤的过程:

1)    在赋值时刻开始时,计算非阻塞赋值RHS表达式。
2)    在赋值时刻结束时,更新非阻塞赋值LHS表达式。

非阻塞赋值操作只能用于对寄存器类型变量进行赋值,因此只能用在"initial"块和"always"块等过程块中。非阻塞赋值不允许用于连续赋值。下面的例子可以说明这个问题:

[例2]. 用非阻塞赋值的反馈振荡器
    module fbosc2 (y1, y2, clk, rst);
      output y1, y2;
      input  clk, rst;
      reg    y1, y2;

      always @(posedge clk or posedge rst)
        if (rst) y1 <= 0;  // reset
        else     y1 <= y2;

      always @(posedge clk or posedge rst)
        if (rst) y2 <= 1;  // preset
        else     y2 <= y1;
    endmodule

同样,按照IEEE Verilog 的标准,上例中两个always块是并行执行的,与前后次序无关。无论哪一个always块的复位信号先到, 两个always块中的非阻塞赋值都在赋值开始时刻计算RHS表达式,,而在结束时刻才更新LHS表达式。所以这两个always块在复位信号到来后,在always块结束时 y1为0而y2为1是确定的。从用户的角度看这两个非阻塞赋值正好是并行执行的。 
---------------------------------------------------------------------------
掌握可综合风格的Verilog模块编程的八个原则会有很大的帮助。在编写时牢记这八个要点可以为绝大多数的Verilog用户解决在综合后仿真中出现的90-100% 的冒险竞争问题。
1)    时序电路建模时,用非阻塞赋值。
2)    锁存器电路建模时,用非阻塞赋值。
3)    用always块建立组合逻辑模型时,用阻塞赋值。
4)    在同一个always块中建立时序和组合逻辑电路时,用非阻塞赋值。
5)    在同一个always块中不要既用非阻塞赋值又用阻塞赋值。
6)    不要在一个以上的always块中为同一个变量赋值。
7)    用$strobe系统任务来显示用非阻塞赋值的变量值
8)    在赋值时不要使用 #0 延迟

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值