ic基础|时序篇:握手协议valid和ready的时序优化

本文详细介绍了在IC行业中处理AXI协议握手信号时序问题的方法,重点讲解了ForwardRegistered、BackwardRegistered和Forward-BackwardRegistered三种打拍模式,以及如何利用同步FIFO优化数据传输。适合IC设计者和准备入行者学习参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家好,我是数字小熊饼干,一个练习时长两年半的ic打工人。我在两年前通过自学跨行社招加入了IC行业。现在我打算将这两年的工作经验和当初面试时最常问的一些问题进行总结,并通过汇总成文章的形式进行输出,相信无论你是在职的还是已经还准备入行,看过之后都会有有一些收获,如果看完后喜欢的话就请关注我吧~谢谢~

当我们遇到时序违例时,通常采用的方式为插入寄存器(打拍)或者是后端插入buffer,这对使能信号或数据信号是有用的,但是对于那些需要满足握手协议的信号来说(例如:AXI协议中的多组握手信号xxready和xxvalid)单纯的打拍是行不通的,因为需要满足valid-ready协议,如果仅仅使用打拍很容易丢失数据。

因此,需要采取特殊的方法进行打拍,这种针对AXI协议中握手信号的打拍通常称为axi register slice,通常来说,根据需要打拍的信号的不同有三种模式:

  • 前向寄存器Forward Registered :对valid和data路打拍。
  • 后向寄存器Backward Registered :对ready路打拍。
  • 双向寄存器Forward-Backward Registered :同时对valid/data路和ready路打拍。

一、Forward Registered

首先来分析的是第一种Forward Registered。

Forward指的是从数据发送方到数据接收方之间的数据传递方向,为了保持时序上的一致性,需要打拍的当然不止是valid信号,还得包括data信号。否则,valid信号和data信号对不齐的话,数据传输会出错。

Forward 打拍的电路结构如下所示:
在这里插入图片描述
对于forward寄存器打拍,我们所要考虑的是何时可以往寄存器里打拍,即满足以下两个条件:

1.forward寄存器里是空的;

2.forward寄存器非空,但是接收端准备好接收数据了;

因此有以下的verilog代码:

module forward_buffer(
   input  wire         clk         ,
   input  wire         rst_n       ,
   input  wire         valid_src   ,
   input  wire [31:0]  data_src,
   output wire         ready_src   ,
   output reg          valid_dst   ,
   output reg  [31:0]  data_dst    ,
   input  wire         ready_dst
);
   always @(posedge clk or negedge rst_n) begin
      if (!rst_n) begin
          valid_dst   <= 1'b0;
      end else if (ready_src) begin
          valid_dst   <= valid_src;
      end
   end    
   
   always @(posedge clk) begin  
      if (valid_src & ready_src) begin
          data_dst    <= data_src;
      end
   end
   
   assign ready_src = ready_dst || (~valid_dst);
   
endmodule



解释一下上面的代码:

-valid_dst: 用于对发送端的valid_src进行打拍,它会在以下两种情况接收发送端的valid_src的值:

	valid_dst为0:代表此时valid_dst寄存器和data_dst寄存器没有有效数据,为空,可以接收发送端的输入valid_src。

	ready_dst为1:代表此时接收端准备好了接收数据,则有以下两种情况:

		-若此时valid_dst为0:代表此时接收方先准备好接受数据,而valid_dst寄存器和data_dst寄存器没有有效数据,可以接收发送端的输入valid_src。

		-若valid_dst为1:发送方和接收方都完成了一次握手,valid_dst寄存器和data_dst寄存器中的数据可以更新。

-data_dst: 数据的打拍很好理解,同时满足以下两个条件即可对数据打拍:

当发送端数据准备好(即valid_src有效时)。

接收端准备好接受数据时(ready_dst为1)或者是valid_dst寄存器和data_dst寄存器没有有效数据时(valid_dst为0)。

提示一下,这里的数据通路如果没有用于控制逻辑是可以使用不带复位端以节省面积的。

-ready_src: 输出给发送端的ready信号,满足以下两个条件之一即有效:

当接受端准备好接受数据时(ready_dst);

valid_dst寄存器和data_dst寄存器为空,即没有有效数据时(valid_dst为0)。

简单仿真一下上述模块,波形如下所示:
在这里插入图片描述

二、backward Registered

Backward指的是接收端向输入端发送的ready信号这条路径,虽然只有ready这一个信号,但是我们也不能简单的对其进行打拍处理。因为,如果只对ready信号打拍,而不对valid和data信号进行处理,很容易导致两端握手信号将会无法正确同步,而导致数据丢失,例如下图所示:
在这里插入图片描述
如图所示,由于发送端的ready_src与ready_dst相比存在一个周期的延迟,导致ready_dst变化后,ready_src在一个周期后才相应变化,从而导致了data2丢失,data3重复采样。

因此合适的方法还是在为发送端的信号加一级寄存器。以满足接收端没有ready时发送端发来请求,需要暂存数据的场景,即valid_src & ready_src & ~ready_dst。等接收端准备好接受数据后,会接受到暂存的数据,因此数据不会丢失,也不会重复采样。

backward 打拍的电路结构如下所示:
在这里插入图片描述
verilog代码如下所示:




