当前位置:   article > 正文

握手信号valid/ready的打拍技巧_握手信号打拍

握手信号打拍
一、前言
  • valid和ready信号,尤其是ready信号的时序一般很差,因为它通常是接收端通过组合逻辑输出的。当流水线的级数较多时,ready反压信号一级一级往前传递,时序将会变得更差。
  • 为了优化时序,通常需要对valid和ready信号进行打拍处理。但是由于握手信号的特点,使得这两个信号直接打拍时会发生协议错误,此时就需要使用一些技巧来解决这个问题。

通常对于握手信号的打拍处理有以下三种方式:

  • Forward Register Slice:仅处理valid和data信号的打拍
  • Backward Register Slice:仅处理ready信号的打拍
  • Full Register Slice:同时处理valid信号与ready信号的打拍
二、Forward Register Slice

传送门:valid-ready握手协议中valid及data信号的打拍技巧

module valid_flop
        (
        CLK,
        RESET,
        VALID_UP,
        READY_UP,
        DATA_UP,
        VALID_DOWN,
        READY_DOWN,
        DATA_DOWN
        );

//-----------------------------------------------------------------------------
parameter WIDTH            = 32                                                 ;

//-----------------------------------------------------------------------------
input                      CLK;
input                      RESET;
input                      VALID_UP;
output                     READY_UP;
input  [WIDTH-1:0]         DATA_UP;
output                     VALID_DOWN;
input                      READY_DOWN;
output [WIDTH-1:0]         DATA_DOWN                                            ;

//-----------------------------------------------------------------------------
wire                       CLK;
wire                       RESET;
wire                       VALID_UP;
wire                       READY_UP;
wire   [WIDTH-1:0]         DATA_UP;
//Down Stream
reg                        VALID_DOWN;
wire                       READY_DOWN;
reg    [WIDTH-1:0]         DATA_DOWN;

//-----------------------------------------------------------------------------
//Valid
always @(posedge CLK)
if (RESET)  VALID_DOWN <= 1'b0;
else        VALID_DOWN <= READY_UP ? VALID_UP : VALID_DOWN;
//Data
always @(posedge CLK)
if (RESET)  DATA_DOWN <= {WIDTH{1'b0}};
else        DATA_DOWN <= (READY_UP && VALID_UP) ? DATA_UP : DATA_DOWN;
//READY with buble collapsing.
assign READY_UP = READY_DOWN || ~VALID_DOWN;
//READY with no buble collapsing.
//assign READY_UP = READY_DOWN;
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

在这里插入图片描述

中心思想:在接收端握手成功之前,发送端要将valid和data一直保持住!为了保证接收端在ready_i(READY_DOWN)信号有效时能够采集到data,data必须提前打拍。data在打拍时,使用的是接收端传递给发送端的ready_o(READY_UP)信号,而为了实现提前打拍,ready_o(READY_UP)的值可以等于ready_i(READY_DOWN)|| ~ (valid_o)VALID_DOWN

三、Backward Register Slice

芯片设计-skid buffer(ready打断) 这篇文章中详细介绍的方案一就是典型的skid buffer处理方式。

// ports
output reg     data_i_ready;
input          data_i_valid;
input [DW-1:0] data_i;

input           data_o_ready;
output          data_o_valid;
output [DW-1:0] data_o;

// signals
wire          buf_valid;
reg  [DW-1:0] buf_data;

// data_i_ready由组合逻辑改为时序逻辑
// assign data_i_ready = ~data_o_valid || data_o_ready;
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n)
        data_i_ready <= 1; //有个buf放在那,因此复位后至少可以收一个数据。
    else if(data_o_ready)
        data_i_ready <= 1;
    else if(data_i_valid)  //在data_o_ready拉低后的那一个周期,如果没有数据过来,表明buf为空,可以进数据,data_i_ready保持为1
        data_i_ready <= 0; //如果有数据过来,buf被占,则不允许再进数据了,data_i_ready拉低。
end

// ************关于buf_valid和buf_data有2种方案
/*
方案1:所有输入端口的数据都会进入buf,但其实只有(!data_o_ready && data_i_valid && data_i_ready)时刻进去才会发挥作用
优点:控制逻辑简单;缺点:功耗有所增加
*/
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) //可能有些控制信号通过本模块的data端口进行传输,因此有必要进行复位。
        buf_data <= 0;
    else if(data_i_ready) //不挑时刻,所有数据都进,但是真正有效的还是(!data_o_ready && data_i_valid && data_i_ready)时刻的数据
        buf_data <= data_i;
end

assign buf_valid = ~data_i_ready;

/*
方案2:只有(!data_o_ready && data_i_valid && data_i_ready)时刻数据才会进入buf
优点:功耗略低;缺点:控制逻辑稍微复杂点
*/
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) 
        buf_data <= 0;
    else if(!data_o_ready && data_i_valid && data_i_ready) //该条件下,将向buf中存入数据
        buf_data <= data_i;
end

// 这段其实等效于assign buf_valid = ~data_i_ready; 写成下面这样更好理解
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) 
        buf_valid <= 0;
    else if(data_o_ready)
        buf_valid <= 0; //当data_o_ready拉高,优先读走buf中的数据。
    else if(!data_o_ready && data_i_valid && data_i_ready) //该条件下,将向buf中存入数据
        buf_valid <= 1; 
end

// out,如果buf中有数据,优先读走buf中的数据
assign data_o_valid = data_i_ready? data_i_valid : buf_valid;
assign data_o       = data_i_ready? data_i       : buf_data;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

在这里插入图片描述
在这里插入图片描述

中心思想:在情况1时刻,如果输入端有数据进入,则使用buf对该数据进行暂存。在情况2时刻,如果buf中存有数据,则优先输出buf中的数据。

四、Full Register Slice

https://zhuanlan.zhihu.com/p/212356622 这篇文章中的FIFO处理模式就是Full Register Slice。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号