【数字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

cpp 复制代码
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

top.sv

cpp 复制代码
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

tb.sv

cpp 复制代码
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

以及makefile脚本:

bash 复制代码
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

当设置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)

相关推荐
一口一口吃成大V3 小时前
FPGA随记——FPGA时序优化小经验
fpga开发
贾saisai4 小时前
Xilinx系FPGA学习笔记(九)DDR3学习
笔记·学习·fpga开发
redcocal8 小时前
地平线秋招
python·嵌入式硬件·算法·fpga开发·求职招聘
思尔芯S2C1 天前
高密原型验证系统解决方案(下篇)
fpga开发·soc设计·debugging·fpga原型验证·prototyping·深度调试·多fpga 调试
坚持每天写程序1 天前
xilinx vivado PULLMODE 设置思路
fpga开发
redcocal2 天前
地平线内推码 kbrfck
c++·嵌入式硬件·mcu·算法·fpga开发·求职招聘
邹莉斯3 天前
FPGA基本结构和简单原理
fpga开发·硬件工程
悲喜自渡7213 天前
易灵思FPGA开发(一)——软件安装
fpga开发
ZxsLoves3 天前
【【通信协议ARP的verilog实现】】
fpga开发
爱奔跑的虎子3 天前
FPGA与Matlab图像处理之伽马校正
图像处理·matlab·fpga开发·fpga·vivado·xilinx