尤其是在NP芯片中,经常涉及到报文的数据流处理;为了防止数据丢失,和各模块的流水处理;因此需要到反压机制;
反压机制目前接触到的有两种:一是基于握手valid-ready信号的反压;二是基于credits的反压;
所谓反压,就是能够压住FIFO前级的发送和本级FIFO源头反压;若是有多个FIFO的话,还会涉及到调度问题;
本篇博客重点介绍带存储体(FIFO)的反压原理和实现;
1 带存储体的反压
1.1 原理图
示意图如下:基本上涉及数据流的处理,会在本级模块前级上放置一个FIFO,首先本级模块会进行处理,由于比如grant或者某些ctrl信号无法及时处理,FIFO无法pop出去,就会积压在本模块前级FIFO中,最终当累积到某种深度上,会向前级模块进行反压;不要在发送;
1.2 Demo
设计一个并行6输入32bit加法器,输出一个带截断的32bit加法结果,要求用三级流水设计,带反压信号;
本Demo的存储体是存储在本级模块后面中,前提是前级模块都是能够立即使用的,不需要等待什么条件
module pressure_fifo #(
parameter FIFO_DATA_WIDTH = 32,
parameter FIFO_DEPTH = 8
)(
input wire clk,
input wire rst,
input wire valid_i,
output logic ready_o, //给前级信号的准备信号
input wire [32 -1:0] a,b,c,d,e,f,
//output
input wire ready_i,
output logic [32 -1:0] dout,
output logic valid_o
)
localparam WATERLINE = FIFO_DEPTH - 3; //three levels' pipeline
logic handshake;
logic handshake_ff1;
logic handshake_ff2;
logic wr_en;
assign handshake = ready_o & valid_i; //按理说ready_o拉高后,前级模块不应该进行发数据,在这里就是保险判断,即使valid_i有效,ready_o未准备好的话,会进行丢弃数据;
always @ (posedge clk or posedge rst) begin
if(rst) begin
handshake_ff1 <= '0;
handshake_ff2 <= '0;
end
else begin
handshake_ff1 <= handshake;
handshake_ff2 <= handshake_ff1;
end
end
reg [31 : 0] r1_ab;
always @ (posedge clk or posedge rst) begin
if(rst) begin
r1_ab <= '0;
end
else if(handshake)begin
r1_ab <= a + b;
end
end
reg [31 : 0] r1_cd;
always @ (posedge clk or posedge rst) begin
if(rst) begin
r1_cd <= '0;
end
else if(handshake)begin
r1_cd <= c + d;
end
end
reg [31 : 0] r1_ef;
always @ (posedge clk or posedge rst) begin
if(rst) begin
r1_ef <= '0;
end
else if(handshake)begin
r1_ef <= e + f;
end
end
reg [31 : 0] r2_abcd;
always @ (posedge clk or posedge rst) begin
if(rst) begin
r2_abcd <= '0;
end
else if(handshake_ff1) begin
r2_abcd <= r1_ab + r1_cd;
end
end
reg [31 : 0] r2_ef;
always @ (posedge clk or posedge rst) begin
if(rst) begin
r2_ef <= '0;
end
else if(handshake_ff1) begin
r2_ef <= r1_ef;
end
end
reg [31 : 0] r3;
always @ (posedge clk or posedge rst) begin
if(rst) begin
r3 <= '0;
end
else if(handshake_ff2) begin
r3 <= r2_ef + r2_abcd;
end
end
always @ (posedge clk or posedge rst) begin
if(rst) begin
wr_en <= 1'b0;
end
else if(handshake_ff2) begin
wr_en <= 1'b1;
end
else begin
wr_en <= 1'b0;
end
end
always_ff @(posedge clk)begin
if(rst)begin
ready_o <= 1'b0;
end
else if(usedw > WATERLINE)begin //当使用的深度超过fifo存储体时,就要对上级反压;
ready_o <= 1'b0;
end
else begin
ready_o <= 1'b1;
end
end
assign valid_o = ~empty;
// 同步FIFO存储体-->看用了多少fifo深度;3级流水线在反压起作用时,会一下子进入3个数据;
sync_fifo # (
.MEM_TYPE ("auto" ),
.READ_MODE ("fwft" ),
.WIDTH (FIFO_DATA_WIDTH),
.DEPTH (FIFO_DEPTH )
)fifo_inst(
.clk (clk ), // input wire
.rst_n (rst_n ), // input wire
.wren (wr_en ), // input wire
.din (r3 ), // input wire [WIDTH-1:0]
.rden (ready_i ), // input wire
.dout (dout ), // output reg [WIDTH-1:0]
.empty (empty ), // output wire
.usedw (usedw )
);
endmodule
这个比较简单,相当于是从入口来我这的每排都有效,不要事先在前面进行先存储住;比如说另一种情况,前级模块给我发过来的数据,但是因为其他条件未准备好,我无法立即进行使用,因此也需要先暂存在本模块最前面的fifo存储体中
基本上划分模块的时候真实应用场景都是采用逐级模块向前反压,在这里简单有个概念:逐级反压和跨级反压;
逐级反压:流水线深度,好把握,也是项目中最经常用到的;
跨级反压:流水线深度:是waterlie3 + 在途1 + waterline1 + 在途2 + waterline2 + 在途3 (在途means 流水线深度)
【Refer】
1.https://zhuanlan.zhihu.com/p/359330607