1. 嵌入式系统内存全景:从MCU到MPU的内存层次结构

做嵌入式这么多年,我见过不少工程师把内存当成一个黑盒子。写代码时只管malloc,出了问题就抓瞎。其实,搞懂内存的脾气,比背一百条优化技巧都管用。

这一章,咱们把嵌入式系统的内存扒开来看。从最底层的SRAM单元,到外挂的DDR颗粒,再到CPU内部那点可怜的Cache。我会结合自己踩过的坑,帮你建立起完整的内存全景图。

1.1 内存层次结构:为什么需要这么多层?

先问个问题:为什么不能只用一种内存?

答案很简单——速度、容量、成本三者不可兼得。CPU想要飞快,就得用离它最近的SRAM;但SRAM贵得离谱,1MB就要几十块钱。所以,工程师们搞出了一套分层方案:

核心原则:越靠近CPU的内存,速度越快、容量越小、成本越高。数据按“热”度分层存放,这是所有现代计算机系统的基石。

我画了一张图,把典型嵌入式系统的内存层次串起来:

嵌入式系统内存层次结构(从CPU到外存) CPU寄存器 ~1ns 延迟,几十字节 L1 Cache (SRAM) ~2ns 延迟,16~64KB L2 Cache (SRAM) ~10ns 延迟,128KB~1MB 主存 (DRAM / SDRAM / DDR) ~50~100ns 延迟,MB~GB级 Flash (NOR / NAND) ~10~100μs 延迟,MB~GB级 外部存储 (SD卡 / eMMC / SSD) ~ms 延迟,GB~TB级 说明: 箭头方向表示CPU访问数据的路径。每次访问都要从顶层开始查找,找不到就逐级向下。这就是“缓存命中/缺失”的基本模型。

这张图你得记在心里。每次写代码时,想想你的数据正躺在哪一层——是寄存器里?Cache里?还是慢吞吞的Flash里?

1.2 SRAM vs DRAM vs Flash:工作原理与性能差异

这三种内存,是嵌入式系统里最常见的。我分别说说它们的脾气。

1.2.1 SRAM(静态随机存取存储器)

SRAM用6个晶体管存1个bit。说白了,就是个锁存器。只要不断电,数据就不丢。速度极快,跟CPU几乎同频。

  • 优点:速度快(~1-10ns),不需要刷新,接口简单
  • 缺点:密度低(6T/bit),成本高,容量做不大
  • 典型用途:CPU Cache、MCU片内RAM、寄存器文件

我的经验:在STM32F4这类MCU上,片内SRAM只有192KB。我曾经在一个音频处理项目里,为了省SRAM,把查找表硬塞到Flash里,结果循环查表时CPU stall严重。后来改用SRAM放查找表,速度提升了3倍。记住:SRAM是黄金,Flash是青铜。

1.2.2 DRAM(动态随机存取存储器)

DRAM用1个晶体管+1个电容存1个bit。电容会漏电,所以需要定期刷新(典型值64ms刷新一次)。

  • 优点:密度高(1T1C/bit),成本低,容量大
  • 缺点:需要刷新,访问延迟比SRAM高一个数量级(~50-100ns)
  • 典型用途:Linux系统的内存、大缓冲区、视频帧缓存

注意:DRAM的刷新周期对实时性有影响。我在一个工业相机项目里,DDR控制器刷新时正好赶上DMA传输关键数据,导致丢帧。后来把DMA优先级调高,并把刷新窗口错开,才解决问题。做实时系统时,一定要算DRAM的刷新开销。

1.2.3 Flash(闪存)

Flash是嵌入式系统的“硬盘”。NOR Flash支持XIP(片上执行),NAND Flash密度高但需要坏块管理。

  • NOR Flash:读快写慢,支持随机访问,适合存代码
  • NAND Flash:密度高,按页读写,适合存大块数据
  • 擦写寿命:一般10万次(NOR)到1万次(NAND)

我把它们的核心参数整理成了一张表,方便对比:

