3. 握手协议:VALID/READY握手机制、依赖关系、背压处理、传输完成条件

各位同学,今天我们来聊聊AXI总线最核心、也是最基础的东西——握手协议。说白了,就是数据怎么在Master和Slave之间“商量着”传输。

我刚入行那会儿,总觉得握手协议不就是两个信号嘛,VALID和READY,有啥好学的?结果第一次调AXI接口的时候,死锁了整整两天。嗯,从那以后我再也不敢小看这两个信号了。

3.1 VALID/READY握手机制

AXI的传输,靠的就是VALID和READY这一对信号。它们俩的关系,有点像“你递东西,我接东西”。

  • VALID:发送方说“我准备好了,数据在总线上,你来拿”。
  • READY:接收方说“我准备好了,你可以把数据给我了”。

数据真正传输成功的条件只有一个:VALID和READY同时为高。就在它们都拉高的那个时钟上升沿,数据被锁存,传输完成。

核心要点:只有VALID和READY同时为高,才算一次成功的传输。缺一个都不行。

你想想看,如果只有VALID拉高,READY没拉高,数据就卡在总线上,发送方得一直等着。反过来,如果READY拉高了,VALID没拉高,接收方就干等着,啥也收不到。

3.2 三种握手场景

在实际项目中,VALID和READY的时序关系有三种情况。我给大家画个图,一看就明白。

VALID/READY 三种握手场景 场景1: VALID先于READY CLK VALID READY DATA ✓ 传输完成 场景2: READY先于VALID CLK VALID READY DATA ✓ 传输完成 场景3: VALID和READY同时有效 CLK VALID READY DATA ✓ 传输完成(零等待)

三种场景,对应三种不同的握手情况:

  1. VALID先有效:发送方先准备好,等接收方。这是最常见的场景,比如Slave正在忙,Master得等着。
  2. READY先有效:接收方先准备好,等发送方。比如Slave早就空着了,但Master的数据还没准备好。
  3. 同时有效:一拍搞定,零等待。这是最高效的情况,但实际项目中不常见。

我的经验:在设计高性能AXI接口时,尽量让VALID和READY同时有效。但别强求,很多时候Slave的处理速度跟不上,背压是必然的。

3.3 依赖关系

握手信号之间是有依赖关系的。这个坑,我踩过不止一次。

AXI协议规定:VALID信号不能依赖READY信号。什么意思呢?就是说,发送方拉高VALID的时候,不能等着READY拉高才拉高VALID。VALID必须“独立”拉高。

但READY可以依赖VALID。接收方可以等到看见VALID拉高了,再拉高READY。这完全没问题。

警告:如果你在代码里写了类似 assign valid = ready && data_ready 这样的逻辑,那就违反了协议!VALID不能等READY。我曾经在一个项目中看到有人这么写,结果综合出来的电路在时序分析时出了大问题。

为什么会这样规定?你想想看,如果VALID依赖READY,那万一READY一直不拉高,VALID就永远拉不高,系统就死锁了。AXI协议设计者早就想到了这一点。

3.4 背压处理

背压(Backpressure),说白了就是接收方处理不过来,让发送方“慢一点”。在AXI总线里,背压是通过READY信号来实现的。

当Slave处理不过来的时候,它会把READY拉低。这时候,即使Master的VALID是拉高的,数据也传不过去。Master就得保持VALID和数据的稳定,一直等到Slave把READY拉高为止。

我给大家看一段典型的背压处理代码:

// Slave接收端背压处理示例
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        ready <= 1'b0;
        fifo_wr_en <= 1'b0;
    end else begin
        // 如果FIFO快满了,拉低READY,产生背压
        if (fifo_almost_full) begin
            ready <= 1'b0;
        end else begin
            ready <= 1'b1;
        end
        
        // 只有在VALID和READY同时为高时才写入FIFO
        if (valid && ready) begin
            fifo_wr_en <= 1'b1;
        end else begin
            fifo_wr_en <= 1'b0;
        end
    end
end

这段代码的逻辑很清楚:FIFO快满的时候,拉低READY,告诉Master“别发了,我吃不下了”。等FIFO被读走一些数据,再拉高READY,继续接收。

背压的本质:就是接收方通过READY信号控制数据流的速度。这是AXI总线实现流量控制的核心机制。

3.5 传输完成条件

一次传输什么时候算完成?很简单:VALID和READY同时为高的那个时钟上升沿。

但这里有个细节要注意——对于突发传输(Burst),每个数据节拍(Beat)都要单独握手。也就是说,一次突发传输有N个数据,就需要N次成功的握手。

传输类型 完成条件 说明
单次传输 VALID & READY = 1 一次握手即完成
突发传输 每个Beat都完成握手 N个Beat需要N次握手
写响应 BVALID & BREADY = 1 写响应通道单独握手
读数据 RVALID & RREADY = 1 最后一个数据带RLAST信号

我记得有一次调试一个DMA控制器,发现读传输总是少一个数据。查了半天,原来是RLAST信号没处理好。最后一个数据传完了,RLAST没拉高,Master以为还有数据要传。嗯,这种边界情况最容易出问题。

调试小技巧:在仿真的时候,把VALID和READY同时为高的时刻打印出来,数一数握手次数对不对。如果突发长度是8,那就应该有8次握手。少一次都不行。

3.6 本章小结

握手协议是AXI总线的基础,也是最容易出错的地方。我个人习惯在设计任何AXI接口时,先把握手逻辑单独写出来,验证好了再集成到系统中。

记住三个要点:

  • VALID不能依赖READY,这是死规矩
  • 背压通过READY实现,别让FIFO溢出
  • 传输完成的唯一标志是VALID和READY同时为高

把这些搞清楚了,后面的通道协议学起来就轻松多了。

可可精品资料库