zoukankan      html  css  js  c++  java
  • 阻塞赋值与非阻塞赋值

     

         过程赋值:用于对reg型变量赋值,改变寄存器的值或为以后排定改变。

         语法

    {阻塞性(blocking)赋值}

         RegisterLValue = [ TimingControl] Expression;

    {非阻塞性(non-blocking)赋值}

    RegisterLValue <= [ TimingControl] Expression;

         阻塞:在本语句中“右式计算”和“左式更新”完全完成之后,才开始执行下一条语句;

         非阻塞:当前语句的执行不会阻塞下一语句的执行。

         过程赋值右边的表达式在赋值执行的时候算出。如果没有内部赋值延时,左边的寄存器由于阻塞性赋值将立即更新,而非阻塞性赋值则下一个仿真周期才更新左边的寄存器。如果有内部赋值延时,左边的寄存器只在发生内部赋值延时后更新。

         例如对于阻塞性赋值:

    • 当执行赋值语句时,算出右边的表达式的值,但左边的值不更新,直到产生定时控制事件或延时(称为“内部赋值延时”)。
    • 直到左边被更新后(即经过内部赋值延时后)阻塞性赋值才完成。begin-end块中的下一个语句直到此时才开始执行。

        结合编程语句区分如下:

        • 非阻塞(non-blocking) 赋值语句(b<= a):     

           - 块内的赋值语句同时赋值;    

           - b的值被赋成新值a的操作, 是与块内其他赋值语句同时完成的;    

           - 建议在可综合风格的模块中使用非阻塞赋值。

         • 阻塞(blocking) 赋值语句(b = a):    

           - 完成该赋值语句后才能做下一句的操作;    

           - b的值立刻被赋成新值a;    

           - 硬件没有对应的电路,因而综合结果未知。

         阻塞赋值和非阻塞赋值如果使用不当会存在冒险和竞争现象,必须按照下面两条准则:

         1)在描述组合逻辑的always块中使用阻塞赋值,则综合组合逻辑的电路结构;

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

         在时钟沿触发的always块中,如果用非阻塞赋值语句对reg型变量赋值;或者当reg型变量经过多次循环其值仍保持不变,则会在综合中生成触发器。若不想生成触发器,而希望用reg型变量生成组合逻辑,则应使用电平触发。在组合逻辑中,阻塞赋值只与电平有关,往往和触发沿没有关系,可以将其看成并行执行的;在时序逻辑中,非阻塞赋值是并行执行的;因此,优秀的HDL设计,其内部语句也是并行执行的。

     

         非阻塞赋值与阻塞赋值示例:

         1. 非阻塞赋值方式

     1 module nonblocking(input clk, 
     2                    input a,
     3                    output reg c); 
     4 reg b;
     5 
     6 always @(posedge clk) begin
     7     b <= a;
     8     c <= b;
     9 end
    10 
    11 endmodule
    non-blocking

         编译结果显示使用了两个寄存器。Clock "clk" Internal fmax is restricted to 420.17 MHz between source register "b" and destination register "c~reg0" (the specified clock operates at the specified fMAX between the specified source pin or register and the specified destination pin or register). 器件内部的fmax(Internal fmax)分析器件中同步元件(如寄存器)到同步元件之间的延时,然后计算出最高频率。

         RTL原理图如下所示:

     

         波形功能和时序仿真分别如下所示:

         非阻塞赋值在块结束时才完成赋值操作。c的值比b的值落后一个时钟周期(功能仿真表现为b→c,时序仿真表现为b→c~reg0)。时序仿真中,第一个时钟沿15.743ns后(时钟周期 + tco = 10ns+5.743ns)输出c。

     

         2. 阻塞赋值方式

     1 module blocking(input clk, 
     2                 input a,
     3                 output reg c); 
     4 reg b;
     5 
     6 always @(posedge clk) begin
     7     b = a;  //两句交换,则等效于非阻塞赋值方式
     8     c = b;
     9 end
    10 
    11 endmodule
    blocking

         编译结果显示使用了1个寄存器,所以时序列表中没有Clock Setup: 'clk'一项。

         RTL原理图如下所示:

     

         波形功能和时序仿真分别如下所示:

         阻塞赋值在该语句结束时就完成赋值操作。在一个块语句中,如果有多条阻塞赋值语句,在前面的赋值语句没有完成之前,后面的语句就不能被执行,就像被阻塞了一样,因此称为阻塞赋值方式。可以看到,b被优化掉了,因为这里c的值与b的值一样!

         在阻塞赋值语句中,赋值次序非常重要,而在非阻塞赋值语句中,赋值的次序并不重要。

         仿真器首先按照仿真时间对事件进行排序,然后再在当前仿真时间里按照事件的优先级顺序进行排序。活跃事件是优先级最高的事件。在活跃事件之间,它们的执行顺序是随机的。阻塞赋值(=)、连续赋值(assign)以及非阻塞赋值的右式计算等都属于活跃事件。

         下面再通过一个典型案例,进一步说明阻塞赋值和非阻塞赋值的区别。

        【例】数组Data[0]、Data[1]、Data[2]和Data[3]都是4bit的数据。找到它们当中最小的数据,并将该数据的索引输出到LidMin中(类似 “冒泡排序”),且在一个时钟周期内完成。首先将Lid_Min设置一个任意初始值,然后将Data[0]~Data[3]与Data[Lid_Min]进行比较,每比较一个数,就将较小的索引暂存在Lid_Min中,然后再进行下一次比较。当4组数据比较完成之后,最小的数据索引就会保留在Lid_Min中。例如,若4个数据中Data[2]最小,则LidMin的值为2。

     1 module Bubble_Up(input Rst_n,
     2                  input Clk,
     3                  input [5:0] Data [0:3],  //需要SystemVerilog extensions(通常端口不可用数组型)
     4                  output reg [1:0] Lid_Min );
     5 always @(posedge Clk or negedge Rst_n) begin
     6         if (~Rst_n) begin
     7                 Lid_Min <= 2'd0;
     8             end
     9         else
    10             begin    //begin…end中为非阻塞赋值
    11                 if (Data[0] <= Data[Lid_Min]) begin
    12                         Lid_Min <= 2'd0;    
    13                     end
    14                 if (Data[1] <= Data[Lid_Min]) begin
    15                         Lid_Min <= 2'd1;
    16                     end
    17                 if (Data[2] <= Data[Lid_Min]) begin
    18                         Lid_Min <= 2'd2;
    19                     end
    20                 if (Data[3] <= Data[Lid_Min]) begin
    21                         Lid_Min <= 2'd3;
    22                     end
    23             end
    24     end
    25 
    26 endmodule
    Bubble_Up

        【注】SystemVerilog extensions设置:Settings --> Analysis & Synthesis Settings-->Verilog HDL Input --> Verilog Version--> SystemVerilog-2005。

         以上代码中使用了非阻塞赋值,但仿真波形结果并不正确,如下图所示。图中的Data[0]~Data[3]分别为11、3、10和12,Lid_Min的初始值为0。Lid_Min结果应为1,因为Data[1]最小。

         原因如下:

         在时钟上升沿到来后,且Rst_n信号无效时开始执行后续4个语句,假设此时Lid_Min为0,Data[0]~Data[3]分别为11、3、10和12:

         第一句的if为真,因此执行Lid_Min <= 2’d0,而此时Lid_Min并未立刻被赋值,而是调度到事件队列中等待执行,这是非阻塞赋值的特点。

         第二句的if为真,因此执行Lid_Min <= 2’d1,这是Lid_Min也没有立刻被赋值为1,而是调度到事件队列中等待执行。当前的Lid_Min还是0,没有发生任何变化。

         同样,第三句的if也为真,因此执行Lid_Min <= 2’d2,将更新事件调度到事件队列中等待执行。当前的Lid_Min还是0。

         而第四句的if为假,因此直接跳过Lid_Min <= 2’d3,这时跳出always语句,等待下一个时钟上升沿。

         在以上的always语句执行完成以后,仿真时间没有前进。这时存在于事件队列中当前仿真时间上的3个被调度的非阻塞更新事件开始执行,它们分别将Lid_Min更新为0、1和2。

         按照Verilog语言的规范,这3个更新事件属于同一仿真时间内的事件,它们之间的执行顺序随机,这就产生了不确定性。一般的仿真器在实现的时候是根据它们被调度的先后顺序执行的,事件队列就像一个存放事件的FIFO,它是分层事件队列的一部分,如图所示:

         这3个事件在同一仿真时间被一一执行,而真正起作用的时最后一个更新事件,因此在仿真的时候得到的最终结果时Lid_Min为2。

         然而我们想要得到的结果是,在每个if语句判断并执行完成以后,Lid_Min先暂存这个中间值,再进行下一次比较,也就是说在进行下一次比较之前,这个Lid_Min必须被更新,而这点正是阻塞赋值的特点。将代码段”else”内部改为阻塞赋值(<=→=),仿真波形如图所示:

     

         在仿真过程中,”else”段第二句的if为真,执行Lid_Min = 2'd1,根据阻塞赋值的特点,Lid_Min被立刻赋值为1。执行第三句if时if为假,直接跳过Lid_Min = 2'd2不执行,同样也跳过Lid_Min = 2'd3不执行。Lid_Min被最终赋值为1,这正是我们想要的结果。

         仿真采用Cyclone II系列EP2C5F256C6器件。编译结果显示,非阻塞赋值占用39个LE(logic elements),阻塞赋值占用80个LE,两者均占用2个寄存器(registers)。

         为了使代码看起来更简洁,也可使用for语句改写”else”段代码如下:

    1 begin
    2     for (i = 2'd0; i <= 2'd3; i = i + 2'd1) begin
    3             if (Data[i] <= Data[Lid_Min]) begin
    4                     Lid_Min = i;
    5                 end
    6         end
    7 end
    for loop

    需要注意的是,for语句的电路功能比较难理解,其展开形式往往更具可读性。

  • 相关阅读:
    求正整数N(N>1)的质因数的个数。
    手机键盘输入字母
    第二部分进度
    第一部分:地域维度标准化
    利用python解析地址经纬度
    输入任意4个字符(如:abcd), 并按反序输出(如:dcba)
    python-->微信支付
    python-图片流传输(url转换二维码)
    python-qrcode-二维码
    ajax和axios、fetch的区别
  • 原文地址:https://www.cnblogs.com/clover-toeic/p/3755360.html
Copyright © 2011-2022 走看看