如何写控制逻辑(三):模块级流水和valid/ready协议
大概八月份就开始想总结下控制逻辑的写法了,然后开始找资料,没有直接讲这个的,零零散散的看了很多,断断续续的写了很长时间,自闭无岁月···
就先写一篇发出来吧,抓住2020的尾巴 <-_<-
所以上两篇嘛,第一篇总结下控制逻辑,第二篇写FSM状态机,敬请期待 :)
全程都是菜鸟的理解,代码也未经验证,如果觉得不对欢迎指出来!
OutLine
1 基本流水中间单元
1.1 OutReady 恒为1的情况
1.2 有OutReady 反压的情况
1.2.1 推理逻辑一
1.2.1 推理逻辑二
1.3 多个输入模块对多个输出模块
1.3.1 在valid/ready中加入一些控制信号
1.3.2 多对多握手
2 skid buffer
3 死锁
4 Valid/ready 撤销
Reference
规模较大的设计一般划分为若干子模块,子模块之间通过FIFO连接。FIFO的作用就是实现模块间的rate balance,匹配不同模块的处理速率,从而实现模块间的解耦,这样每个模块可以单独设计控制逻辑,不需要考虑其他模块的影响。
这些子模块可以是一些实现具体功能的模块,也可以是再划分为若干子模块,然后通过模块级的流水实现控制,无论哪种方式,都需要和FIFO的空满状态打交道。
我们知道FIFO的空满其实就是Valid/ready,所以对于模块级流水而言,这些流水模块一定是处于两个FIFO之间的,这两个FIFO就是流水的发端和收端。而且发端FIFO的Valid很独立,没有依赖于Ready;收端FIFO的Ready很独立,没有依赖于Valid;所以他们是完美的发端和收端,而中间这些流水模块的Valid/ready有依赖关系是完全没问题的,带来的后果也仅仅是当Valid依赖ready时是ready before valid,会有气泡;而当ready依赖valid时是valid before ready,没有气泡。而且如同我们下面介绍的ready before valid时还可以有方法挤掉气泡。
而如果两个FIFO之间没有再划分子模块,则可以将其视为模块流水中的一级。
此外,对于Valid/ready传输协议,若是在一堆组合逻辑模块传来传去,则都不用打拍的,直接一根线贯穿到底即可,但是这样组合逻辑太长,Timing会紧张,所以有了(模块)流水线,在每一级模块对数据和InValid插入寄存器切断组合逻辑,以跑更高的时钟频率;而OutReady一般不用打拍,但是由于它是一根信号驱动中间所有模块的ready,所以如果流水级数多了fanout会比较大,而且还有gate delays,进而Timing也可能会有问题,所以这时也需要对ready打拍。
1 基本流水中间单元
如上所讲,流水线的sender和receiver分别是两边的FIFO,所以下面介绍的都是中间的单元,他们传递InValid到OutValid,传递OutReady到InReady,在InReady和InValid握上手时对InData做一些处理,比如我们这里将其乘3。它们的模块接口都如下:
module MiddlePipe #(parameter
DW = 10
)
(
//Interface
input Clk ,
input Clear ,
input Rstn ,
//In interface
input [DW-1:0] DataIn ,
input DataInVld ,
output DataInRdy ,
//Out interface
output [DW+1:0] DataOut ,
output DataOutVld ,
input DataOutRdy
);
//---------------------------------------------------------------------
reg data_in_rdy;
assign DataInRdy = data_in_rdy;
reg [DW+1:0] data_out;
assign DataOut = data_out;
reg data_out_vld;
assign DataOutVld = data_out_vld;
//---------------------------------------------------------------------
1.1 OutReady 恒为1的情况
OutReady 恒为1意味着不会有来自后级模块的反压,也即此时的模块级流水和模块内的流水线运算是一样的,写法如下:
//------Version 1: if DataOutRdy = 1-----------
always @ *
begin
data_in_rdy = 'h1;
end
always @( posedge Clk or negedge Rstn )
begin
if( ~Rstn )
data_out <= 'h0;
else if( Clear )
data_out <= 'h0;
else if( DataInVld )
data_out <= DataIn << 1 + DataIn;
end
always @( posedge Clk or negedge Rstn )
begin
if( ~Rstn )
data_out_vld <= 'h0;
else if( Clear )
data_out_vld <= 'h0;
else
data_out_vld <= DataInVld;
end
//------Version 1: if DataOutRdy = 1-----------
1.2 有OutReady 反压的情况
1.2.1 推理逻辑一
此时来自后级模块的反压,其实追跟到底是来自后面那个作为receiver的FIFO的反压,比如它满了,则传给前面的Ready都要拉下来以防丢数据。
前面我们提过,模块级流水就是在valid/ready协议的valid和数据通路中插入寄存器切断组合逻辑,ready要具有反压能力自然要控制这些寄存器,以在ready=0时使其停下来,而且ready可以不打拍,所以逻辑如下:
//----------------------Version 2:without bubble collapse--------------------------
always @ *
begin
data_in_rdy = DataOutRdy;
end
always @( posedge Clk or negedge Rstn )
begin
if( ~Rstn )
data_out <= 'h0;
else if( Clear )
data_out <= 'h0;
else if( data_in_rdy && DataInVld )//backpressure:data_in_rdy &&
data_out <&