P2P接口是一种双向握手接口,传输的前级和后级各提供一个数据有效信号valid和忙信号busy信号,只有当两个信号达成某种指定情况时,握手完成,数据传输完成,否则数据传输均未完成。这可以看成一种分布式控制方式,每个模块的开发人员仅需要考虑上下级的握手信号即可。
端口名 | 类型 | 位宽 | 功能 |
---|---|---|---|
din_valid | input | 1 | 输入数据有效信号 |
din_busy | output | 1 | 输入部分忙,不接受输入数据 |
dout_valid | output | 1 | 输出有效信号 |
dout_busy | input | 1 | 输出部分忙,下一级不接受输入 |
din | input | - | 输入数据 |
dout | output | - | 输出数据 |
din_valid | din_busy | 状态 |
---|---|---|
0 | 0 | 静默,无数据传输 |
1 | 0 | 正常接收数据,无阻塞情况发生 |
0 | 1 | 输入忙,但无数据输入 |
1 | 1 | 输入数据被阻塞 |
dout_valid | dout_busy | 状态 |
---|---|---|
0 | 0 | 静默,无数据传输 |
1 | 0 | 正常发送数据,无阻塞情况发生 |
0 | 1 | 输出忙,但无数据输出 |
1 | 1 | 输出数据被阻塞 |
din_valid | din_busy | dout_valid | dout_busy | 状态 |
---|---|---|---|---|
1 | 1 | 1 | 1 | 全阻塞 |
1 | 0 | 1 | 1 | 接收在本级被阻塞的数据 |
1 | 1 | 1 | 0 | 释放被阻塞被本级的数据 |
下图分析了一个在四级流水线中数据的传输过程,其中:
分析过程如下:
上图为一个通常情况的可用于流水线的P2P接口时序图,当连续传递无阻塞时(d0和d1),busy信号复位,valid信号和数据相对于上一级延迟一个时钟周期。d2~d5表示的是在流水线过程中发送后级阻塞的处理方法:
上图是传输开始时就有后级busy信号的情况,可以发现,该情况与上一种情况相同,参照上一种情况可分析。
上图是仅传输一个单独数据且遇到阻塞的情况,该情况最大的不同是din_valid
信号仅置位一个时钟周期,而dout_valid
要等发送数据d2完成发送后再复位,因此两个valid信号之间并不是简单的延迟一个周期的关系。
上图是最后一个典型情况——仅发送两个数据且遇到阻塞的情况:
首先定义了一些用于简化判断的wire型变量:
wire busy_drop_buf = (!dout_busy) && din_busy ;
wire is_din_busy = dout_busy && dout_valid ;
wire is_no_dout = (!lock_valid) && (!(din_valid && (!din_busy))) ;
wire is_lock = is_din_busy && (!din_busy) && din_valid ;
wire is_unlock = busy_drop_buf && lock_valid ;
busy_drop_buf
表示dout_busy信号下降沿,该信号在后期判断中经常使用;is_din_busy
表示后级发生阻塞行为,当后级的valid和busy同时置位时,表示后级有数据未完成传输,此时阻塞信号应传递到前级;is_no_dout
表示没有数据需要发送,即旁路缓存中没有数据且上一级没有发送给这一级的数据;is_lock
和is_unlock
分别表示数据存入旁路缓存和数据从旁路缓存中取出,其中:
旁路缓存用于在打断busy反馈的组合路径且不丢失数据,该部分分为两个信号:
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
lock_valid <= 1'b0;
end else if(is_lock) begin
lock_valid <= 1'b1;
end else if(is_unlock) begin
lock_valid <= 1'b0;
end
end
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
lock_data <= 'b0;
end else if(is_lock) begin
lock_data <= din_data;
end
end
这两个信号控制基本相同,通过is_lock
和is_unlock
分别进行进缓存和出缓存控制。
由上分析,当上一级传输完成时,需要将上级传输来的数据转发给下一级,此时dout_valid
信号置位;当当前传输完成且没有需要发送的数据时,后级没有需要发送的数据,dout_valid
信号复位,代码如下所示:
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
dout_valid <= 1'b0;
end else if(din_valid && (!din_busy)) begin
dout_valid <= 1'b1;
end else if(dout_valid && (!dout_busy) && is_no_dout) begin
dout_valid <= 1'b0;
end
end
din_busy
信号由寄存器产生,因此可以打断从最后一级向前传递的组合逻辑busy反馈路线,当后级发生阻塞时,前级不能继续发送数据,此时该信号置位;当后级busy信号复位后,无论是否发送数据,均可接收新数据(参考时序图分析),因此当后级busy信号复位后该信号复位。
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
din_busy <= 'b0;
end else if(is_din_busy) begin
din_busy <= 1'b1;
end else if(!dout_busy) begin
din_busy <= 'b0;
end
end
dout_data
信号为转发数据,有三种情况:
整体代码如下所示:
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
dout_data <= 'b0;
end else if(is_din_busy) begin
dout_data <= dout_data;
end else if(is_unlock) begin
dout_data <= lock_data;
end else begin
dout_data <= din_data;
end
end