大容量 SRAM 到底挂什么总线,这个选择题在很多项目里其实是被“顺手”决定的:以前几十 KB 的存储挂在 AHB 上跑得好好的,新的芯片容量一上来,也照着老样子接。结果等 CPU、DMA、显示控制器、加密引擎几个模块同时抢一块存储的时候,总线带宽的瓶颈、仲裁的麻烦、低功耗的失控会一次性全冒出来。我在这类问题上反复折腾过几轮,今天就把接口选型、带宽计算、仲裁行为和低功耗优化这几条线串起来讲清楚,希望对正在做大容量缓存、帧缓冲或者报文缓冲的朋友有直接帮助。
1. 存储体上了 MB 级之后,总线的“存在感”才开始暴露
1.1 大容量 SRAM 的典型使用场景与压力来源
先明确一下“大容量”在这个语境下的含义。我通常把 SoC 内部单块超过 512 KB 的 SRAM 称为大容量存储体,很多设计中会是 1 MB、2 MB 甚至更大,用做 CPU 的紧耦合缓存、显示控制器的帧缓冲、网络报文的收发缓冲、AI 加速器的中间结果存储等。
这个容量级别有个共同特征:它不再只是“给某个模块自己用”的私有存储,而是会被多个请求源共享。CPU 要往里面写数据,DMA 要从里面搬数据,显示控制器要周期性地读取刷新,外设协处理器也可能插进来。存储体一旦被共享,总线接口就必须同时承接多种流量特征:CPU 的随机突发访问、DMA 的长块搬运、显示控制器的稳定读突发。
如果只有一个访问源,那总线选择几乎无所谓,AHB 甚至 APB 都能工作。但多个请求源共享同一块存储时,总线本身就成了系统级瓶颈。总线协议决定了事务如何发起、如何排队、如何返回响应,也就决定了在并发场景下存储能被压榨出多少有效带宽。
1.2 为什么 APB 直接被排除,AHB 又差在哪里
APB 协议的设计目标是低复杂度、低功耗外设通路。它的每次访问至少需要两个周期,而且不支持突发传输,更没有多主仲裁机制。把大容量 SRAM 挂在 APB 上,相当于让一辆自行车去拉集装箱,带宽完全不在一个量级。APB 只适合寄存器类配置接口,这个基本不用讨论。
AHB 比 APB 强很多,支持流水线、支持不定长突发,也有多主仲裁。很多工程师对 AHB 有感情是因为它在中小容量场景下确实“够用而且好调试”。但 AHB 的根本限制在于:总线上同时只存在一个事务。
AHB 有单独的地址阶段和数据阶段,支持 flow transfer,但多主共享时仍需一个总线 master 占用整个事务周期。我有一个实际经历:在 100 MHz 频率、32 位数据位宽的 AHB 总线上挂 1 MB SRAM,单主读写时效率尚可,一旦 DMA 开始连续搬运数据,CPU 想插入一个写事务,就必须等 DMA 当前 burst 结束或者仲裁器允许抢占。DMA 如果一直发起长 burst,CPU 的等待延迟就会直线上升,极端情况下产生类似“总线饥饿”的观感。
AXI 从设计之初就是为了解决这类并发问题而生的,它的读写通道分离、多笔未完成事务、独立地址和数据阶段,让多个 master 不再必须严格串行占用总线。这也是为什么大容量 SRAM 几乎都选择 AXI 的根本原因:存储体越大,并发访问越频繁,AHB 的串行模型就越撑不住。
2. 带宽账不能只看峰值:AXI 是怎么把有效带宽拉高的
2.1 峰值带宽和有效带宽之间差了一个“流水线利用率”
很多项目选接口只看一个数字:频率乘上数据位宽。比如 250 MHz、128 位数据位宽,峰值带宽就是:
250 MHz × 16 Bytes = 4000 MB/s,也就是 4 GB/s。
这个数字算起来很爽,但它不代表你能实际拿到 4 GB/s。AHB 在这种配置下达不到理论值,AXI 同样达不到,区别在于两者分别能到百分之几十。
有效带宽损耗主要来自三部分:事务切换时的空闲周期、地址阶段的竞争、写响应和读数据的返回等待。AHB 在地址阶段和数据阶段之间虽然做了流水,但多个 master 切换时,总线需要插入空闲周期来避免数据碰撞和仲裁冲突,事务排队延迟会进一步拉低吞吐。特别是读写混合场景,AHB 读转写、写转读的切换损耗非常明显。
AXI 把总线拆成五个独立通道:读地址、读数据、写地址、写数据、写响应。通道各自独立握手,读事务和写事务可以在总线上并行推进,一个 master 的读返回数据时,另一个 master 的写数据可能正在总线上传输。这种物理通道上的并行性,是 AXI 有效带宽高于 AHB 的第一层原因。
2.2 Burst 设计对存储效率的影响远超绝大多数人的估算
带宽模型里真正拉开差距的第二个点是 burst 传输。AHB 也支持 burst,但 AXI 在 burst 上做了更细的规则,尤其是 AXI4 里 INCR 类型突发长度可以扩展到 256 beats,而 AXI3 是 16 beats 上限。
举个例子,CPU 往 SRAM 中写一个 cache line,常见 64 字节。如果接口数据位宽是 128 位,也就是 16 字节,64 字节需要 4 个 data beats。AXI 发起一笔 WRAP4 或 INCR4 突发,只需要发送一次地址,然后连续 4 拍传数据。如果不支持突发,每个数据都要发一次地址,那么地址总线本身会成为瓶颈。
实际项目中我计算过一个简单模型:假设 128 位数据位宽,地址周期和数据周期等长,写 16 拍数据。不使用 burst 时,需要 16 个地址周期加 16 个数据周期,总线占用 32 个周期;使用 INCR16 突发时,1 个地址周期加 16 个数据周期,总线占用 17 个周期。地址开销占比从 50% 降到 5.9%。对于大容量 SRAM 的高带宽访问,这个差距直接决定系统能跑到的有效带宽上限。
AXI 的 burst 还支持 FIXED 类型,这对 FIFO 型存储或者寄存器型多端口 RAM 特别有用,因为地址保持不变,每次都访问同一位置。用 SRAM 实现大 FIFO 时,FIXED burst 能显著降低控制器内部地址加减逻辑的功耗和翻转次数。
2.3 Outstanding 事务:用地址先行掩盖存储延迟
AXI 还有一个 AHB 很难做到的特性:outstanding 事务。允许主设备连续发出多笔地址请求,而不必等待前一笔数据完整返回。
这对大容量 SRAM 极其重要,因为 SRAM 读延迟通常有 2 到 4 个周期。如果每笔读都要等到数据回来再发下一笔地址,流水线就空了。AXI 主设备可以把 8 笔甚至 16 笔读地址一次性发出去,SRAM 控制器内部做好排队和重排序,数据返回时按序送回即可。这样读连续数据时,除了第一笔的初始延迟,后面的数据可以连续每周期返回一拍,真正达到一个时钟一个数据的满吞吐状态。
所以我的经验是:评估 AXI 接口的带宽潜力时,不要只盯着频率和位宽,还要把 burst 长度、outstanding 深度、读写通道并行、地址和数据通道的分离度一起算进去。这四者协同,才决定系统能不能逼近峰值。在很多项目里,同样的 SRAM 频率和位宽,从 AHB 迁移到 AXI 之后,有效吞吐提升 30% 到 60% 是完全可以实现的。
3. 仲裁器设计:大容量 SRAM 并发访问的核心矛盾
3.1 单端口 SRAM 搭配外部仲裁器是最务实的方案
先讲一个选型现实:双端口 SRAM 确实存在,有些场景下也很有用,但它面积代价大、功耗高,而且两个端口的访问效率并不完全独立。大多数大容量 SRAM 方案仍然用单端口存储体加上外部仲裁器来做并发控制。
仲裁器做在哪一层,往往决定了系统的性能上限。一种常见方案是直接做一个 AXI 互联或者 AXI 转 SRAM 控制器,由它把多个 AXI master 的访问请求排队转发给 SRAM 控制器。另一种方案是设计一个多主端口的专用 SRAM 控制器,内部直接集成仲裁逻辑。后者的好处是可以在仲裁时结合 SRAM bank 的物理特性做调度,而不是先过一层 AXI 互联再做一次仲裁,增加不必要的延迟。
我用过一种比较典型的架构:三个 AXI master(CPU、DMA、显示控制器)接一个 AXI SRAM 控制器,控制器内部采用请求缓存加仲裁调度的方式,对外表现为一个 AXI slave 端口,对内是一个单端口 SRAM 控制器。仲裁并不在总线层面做,而是在控制器内部的请求队列层面做。这样总线层面已经完成了协议转换和通道分离,队列层面的仲裁反而是简单的优先级判断。
3.2 公平性、服务质量和最坏延迟的平衡
仲裁策略不外乎固定优先级、轮询、加权轮询、基于 QoS 的动态仲裁。但真正难的不是选哪种策略,而是弄清楚系统里谁最不能等。
固定优先级最容易实现,CPU 永远最高,DMA 第二,显示控制器第三。但固定优先级有个常见坑:如果 DMA 配置了连续大块搬运而且仲裁器没有对低优先级 master 设置最小带宽保障,显示控制器可能长时间拿不到总线,屏幕刷新来不及,图像撕裂或者闪烁。这种问题在仿真时极难发现,等集成到 FPGA 跑实时视频流时才暴露。
轮询仲裁能保证绝对公平,但它不能保证延迟上限。假设 CPU 访问延迟要求是 20 个周期,轮询到最坏情况可能要等好几个其他 master 各完成一笔长 burst,延迟超标。一个折中方案是使用加权轮询加优先级抢占的组合:DMA 这类高吞吐 master 分配高权重,CPU 这类低延迟敏感 master 配置紧急通道。AXI 协议本身支持 QoS 信号,就是给这种场景准备的,主设备可以标记自己这笔请求的服务等级,仲裁器根据 QoS 值动态调整调度权重。
我踩过一个非常具体的坑:给 CPU 配置了最高优先级,但 CPU 发出的是一笔很长的 INCR burst,其他 master 全部饿死。后来在仲裁器里加了一拍超时机制,低优先级请求等待超过某个阈值后强制提升优先级,总算把系统的最大访问延迟控制住了。
3.3 多 bank 设计与仲裁的联动
容量大了之后,SRAM 一般会拆成多个 bank。多 bank 的意义不只是物理布局方便,更重要的是仲裁器可以对不同 bank 的访问做并行调度。
仲裁器如果只看见一个存储体接口,那么任何时刻都只能服务一笔访问。但如果控制器知道 SRAM 内部有两个 bank,且两笔请求分别命中不同 bank,理论上可以背靠背连续服务,减少等待周期。AXI 地址通道里的低 bit 往往就是 bank 选择位,仲裁器可以在排队阶段提前判断请求间的 bank 冲突关系,把不冲突的请求穿插执行。
这个特性对提高随机访问场景下的有效带宽很有帮助。比如 CPU 发起的表项查询和多笔 DMA 搬运同时命中不同 bank 时,可以交错处理而不产生等待。我在一个网络报文处理项目里,就是把 1 MB SRAM 拆成四个 bank,仲裁器按照 bank 号做两级调度,整体吞吐比单 bank 方案提升了大约 25%。
4. 低功耗设计:AXI 并不等于省电,关键是“少占用总线”
4.1 功耗大头往往在数据翻转,而不是 SRAM 本身
低功耗经常会让人产生一个错觉:把 SRAM 换成低功耗工艺,或者让 SRAM 进入保持模式,问题就解决了。实际上在高速系统中,总线上的数据翻转功耗往往比存储体本身的动态功耗更可观。
计算公式是 P = αCV²f,其中 α 是翻转率,C 是节点电容,V 是电压,f 是时钟频率。AXI 总线如果位宽是 128 位或者 256 位,那么在同一拍里翻转的数据线数量极多,电容也大,动态功耗非常可观。
所以接口级低功耗的一个核心思路是:别让总线无缘无故地翻转,尽量用更少的时钟周期完成同样多的数据传输。这里恰恰是 AXI burst 和 outstanding 特性的优势所在。相比 AHB 的多地址周期,AXI 长 burst 用一次地址传输后续连续多拍数据,地址总线翻转次数大幅减少;数据总线在连续传输时虽然仍在翻转,但无效的等待周期少了,完成同样传输量需要的总翻转次数更低。
4.2 门控时钟、写响应通道和低功耗模式
接口级低功耗的具体招式有这么几个方向:
第一,时钟门控。SRAM 控制器内部最好把状态机划分成独立时钟域,总线空闲时彻底关掉读写地址通道的时钟。很多控制器只要地址通道上 VALID 信号一直拉低,内部逻辑就不会翻转,功耗自然降下来。
第二,写响应通道不是可有可无的。AXI 的写响应通道允许主设备在数据到达后被异步确认,不必像 AHB 那样等数据在写周期同步完成。这种异步性让主设备可以更早进入低功耗空闲状态,总线也能更快地让给其他事务。
第三,大容量 SRAM 的保持模式。系统进入休眠时,大块 SRAM 可以切换到低电压保持状态,但前提是整个 AXI 通道都处于无未完成事务的空闲状态。所以需要在低功耗控制逻辑里明确检查 AXI 的 outstanding 队列是否清空,再给 SRAM 阵列下发 standby 请求。队列里只要有遗留事务,SRAM 就不能安全休眠,否则数据在唤醒回读时会不一致。
我在一个 MCU 低功耗项目里碰到过这种情况:CPU 发出最后几笔写数据后立刻进入了睡眠,但 AXI 总线上还有一笔 outstanding 写请求没有完成。SRAM 控制器收到低功耗请求时如果不检查这个队列,直接在写数据还没落地时把 SRAM 切到保持模式,唤醒回来后数据丢失。后来在低功耗控制逻辑里加了一个写排空状态,确保所有 outstanding 写事务完成后才允许 SRAM 降功耗。
4.3 降低频率不如降低翻转效率,但别忘了带宽约束
低功耗场景下常见的调参手段是降频率,但降频率是有代价的。如果系统的有效带宽需求是固定的,降频率意味着总线忙碌时间变长,总翻转次数反而可能不降反升。更合理的思路是先提升单拍的传输效率,再降频率。
举个例子,同样是搬移 256 字节数据,如果用 AHB 加较短 burst,可能需要 300 个周期;用 AXI 长 burst 加足够 outstanding 深度,可能 200 个周期就完成了。剩下的 100 个周期整个总线可以完全空闲,时钟门控也能更早生效。这时候再把频率从 250 MHz 降到 200 MHz,只要保证 200 个周期×低频率仍然能满足带宽需求,总功耗下降效果比单纯降频好得多。
所以我在设计大容量 SRAM 的 AXI 接口时,一般先固定带宽需求,倒推需要的有效传输周期数,再考虑低功耗模式需要的空闲窗口。逻辑顺序一定不能反过来。
5. 落地选型:哪些场景可以继续停在 AHB,哪些非 AXI 不可
5.1 一张表理清接口选型
很多工程师问我一个问题:那我是不是所有设计都应该把 SRAM 接到 AXI 上?不是。AXI 接口面积更大、协议更复杂、调试成本更高,中小场景下并不划算。下面这个表是我几次流片和 FPGA 调试后得出的实操建议:
| 场景特征 | 推荐总线 | 理由 |
|---|---|---|
| 单主设备私有缓存,容量低于 64 KB | AHB | 面积小,协议简单,单主无仲裁压力 |
| 单一 DMA 引擎连续搬运大块数据到 SRAM | AHB 或 AXI | 取决于频率和带宽是否够用,AHB 流水线也能顶住 |
| 多 master 共享大容量帧缓冲/报文缓冲 | AXI4 | 需要并行读写、长 burst 和 outstanding 支持 |
| 低功耗休眠态需要快速进入保持模式 | AXI | 写响应通道和排空机制好实现 |
| CPU 和 DMA 同时访问同一块存储体 | AXI | 避免 AHB 串行化造成的 CPU 等待延迟 |
| 存储体小于 256 KB,并发访问不高 | AHB | 避免多余翻转功耗和面积开销 |
5.2 别在大容量 SRAM 上省掉 AXI 的必要性检查
大容量 SRAM 使用 AXI 时,有几个检查点要在设计早期就确认清楚,省得后端再改:
检查 AXI 数据位宽和 SRAM 物理位宽是否匹配。如果 AXI 是 128 位,而 SRAM 内部是 64 位,控制器就需要做数据拼接,带宽会打折。如果反过来,SRAM 是 128 位,AXI 是 64 位,那么单次读访问会浪费一半存储带宽。
检查 ID 信号的处理。AXI 协议用 ID 区分不同事务来源,SRAM 控制器通常要把多个 master 的 ID 统一映射成内部的请求标签。ID 宽度不足会导致事务乱序错误,特别是读数据返回不按顺序时,必须靠 ID 判断该回给谁。
检查地址对齐。AXI burst 要求起始地址与传输大小对齐,如果地址不对齐,控制器需要拆分事务。大容量 SRAM 如果被 CPU、DMA 以字节粒度写入,不对齐访问会显著增加控制器复杂度。我的建议是,共享存储区最好做对齐分配,每笔 burst 至少对齐到数据位宽。
5.3 一点点现场调试的体会
最后说一点纯粹来自调试的体会。AXI 总线调试比 AHB 难得多,主要因为 outstanding 事务让总线行为变得不那么直观。你在波形里看到读地址已经发出,但读数据迟迟没有回来,很可能是仲裁器卡住了优先级,也可能是 SRAM 控制器等待某个 bank 释放,还可能是 ID 映射出了问题。早期设计里多留一些 debug 信号,比如每个 master 的未完成事务计数、仲裁器队列深度、SRAM bank 忙状态,会比事后再去抓波形省非常多时间。
关于低功耗,我的建议是不要让低功耗逻辑直接跟 AXI 的 VALID/READY 握手信号耦合。正确做法是设计一个独立的活动检测模块,观察地址通道上的 request 队列是否为空、写排空是否完成、SRAM 是否有正在执行的访问,只有当这几个条件全部满足时,才认为总线进入可休眠状态。相对激进一点的实现是统计总线上连续 N 个周期没有新的 VALID 拉起,再加一个可配置的超时计数,这样既能保证不打断未完成事务,又能让空闲窗口尽早出现。
工程上没有绝对正确的总线选型,只有一个适合当前系统规模、并发模型和功耗目标的方案。大容量 SRAM 配 AXI,本质上是因为它的物理特性——大、共享、并发——决定了需要一套支持并行传输的协议来撑起带宽模型。只要记得把接口层和存储体的效率看成一个整体,而不是把 SRAM 当成孤立储存单元,这类设计基本不会跑偏。