一、握手协议概述

什么是握手协议

握手协议,说白了就是两个模块之间的一种「对话规则」。

你想想看,当A模块想给B模块传数据时,B可能正忙着。如果A不管不顾直接扔数据,B没接住,数据就丢了。握手协议就是解决这个问题的——它让发送方和接收方达成一致:你什么时候发,我什么时候收,咱们步调对齐。

在数字电路里,最常见的握手信号就两个:

  • valid(有效信号)—— 发送方说:「我准备好了,数据在总线上,你快来拿」
  • ready(就绪信号)—— 接收方说:「我准备好了,你尽管发」

当 valid 和 ready 同时为高,数据就成功传输了。这个时刻,我们叫它「握手成功」。

核心要点:握手协议的本质就是 valid 和 ready 的「与」逻辑。两者都为高,数据才算真正被接收。

为什么需要握手协议

我刚开始做数字设计时,总觉得握手协议有点「多余」。直接一拍子打过去不就行了?后来踩了坑才明白——

没有握手协议,你会遇到三个典型问题:

  1. 数据丢失 —— 接收方还没准备好,发送方就把数据更新了。老数据被覆盖,新数据没被接收。
  2. 时序混乱 —— 跨时钟域时,两个模块工作在不同频率。没有握手,你根本不知道对方什么时候能处理完。
  3. 死锁风险 —— 发送方等接收方,接收方等发送方,两边都在等,谁也不动。

握手协议就是给这些场景定了个规矩。有了它,数据流就有了「节奏感」。

我的经验:我曾经在一个多时钟域的项目里,因为没加握手信号,仿真跑得好好的,上板就丢数据。查了三天,最后发现是跨时钟域采样出了问题。从那以后,凡是跨时钟域的数据通路,我必加握手。

握手协议在数字电路中的典型应用场景

握手协议不是某个特定模块的专利。它几乎出现在所有需要「协调」的地方。我挑三个最常见的场景说说:

1. FIFO(先进先出队列)

FIFO 是握手协议最经典的例子。写端有写使能(wr_en),读端有读使能(rd_en)。本质上,这就是一对 valid-ready 握手。

  • 写操作:写数据 + wr_en(valid)→ FIFO 内部有空位(ready)
  • 读操作:读使能(valid)→ FIFO 内部有数据(ready)

FIFO 的空满标志,其实就是握手状态的「统计结果」。满的时候,写端就不能再发 valid 了;空的时候,读端就不能再发 valid 了。

注意:FIFO 深度设计要留余量。我曾经遇到过 FIFO 深度刚好等于最大突发长度,结果因为握手延迟导致溢出。嗯,后来我习惯加 2~4 个深度的余量。

2. 总线协议(如 AXI、AHB)

总线协议是握手协议的「高级玩家」。以 AXI 为例,它的五个独立通道(读地址、读数据、写地址、写数据、写响应),每个通道都有一套 valid-ready 握手。

你想想看,一个 AXI 总线上的 master 和 slave,可能一个快一个慢。没有握手,master 发地址时 slave 还没准备好,数据就乱套了。

AXI 的握手规则很简单:

  • valid 不能依赖 ready —— 发送方可以提前拉高 valid,等接收方就绪
  • ready 可以依赖 valid —— 接收方可以等看到 valid 再拉 ready
  • 但两者都拉高时,数据必须在下一个时钟沿被采样

避坑指南:AXI 协议里有个容易忽略的点——valid 一旦拉高,就不能在握手成功前拉低。我曾经在写 AXI slave 时犯过这个错,结果 master 端采样到了错误数据。记住:valid 是「承诺」,不是「试探」。

3. 跨时钟域(CDC)

跨时钟域是握手协议的「硬仗」。两个时钟域之间,频率不同、相位不同,直接传数据肯定出问题。

常用的方法有两种:

  • 两级同步器 + 握手 —— 把 valid 信号同步到接收时钟域,接收方处理完后回传一个 ack(应答)信号,再同步回发送时钟域。发送方看到 ack 后,才能发下一笔数据。
  • 异步 FIFO —— 本质上也是握手,只不过把握手逻辑封装进了 FIFO 内部。读写指针的同步,就是握手信号的跨时钟域传递。

我个人更推荐异步 FIFO 的方式。为什么?因为手写的握手同步器容易出问题——比如 ack 信号还没同步回来,发送方就以为数据被接收了,结果覆盖了旧数据。

我的习惯:跨时钟域的数据通路,我一般先用异步 FIFO 兜底。如果数据量很小(比如就几个控制信号),才用手写握手同步器。但不管哪种方式,我都会在仿真时加随机延迟来验证握手逻辑的健壮性。

握手协议的核心逻辑图

下面这张图展示了最基本的 valid-ready 握手状态机。它只有三个状态:空闲、等待、传输完成。

Valid-Ready 握手状态机 空闲 valid=0, ready=0 等待 valid=1, ready=0 传输完成 valid=1, ready=1 发送数据 ready=1 传输结束,回到空闲 ready=0 状态转移说明: 空闲 → 等待:发送方拉高 valid,开始等待接收方就绪 等待 → 传输完成:接收方拉高 ready,握手成功 传输完成 → 空闲:数据被采样,双方回到初始状态

这个状态机看着简单,但实际项目中,握手逻辑往往比这复杂。比如:

  • 发送方可能连续发送多笔数据(流水线握手)
  • 接收方可能随时拉低 ready(反压)
  • 跨时钟域时,握手信号需要同步,会引入额外延迟

再提醒一句:握手状态机最怕「死锁」。比如发送方等 ready,接收方等 valid,但双方都以为对方先给信号。设计时一定要确保至少有一方是「主动」的——要么发送方先拉 valid,要么接收方先拉 ready。别让两边都等着。

好了,这一章我们聊了握手协议的基本概念、为什么需要它,以及它在 FIFO、总线、跨时钟域里的典型用法。下一章我会深入讲 valid-ready 握手的几种具体实现方式,包括组合逻辑和时序逻辑的写法,以及它们各自的优缺点。

可可精品资料库