FIFO 深度计算与设计:突发长度、读写速率与水位标志
更新时间:2026-09-05。本文是「集成电路大类 · 芯片设计」**进阶层(intermediate)**第三篇新正文(#05)。入门层 三个最常见基础模块 讲了 FIFO 是什么,这一篇回答工程师真正要算的题:这个 FIFO 做多深才不丢数据、又不浪费 SRAM。下一篇的 valid/ready 握手 与本篇是配套的一对。
本文要回答的问题
- FIFO 内部到底是什么结构?空和满怎么判断,为什么指针要多 1 bit?
- 给定突发长度和读写速率,深度怎么算?
- almost full / almost empty 水位标志解决什么问题?
- 异步 FIFO 为什么非用 Gray 码不可?
一、FIFO 结构:环形缓冲 + 两个指针
FIFO 本质是一块 RAM 加两个只会加 1 的指针:写指针 wptr 指向下一个写入位置,读指针 rptr 指向下一个读出位置。写到末尾绕回头部,形成环。数据在环里排队,写推进 wptr,读推进 rptr。
- 空(empty):wptr == rptr(两个指针重合,没有存货);
- 满(full):wptr 又追上了 rptr——但「追上」时指针值也相等!无法区分空和满。
经典解法:指针位宽比地址多 1 bit(深度 2ⁿ 用 n+1 bit 指针)。最高位当「绕圈标志」:指针全等(含高位)→ 空;高 bit 相反、低位全等 → 满(套了一圈)。不要用「计数器加减」做空满判断——同步 FIFO 里计数器勉强可行,异步 FIFO 里计数器本身就是跨时钟域多 bit 信号,必错。
二、深度推导:一道必须会算的工程题
FIFO 的作用是吸收「写速率」和「读速率」在突发窗口内的不匹配。通用思路:最坏情况下,写端一口气灌进来的数据量,减去同一时间窗口内读端最多能取走的数据量,就是 FIFO 必须装下的量。
设:连续突发写入 B 个数据(写时钟周期计)、写时钟频率 fw、读时钟频率 fr、读端从突发开始到真正开始取数允许延迟 L 个写周期(仲裁、响应、软件调度的滞后)。
深度 D ≥ B − (B − L) × (fr / fw),结果向上取整,再圆整到 2 的幂(RAM 深度天然是 2ⁿ)。
标准例题:写端每 100 拍连续发 100 个数据(100% 占空比突发),fw=80MHz,fr=50MHz,读端最坏 10 个写周期后才开始读。
| 项目 | 计算 | 值 |
|---|---|---|
| 写 100 个数据耗时 | 100 个写周期 | 100 |
| 读端开始读前的窗口 | 前 10 个写周期一个读走 | 灌进 10 |
| 开始读后剩余窗口 | 90 写周期 × (50/80) | 读走 56.25 |
| 峰值积压 | 100 − 56.25 = 43.75 → 44 | D ≥ 44 |
| 圆整到 2 的幂 | 32 < 44 < 64 | 取 64 深 |
工程上还要再加余量:时钟抖动、读端也有突发停顿、水位标志本身有反应延迟(见下节),所以「算出 44 取 64」不是浪费是规矩。反过来若长期平均写速率 ≥ 平均读速率,任何深度的 FIFO 都救不了——FIFO 只吸收短期突发,不改变吞吐平衡,那是架构问题,得加通道或降速。
三、almost 水位:反压要留提前量
满标志拉高时 FIFO 已经一个都塞不进了,可上游从看到 full 到真正停笔还隔着组合逻辑/同步延迟——这几拍照写,数据直接 overflow 丢弃。解法是 almost full / prog_full(可编程水位):FIFO 剩 α 个空位时就拉高「快满」,上游收到立刻停写,α 取「反应链路延迟拍数」。对称地 almost empty / prog_empty 提示下游「快断粮」,让读端/DMA 提前发起续传,避免 underflow 读出气泡。α 就是把链路延迟(跨时钟还要算两级同步的 2 拍)折算成深度余量,和第二节的圆整余量合并考虑。
四、同步 vs 异步 FIFO
| 同步 FIFO | 异步 FIFO | |
|---|---|---|
| 读写时钟 | 同一个 | 两个独立时钟域 |
| 指针 | 二进制计数器直接比 | Gray 码 + 两级同步器后再比 |
| 用途 | 同域速率匹配、位宽转换 | CDC 数据通道(见 CDC 基础 #10) |
| 深度 | 寄存器或 SRAM | 通常 SRAM |
异步 FIFO 的核心机关:wptr 在写时钟域是二进制,转成 Gray 码(相邻值只翻 1 bit)后送到读时钟域打两拍同步——多 bit 跨时钟若同时跳变,采样端可能采到任意中间值,Gray 码保证即使采偏也只是「旧值或新值」之一,不会是错乱地址。rptr 反向同理。空满各自在自己的时钟域里判断。完整推导是进阶 #11 异步 FIFO 专篇的内容,这里记住结论:异步 FIFO 不要自己发明写法,用经过验证的模板/IP,Gray 指针和同步器一处都不能省。
五、通用公式与更多算例:把「启动延迟」和「占空比」分开
第二节的公式可以拆成更清楚的两项,便于套不同场景。任何 FIFO 的最小深度都来自这个平衡:
D_min =(突发窗口内写入总数)−(同一窗口内被读走总数)
把它按「读端什么时候开始读」分两段:
D_min = L (启动延迟 L 个写周期内读端一个没取,纯积压 L 个)
+ (B − L)·(1 − fr/fw) (开始读后,每写周期净积压 1 − fr/fw 个)
= B − (B − L)·(fr/fw)两个直觉一定要立住:①前 L 拍是纯积压——读端再快也救不了启动延迟期涌入的数据,这部分必须 FIFO 硬扛,所以「响应延迟」往往比「速率差」更吃深度;②若读写等速且读端立即开始(fr=fw、L=0),理论深度可以很小,FIFO 只用来吸收相位差和反压抖动。
算例二(写快读慢、无延迟,看纯速率差):写 100 个数据,fw=100MHz、fr=80MHz,读端立刻开始读。D ≥ 100 − 100×0.8 = 20,圆整取 32。可见即使读端毫不拖延,20% 的速率差也会积压 20 个。
算例三(等速但有突发停顿):读写同为 100MHz,但读端是仲裁总线的从设备,最坏要停 30 拍才能恢复取数,而写端连续突发 100 个。等速意味着一旦读端在跑就能跟上,FIFO 只需吸收那 30 拍停顿的积压:D ≥ 30,圆整取 32(再叠加水位 α)。这种「平均速率够、但会打嗝」的场景在总线/DMA 系统里比纯速率不匹配更常见。
反向校验(长期可承载性):算出负数或极小值时别高兴太早,要再检查长期平均——只要长期平均写速率 > 长期平均读速率,任何有限深度最终都会溢出,FIFO 只改变溢出的时间。必须保证在一个足够长的统计窗口(含所有突发和停顿)里平均读 ≥ 平均写,深度计算才有意义。
异步 FIFO 的拍数口径:读写两个时钟时,把所有量统一折算到同一侧时钟周期再代入公式(例题就是统一折算成写周期),并额外加 2~4 拍的两级同步延迟余量(水位跨域也走两拍),保守工程经验是「算出值圆整到 2 的幂后,再留约 20%~30% 余量」。
六、容易栽的细节
- SRAM 读延迟:大 FIFO 用 SRAM 编译实例(见 片上存储器),读出有 1 拍延迟,空标志要把「已发出读地址但数据未出」的在途量算进去,否则会 underflow;
- 复位只复位指针和标志,不复位 RAM 内容(数据无意义,清 SRAM 白烧功耗);
- 写满还写 / 读空还读要在设计上保证不发生(靠握手和水位),仿真里加断言抓 overflow/underflow;
- 位宽转换(写 32 位、读 8 位)时深度按窄端口一侧的条目数算。
相关与延伸
上一篇:状态机设计。下一篇:valid/ready 握手:反压、气泡与 Skid Buffer——FIFO 的满空标志驱动的就是握手反压。入门铺垫:FIFO、仲裁器、握手、中断与 DMA;跨时钟细节见进阶 #10 CDC 基础、#11 异步 FIFO;存储实例见进阶 #25 Memory Compiler。
总结
FIFO 深度按最坏突发算:突发写入量减去读端在窗口内最多取走量(含启动延迟),向上圆整到 2 的幂再留水位余量;空满靠多 1 bit 的绕圈指针判断,异步 FIFO 必须 Gray 码指针加两级同步,almost 水位给反压留足反应拍数。