AXI-Stream 包缓冲:store-and-forward 与 cut-through、FIFO 深度计算与 TLAST 对齐
更新时间:2026-09-28。本文是「集成电路大类 · 芯片设计」AXI-Stream 主题簇里的一篇长尾文。AXI-Stream 帧结构 TLAST/TKEEP/TUSER 讲了包边界怎么标,AXI-Stream 背压与时序 讲了 TREADY 怎么打拍,但都没有回答一个数据通路设计里绕不开的问题:中间那个 FIFO 到底按什么单位进、按什么单位出、要多深。这就是 AXI-Stream 包缓冲要解决的。
本文要回答的问题
- store-and-forward(整包缓冲转发)和 cut-through(直通)差别在哪,各适合什么场景?
- packet FIFO 的深度怎么算,为什么「能装下最大包」往往还不够?
- 写指针怎么跟 TLAST 对齐,收到坏包时怎么把已写入的数据作废?
- cut-through 模式下什么条件才允许提前发,什么情况下必须退回整包模式?
- 背压持续时,两种模式的行为和死锁风险有何不同?
一、两种缓冲模式:先收齐 vs 边收边发
AXI-Stream 本身只定义握手,不定义缓冲语义——FIFO 怎么用完全由设计者决定。工程上归成两派:
store-and-forward(整包缓冲转发):下游端口在一个包完整写入 FIFO(收到 TLAST 那一拍)之前,一个 flit 都不往外发。写侧只管收,读侧的使能条件不是「FIFO 非空」,而是「FIFO 里存在至少一个完整的包」。
cut-through(直通):只要 FIFO 非空就开始往外发,第一个 flit 进来后隔一两拍就出现在下游。包头和包身是「流」过去的,TLAST 只是流经的一个标记。
上图的要点在于:两种模式共享同一个 FIFO 物理结构,差别只在读使能条件。这一点的直接推论是——面积上两者几乎一样,差别全在延迟和正确性边界上。
延迟对比很直观。设包长 N 拍,FIFO 打拍延迟为 D:store-and-forward 的首 flit 延迟约为 N + D 拍(要等最后一拍进来),cut-through 只有 D 拍(常数)。对 64 拍的包、200MHz 时钟,前者多付出约 300ns。在对延迟敏感的转发路径(交换机、NoC 网关、DMA 直通通道)上,这个差距就是选 cut-through 的全部理由。
二、为什么还需要整包模式:三条硬理由
既然 cut-through 又快又省,为什么 store-and-forward 仍然到处都是?三条理由:
第一,坏包作废。 上游可能在一个包中途出错(校验失败、超时、复位),此时已经直通出去的 flit 收不回来,下游看到一个「断头包」。整包模式下,写侧可以在包完整落袋并确认无误后才让它可见,坏包直接回收写指针即可,下游永远只见完整包。凡是「出错不能有半截输出」的通路——协议转换、加密引擎输入、DMA 描述符——都必须整包。
第二,需要包长才能决策。 有些处理在转发前需要知道整个包多长(比如按包长计费、按包长选队列、在包头插入长度字段)。包长只有收到 TLAST 才知道,这类逻辑天然是 store-and-forward。
第三,下游只接受无间断传输。 某些下游接口一旦开始一个包就不允许中间停顿(TVALID 不能在一个包内回退)。cut-through 下如果上游发包比下游消费慢,包中间会断流;整包模式因为整个包已在 FIFO 里,可以保证不间断吐出。
三、FIFO 深度怎么算
深度计算先看一句总原则:FIFO 深度 = 最大包长 + 上下游延迟差吸收的余量,而不是拍脑袋的「2 的幂里挑一个大的」。
整包模式下界很硬:必须装得下最大包。最大包长 L_max 拍,深度至少 L_max;如果写侧在包满时不能立即停上游(AXI-Stream 没有「包中途反悔」的握手,只能靠 TREADY 背压),还要加上 TREADY 撤销到上游真正停发之间的在途拍数——寄存切片打两拍,就在途两拍,深度再加 2。
深度 ≥ L_max + 在途拍数(round-trip latency)cut-through 模式可以浅得多,因为读侧几乎同时消费,FIFO 只起「吸收速率差」的作用。经典公式来自异步 FIFO 的守恒推导:写速率 w、读速率 r(r ≥ w 才能保证不无限积压),深度只需覆盖下游背压撤销的在途拍。典型场景下游打两拍寄存切片,深度 4~8 拍就够;但注意这是「r ≥ w」成立的前提——如果下游长期比上游慢,任何有限深度都会满,此时深度只决定多久触发背压,不决定会不会丢包。
| 场景 | 深度估算 | 说明 |
|---|---|---|
| 整包转发,最大包 64 拍,切片 2 拍 | ≥ 66,取 72 或 128 | 硬约束是最大包长 |
| 整包 + 双包流水(收下一个包时发上一个) | ≥ 2 × (L_max + 在途) | 吞吐不降级要双缓冲 |
| cut-through,下游不更慢,切片 2~3 拍 | 4~8 | 只吸收握手往返 |
| 时钟域跨越 + 整包 | max(L_max, 异步 FIFO 格雷码余量) | 见 异步 FIFO 与 CDC |
一个常见的过度设计是把 cut-through 的 FIFO 也开到最大包长——面积白白翻倍,时序还更难收敛。先定模式,再算深度,顺序不能反。
四、TLAST 对齐与写指针回收
整包模式的 RTL 核心是「提交指针」机制。写侧维护两个指针:工作写指针 wr_ptr 正常随每拍递增;提交指针 cmt_ptr 记录「最后一个完整包结束后的位置」。每收到一个 TLAST 且无错误,就把 wr_ptr 拷进 cmt_ptr——这个动作叫提交。读侧的可读条件用 cmt_ptr 而不是 wr_ptr 计算,于是只有完整包对它可见。
上图里「回收」分支是调试重灾区:把 wr_ptr 退回 cmt_ptr 意味着这个半途包的存储空间被下一个包覆盖重写,读侧从头到尾不知道它存在过。两个实现细节必须钉死:其一,TUSER 里的错误标记必须在 TLAST 同拍或之前到达,提交判定要用寄存后的标记,不能与写使能错拍;其二,回收发生时如果读侧正在发前一个包,cmt_ptr 不能动——所有指针更新都要以「读侧当前包发完」为安全点。验证时定向用例要覆盖「坏包紧挨好包」「坏包恰好在 FIFO 满边界」「连续多个坏包」三个角落。
TLAST 对齐还有一层含义:包边界不得跨越 FIFO 的空/满翻转逻辑出错。深度不是 2 的幂时指针回卷逻辑容易在 TLAST 拍算错可读量,工程上宁可深度取 2 的幂、用满标志保护,让 TLAST 永远落在「普通的一拍」上。背压时序细节见 AXI-Stream 背压与时序。
五、背压与死锁:两种模式的边界
下游持续拉低 TREADY 时,两种模式都会把 FIFO 填满,然后向上游施加 TREADY 背压——这是 AXI-Stream 的正常流控,不丢数据。真正的差别在多包交织场景:如果上游用 TID/TDEST 交织多个包(见 AXI-Stream TDEST/TID 路由),cut-through 可能把 A 包的前半截发出去后被 B 包的数据占住 FIFO,若下游对 A 的接收依赖 B 的完成,就会形成环形等待。整包模式天然免疫这类问题,因为任一时刻流向读侧的都是完整单元。
工程选型结论:单向、单流、下游保证不更慢的纯数据通路用 cut-through 配浅 FIFO;涉及包级决策、错误作废、多流交织、或下游有「不间断」要求的,一律 store-and-forward,深度按最大包加余量算。
一个真实案例可以加深理解:某以太网 MAC 的接收通路在整包模式下,必须等 TLAST 到达才向上游提交帧,期间 FIFO 占用按最长帧计算;改成直通模式后延迟降了一个数量级,但 CRC 校验失败时只能用「毒化末拍」的方式通知下游丢弃,两种模式在错误处理路径上的差异远比延迟数字本身更影响架构选择。
这一篇在知识体系里的位置
包边界的信号语义见 AXI-Stream 帧结构 TLAST/TKEEP/TUSER;TREADY 打拍与在途拍计算见 AXI-Stream 背压与时序;FIFO 深度的通用推导见 FIFO 深度设计;跨时钟域的缓冲见 异步 FIFO 与 CDC;包级缓冲之后接 DMA 的用法见 AXI4-Stream DMA 散列。
总结
AXI-Stream 包缓冲只有两种语义:store-and-forward 等 TLAST 收齐整包再发,靠「提交指针/工作写指针」双指针实现坏包回收,深度硬约束是最大包长加握手在途拍;cut-through 非空即发、延迟是常数,FIFO 只需吸收速率差与寄存切片往返,但遇到坏包作废、包长决策、不间断下游和多流交织时必须退回整包模式。