☰
FIFO存储选型指南:寄存器、SRAM还是DDR?
2026/10/20 8:30:48 网站建设 项目流程

前阵子帮某数据采集项目做方案评审,链路里有一段跨时钟域的数据缓存,同事随手写了一个深度4096的FIFO。问为什么是4096,答曰“习惯了,反正BRAM够用”。结果资源评估下来,寄存器版本要吃掉几千个触发器,BRAM版本又把本来紧张的存储块占掉一块。这个场景我遇到太多次了——FIFO的存储介质选型,从来不是“哪块大用哪块”,而是由你的带宽差、突发长度、时钟关系和读侧抖动需求共同决定的。

这篇内容不打算做泛泛的三种存储对比,而是直接回答一个实际问题:手上这个FIFO,到底该用寄存器阵列、SRAM,还是干脆把DDR拉进来当大FIFO用?适合正在做FPGA、ASIC前端设计、或者刚接手数据通路方案的工程师参考,尤其是那种“深度和位宽看着都合理,但资源就是吃紧”的情况。

1. 选型前先回答的三个问题

1.1 FIFO缓存的是“欠账”,不是全部数据

很多人对FIFO深度有一个误解:数据量大,深度就要大。这个思路在误判现场非常常见。真正做事的时候,FIFO只负责吸收速率不匹配带来的“欠账”,而不是把所有经过的数据都装进去。

举个直白例子,写端持续写入,读端偶尔暂停,那FIFO里积压的只是“写端多写的那部分”。如果写端也暂停、读端也暂停,两个端口的平均吞吐率是一样的,FIFO深度再大也派不上用场,反而浪费存储资源。所以选型第一件事,是先算清楚“瞬时写入速率 − 瞬时读出速率”在极端工况下能积压多少,这才是容量下限。

1.2 选型前必须厘清的三个问题

我现在做FIFO存储选型,都会先拉着需求方把这几个问题定下来,否则后续全是白忙:

  1. 这个FIFO的位宽和深度是确定的吗?如果位宽后面要翻倍,寄存器阵列的面积会很难看,SRAM的规划也要重做。

  2. 读写时钟是否同源?同步FIFO和异步FIFO的存储要求差别很大。异步FIFO通常在指针同步上要吃更多逻辑,寄存器阵列和SRAM实现各有讲究。

  3. 读侧是“稳定连续读”还是“读一下停一下”?连续读代表你需要保证不欠料,停一下读则给了你用DDR这类大容量慢速存储的空间。

这三个问题的答案,直接决定了你是往寄存器方向走,还是往SRAM方向走,还是需要往上挂DDR。别急着搜IP核,先把需求盘清楚。

2. 三种存储介质的物理底牌到底有什么不同

2.1 寄存器阵列:每个字都有自己的“单间”

寄存器FIFO的本质,是一组带写使能的触发器阵列加上读写指针。每个数据字都独占一组触发器,读的时候可以直接把数据拉出来,也可以再做一级寄存。

这种结构的优势是快。读写路径短,深度小的时候组合逻辑简单,频率可以做到很高。很多高速接口里的同步FIFO,深度8到16就够,完全没必要用SRAM,寄存器直接堆就完了。

但它的劣势也很明显:面积大。一个字16bit,做深度32,也就512个触发器;做深度256,就是4096个触发器,连同读写译码逻辑,资源蹭蹭往上涨。在FPGA里表现为寄存器资源爆炸,在ASIC里表现为面积和功耗都压不住。

所以寄存器FIFO的适用边界非常明确:小深度、高位宽和极短延迟需求的同步场景。一旦深度超过几十,就要考虑换存储介质了。

2.2 SRAM:共用译码器的“公寓”

SRAM和寄存器最大的区别在于集中管理。数据存在存储阵列里,读写时通过地址译码去找到对应的存储单元,而不是每个字都准备一套逻辑。

