流水线设计中的反压操作

在流水线设计中,如果考虑数据的安全性,就需要与前后级模块进行握手通信,这个时候就需要对流水数据进行反压处理

  在握手协议中valid与ready不可以过度依赖,比如valid不可以等待ready到达再拉高,原因在于“防止死锁”。但是axi协议中的握手信号,ready是可以等待valid信号拉高在进行拉高的。同时,valid拉高要与有效数据同步,时钟对齐。此外,数据准备好后,valid可以拉高等待ready拉高,但是每当握手成功后,数据需要更新,此时没有有效数据的话,valid需要拉低。

  当输入流量大于输出流量的时候,就需要反压。或者说,后级没有准备好进行数据接收,如果前级进行数据传递,就需要后级进行反压前级,如果只反压后级的前一级,就会导致后级的前一级数据丢失。

  带存储体的反压

  如果对每一级流水进行反压的话,就太过于麻烦,可以使用带存储体的反压,也就是增加RAM或者FIFO,在反压上一级模块的同时,还能够安全存储流水线上的数据。

假设车库就是最后一级模块,闸机是上级模块,流水线(也就是途中)最多10个数据,那么安全起见,车库在数据量达到90的时候就需要进行反压,不再让闸机向流水线放数据。

示例

  设计一个6输入的32bit加法器,输出一个带截断的32bit加法结果,要求用三级流水设计,带前后反压

  分析:在多级流水设计中,如果每一级只有一个寄存器,并在一个模块中,也就是当颗粒度划分很细的时候,一般使用带存储体的反压,比如6级流水,那么就设计好流水线,在FIFO未满的时候提前发出反压信号,一般水线设置为FIFO_DEPTH - 流水线级数,当FIFO未达到水线的时候,给上一级的ready持续拉高,否则拉低;FIFO非空的时候给下一级的valid拉高,下一级给FIFO的ready信号可以当做读使能信号

  

带存储体的反压

//多级流水设计中,每一级都只有一个寄存器,并且都在一个模块,也就是颗粒度划分很细,
//一般使用带存储体的反压,设计好流水线,FIFO没有满的时候提前发出反压信号,一般水线=FIFO_DEPTH - 流水级数
//核心思想 fifo未达到水线时候给上一级的ready_o信号持续拉高,否则为低;fifo非空,就给下一级valid_o拉高,
//下一级的反压信号ready_i还可以作为FIFO读使能信号
//https://zhuanlan.zhihu.com/p/359330607
module hand_shake_fifo#(
	parameter	FIFO_DATA_WIDTH = 32,
	parameter	FIFO_DEPTH = 8
)(
	input 			clk,
	input			rst,
	input			valid_i,
	input			ready_i,
	input [31:0]	a,
	input [31:0]	b,
	input [31:0]	c,
	input [31:0]	d,
	input [31:0]	e,
	input [31:0]	f,
	
	output 	[31:0]	dout,
	output			ready_o,
	output			valid_o
);


localparam	WATERLINE	 = FIFO_DEPTH - 3;
wire handshake;
reg	handshake_ff1;
reg handshake_ff2;
reg wr_en;

assign handshake = ready_o & valid_i;

always@(posedge clk or negedge rst)begin
	if(rst)begin
		handshake_ff1	<= 'd0;
		handshake_ff2	<= 'd0;
	end
	else begin
		handshake_ff1	<= handshake;
		handshake_ff2	<= handshake_ff1;
	end
end

reg [31:0]	r1_ab;
always@(posedge clk or negedge rst)begin
	if(rst)
		r1_ab <= 'd0;
	else if(handshake)
		r1_ab <= a+b;
end

reg [31:0]	r1_cd;
always@(posedge clk or negedge rst)begin
	if(rst)
		r1_cd <= 'd0;
	else if(handshake)
		r1_cd <= c+d;
end

reg [31:0]	r1_ef;
always@(posedge clk or negedge rst)begin
	if(rst)
		r1_ef <= 'd0;
	else if(handshake)
		r1_ef <= e+f;
end

reg [31:0]	r2_abcd;
always@(posedge clk or negedge rst)begin
	if(rst)
		r2_abcd <= 'd0;
	else if(handshake_ff1)
		r2_abcd <= r1_ab+r1_cd;
end

reg [31:0]	r2_ef;
always@(posedge clk or negedge rst)begin
	if(rst)
		r2_ef <= 'd0;
	else if(handshake_ff1)
		r2_ef <= e+f;
end

reg [31:0]	r3;
always@(posedge clk or negedge rst)begin
	if(rst)
		r3 <= 'd0;
	else if(handshake_ff2)
		r3 <= r2_abcd + r2_ef;
end


always@(posedge clk or negedge rst)begin
	if(rst)
		wr_en <= 1'b0;
	else if(handshake_ff2)
		wr_en <= 1'b1;
	else
		wr_en <= 1'b0;
end

always@(posedge clk or negedge rst)begin
	if(rst)begin
		ready_o <= 1'b0;
	end
	else if(usedw > WATERLINE)
		ready_o <= 1'b0;
	else
		ready_o <= 1'b1;
end

assign valid_o = ~empty;

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该怎么处理呢?

  核心思想就是保证每一级流水的每一个寄存器的安全。将每一级寄存器当做深度为1的fifo,下一级有无数据可以看对应的valid信号。下一级无数据或者下一级已经准备好了,就可以向上一级取数据,本质上是pre-fetch结构。

 

跨级反压与逐级反压

 

上图中每一个模块内部都含有存储体,假设module3到达水线,那么有两种方式反压,跨级反压和逐级反压。

当每一个模块都有存储体的话,逐级反压会更好。

解释:如果采用逐级反压,方法就是module3到达水线的时候反压module2,module2反压module1.如果进行跨级反压,那么module3的存储体的深度=途1+途2+途3+waterline1+waterline2+waterline3,可以看到,存储体3变大并且计算复杂。

 

 

 

 

 

 

posted @ 2023-04-27 10:48  VincentZJ  阅读(1275)  评论(0编辑  收藏  举报