• Verilog中的阻塞与非阻塞


    这篇文档值得阅读  

    按说阻塞与非阻塞是Verilog中最基本的东西,也是老生常谈。但是最近看到很多程序里用到阻塞语句竟然不是很明白,说到底是从来没有自己仔细分析过。当然一般情况程序中也是推荐用非阻塞的。

      一般来说大家都会用以下几个例子来说明阻塞与非阻塞:

    大家可以参考http://www.cnblogs.com/crazybingo/archive/2012/03/20/2408980.html 

    HDL源代码
    对应的RTL电路
    module Shifter1(
    Clk,
    D,
    Q3
    );
    input Clk;
    input [7:0] D;
    output [7:0] Q3;
    reg [7:0] Q3, Q2, Q1;

    always @(posedge Clk)
    begin
    Q1 = D;
    Q2 = Q1;
    Q3 = Q2;
    end
    endmodule
    ▲ 大家可以看到Q1、Q2被优化掉了
    module Shifter2(
    Clk,
    D,
    Q3
    );
    input Clk;
    input [7:0] D;
    output [7:0] Q3;
    reg [7:0] Q3, Q2, Q1;

    always @(posedge Clk)
    begin
    Q1 <= D;
    Q2 <= Q1;
    Q3 <= Q2;
    end
    endmodule
    module Shifter3(
    Clk,
    D,
    Q3
    );
    input Clk;
    input [7:0] D;
    output [7:0] Q3;
    reg [7:0] Q3, Q2, Q1;

    always @(posedge Clk)
    begin
    Q3 = Q2;
    Q2 = Q1;
    Q1 = D;
    end
    endmodule
    module Shifter4(
    Clk,
    D,
    Q3
    );
    input Clk;
    input [7:0] D;
    output [7:0] Q3;
    reg [7:0] Q3, Q2, Q1;
    always @(posedge Clk)
    begin
    Q1 <= D;
    Q2 = Q1;
    Q3 = Q2;
    end
    endmodule
    module Shifter5(
    Clk,
    D,
    Q3
    );
    input Clk;
    input [7:0] D;
    output [7:0] Q3;
    reg [7:0] Q3, Q2, Q1;

    always @(posedge Clk)
    begin
    Q1 <= D;
    Q2 <= Q1;
    Q3 = Q2;
    end
    endmodule
    module Shifter6(
    Clk,
    D,
    Q3
    );
    input Clk;
    input [7:0] D;
    output [7:0] Q3;
    reg [7:0] Q3, Q2, Q1;
    always @(posedge Clk)
    begin
    Q1 <= D;
    Q2 = Q1;
    Q3 <= Q2;
    end
    endmodule

     从上面的例子可以看出:

    (1)阻塞语句定义的寄存器是可能被优化掉的

    (2)阻塞语句定义的寄存器是否被优化是与语句顺序有关系的

    参考文档中提到非阻塞语句执行的过程中先把每条语句当作一个事件放入事件队列中来执行的。它能很方便帮助理解非阻塞过程。

     

     所谓非阻塞赋值,顾名思义,就是指当前语句的执行不会阻塞下一语句的执行。

     

     always @(posedge Clk)
    begin
    Q1 <= D;
    Q2 <= Q1;
    Q3 <= Q2;
    end

     

       首先执行Q1 <= D,产生一个更新事件,将D的当前值赋给Q1,但是这个赋值过程并没有立刻执行,而是在事件队列中处于等待状态。
            然后执行Q2 <= Q1,同样产生一个更新事件,将Q1的当前值(注意上一语句中将D值赋给Q1的过程并没有完成,Q1还是旧值)赋给Q2,这个赋值事件也将在事件队列中处于等待状态。
            再执行Q3 <= Q2,产生一个更新事件,将Q2的当前值赋给Q3,这个赋值事件也将在事件队列中等待执行。
            这时always语句块执行完成,开始对下一个Clk上升沿敏感。

     

      那么什么时候才执行那3个在事件队列中等待的事件呢?只有当当前仿真时间内的所有活跃事件和非活跃事件都执行完成后,才开始执行这些非阻塞赋值的更新事件。这样就相当于将D、Q1和Q2的值同时赋给了Q1、Q2和Q3。

     

     下面是参考中的例子及阻塞实现过程:

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

    module Bubble_Up(                                                 
                        Rst_n,                                        
                        Clk,                                          
                        Data0, 
                        Data1,
                        Data2,
                        Data3,                                        
                        Lid_Min                                       
                        );                                            
        input Rst_n;                                                  
        input Clk;   
        
        input [3:0] Data0;
        input [3:0] Data1;
        input [3:0] Data2;
        input [3:0] Data3;                                                 
        reg [3:0] Data [3:0];                                       
        output [1:0] Lid_Min;                                         
        reg [1:0] Lid_Min;      
        
        always @( posedge Clk )
        begin
                Data[0] <= Data0;
                Data[1] <= Data1;
                Data[2] <= Data2;
                Data[3] <= Data3;
        end                                      
        always @(posedge Clk   )                        
        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                                                       
        endmodule   

    从实现的RTL视图上,我突然觉得阻塞一步到位的完成了很多组合逻辑。接下来我再说一些在程序上经常会遇到的例子。

    reg                     current_state;         
    reg                     next_state;            
    reg                     inc_loops;             
    reg                     clr_loops;             
    reg                     set_valid;             
    reg [4-1:0]   loops;                 
    reg [23:0]              threshold;             
    reg [28-1:0] dim_cnt_thresh_r;  
    reg [28-1:0] cnt_thresh;        
    
                
    always @ (posedge clk )
    begin
            current_state <= next_state;  
    end
    
    always @ (*)
    begin
        next_state = current_state;
        clr_loops = 1'b0;
        inc_loops = 1'b0;
        set_valid = 1'b0;
          
        case (current_state)
            0: begin
                clr_loops = 1'b1;
                if (measure_valid) begin
                    next_state = 1'b1;
                end        
            end
            
            1: begin
                inc_loops = 1'b1;
                if (match | loops_max) begin
                   set_valid = 1'b1;
                   clr_loops = 1'b1;
                   next_state = 1'b0;
                end                         
            end
        endcase  
    end 

     上面一段程序,从RTL视图可以看到,除current_state为寄存器之外,其余全部综合为了组合逻辑,所以说,reg定义的数据也不全会综合成寄存器。但是贴出程序还是为了要体会阻塞的用法。

    好像记得有这样的说法:在组合逻辑中用阻塞,在时序逻辑中用非阻塞。

    如果把上面的程序修改成非阻塞赋值,如下,这样会不会存在问题呢?我个人认为有可能,可能要看综合器本身,如果case语句外的next_state <= current_state与case内的next_state <= 1'b0(或者1‘b1)是随机的,那状态可能会出错。不会一般都不会(因为我以前就有这样做过,没问题(>'~'<))

    reg                     current_state;         
    reg                     next_state;            
    reg                     inc_loops;             
    reg                     clr_loops;             
    reg                     set_valid;             
    reg [4-1:0]   loops;                 
    reg [23:0]              threshold;             
    reg [28-1:0] dim_cnt_thresh_r;  
    reg [28-1:0] cnt_thresh;        
    
                
    always @ (posedge clk )
    begin
            current_state <= next_state;  
    end
    
    always @ (*)
    begin
        next_state <= current_state;
        clr_loops <= 1'b0;
        inc_loops <= 1'b0;
        set_valid <= 1'b0;
          
        case (current_state)
            0: begin
                clr_loops <= 1'b1;
                if (measure_valid) begin
                    next_state <= 1'b1;
                end        
            end
            
            1: begin
                inc_loops <= 1'b1;
                if (match | loops_max) begin
                   set_valid <= 1'b1;
                   clr_loops <= 1'b1;
                   next_state <= 1'b0;
                end                         
            end
        endcase  
    end 

     不过在这篇博客中,也给出了一种表达

     
    module and_nonblocking
    (
      input      a,
      input      b,
      input      c,
      output reg y
    ); 
     
    always@*
    begin               // y_entry = y
      y <= a;           // y_exit  = a
      y <= y & b;       // y_exit  = y_entry & b
      y <= y & c;       // y_exit  = y_entry & c
    end                 // y       = y_exit
     
    endmodule

    注意always块内的前2条语句将不会产生任何效果。上述always块等价与:

    always@*       
      y <= y & c;
     
    
    
  • 相关阅读:
    XTU 1250 Super Fast Fourier Transform
    XTU 1249 Rolling Variance
    XTU 1243 2016
    CodeForces 710A King Moves
    CodeForces 710B Optimal Point on a Line
    HDU 4472 Count
    并查集
    高精度四件套
    Kruskal最小生成树
    [蓝桥杯]翻硬币(贪心的详解附带题目测试链接)
  • 原文地址:https://www.cnblogs.com/zhongguo135/p/8393635.html
Copyright © 2020-2023  润新知