握手协议:VALID与READY信号的握手机制

大家好,我是你们的AXI总线讲师。今天我们来聊聊AXI总线里最核心、也最基础的一个概念——握手协议。

说白了,AXI总线上的每一次数据传输,都是一次「握手」。主设备(Master)和从设备(Slave)之间,通过两根信号线来达成默契:VALID 和 READY。

我刚开始学AXI的时候,觉得这玩意儿很简单嘛——一个发数据,一个收数据,不就完了?后来在实际项目中踩过坑才发现,握手协议里的门道真不少。尤其是那三种握手场景,搞不清楚的话,你的总线系统可能直接就卡死了。

1. VALID与READY:谁先谁后?

先看最基本的定义:

  • VALID:由主设备发出,表示「我这边数据已经准备好了,你随时可以取走」。
  • READY:由从设备发出,表示「我这边已经准备好接收数据了,你可以发过来」。

数据真正传输成功的时刻,是VALID和READY同时为高的那个时钟上升沿。这个点,我们叫它「握手成功」。

嗯,这里要注意:VALID和READY谁先拉高,谁后拉高,并没有硬性规定。但不同的先后顺序,会带来不同的行为表现。这就是我们要讲的三种握手场景。

2. 三种握手场景

我个人习惯把握手场景分成三类:提前握手、同时握手、延迟握手。咱们一个一个来看。

2.1 提前握手(VALID先有效)

这是最常见的情况。主设备先把VALID拉高,表示数据已经就绪。从设备可能还在忙,等它准备好了,再把READY拉高。

举个例子:

时钟周期:  T1    T2    T3    T4
VALID:     0     1     1     1
READY:     0     0     1     0
数据:      X     D1    D1    D1

在T2时刻,VALID拉高,但READY还是低。数据D1在总线上等待。直到T3时刻,READY也拉高了,握手成功,数据被取走。

我在项目中遇到过这种情况:主设备是个高速DMA控制器,它恨不得一口气把所有数据都扔出去。但从设备是个慢速的SPI接口,处理速度跟不上。这时候,DMA就得等SPI准备好才能发下一笔数据。

我的经验: 提前握手场景下,主设备必须保持数据稳定,直到握手成功。你不能在VALID拉高后把数据换掉,否则从设备会拿到错误的数据。

2.2 同时握手(VALID和READY同时有效)

这是最理想的情况。主设备和从设备一拍即合,VALID和READY在同一个时钟周期内同时拉高,数据瞬间传输完成。

时钟周期:  T1    T2
VALID:     0     1
READY:     0     1
数据:      X     D1

在T2时刻,VALID和READY同时为高,数据D1被成功传输。没有等待,没有延迟,效率最高。

你想想看,什么时候会出现同时握手?通常是主设备和从设备都处于空闲状态,而且从设备已经提前把READY拉高了。嗯,这里有个小技巧:很多从设备在复位后就把READY拉高,表示「我随时可以接收数据」。这样主设备只要一发VALID,就能立刻握手成功。

2.3 延迟握手(READY先有效)

这种情况稍微少见一些,但也很重要。从设备先把READY拉高,表示「我已经准备好了,你随时可以发」。但主设备的数据还没准备好,所以VALID还拉着低。等主设备准备好了,再把VALID拉高,握手成功。

时钟周期:  T1    T2    T3
VALID:     0     0     1
READY:     1     1     1
数据:      X     X     D1

在T1和T2时刻,READY一直为高,但VALID为低。直到T3时刻,VALID拉高,握手成功。

我曾经在调试一个视频处理模块时遇到过这种场景。从设备是个FIFO,它一直处于「空」状态,所以READY始终拉高。但主设备需要从外部存储器读取数据,有几十个时钟周期的延迟。等数据读回来了,VALID才拉高。

注意: 延迟握手场景下,从设备不能因为READY拉高了很久没等到数据,就把READY拉低。READY一旦拉高,必须保持到握手成功,或者直到你确定主设备不会发数据了。否则会造成协议违规。

