• 【数字IC设计/FPGA】FIFO与流控机制


    流控,简单来说就是控制数据流停止发送。常见的流控机制分为带内流控和带外流控。

    FIFO的流水反压机制

    一般来说,每一个fifo都有一个将满阈值afull_value(almost full)。当fifo内的数据量达到或超过afull_value时,将满信号afull从0跳变为1。上游发送模块感知到afull为1时,则停止发送数据。在afull跳变成1后,fifo需要能够缓存路径上的data以及上游发送模块停止发流之前发出的所有data。这就是fifo的流控机制。下图是fifo流控机制的示意图。
    在这里插入图片描述
    如下图所示,数据data和有效信号vld从模块A产生,经过N拍延时后,输入到FIFO,FIFO产生将满信号afull,经过M拍延时反馈到模块A。假设模块A接收到afull=1时,立即停止发送数据。假设FIFO深度为fifo_depth,每拍为一个时钟周期。
    在这里插入图片描述
    则,我们考虑以下问题:

    1. 为了保证FIFO不发生溢出,将满阈值afull_value至少应该设置成多少?
    2. 为了充分发挥FIFO的性能,FIFO深度depth应该为多少?

    首先考虑第一个问题,即FIFO将满阈值如何设置:
    当FIFO中的数据为afull_value时,产生afull=1,afull=1经过M拍到达模块A,此时FIFO中至多可以有(afull_value+M)个数据。afull=1到达模块A时,模块A立即停止发送数据,此时电路中还存在N拍数据将陆续送到FIFO中,所以最后FIFO中应该为(afull_value+M+N)个数据。
    为了保证数据不会溢出,应满足公式fifo_depth >= afull_value+M+N,因此,将满阈值应该至多为depth_fifo - (M+N)
    为了验证上述结论,我们编写了如下代码进行实验:
    delayed.sv

    module delayed
    #(
        parameter N  = 5,
        parameter DW = 1
     )
     (
        input  logic          clk,
        input  logic          rst_n,
        input  logic [DW-1:0] din,
        output logic [DW-1:0] dout
     );
    
    logic [N*DW-1:0] data_reg;
    
    always_ff@(posedge clk or negedge rst_n) begin
       if(~rst_n) begin
           data_reg <= (N*DW)'(0);
       end
       else begin
           data_reg <= {data_reg[N*DW-DW-1:0], din};
       end
    end
    
    assign dout = data_reg[N*DW-1:N*DW-DW];
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    top.sv

    module top
    #(parameter DATA_WIDTH  = 32,
      parameter DEPTH       = 32,
      parameter M           = 5,
      parameter N           = 10,
      parameter AF_VALUE    = (M+N-1)
     )
    (
    input  logic                  clk,
    input  logic                  rst_n,
    input  logic                  wr_en,
    input  logic [DATA_WIDTH-1:0] wr_data,
    output logic                  afull,
    output logic [DATA_WIDTH-1:0] rd_data,
    output logic                  empty,
    input  logic                  rd_en
    );
    
    logic                  wr_en_dly;
    logic [DATA_WIDTH-1:0] wr_data_dly;
    logic                  almost_full;
    logic                  error;
    logic                  full;
    //
    delayed
    # 
    (.N (N ),
     .DW(1 )
    )
    wr_en_delay_inst
    (
       .clk  (clk      ),
       .rst_n(rst_n    ),
       .din  (wr_en    ),
       .dout (wr_en_dly)
    );
    //
    delayed
    #
    (.N  (N         ),
     .DW (DATA_WIDTH)
    )
    wr_data_delay_inst
    (
       .clk   (clk        ),
       .rst_n (rst_n      ),
       .din   (wr_data    ),
       .dout  (wr_data_dly)
    );
    //
    delayed
    #
    (.N (M),
     .DW(1)
    )
    almost_full_delay_inst
    (
       .clk  (clk        ),
       .rst_n(rst_n      ),
       .din  (almost_full),
       .dout (afull      )
    );
    //
    DW_fifo_s1_sf 
    #
    (.width   (DATA_WIDTH ),  
     .depth   (DEPTH      ),  
     .ae_level(1          ),  
     .af_level(AF_VALUE   ),  
     .err_mode(1          ),  
     .rst_mode(0          )
    )
     U  (.clk          (clk              ),   
         .rst_n        (rst_n            ),   
         .push_req_n   (~wr_en_dly       ),
         .pop_req_n    (~rd_en           ),   
         .diag_n       (1                ),
         .data_in      (wr_data_dly      ),   
         .empty        (empty            ),
         .almost_empty (                 ),   
         .half_full    (                 ),
         .almost_full  (almost_full      ),   
         .full         (full             ),
         .error        (error            ),   
         .data_out     (rd_data          ) 
        );
    
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    tb.sv

    module tb;
    parameter DATA_WIDTH = 32;
    parameter DEPTH      = 32;
    parameter M          = 5;
    parameter N          = 10;
    parameter AF_VALUE   = M + N;
    
    logic rst_n;
    logic clk;
    logic rd_en;
    logic rd_en_r;
    logic wr_en;
    logic wr_en_r;
    logic empty;
    logic afull;
    logic [DATA_WIDTH-1:0] wr_data;
    logic [DATA_WIDTH-1:0] rd_data;
    logic [DATA_WIDTH-1:0] ref_data;
    logic error;
    
    assign error = (rd_en && ~empty && (ref_data != rd_data)) ? 1'b1 : 1'b0;
    
    always_ff@(posedge clk or negedge rst_n) begin
      if(~rst_n) begin
        ref_data <= '0;
      end
      else if(rd_en && ~empty) begin
        ref_data <= ref_data + 1'b1;
      end
    end
    
    //rd_en
    always_ff@(posedge clk or negedge rst_n) begin
      if(~rst_n) begin
        rd_en_r <= 1'b0;
      end
      else if($urandom % 100 < 1) begin
        rd_en_r <= 1'b1;
      end
      else begin
        rd_en_r <= 1'b0;
      end
    end
    
    assign rd_en = rd_en_r && ~empty;
    //wr_data
    always_ff@(posedge clk or negedge rst_n) begin
      if(~rst_n) begin
        wr_data <= '0;
      end
      else if(wr_en && ~afull) begin
        wr_data <= wr_data + 1'b1;
      end
    end
    
    //wr_en
    always_ff@(posedge clk or negedge rst_n) begin
      if(~rst_n) begin
        wr_en_r <= 1'b0;
      end
      else if($urandom % 100 < 100) begin
        wr_en_r <= 1'b1;
      end
      else begin
        wr_en_r <= 1'b0;
      end
    end
    
    assign wr_en = wr_en_r && ~afull;
    
    //clk
    initial
    begin
       clk = 1'b0;
       forever begin
          #5 clk = ~clk;
       end
    end
    
    //rst
    initial
    begin
       rst_n = 1'b0;
       #100
       rst_n = 1'b1;
    end
    
    //
    initial
    begin
       #20000
       $finish;
    end
    
    //
    initial begin
     $fsdbDumpfile("./top.fsdb");
     $fsdbDumpvars(0);
    end
    
    //inst
    top 
    #(.DATA_WIDTH(DATA_WIDTH),
      .DEPTH     (DEPTH     ),
      .M         (M         ),
      .N         (N         ),
      .AF_VALUE  (AF_VALUE  )
     )
    U(.*);
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111

    以及makefile脚本:

    all: listfile com sim verdi clean
    
    listfile:
    	find -name "*.sv" > filelist.f
    
    com:
    	vcs -full64 -cpp g++-4.8 -cc gcc-4.8 -LDFLAGS -Wl,--no-as-needed -sverilog -debug_access -timescale=1ns/10ps \
    	-f filelist.f -l com.log -kdb -lca -y ${SYNOPSYS}/dw/sim_ver +libext+.v +incdir+${SYNOPSYS}/dw/sim_ver+
    
    sim:
    	./simv -l sim.log
    
    
    verdi:
    	verdi -sv -f filelist.f -ssf *.fsdb -nologo &
    
    clean:
    	rm -rf csrc *.log *.key *simv* *.vpd *DVE*
    	rm -rf verdiLog *.fsdb *.bak *.conf *.rc *.f
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    当设置fifo的将满阈值为M+N时,fifo不会丢失数据,流控正确。(:dw fifo中的将满阈值afull的定义为:当fifo中还有小于等于afull个空位置时,拉高afull信号)
    在这里插入图片描述
    当设置fifo的将满阈值为M+N-1时,fifo会丢失数据,流控出错。如下图所示:
    在这里插入图片描述
    现在考虑第二个问题,即FIFO深度depth应该为多少?
    分析:若fifo_depth过小,afull有效之后,fifo中存储的数据将很快被下游读取,而新的数据又无法及时到达FIFO,因此会造成流水气泡,影响电路的性能。
    假设M=5, N=10,且fifo_depth=20,则afull_value=5。在T时刻,fifo中存了5个数据后afull=1会变为有效,在之后的15个周期内会陆续存入15个数据。假设下游模块B每个周期读取FIFO中的一个数据,因为当FIFO内的数据data_cnt小于5时,afull才会无效(为0),因此在T+15和T+30的时刻内,下游电路B只能读5个数据,从而造成数据断流,影响电路性能。
    为了保证电路性能,在T+15到T+30这个时间段内应该有15个数据可读,因此afull_value应该不小于15(=M+N)。故FIFO深度应该不小于2*(M+N)

  • 相关阅读:
    二分算法(蓝桥杯 C++ 题目 代码 注解)
    记录一个切换视频横竖屏导致tableView刷新的问题
    2019阿里java面试题(一)
    最小化安装debian11
    【HTTP】URL结构、HTTP请求和响应的报文格式、HTTP请求的方法、常见的状态码、GET和POST有什么区别、Cookie、Session等重点知识汇总
    RocketMQ事务消息原理
    好文收藏|Clickhouse 常见问题及解决方案汇总
    Java基础:Java类与对象
    uniapp四个元素点击那个哪个变色,其他的还变原来的颜色
    uni-app运行到微信开发者工具-没有打印的情况
  • 原文地址:https://blog.csdn.net/qq_40268672/article/details/133964386