3、FIFO深度与位宽设计:如何根据系统吞吐量计算FIFO深度,位宽与SPI数据长度的匹配

好,咱们今天聊点实在的。FIFO深度和位宽怎么定?这问题我当年刚入行时也头疼过。说白了,FIFO就是个缓冲池,池子挖多大、水管用多粗,得看你上游来水多快、下游用水多急。

3.1 位宽设计:别让数据“卡脖子”

先讲位宽。SPI的数据长度通常是8位、16位、32位,甚至更多。FIFO的位宽怎么选?我个人习惯是:FIFO位宽 = SPI数据帧长度。

举个例子。你设计一个SPI控制器,支持8位、16位、32位可配。那FIFO位宽直接定32位。为什么?因为32位能兼容所有情况。8位数据存进去,只用了低8位,高位填0就行。16位同理。这样设计,你后续改配置时不用动FIFO。

核心原则:FIFO位宽 ≥ 最大SPI数据帧长度。宁可宽一点,别让数据“卡脖子”。

我在项目中遇到过一件事。有个同事把FIFO位宽设成8位,结果后来需求改成16位SPI。整个FIFO得重做,时序也得调,折腾了两周。你想想看,一开始多花10分钟考虑兼容性,后面能省多少事。

3.2 深度计算:吞吐量才是硬道理

深度怎么算?这得看系统吞吐量。我一般用这个公式:

FIFO深度 = (写入速率 - 读出速率) × 最大突发时间 / 数据宽度

嗯,公式看着简单,但实际用起来有几个坑。我拆开讲。

3.2.1 先搞清楚读写速率

写入速率:SPI时钟频率 × 每时钟传输位数。比如SPI时钟50MHz,双线模式(Dual SPI),每时钟传2位。那写入速率就是100Mbps。

读出速率:系统总线(比如AHB)的吞吐量。假设AHB时钟100MHz,32位总线,每时钟传一个数据。那读出速率就是100Mbps × 32 = 3.2Gbps。等等,这比写入快多了?对,所以很多时候FIFO深度不需要很大。

小技巧:实际项目中,读出速率往往远大于写入速率。这时候FIFO深度主要由“写入端最大突发长度”决定。我一般取2倍突发长度作为安全值。

3.2.2 最大突发时间怎么估

这个最容易被忽略。我曾经吃过亏——算深度时只算了稳态,没考虑系统总线的仲裁延迟。

举个例子。SPI连续发64个32位数据,每个数据间隔8个SPI时钟。那最大突发时间就是:64 × 8 × SPI时钟周期。假设SPI时钟50MHz,周期20ns。那突发时间 = 64 × 8 × 20ns = 10.24μs。

在这10.24μs内,如果系统总线被更高优先级的事务占用了,FIFO就得扛住。所以深度至少得能存下这64个数据。

3.3 实战案例:一个典型的SPI FIFO设计

好,咱们看个完整的例子。假设:

  • SPI时钟:50MHz
  • 数据帧:32位
  • SPI模式:Standard SPI(每时钟1位)
  • 系统总线:AHB,100MHz,32位
  • 最大突发:连续传输128个数据

计算过程:

  1. 写入速率:50MHz × 1位 = 50Mbps。换算成32位字:50Mbps / 32 = 1.5625M words/s
  2. 读出速率:100MHz × 32位 = 3.2Gbps。换算成32位字:100M words/s
  3. 深度需求:128个数据(突发长度) + 安全余量(20%)= 154个。取2的幂次方:256

最终参数:FIFO深度256,位宽32位。够用,还有余量。

3.4 避坑指南:我踩过的三个坑

坑一:深度取太小,导致溢出。 我曾经设计一个SPI从机,FIFO深度只取了16。结果主机一发就是64个数据,直接溢出。后来改成64才解决。记住:深度至少大于最大突发长度。

坑二:位宽不匹配,浪费带宽。 有个项目,SPI数据是8位,FIFO位宽却用了32位。每次读写都要做位宽转换,浪费了3/4的存储空间。其实用8位FIFO就够了,或者用32位但做好打包逻辑。

坑三:没考虑异步时钟域。 SPI时钟和系统总线时钟通常是异步的。FIFO必须用双端口、异步设计。我见过有人直接用单端口FIFO,结果跨时钟域采样出错,数据全乱套。

警告:跨时钟域FIFO一定要做格雷码同步,或者直接用成熟的异步FIFO IP。别自己手搓同步逻辑,容易出bug。

3.5 知识体系图

下面这张图总结了FIFO深度和位宽设计的核心逻辑。你看一眼就能明白整个决策流程。

FIFO深度与位宽设计决策流程 系统需求输入 位宽 = max(SPI数据帧长度) 兼容8/16/32位,取最大值 深度计算:考虑三个因素 ① 写入速率 (SPI时钟 × 位/时钟) ② 读出速率 (系统总线吞吐量) ③ 最大突发长度 (连续传输数据量) 输出:深度(2^n) + 位宽 注意:深度取2的幂次方,方便地址译码

3.6 总结一下

FIFO深度和位宽设计,说白了就是三个字:够用、兼容、留余量。位宽往大了选,深度按突发长度算,再留20%的安全余量。异步时钟域别忘了做同步处理。

嗯,这些经验都是我用实际项目换来的。你按这个思路走,基本不会出大问题。

最后一个小建议:设计完成后,用仿真跑一下最坏情况——连续最大突发、总线被抢占、时钟抖动。看看FIFO会不会溢出或空读。这一步能帮你发现90%的问题。