3. 双线握手(全握手):req/ack模型、四步握手流程、双线握手的可靠性
双线握手,圈里人常叫它「全握手」。说白了,就是两根信号线来回倒腾,把数据传稳了。
我刚开始接触这个模型时,觉得挺简单的——不就是发个请求,回个确认嘛。后来在项目里踩过坑才明白,这里面的门道,比想象中深得多。
3.1 req/ack 模型:谁在说话?
先看最基本的角色划分。双线握手涉及两个模块:
- 发送方(Source):负责发出请求信号
req - 接收方(Destination):负责返回确认信号
ack
你想想看,这就像两个人打电话。一个人说「喂,你听着呢吗?」(req),另一个人回「嗯,你说吧」(ack)。一来一回,才算建立连接。
关键点:req 和 ack 是电平敏感的,不是边沿触发。这意味着只要电平有效,就代表请求或确认正在持续。
我个人习惯把 req 和 ack 看作一对「握手伴侣」。它们必须成对出现,缺一个都不行。我在一个 FPGA 项目里见过有人只接了 req 没接 ack,结果数据全丢了——嗯,那场面,挺尴尬的。
3.2 四步握手流程:一步一步来
双线握手的完整流程,我习惯叫它「四步舞」。每一步都有明确的状态变化:
- 发送方拉高 req:告诉接收方「我有数据要给你,准备好了吗?」
- 接收方拉高 ack:回应「准备好了,你发吧」
- 发送方拉低 req:表示「数据已发出,你可以收了」
- 接收方拉低 ack:确认「收到,握手结束」
这四步,一步都不能跳。我曾经在调试一个高速接口时,发现接收方在第二步就急着去读数据,结果读到的全是垃圾。为什么?因为发送方还没把数据稳定在总线上呢。
小技巧:在第二步和第三步之间,发送方应该把数据放到总线上,并保持稳定。接收方在第三步之后才能采样数据。这个时序窗口,我一般留至少一个时钟周期。
用代码表示这个流程,大概是这样的:
// 发送方状态机
state_idle:
if (data_ready) begin
req <= 1; // 第一步:拉高 req
next_state = wait_ack;
end
state_wait_ack:
if (ack == 1) begin
data_bus <= tx_data; // 放数据
req <= 0; // 第三步:拉低 req
next_state = wait_ack_low;
end
state_wait_ack_low:
if (ack == 0) begin
next_state = state_idle; // 第四步完成
end
接收方那边,逻辑是对称的:
// 接收方状态机
state_idle:
if (req == 1) begin
ack <= 1; // 第二步:拉高 ack
next_state = wait_req_low;
end
state_wait_req_low:
if (req == 0) begin
rx_data <= data_bus; // 采样数据
ack <= 0; // 第四步:拉低 ack
next_state = state_idle;
end
3.3 双线握手的可靠性:为什么它比单线强?
单线握手(比如只有一根 strobe 信号)有个致命问题:发送方不知道接收方到底收到没有。万一接收方没准备好,数据就丢了。
双线握手解决了这个问题。它提供了双向确认机制:
- 发送方知道接收方已经准备好(通过 ack 高电平)
- 接收方知道发送方已经释放(通过 req 低电平)
- 双方都知道握手已经完成(通过 ack 低电平)
说白了,双线握手是一种无时钟域的同步协议。它不依赖时钟边沿,只依赖电平变化。这意味着它可以跨时钟域工作——我在一个多时钟域的项目里就用它来桥接两个不同频率的模块,效果很好。
注意:双线握手虽然可靠,但速度慢。每传输一次数据,需要四个电平跳变。如果数据量很大,建议改用握手+流水线,或者直接上同步 FIFO。
我曾经在一个项目中,用双线握手来传输配置寄存器。每次配置需要几十个字节,结果发现握手占用了大部分时间。后来我改成批量传输——先握手一次,然后连续发多个数据,最后再握手一次。速度提升了将近 4 倍。
3.4 避坑指南:我踩过的那些坑
做双线握手设计,有几个地方特别容易出问题:
- 信号同步:如果 req 和 ack 跨时钟域,必须用两级触发器同步。我曾经偷懒没做同步,结果亚稳态把整个系统搞崩了。
- 电平竞争:发送方拉低 req 的同时,接收方可能正在采样。这个窗口期,数据总线必须保持稳定。我一般会在 req 拉低前一个时钟就把数据放好。
- 死锁:如果接收方永远不拉高 ack,发送方就会卡死在等待状态。我习惯加一个超时计数器,超过一定时间就复位握手。
我的经验:在仿真阶段,一定要把握手信号的所有组合都测一遍。特别是 req 和 ack 同时变化的情况——这种 corner case 最容易出 bug。
3.5 知识体系图:双线握手全貌
下面这张图,是我自己总结的双线握手知识体系。它把核心概念、流程、可靠性和注意事项串在了一起:
嗯,这张图基本把双线握手的核心都涵盖了。你把它打印出来贴在工位上,写代码时瞄一眼,思路会清晰很多。
可可精品资料库