3. 依赖关系与死锁避免

讲完了三种握手场景,咱们来聊聊一个更深入的话题——依赖关系和死锁。

为什么会发生死锁?说白了,就是主设备和从设备互相等待,谁也不肯先让步。

举个例子:

  • 主设备在等从设备把READY拉高,它才肯把VALID拉高。
  • 从设备在等主设备把VALID拉高,它才肯把READY拉高。

结果就是,两个信号都拉低,永远等不到握手成功的那一刻。这就是死锁。

我刚开始做AXI设计时,就犯过这个错误。当时设计了一个简单的从设备,逻辑是「只有收到VALID才拉高READY」。而主设备的逻辑是「只有看到READY才拉高VALID」。结果一上电,总线直接卡死,数据一动不动。

怎么避免?有两条基本原则:

  1. 主设备不能依赖READY来产生VALID。 主设备应该在自己数据准备好后,无条件地拉高VALID。至于从设备什么时候收,那是从设备的事。
  2. 从设备可以依赖VALID来产生READY,但最好有超时机制。 如果从设备等不到VALID,它应该能自己恢复,而不是一直死等。
核心原则: VALID可以等待READY,但READY不能等待VALID。或者说,主设备是主动方,从设备是被动方。这个依赖关系一旦搞反,死锁就来了。

嗯,这里再补充一点。AXI协议里还有一个隐含规则:一旦VALID拉高,就不能再拉低,直到握手成功。这个规则是为了防止主设备反悔。同样,READY一旦拉高,也不能随意拉低。这两个规则合在一起,保证了握手过程的确定性。

4. 握手协议的核心逻辑图

为了让大家更直观地理解,我画了一张握手协议的状态图。这张图展示了VALID和READY的交互过程,以及三种握手场景的路径。

AXI握手协议状态图 空闲状态 VALID=0, READY=0 等待READY VALID=1, READY=0 等待VALID VALID=0, READY=1 握手成功 VALID=1, READY=1 传输完成 下一笔数据 主设备发数据 从设备就绪 从设备就绪 主设备发数据 数据被取走 准备下一笔 同时就绪 图例: 提前握手 延迟握手 同时握手 传输完成 下一笔准备

这张图里,从「空闲状态」出发,有三条路径:

  • 提前握手:先到「等待READY」,再到「握手成功」。
  • 延迟握手:先到「等待VALID」,再到「握手成功」。
  • 同时握手:直接从「空闲」跳到「握手成功」。

不管走哪条路,最终都要到达「握手成功」这个状态,数据才算真正传输完成。

5. 避坑指南

最后,分享几个我踩过的坑,希望能帮大家少走弯路。

避坑1: 我曾经设计过一个从设备,在收到VALID后,需要两个时钟周期才能准备好数据。于是我在代码里写了「延迟两个周期再拉高READY」。结果主设备等不及,以为从设备出错了,直接报错。后来我才发现,正确的做法是:在收到VALID后,先拉高READY,然后再准备数据。只要保证在握手成功时数据是有效的就行。
避坑2: 还有一个常见问题——多个主设备共享一个从设备时,握手信号会变得复杂。比如两个主设备同时向同一个从设备发数据,从设备的READY该给谁?这时候就需要仲裁器来协调。我建议在系统设计初期就把仲裁逻辑考虑进去,不要等到调试时再补。
避坑3: 死锁问题。我再强调一遍:主设备不能等READY才发VALID。如果你发现总线卡死了,先检查主设备的VALID产生逻辑。大概率是它在那里傻等READY。

好了,关于握手协议的内容就讲到这里。VALID和READY虽然只有两根线,但用好它们,你的AXI总线系统才能跑得稳、跑得快。下一章我们会聊到地址读写通道的细节,到时候这些握手知识还会用上。

可可精品资料库