原创 【博客大赛】Verilog阻塞与非阻塞赋值的区别--冒泡法实例

2013-2-14 16:41 1072 0 分类: FPGA/CPLD

     这里有一个数组:Data[0]、Data[1]、Data[2]和Data[3],它们都是4比特的数据。我们需要在它们当中找到一个最小的数据,同时将该数据的索引输出到LidMin中,这个算法有点类似于“冒泡排序”的过程,而且需要在一个时钟周期内完成。例如,如果这4个数据中 Data[2]最小,那么LidMin的值则为2。

   module Bubble_Up(
                    Rst_n,
                    Clk,
                    Data,
                    Lid_Min
                    );
    input Rst_n;
    input Clk;
    input [3:0] Data [0:3];
    output [1:0] Lid_Min;
    reg [1:0] Lid_Min; 
    always @(posedge Clk or negedge Rst_n)
    begin
        if (~Rst_n)
        begin
             Lid_Min <= 2'd0;
        end
        else
           begin
               if (Data[0] <= Data[Lid_Min])    //"<="表示小于等于
               begin
                   Lid_Min <= 2'd0;    //"<="表示非阻塞赋值
               end

               if (Data[1] <= Data[Lid_Min])
               begin
                   Lid_Min <= 2'd1;
               end
                        
                if (Data[2] <= Data[Lid_Min])
                begin
                    Lid_Min <= 2'd2;
                end

                if (Data[3] <= Data[Lid_Min])
                begin
                    Lid_Min <= 2'd3;
                end
            end
        end
    endmodule

  我们的原意是首先将Lid_Min设置为一个初始值(任意值都可以),然后将Data[0]~Data[3]与Data[Lid_Min]进行比较,每比较一个数,就将较小的索引暂存在Lid_Min中,然后再进行下一次比较。当4组数据比较完成之后,最小的数据索引就会保留在Lid_Min 中。

  我们在以上代码中使用了非阻塞赋值,结果发现,仿真波形根本不是我们所需要的功能,如图所示,图中的Data[0]~Data[3]分别为 11、3、10和12,Lid_Min的初始值为0。按道理来说,Lid_Min的计算结果应该为1,因为Data[1]最小,但仿真波形却为2。

1.jpg

为什么会得出这样的结果呢?

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

             if (Data[0] <= Data[Lid_Min])    //"<="表示小于等于
              begin
                   Lid_Min <= 2'd0;    //"<="表示非阻塞赋值
               end

               if (Data[1] <= Data[Lid_Min])
               begin
                   Lid_Min <= 2'd1;
               end
                        
                if (Data[2] <= Data[Lid_Min])
                begin
                    Lid_Min <= 2'd2;
                end

                if (Data[3] <= Data[Lid_Min])
                begin
                    Lid_Min <= 2'd3;
                end

  第一句的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必须被更新,而这一点也正是阻塞赋值的特点,因此我们将代码作如下更改:
 

2.jpg

 

module Bubble_Up(
                    Rst_n,
                    Clk,
                    Data,
                    Lid_Min
                    );
    input Rst_n;
    input Clk;
    input [3:0] Data [0:3];
    output [1:0] Lid_Min;
    reg [1:0] Lid_Min; 
    always @(posedge Clk or negedge Rst_n)
    begin
        if (~Rst_n)
        begin
             Lid_Min <= 2'd0;
        end
        else
           begin
               if (Data[0] <= Data[Lid_Min])    //"<="表示小于等于
               begin
                   Lid_Min = 2'd0;    //"<="表示非阻塞赋值
               end

               if (Data[1] <= Data[Lid_Min])
               begin
                   Lid_Min = 2'd1;
               end
                        
                if (Data[2] <= Data[Lid_Min])
                begin
                    Lid_Min = 2'd2;
                end

                if (Data[3] <= Data[Lid_Min])
                begin
                    Lid_Min = 2'd3;
                end
            end
        end
    endmodule

  其仿真波形如图所示:

3.jpg

 

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

        另外,为了使代码看起来更简洁,我们使用for语句改写了代码:

     module Bubble_Up(
                     Rst_n,
                     Clk,
                     Data,
                     Lid_Min
                     );
      input Rst_n;
      input Clk;
      input [5:0] Data [0:3];
      output [1:0] Lid_Min;
      reg [1:0] Lid_Min;
      integer i;
      always @(posedge Clk or negedge Rst_n)
      begin
          if (~Rst_n)
          begin
              Lid_Min = 2'd0;
          end
          else
          begin
              for (i = 2'd0; i <= 2'd3; i = i + 2'd1)
              begin
                  if (Data <= Data[Lid_Min])
                  begin
                        Lid_Min = i;
                  end
              end
          end
       end
     endmodule

  这种写法与前面展开的写法完全等效,功能完全一致。今后大家在读代码时发现带有for语句的电路功能比较难理解,可以将这些语句展开,增强代码的可读性。

 

广告

文章评论 0条评论)

登录后参与讨论
相关推荐阅读
sunyzz 2017-08-19 10:38
【博客大赛】AVALON总线介绍
1、AVALON总线简介Avalon总线是一种协议较为简单的片内总线,是ALTERA公司定义的片上互联总线,该总线可以将诸如NIOS II的CPU与其他外设连接起来,进而进行数据交换。AVALON总线...
sunyzz 2017-08-17 21:36
【博客大赛】不要轻易做职场滥好人
小A毕业于国内普通高校,但是他聪明,勤奋,能干,动手能力强,可是即便有这些优点也不能让小A轻轻松松找到一份好工作。这不,去年9月份小A好不容易找到一份工作,然后立马就入职了C公司,生怕C公司过两天不要...
sunyzz 2017-08-16 21:15
【博客大赛】IC设计低功耗技术四
五:工艺层面的降低功耗前面几节都是在讨论设计人员如何在前期阶段,中期阶段降低功耗,涉及到软件层面的,硬件层面的,这些技巧基本都是前辈总结出来的,或者根据理论推论出来的。但是到了后期,想降低功耗基本就要...
sunyzz 2017-08-14 22:35
【博客大赛】IC设计之低功耗技术三
四:RTL(寄存器传输)级的低功耗设计4.1 状态机的设计状态机编码中一般有两种方式,普通的二进制编码,特殊的格雷码,格雷码的特点是两个数据之间的跳变时只会有一个bit在toggle,显然比起多bit...
sunyzz 2017-08-12 16:51
【博客大赛】IC设计之低功耗技术二
三、架构层面的降低功耗系统的实现有很多的方式,每种方式对功耗的影响都不相同,本节主要介绍架构对功耗的影响。3.1 高级门口电路 在同步电路系统中,时钟占据了大部分的动态功耗,因而在一些情况下,如果有些...
sunyzz 2017-08-12 10:37
【博客大赛】IC 设计之低功耗技术一
一、前言随着计算机技术和微电子技术的迅速发展,嵌入式系统应用领域越来越广泛。节能是全球化的热潮,如计算机里的许多芯片过去用5V供电,现在用3.3V,1.8V,甚至更低的电压。目前的低功耗设计主要从芯片...
广告
我要评论
0
0
广告
关闭 热点推荐上一条 /2 下一条