Verilog HDL中阻塞语句和非阻塞语句的区别

在Verilog中有两种类型的赋值语句:阻塞赋值语句(“=”)和非阻塞赋值语句(“<=”)。正确地使用这两种赋值语句对于Verilog的设计和仿真很是重要。html

Verilog语言中讲的阻塞赋值与非阻塞赋值,但从字面意思来看,阻塞就是执行的时候在某个地方卡住了,等这个操做执行完在继续执行下面的语句,而非阻塞就是无论执行完没有,我无论执行的结果是什么,反正我继续下面的事情。而Verilog中的阻塞赋值与非阻塞赋值正好也是这个意思,经过执行一个例子,就能够简单地明白了:
一、阻塞赋值能够理解为语句的顺序执行,所以语句的执行顺序很重要
二、非阻塞赋值能够理解为语句的并行执行,因此语句的执行不考虑顺序
三、在assign的结构中,必须使用的是阻塞赋值面试

也就是说:工具

阻塞:在本语句中“右式计算”和“左式更新”彻底完成以后,才开始执行下一条语句;
非阻塞:当前语句的执行不会阻塞下一语句的执行。spa

下面给出实例来讲明:设计

给出相应的案例来帮助理解:code

复制代码
module prj1(in,b,c,d,clk,rst_n);

input in;
input clk;
input rst_n;
output b,c,d;
reg b,c,d;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        b <=0;
        c <=0;
        d <=0;
    end
    else begin
        b <=in;
        c <=b;
        d <=c;
        end
    end
endmodule
复制代码

这个目的是为了展现非阻塞赋值过程当中的时序变化,对应的RTL电路图和仿真波形以下图:
htm

从仿真图能够看书,b,c,d是在每一个时钟后依次传递的,若是采用阻塞赋值,若是in改变,那么b,c,d马上改变,这个就在这里不给出仿真。blog

阻塞赋值和非阻塞赋值的另一个区别在于综合的时候,若是输出只有d,bc做为中间变量,阻塞赋值在综合的过程当中会自动省略掉中间过程。给出以下仿真,理解更为清楚排序

复制代码
module prj1(in,b,c,clk,rst_n);

input in;
input clk;
input rst_n;
output b,c;
reg b,c, e,f, m,n;
/* <= */
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) b <=0;
    else begin
        e <=in;
        f <=e;
        b <=f;
        end
    end
/* = */
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) c=0;
    else begin
        m = in;
        n = m;
        c = n;
        end
    end    
endmodule
复制代码

综合后结果如图,能够看出,采用阻塞赋值,综合后的逻辑单元只有一个,中间变量m,n直接省去了。队列

下面咱们来看看二者代码之间究竟是怎么运行的。

(1)对于阻塞赋值的状况:

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) c=0;
    else begin
        m = in;
        n = m;
        c = n;
        end
    end    

 always语句块对Clk的上升沿敏感,当发生Clk 0~1的跳变时,执行该always语句。

 在begin...end语句块中全部语句是顺序执行的,并且最关键的是,阻塞赋值是在本语句中“右式计算”和“左式更新”彻底完成以后,才开始执行下一条语句的。

 在本例中,in的值赋给m之后,再执行n = m;一样在n的值更新之后,才执行c = n。这样,最终的计算结果就是in = c。也就是说时钟上升沿到来的时候,整个语句块执行完后,in,m,n,c的值都是同样的,这也就是咱们前面说的,in变化以后,m,n,c都跟着变化。全部的语句执行完之后,该always语句等待Clk的上升沿,从而再一次触发begin...end语句。

总结

完成阻塞赋值的过程为:首先计算等号右边表达式的结果;接着这个结果存入仿真系统的内部临时寄存器中,这个寄存器也称为赋值事件队列和调度的临时寄存器。若是赋值时没有延迟信息,则这个事件当即被调度执行。

(2)对于非阻塞赋值的状况

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) b <=0;
    else begin
        e <=in;
        f <=e;
        b <=f;
        end
    end

首先执行e <= in,产生一个更新事件,将in的当前值赋给e,可是这个赋值过程并无马上执行,而是在事件队列中处于等待状态。

而后执行f <= e,一样产生一个更新事件,将e的当前值(注意上一语句中将in值赋给e的过程并无完成,e仍是旧值)赋给f,这个赋值事件也将在事件队列中处于等待状态。

再执行b <= f,产生一个更新事件,将f的当前值赋给b,这个赋值事件也将在事件队列中等待执行。

这时always语句块执行完成,开始对下一个Clk上升沿敏感。也就是说,使用非阻塞赋值方式进行赋值时,各个赋值语句同步执行;所以,一般在一个时钟沿对临时变量进行赋值,而在另外一个时钟沿对其进行采样。

那么何时才执行那3个在事件队列中等待的事件呢?只有当当前仿真时间内的全部活跃事件和非活跃事件都执行完成后,才开始执行这些非阻塞赋值的更新事件。这样就至关于将in、e和f的值同时赋给了e、f和b。

注:

    *仿真器首先按照仿真时间对事件进行排序,而后再在当前仿真时间里按照事件的优先级顺序进行排序。

    *活跃事件是优先级最高的事件。在活跃事件之间,它们的执行顺序是随机的。阻塞赋值(=)、连续赋值(assign)以及非阻塞赋值的右式计算等都属于活跃事件。

总结 :

非阻塞语句的执行过程为:首先,它会把非阻塞赋值放入调度队列中;接着,仿真工具开始执行下一条语句而不等待当前这条语句执行完毕。也就是说,先计算出等号右边表达式的结果,再把这个结果的赋值操做保存在事件队列中,等轮到事件被调度的时候,把这个结果赋值给等号左边。若是没有指定等号右边的延迟,赋值的操做会发生在当前时间单位的最后时刻。

知道了阻塞赋值和非阻塞赋值的区别以后,你们确定就会关心何时该用阻塞赋值何时该用非阻塞赋值,下面我简单的说几句:

赋值的类型的选择取决于建模的逻辑类型。通常状况是这样的(也有特殊状况):
(1)在时序逻辑电路中通常使用非阻塞赋值。
非阻塞赋值在块结束后才完成赋值操做,此赋值方式能够避免在仿真出现冒险和竞争现象。
(2)在组合逻辑电路中通常使用阻塞赋值。
使用阻塞方式对一个变量进行赋值时,此变量的值在在赋值语句执行完后就当即改变。
(3)在assign语句中必须使用阻塞赋值语句


但愿你们在懂得了阻塞和非阻塞语句的区别以后,可以很好的在本身的项目中灵活地运用,这也是你们面试的时候,必须会面对的一个问题,但愿你们可以掌握!
相关文章
相关标签/搜索