在FPGA里,SRAM通常以BRAM的形式存在,几十Kbit一块,配置灵活,可以拼位宽、拼深度。在ASIC里,一般直接调用存储编译器生成的单端口、伪双端口或真双端口SRAM。

SRAM的密度远高于寄存器,深度做到几千上万都很轻松,功耗也比寄存器阵列低。代价是有延迟。同步RAM读写至少各占一个时钟周期,而且无法像寄存器那样在不额外打拍的情况下做组合读。对于普通FIFO场景,这点延迟完全不是问题,但对于延迟敏感的数据通路,就得算清楚。

### 2.3 DDR:远程仓库,容量大但路远 当FIFO需要的深度超过几十MB甚至上GB,SRAM也装不下了,就只能往DDR方向想。DDR容量确实大,一颗DDR4颗粒动辄几GB,带宽也够看,但它最大的问题是访问延迟和访问粒度。 DDR读一个数据,不是发一个地址下一拍就能拿回来,而是要经历行激活、列选、预取、返回这一整套流程。随机单笔读的延迟高达几十ns,而且吞吐率会随着地址跳变急剧下降。如果我想让它像传统FIFO那样“每个时钟周期读出一个数据”,除非访问模式完全是预取对齐的突发流,否则性能会惨不忍睹。 这也是为什么DDR参与FIFO方案时,一定要配合小容量的SRAM或寄存器做两级缓冲。用DDR做仓库,用SRAM做门口的中转缓冲区,每次从中转区取一批数据,而不是一脚踩进DDR里逐拍取数。 三者的本质差异,其实可以理解为储量、通勤时间和住址的关系。寄存器FIFO是住公司隔壁,随叫随到,但房间总数有限;SRAM是一栋正常公寓,地址访问道路通畅,房间数量大一些;DDR是城市边上的大型仓储中心,空间管够,但每次往返都得花时间。 ## 3. FIFO容量估算:一个能直接套用的公式 ### 3.1 公式推导 无论最终选择哪种存储介质,第一步都是估算理论深度。最常用的估算思路是:假设写端以速率 W 持续突发写入 B 字节,读端以速率 R 持续读出,其中 W > R,那么FIFO最终会积压多少数据? FIFO的积压深度可以表达为: `FIFO最小深度 = 突发写入总字节数 × (1 - 读速率 / 写速率)` 这个公式的逻辑是这样的:突发期间,读端一直在工作,所以它已经消化了一部分写入数据,剩下没消化掉的才需要FIFO存下来。如果读端速率等于写端速率,那么积压为零,FIFO只需要应对跳变瞬间的几拍储水。如果读端速率是写端一半,那么写多少就有一半积压在FIFO里。B 就是写端在突发期间总共写入的字节数。 有的文档会把突发长度定义为“写端连续写多少拍”,那就需要乘以每拍字节数再代进公式。 ### 3.2 手算一个真实配置 举个例子。某系统写端位宽128bit,时钟250MHz,一次突发连续写2微秒。读端位宽64bit,时钟200MHz,读操作是连续不断的。 写端带宽 W = 128bit / 8 × 250MHz = 4000MB/s = 4GB/s。 读端带宽 R = 64bit / 8 × 200MHz = 1600MB/s = 1.6GB/s。 突发期间写入总字节数 B = 4GB/s × 2us = 8000B。 代入公式:FIFO最小深度 = 8000 × (1 - 1.6/4) = 8000 × 0.6 = 4800B。 如果FIFO位宽按64bit算,深度就是 4800B / 8B = 600 个word。这个容量放在寄存器阵列里是怎么都不省油的,但对SRAM来说完全不是事,选一个1K×64bit配置就够。 有些人会把这个配置直接放到DDR里做,理由是DDR容量大。但实际完全没必要,600个word的FIFO用DDR,等于开卡车送对面楼的快递,延迟反而把人折腾死。所以算完容量之后,再决定存储介质,顺序不能反。 ### 3.3 容量估算里容易漏的隐藏项 公式算出来的是“理论最小值”,工程上还要在几个地方加余量。 1. 读端若存在仲裁等待,比如多个主设备分时访问下游总线,那么读端实际有效速率要按最差情况算,而不是按理想带宽算。 2. 异步FIFO场景下,由于读写指针同步需要几个周期,全满信号是滞后的,设计的深度往往要在理论值基础上再乘一个系数。 3. DDR方案中,如果加了小FIFO做预取,那小FIFO深度要根据DDR突发读的延迟来定,通常至少要把DDR读请求的往返延迟覆盖住。 以上任何一个被忽略,都会直接导致实测跑流量时FIFO溢出。 ## 4. 寄存器、SRAM、DDR方案的分水岭在哪 ### 4.1 一个可以当起点的判断表 根据我在多个项目里的实际工程经验,下面这个分水岭可以作为选型起点,但绝对不要当标准答案。具体边界会因为工艺、时序要求和读写模式不同而偏移。 | 场景特征 | 推荐方向 | 原因 | | --- | --- | --- | | 深度4~16、延迟要求极高 | 寄存器阵列 | 读写路径最短,不需要额外打拍,面积可控 | | 深度32~8K、读写模式接近流式 | 片上SRAM | 容量充足,面积适中,支持伪双端口读写 | | 深度几十K以上、但单次突发仍可预测 | 大块SRAM或SRAM组合 | 片上能塞下,就不建议碰DDR | | 深度超片上存储总量、允许较大延迟 | DDR + 小FIFO两级架构 | 只DDR无法逐拍随机读,必须靠小缓冲补偿 | 这个表有个核心逻辑:能用片上存储解决的,绝不动DDR;能用SRAM解决的,绝不动寄存器。不是因为DDR不好,而是因为DDR的随机访问延迟会让下游逻辑的时序变得不可预期。 ### 4.2 面积、功耗、时序三个维度的实测体会 在FPGA里,同样一个深度512、位宽64bit的FIFO,寄存器实现会消耗接近三个完整的逻辑阵列切片,而BRAM实现可能只占一两块存储块,布局布线压力小得多。时序方面,寄存器FIFO在小深度时主频可以做得比SRAM版本更高,但在大深度时反而会因为大规模译码和长走线把关键路径拖垮。 功耗差异更明显。寄存器阵列的每个时钟沿都在翻转大量触发器,尤其在数据变化频繁的场景下,动态功耗成倍上涨。SRAM的功耗则相对稳定,读功耗和写功耗只与访问操作挂钩,空闲时不耗电。DDR的功耗大头则在控制器和PHY的持续功耗上,如果只是偶尔写几笔数据,用DDR就是拿高静态功耗换大容量,非常不划算。 ### 4.3 SRAM选型:单端口、伪双端口还是真双端口 FIFO场景最常用的是伪双端口SRAM,一个写端口、一个读端口,读写可以同时进行。单端口SRAM读写共用一套总线,FIFO读写并发时会打架,除非做二分频交替访问,否则不推荐直接用。真双端口适合两个端口都要读写的场景,但面积更大,成本更高,普通FIFO用不到。 在FPGA里,BRAM通常可以直接配置成“简单双口模式”,等效伪双端口,做FIFO很顺手。在ASIC里一定要跟存储编译器确认好波特率。如果读写端时钟频率相差很大,确认是否支持异频读写,否则还要额外做跨时钟处理。 ## 5. DDR当FIFO用:能行,但绝不是直接挂上就行 ### 5.1 直接挂DDR的翻车现场 我见过不止一个方案,把DDR控制器读端口直接接到下游数据模块,期望它像一个超大FIFO一样工作。等到实测带宽只剩理论值的零头,才发现问题出在随机访问上。 DDR的优势是大带宽突发传输,劣势是单笔随机访问延迟高。假如一个FIFO的读端每拍都要去DDR里取数,读请求发出后,几十ns内数据回不来,下游就会断流。再加上读写地址来回跳动,bank冲突和频繁的预充电会让效率进一步恶化。 所以“DDR模拟FIFO”的本质,是把DDR当作数据仓库,而不是当作可以直接逐拍读写的FIFO。它解决的问题是大容量缓存,小容量的实时缓冲仍然要靠寄存器或SRAM来兜底。 ### 5.2 环形缓冲配合两级缓存 比较可行的DDR版大FIFO方案,是在DDR地址空间里划一块固定的环形缓冲区。写指针和读指针各自在环上递增,写到尾部就绕回头部。环形缓冲区和普通FIFO的逻辑几乎一致,区别只是存储体在DDR里,指针更新和满空判断由自己实现。 但为了让DDR的带宽不吃亏,读写两侧都要做突发聚合。写侧先在SRAM里攒够一整段数据,再以长突发方式写入DDR;读侧则以突发方式从DDR里读出较长的一批数据,放到一个小FIFO里,再按下游需求的速率逐拍吐出。 这样整个数据通路的时序观感,就是一个小FIFO喂给下游,而不是DDR直接面对下游。DDR的延迟被预取机制隐藏,带宽被长突发打满,FIFO的满空判断则基于环形缓冲区的读写指针差值来算。 ### 5.3 异步时钟域下的特殊考量 如果这个“大FIFO”还是异步FIFO,跨时钟域同步的问题就要格外注意。DDR控制器本身有它的时钟域,如果读写指针都在控制器时钟域里更新,那下游数据模块需要正确同步“空满状态”。通常要先把指针转换成格雷码,再跨时钟域采样,最后比较状态。 这个方案的复杂度比纯SRAM异步FIFO高不少。如果同步FIFO能做到,尽量不碰这个组合。只有当容量需求大到片上存储解决不了,才值得付出这么多额外的验证成本。 ## 6. 几个常常被问到的选型细节与验证方法 ### 6.1 深度单位为什么是word而不是字节 很多刚接触FIFO的人会问,为什么大家说FIFO深度都是“512×64”而不是多少KB。因为FIFO深度是按“数据字”数来定义的,而每个字的位宽取决于数据通路。同样一个512深的FIFO,16bit位宽和256bit位宽,物理面积天差地别,所以单说深度没有意义,必须同时给出位宽。SRAM选型时,存储编译器也是按“多少个字,每个字多少位”来配置的。 ### 6.2 异步FIFO可以用DDR吗 可以,但要先确认三个前提。第一,你对空满判断延迟不敏感,能够容忍指针跨时钟域同步带来的额外延迟;第二,下游能够接受DDR随机访问造成的不确定读取延迟;第三,你已经用片上SRAM做了一个足够深的预取缓冲。如果这三个条件任何一个不满足,我都建议先回头重新审视容量估算。 ### 6.3 如何验证我选的深度够不够 最简单有效的方法,是拿最恶劣工况做仿真。模拟写端全速突发写入,读端按最慢预期速度读取,持续跑数十万个时钟周期,观察FIFO满信号是否长时间拉高、数据是否溢出。如果满信号每次都在写入突发结束前拉高,说明深度不足;如果满信号几乎从不出现,可以尝试压缩深度,节省面积。 在DDR方案里,仿真时还要额外给DDR控制器加随机延迟模型,不能用一个固定延迟的简化模型去验证。很多问题都是因为仿真模型太“乖”而漏掉,上板以后才爆发。 ### 6.4 位宽是不是越宽越好 不是。位宽越宽,SRAM面积越大,寄存器方案消耗的存储单元越多,DDR方案每次突发占用的带宽也越多。位宽应该由上下游接口自然决定,而不是为了“好看”去加宽。FIFO如果只是为了匹配接口位宽变化,宽度转换是它的功能之一,但你每加宽一位,整个存储阵列的代价都在线性增长。 按照现在大多数项目的路径,真正先算欠账、再定深度、最后选介质的流程仍然走得不够规范。很多性能问题看起来是时序收敛不了,根子上其实是存储选型从一开始就选错了。把这三个层次理清楚,很多数据通路问题会变得意外地简单。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询