module backward_buffer(
    input  wire         clk         ,
    input  wire         rst_n       ,
    input  wire         valid_src   ,
    input  wire [31:0]  data_src    ,
    output reg          ready_src   ,


    output wire         valid_dst   ,
    output wire [31:0]  data_dst    ,
    input  wire         ready_dst
);


    reg [31:0] data_r;
    reg        valid_r;


    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            valid_r <= 1'b0;
        end else if (ready_dst) begin
            valid_r <= 1'b0;
        end else if (valid_src & ready_src & ~ready_dst) begin
            valid_r <= 1'b1;
        end
    end


    always @(posedge clk) begin
        if (valid_src & ready_src & ~ready_dst) begin
            data_r    <= data_src;
        end
    end


    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            ready_src   <= 1'b1;
        end else if (ready_dst) begin
            ready_src   <= 1'b1;
        end else if (valid_src) begin
            ready_src   <= 1'b0;
        end
    end  

    assign valid_dst = ready_src ? valid_src : valid_r;
    assign data_dst  = ready_src ? data_src  : data_r;

endmodule

解释一下上面的代码:

-valid_r: 用于对发送端的valid_src进行暂存,会在以下情况置1或置0:

当接收端ready_dst信号为1时,置0,此时代表接受端有能力接受数据,因此无需进行暂存。

当发送端valid_src信号有效,ready_src有效,代表发送方完成了一次握手,若此时接受端ready_dst无效,则代表此时需要缓存数据(即将valid_r置1),以避免丢失。

-data_r: 和valid_dst的逻辑类似,当满足valid_src & ready_src & ~ready_dst时,暂存发送端输入的数据data_src。

-ready_src:通过以下逻辑控制:

复位值为1,用于在接收端未准备好时,一旦发送端准备好数据,即可暂存data_src。

接收端ready_dst有效,则置1。

发送端valid_src有效时,置0。

-valid_dst和data_src,这两个信号根据ready_src的值选择输出来自发送端还是暂存寄存器,即:

当ready_src有效时,代表此时接收端或者是暂存寄存器可以接受数据,此时输入接收端的valid和data为发送端的输入信号valid_src和data_src。

当ready_src无效时,代表此时暂存寄存器中的数据有效,因此输入接收端的数据为暂存信号valid_r和data_r。

使用上述backward寄存器后,理论上有以下时序图:
在这里插入图片描述
可见在使用了backward寄存器后,数据不会出现丢失的情况。

接下来我们对backward_buffer模块进行仿真验证,波形如下:
在这里插入图片描述
观察波形可以发现,ready_src相对于ready_dst延迟了一拍,并且在数据接收方还未准备好(即ready_src有效,ready_dst无效)但数据发送方发送数据(即valid_src有效)的这种情况下,对数据发送端的data_src和valid_src进行了缓存,因此该模块达成了我们的设计目标。

三、Forward-Backward Registered

Forward-Backward Registered即对valid/data和ready都进行时序优化的寄存器,我们可以使用上面提到的Forward Registered 和Backward Registered拼合在一起即可得到Forward-Backward Registered,只要注意一下两类寄存器之间的摆放顺序即可:由于前向寄存器Forward Registered是对valid和ready进行打拍,而后向寄存器Backward Registered是对ready进行打拍,因此我们要将Forward Registered放在距离接收端近的地方,将Backward Registered放在距离发送端近的地方,如下图所示:
在这里插入图片描述
波形仿真如下:

在这里插入图片描述
观察波形,可见该模块对valid_src、data_src和ready_dst都打了一拍,达成了我们的设计目标!

除了上面的方法之外,我们也可以利用同步fifo来实现发送端和同步端之间的数据传输,利用full信号取反后来作为发送端的ready_src信号,fifo非满即可写入数据,利用empty信号取反,来作为接收端的valid_dst信号,fifo非空即说明其中有数据,可进行读操作。

而这个同步fifo的深度至少要大于2,因为如果fifo深度为1,那么fifo就会要么是“满”要么是“空”,接收端和发送端的传输总是会由于fifo的空满状态而变得断断续续的,影响数据传输效率。

如果你喜欢这篇文章的话,请关注我的公众号-熊熊的ic车间,里面还有ic设计和ic验证的学习资料和书籍等着你呢~欢迎您的关注!

Valid-Ready握手协议在Verilog设计中用于数据传输的同步,确保数据的有效性可靠性。它通常用于两个模块之间的数据交换,并且在发送数据之前,接收方必须准备好接收数据。 下面是一个简单的Verilog代码示例,演示了Valid-Ready握手协议的基本原理: ```verilog module ValidReadyHandshake ( input wire clk, input wire reset, input wire data_valid, output wire data_ready, input wire [DATA_WIDTH-1:0] data_in, output wire [DATA_WIDTH-1:0] data_out ); // 内部状态定义 reg [DATA_WIDTH-1:0] internal_data; reg internal_valid; reg internal_ready; // 同步时钟 always @(posedge clk) begin if (reset) begin internal_ready <= 1'b0; internal_data <= {DATA_WIDTH{1'b0}}; internal_valid <= 1'b0; end else begin internal_ready <= data_ready; internal_data <= data_in; internal_valid <= data_valid; end end // 数据输出逻辑 assign data_out = internal_data; // Ready信号逻辑 assign data_ready = internal_ready && !internal_valid; endmodule ``` 在这个示例中,Valid-Ready握手协议的发送方将数据放入`data_in`端口,并设置`data_valid`信号为高电平。接收方通过`data_ready`信号表示它已经准备好接收数据。当接收方准备好时,它将`data_ready`信号设置为高电平,发送方将通过`data_ready`信号的状态来判断是否可以发送数据。 值得注意的是,`clk``reset`信号是必需的,用于同步时钟复位。 这只是一个基本示例,你可以根据具体的设计需求对握手协议进行扩展修改。希望这个示例对你有所帮助!如果你有更多问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数字ic小熊饼干

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值