valid/ready 握手:反压、气泡与 Skid Buffer
更新时间:2026-09-05。本文是「集成电路大类 · 芯片设计」**进阶层(intermediate)**第四篇新正文(#06)。上一篇 FIFO 深度设计 解决了「数据放哪」,这一篇解决「数据怎么走、走不动怎么办」。入门层 中断与 DMA 里收包通路见过 valid/ready 的影子,本篇把它讲透——它是 AXI4-Stream 等所有流式总线的地基。
本文要回答的问题
- valid 和 ready 各表示什么?握手成功的条件是什么?
- 为什么有一条铁律「valid 不能等 ready」?违反会怎样?
- 反压是怎么一级级传回去的?什么是气泡?
- Skid Buffer 为什么几乎是流水线模块的标配?
一、两根线的协议:我说有货,你说能收
数据通路上每个拍都有一次潜在转移,用两根 1-bit 信号描述:
- valid:上游(source)拉起,表示「本拍 DATA 上的数据有效」;
- ready:下游(sink)拉起,表示「本拍我能接收」;
- 握手(transfer):
valid & ready同拍为 1——数据在这一拍真正转移,双方各自推进。
只拉 valid 不拉 ready:数据原样保持(valid 和 data 都不许动),下游下一拍再说——这就是反压(backpressure)。只有 valid 没有 ready 的接口叫「源不可反压」,数据灌进来下游必须无条件接,只适合上游天生能停的场合(如只读 FSM 输出),接到 DMA/总线上必丢数据。

二、铁律:valid 不许等 ready
协议规则只有几条,但第一条决定生死:
- valid 拉起后,在握手完成前不得撤回,data 必须保持稳定——下游可能在任意拍拉 ready;
- valid 不得组合依赖 ready:不能写
valid = ready_prev & have_data这种「你 ready 我才 valid」; - ready 可以组合依赖 valid(「你有货我才表态能收」合法),也可以提前恒高。
为什么?若双方都「看对方脸色」——valid 等 ready、ready 等 valid——组合逻辑成环,上电后谁也不先动,死锁;即使不成环,valid 随 ready 抖动也会让多扇出的对端采样不稳定。合法写法是上游一旦有数据就把 valid 举着不放,握不握手由下游 ready 决定。这条规矩在 AXI/CHI 规范里白纸黑字,验证 IP 的协议检查器(见进阶 #40 VIP)第一件事就是查它。
三、气泡与吞吐:反压的代价
valid & ready == 0 的拍叫气泡(bubble):数据通路空转。连续流场景吞吐 = 时钟频率 × 握手成功率。反压沿接口逐级上传:末端 FIFO 满 → 末级 ready 拉低 → 它的上游保持 → 一路顶回数据源。链路里任何一级停顿,全线停车(没有中间缓冲时)。所以高带宽通路(DMA、NoC、视频流)逐级都要带缓冲,把停顿限制在局部——这就是下一节 skid buffer 和 FIFO 的使命。
四、Skid Buffer:切断 ready 组合链的标准件
天真的写法里,下游 ready 常常是「我内部有空位」的组合函数。三级模块串起来:ready 从最后一级组合逻辑一路穿回第一级——一条跨越多模块的长组合路径,时钟频率被它拖死,更糟时绕回 valid 形成环路。
Skid Buffer(也叫弹性缓冲、寄存器切片)是标准解法:接口处放两个寄存器——主寄存器正常流水,另有一个「滑行寄存器」兜底;当下游突然 ready 拉低时,正在途中的那一拍数据滑入备用寄存器暂存,于是本模块可以提前一拍向上游给出 ready(只要备用槽空着就敢收)。
效果:ready 路径不再穿越下游,每级模块的时序只取决于自己;代价仅约 2 级寄存器深度的缓冲。可以把 skid buffer 理解成「2 深的 FIFO」,把 FIFO 理解成「深的 skid buffer」——同一种思想的两个量级:片内短链路插 skid buffer,跨速率/跨突发用 FIFO(深度算法见上一篇),跨时钟域用异步 FIFO(进阶 #11)。
五、一个最小可综合的 Skid Buffer 模板
光有框图不够,下面这个两寄存器实现是业界最常用的模板之一(主寄存器 + 一个备用槽,容量恰为 2),值得读懂并能默写:
module skid_buffer #(parameter W=8) (
input logic clk, rst_n,
// 上游(入站)
input logic s_valid,
output logic s_ready,
input logic [W-1:0] s_data,
// 下游(出站)
output logic m_valid,
input logic m_ready,
output logic [W-1:0] m_data
);
logic [W-1:0] dat_d, dat_s; // 主寄存器 / 备用(skid)寄存器
logic v_d, v_s; // 各自的有效位
// 出站数据:备用槽有货时优先输出它(更旧的数据先出)
assign m_data = v_s ? dat_s : dat_d;
assign m_valid = v_d | v_s;
// 关键:只要备用槽空着,就敢提前一拍收上游
assign s_ready = ~v_s;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin v_d <= 0; v_s <= 0; end
else begin
// 下游这拍能收
if (m_ready) begin
if (s_valid && s_ready) begin
dat_d <= s_data; v_d <= 1'b1; // 新数据进主寄存器
end else v_d <= 1'b0;
v_s <= 1'b0; // 备用槽被取空
end
// 下游这拍不收,而上游又硬塞进来 → 滑入备用槽
else if (s_valid && s_ready) begin
dat_s <= dat_d; v_s <= v_d; // 主寄存器里现有的先挪去备用
dat_d <= s_data; v_d <= 1'b1; // 新来的进主寄存器
end
end
end
endmodule看懂三个要点即可举一反三:①s_ready 只依赖本地的 v_s(备用槽是否有货),与下游 m_ready 无组合关系,这正是切断长 ready 链的关键;②容量为 2 不是巧合——要在「下游拒收的同一拍还敢收上游」,必须多出一个槽放当前在途数据;③出站选择 v_s ? 备用 : 主,保证备用槽里更旧的数据先出去(FIFO 序)。验证时务必覆盖四种拍型:连续满带宽(valid/ready 恒高)、只 valid 反压、只 ready(空读)、valid/ready 交替抖动,并用 SVA 断言「valid 拉起后 data 在握手前稳定」「无溢出/无丢数」。把它例化在每一级流水线边界,就是比 FIFO 轻、比直连稳的时序与反压隔离标准件。
六、落到 AXI4-Stream
AXI4-Stream 就是 valid/ready 加几个辅助信号的流式标准:
| 信号 | 含义 |
|---|---|
| TVALID / TREADY | 握手两根线,规则同上文 |
| TDATA | 数据载荷(常 8/16/32/64/128 位) |
| TLAST | 包边界(一个 burst/一包的最后一拍) |
| TKEEP/TSTRB | 标识 TDATA 里哪些字节有效(稀疏传输) |
| TID/TDEST/TUSER | 流标识、目的路由、用户侧带(如时间戳/错误标记) |
它和 AXI4 内存映射(地址/读/写五通道,见 AMBA 入门 与进阶 #08)的区别:Stream 没有地址,是单向数据流,适合 DMA 搬数、网络包、传感器样本;内存映射适合 CPU 读写寄存器/内存。AXI4-Lite 从机(进阶 #07)则是软件配置通路——流走数据、LITE 走配置,一个 SoC 里两者配合。
相关与延伸
上一篇:FIFO 深度计算与设计。接下来进总线实战:AXI4-Lite 从机设计(#07)与 AXI4 完整协议(#08)。入门铺垫:AMBA 入门、中断与 DMA、三个最常见基础模块;跨时钟流控见进阶 #10/#11;大芯片里的流控网络见进阶 #42 片上网络 NoC。
总结
valid 举着不放、ready 随你什么时候拉,同拍为 1 才转移;valid 组合等 ready 必死锁,反压靠数据保持逐级上传,链路里插 Skid Buffer 把 ready 组合链切成局部路径——AXI4-Stream 就是这套握手加 TLAST/TKEEP 的流式标准。