反压机制指的是在输出(或者输入)端连接的模块不能接收数据(或者没有输入数据),此时自己的模块的运行状态需要暂停运行。
反压机制适用于所有拥有握手协议的模块,且强烈建议使用反压机制用于自己模块设计。
反压机制在公众号最开始也有写过,但是过去了很长时间,对设计有了新的理解,这篇文章会细述一对一、多对一、一对多、多对多之间反压机制的细节和设计。
以下具体示例可以参见我的GitHub:back-pressure-mechanism
git@github.com:lookout1992/back-pressure-mechanism.git
一、一对一
一对一反压是反压机制中的基础,也是最常见的结构。
如上图,如果模块内部由一个寄存器组成,那模块的输出信号din.ready和dout.valid就可以如下设计:
logic switch_en;
assign switch_en = !dout.valid | dout.ready;
logic r_valid;
always @ (posedge clk or negedge rst_n)begin
if(!rst_n)begin
r_valid <= 'd0;
end
else if(switch_en)begin
r_valid <= din.valid;
end
end
assign dout.valid = r_valid;
assign din.ready = switch_en
增加switch_en信号,代表输出端没有准备好输出数据 ,或者是连接输出端的模块已经 准备好接收数据。
总结一句话就是模块(寄存器)的数据可以流动起来。因此switch_en也是输出口i_ready的赋值信号。
关于valid(或者没有显示的data计算部分),只有在switch_en有效时,可以向下传递。
如果图中模块中由多级一对一的寄存器组成,那么最优的设计思路是像上面设计一样,一级级拼接起来,也是最复杂的。(而不是原始公众号文章中,用一个switch_en控制所有的流水使能)
在实际设计中,有可能会在时序和资源之间平衡,需要把流水线拆开,分时复用运算逻辑。因此会出现一笔输入,但是有两笔输出,且运算时需要使用两次输入数据(即不允许输入寄存器更新太快)。这个时候需要添加新的使能(或者counter)等用于额外的判断条件
以上具体示例可以参见我的GitHub:back-pressure-mechanism/one2one
二、多对一
多对一反压是处理一对一之外最常见的结构,多用于多个输入数据运算输出一个信号。
处理这种结构,主要思路是 所有输入有效 ,对于switch2_en 和 switch3_en见上面一对一反压设计,主要描述switch1_en和输入输出之间的关系:
// din0 和din1 相遇前各自一组寄存器。
logic r_reg_din0_valid;
logic w_reg_din0_ready;
logic r_reg_din1_valid;
logic w_reg_din1_ready;
logic switch1_en;
logic w_reg_din_valid;
logic r_reg_dout_valid;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
r_reg_dout_valid <= 'd0;
end
else if(switch1_en)begin
r_reg_dout_valid<= w_reg_din_valid;
end
end
assign switch1_en = !dout.valid | dout.ready;
assign w_reg_din0_ready = r_reg_din1_valid & switch_en;
assign w_reg_din1_ready = r_reg_din0_valid & switch_en;
assign w_reg_din_valid = r_reg_din0_valid & r_reg_din1_valid;
以上具体示例可以参见GitHub:back-pressure-mechanism/more2one
三、一对多
以一输入、二输出为例:
输入的ready和输出的valid将会取决于所有输出端口是否 准备好 。
logic switch_en;
logic r_din_valid;
assign switch_en = (!dout0.valid | dout0.ready)&
(!dout1.valid | dout1.ready);
assign din.ready = switch_en;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
r_din_valid<= 'd0;
end
else if(switch_en)begin
r_din_valid<= din.valid;
end
end
assign dout0.valid = r_din_valid & dout1.ready;
assign dout1.valid = r_din_valid & dout0.ready;
四、多对多
多对多的反压设计可以看做多对一和一对多反压的集合。
五、总结
反压设计的目的是模块可以在输入或者输出没有准备好是可以自动停止运算,好的反压机制的作用是可以在运算因为输入或者输出的原因停止和启动时减少其中的空档期。
六、快速迭代
如果在设计中时间紧张,要求快速迭代出一版可以使用的反压机制代码,那么就不能再按照上面的设计思路扣细活。
可以直接以模块输出端的valid&ready作为全部寄存器的switch_en,这就会造成一个问题在停止时,所有运算都将停止,这会在启动时有流水中会有一些空挡(气泡)。
使用switch_en信号给所有输入信号ready赋值,中间所有寄存器也都由switch_en控制或者由输入信号的valid&ready控制。
logic switch_en = !dout.valid | dout.ready
assign din.ready = switch_en;
logic r_din_valid;
logic r_din_valid_d1;
logic r_din_valid_d2;
logic [DW-1:0] r_din_data;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
r_din_data <= 'd0;
end
else if(din.valid & din.ready) begin
r_din_data <= din.data;
end
end
...
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
r_din_valid <= 'd0;
r_din_valid_d1 <= 'd0;
r_din_valid_d2 <= 'd0;
end
else if(switch_en) begin
r_din_valid <= din.valid;
r_din_valid_d1 <= r_din_valid;
r_din_valid_d2 <= r_din_valid_d1;
end
end
assign dout.valid = r_din_valid_d2;
全部0条评论
快来发表一下你的评论吧 !