参数 SRAM DRAM (DDR3/4) NOR Flash NAND Flash
访问延迟 ~1-10 ns ~50-100 ns ~80-150 ns (读) ~25-100 μs (读)
写延迟 ~1-10 ns ~50-100 ns ~1-10 μs ~200-500 μs
密度 低 (6T/bit) 高 (1T1C/bit) 中 极高
易失性 是 是 否 否
是否需要刷新 否 是 (64ms) 否 否
典型容量 KB ~ MB MB ~ GB MB ~ 256MB GB ~ TB
每MB成本 极高 低 中 极低

1.3 Cache的工作原理:为什么它能提速?

Cache的本质,就是利用了局部性原理。你想想看,程序执行时,90%的时间都在跑10%的代码。Cache把这10%的热数据放在SRAM里,CPU访问时就不用去慢吞吞的DRAM里找了。

Cache分三种:

  1. 直接映射Cache:每个内存地址只能映射到Cache的固定位置。简单,但容易冲突。
  2. 全相联Cache:任何地址可以放在任何位置。灵活,但硬件复杂。
  3. 组相联Cache:折中方案。把Cache分成若干组,每组内全相联。最常见的是4路或8路组相联。

关键指标:Cache命中率每提高1%,CPU性能可能提升5-10%。我见过一个视频解码项目,因为Cache miss率从5%降到2%,帧率直接从30fps飙到45fps。优化Cache,就是优化钱。

1.4 内存带宽与延迟的量化分析

做性能调优,不能光凭感觉。你得会算账。我给出几个典型场景的量化数据:

1.4.1 延迟对比(CPU视角)

假设CPU主频为1GHz(1个时钟周期=1ns):

访问目标 延迟(时钟周期) 延迟(时间) CPU等多久?
L1 Cache命中 2-4 cycles 2-4 ns 几乎不等
L2 Cache命中 10-20 cycles 10-20 ns 等一小会儿
DRAM访问(页命中) 50-100 cycles 50-100 ns 等很久
DRAM访问(页缺失) 200-400 cycles 200-400 ns CPU几乎在空转
NOR Flash读 80-150 cycles 80-150 ns 严重拖慢
NAND Flash读 25,000-100,000 cycles 25-100 μs CPU可以睡一觉了

看到没?L1 Cache和DRAM之间差了50倍。这就是为什么Cache miss是性能杀手。

1.4.2 带宽计算

带宽 = 总线频率 × 数据位宽 × 传输次数/时钟周期

举个例子:

  • 32-bit SDRAM @ 100MHz: 100MHz × 4字节 × 1 = 400 MB/s
  • 32-bit DDR3 @ 800MHz: 800MHz × 4字节 × 2(DDR双沿)= 6.4 GB/s
  • 64-bit LPDDR4 @ 1600MHz: 1600MHz × 8字节 × 2 = 25.6 GB/s

实战建议:我在做ISP图像处理时,需要从DDR里读4K视频帧。一帧1920×1080×3字节≈6MB。如果帧率30fps,带宽需求就是180MB/s。当时用的DDR3带宽是4.2GB/s,看起来绰绰有余。但实际因为Cache miss和总线竞争,实测带宽只有1.2GB/s。所以,永远不要用理论带宽做设计,留50%余量是底线。

1.5 避坑指南:我踩过的三个内存坑

最后,分享几个真实教训:

  1. Cache一致性:有一次DMA往SRAM里写数据,CPU读到的却是旧值。因为Cache没刷新。从那以后,我每次用DMA都会手动clean/invalidate Cache。
  2. Flash写寿命:一个日志系统,每秒写一次Flash。结果3个月后Flash就挂了。后来加了磨损均衡和写缓存,把寿命延长到5年以上。
  3. DRAM初始化时序:某次板子死活跑不起来,查了三天发现是DDR的ODT配置不对。嗯,DRAM的初始化时序,一定要严格按照芯片手册来,差一个cycle都不行。

内存这东西,你越了解它,它就越听话。下一层,咱们聊聊如何给内存做“体检”——内存性能分析工具与指标。不过那是后话了。