2、MCU存储体系基础:Flash、SRAM、外部存储(NOR/NAND/SPI Flash)的层次结构与特点

在嵌入式系统设计中,MCU(微控制器)的存储体系直接决定了程序能否可靠运行、数据能否安全保存以及系统能否在严苛的功耗与成本约束下满足实时性要求。理解内部Flash、SRAM与外部存储(NOR Flash、NAND Flash、SPI Flash)的层次关系与各自特性,是进行外部存储加载模型设计的前提。本章将系统梳理MCU存储体系的层次结构,剖析各类存储介质的物理特性、访问方式与适用场景,并重点讨论它们在SRAM缓存管理中的角色分工。

MCU存储体系层次结构与数据通路 内部存储(芯片内) 内部Flash 容量:256KB ~ 2MB 读速:~100MHz 指令预取 内部SRAM 容量:32KB ~ 512KB 零等待,CPU直接访问 总线 CPU核心 取指/执行 外部存储(板级) SPI NOR Flash 容量:1MB ~ 64MB 接口:SPI/QSPI/OSPI 读速:~50MB/s (QSPI) 并行NOR Flash 容量:8MB ~ 128MB 接口:x8/x16 并行总线 读速:~100MB/s NAND Flash(SPI或并行) 容量:64MB ~ 8GB+,页/块访问,适合大容量数据存储 读速:~20-40MB/s,写速较慢,需坏块管理 外部存储控制器(XMC/FSMC/QSPI) DMA / 缓存 / 地址映射 外部总线 图:MCU存储层次与外部存储加载通路示意

2.1 内部存储:Flash与SRAM的分工与协同

核心原理:哈佛架构下的存储角色划分

现代MCU普遍采用改进型哈佛架构或统一内存映射架构,内部Flash与SRAM在物理上独立、在逻辑上统一编址。内部Flash用于存放代码和只读数据(如常量表、字符串),其特点是断电非易失、读取速度接近CPU主频(通常支持0~2个等待周期),但写入需要擦除且寿命有限(典型10万次)。内部SRAM则用于存放变量、堆栈和动态数据,其特点是读写速度极快(零等待)、无限次写入,但断电即失。

从系统启动流程看,CPU首先从内部Flash的复位向量取指,执行启动代码后将.data段从Flash拷贝到SRAM,将.bss段清零,随后跳转到main函数。这一过程体现了Flash与SRAM的基本协同:Flash提供非易失的代码存储,SRAM提供易失的运行空间。

判断方法:如何评估内部存储是否够用

在实际项目中,工程师需要根据以下指标判断内部存储是否满足需求:

  • 代码体积:编译后生成的.bin或.hex文件大小,需小于内部Flash容量减去Bootloader和参数存储区预留空间。
  • RAM占用:通过链接器映射文件(.map)查看.data + .bss + 堆栈的总和,需小于内部SRAM容量。
  • 实时性要求:若中断服务程序或关键算法需要零等待访问,则必须使用内部SRAM;若可容忍少量等待周期,可考虑外部存储映射执行。
存储类型 容量范围 访问速度 写入方式 寿命 典型用途
内部Flash 256KB ~ 2MB 0~2等待周期 按扇区擦除后编程 10万次擦写 代码、常量、Bootloader
内部SRAM 32KB ~ 512KB 零等待 任意字节读写 无限 变量、堆栈、DMA缓冲
外部SPI NOR 1MB ~ 64MB 10~50MB/s 按扇区擦除后编程 10万次擦写 代码扩展、字库、固件升级
外部并行NOR 8MB ~ 128MB 50~100MB/s 按扇区擦除后编程 10万次擦写 高速代码执行、数据记录
外部NAND 64MB ~ 8GB+ 20~40MB/s 按页编程、按块擦除 1~10万次擦写 大容量文件系统、日志存储

案例拆解:内部存储不足时的典型场景

某工业网关项目需要运行TCP/IP协议栈、MQTT客户端和图形界面库,编译后代码体积达到1.2MB,而所选MCU内部Flash仅有1MB。工程师面临两个选择:更换更大Flash的MCU(成本增加约30%),或使用外部SPI Flash存放部分只读数据(如字库、证书、固件备份)。通过分析链接脚本,发现字库和证书等数据占300KB,且访问频率较低,最终决定将这些数据重定位到外部SPI Flash地址空间,通过XIP(Execute in Place)方式直接读取。系统启动时仅需初始化QSPI控制器,无需将数据拷贝到SRAM,既节省了内部Flash空间,又避免了RAM占用。

经验提醒:当内部Flash不足时,优先考虑将“大块只读数据”迁移到外部存储,而不是将代码整体外置。代码外置会引入取指延迟和缓存命中率问题,而数据外置只需在访问时增加少量等待。

2.2 外部存储:NOR Flash与NAND Flash的本质差异

核心原理:接口、寻址与可靠性模型

外部存储中,NOR Flash与NAND Flash是两种截然不同的技术路线。NOR Flash支持随机字节读取,具有独立的地址线和数据线(或通过SPI/QSPI串行传输),可实现XIP(片上执行),即CPU可以直接从NOR Flash取指运行代码。NAND Flash则以页(Page,通常2KB~16KB)为读取单位,以块(Block,通常128KB~8MB)为擦除单位,不支持随机字节访问,因此不能直接XIP,必须先将代码拷贝到SRAM或SDRAM中执行。

从可靠性角度看,NOR Flash的坏块率极低,几乎不需要坏块管理;而NAND Flash出厂即可能存在坏块,使用过程中还会新增坏块,必须依赖软件层的坏块管理(BBM)和错误校验(ECC)。这一差异直接影响了存储方案的设计复杂度。

判断方法:根据应用场景选择外部存储类型

  1. 需要XIP执行代码:选择NOR Flash(并行或SPI接口),确保MCU支持外部存储映射地址空间。
  2. 需要大容量数据存储:选择NAND Flash,配合文件系统(如LittleFS、SPIFFS)和坏块管理。
  3. 需要快速启动:NOR Flash的XIP特性可省去代码拷贝时间,适合对上电启动时间有严格要求的场景。
  4. 成本敏感:NAND Flash单位比特成本远低于NOR,适合海量数据存储。

案例拆解:SPI NOR Flash在固件升级中的应用

某车载T-Box产品需要支持OTA固件升级,MCU内部Flash为512KB,固件实际大小为480KB,几乎没有剩余空间存放升级包。设计采用外部SPI NOR Flash(16MB)作为升级暂存区。升级流程如下:

1. 上位机通过CAN/以太网将新固件分包发送至MCU;
2. MCU将数据包写入外部SPI NOR Flash的升级暂存区(地址0x90000000 + 偏移);
3. 接收完成后,校验整个固件的CRC32;
4. 校验通过后,将暂存区固件逐扇区拷贝至内部Flash;
5. 拷贝完成后,跳转至新固件入口地址执行。

该方案的关键参数:SPI Flash擦除扇区大小为4KB,写入页大小为256字节,擦除+写入1MB固件约耗时8秒(QSPI 80MHz时钟)。若改用并行NOR Flash,时间可缩短至3秒,但成本增加约2倍。最终根据产品需求选择了SPI NOR方案,并通过在升级过程中保持系统关键功能运行(如CAN通信)来规避长时间阻塞问题。

风险提示:使用外部NOR Flash进行XIP时,必须关注MCU外部存储控制器的等待周期配置。若等待周期设置过短,会导致数据读取错误;设置过长则降低执行效率。建议通过逻辑分析仪测量实际时序,并预留软件配置接口以便调试。

2.3 外部存储加载模型中的SRAM缓存管理

核心原理:缓存机制与一致性维护

当MCU从外部存储加载代码或数据时,由于外部存储的访问速度远低于内部SRAM,通常需要引入缓存机制。缓存的基本原理是将外部存储中频繁访问的数据块复制到SRAM中,后续访问直接命中SRAM,从而减少外部总线访问次数。在MCU环境中,缓存管理分为硬件缓存(如Cortex-M7的L1 Cache)和软件缓存(如DMA双缓冲、手动拷贝)两种形式。

硬件缓存由CPU自动管理,对软件透明,但存在缓存一致性问题:当外部存储内容被DMA或其他主机修改时,缓存中的副本可能过期。软件缓存则需要开发者显式控制数据的加载、失效和回写,灵活性更高,但增加了代码复杂度。

操作流程:设计SRAM缓存管理策略

  1. 分析访问模式:统计代码或数据的访问频率、顺序性和局部性特征,确定哪些内容适合缓存。
  2. 划分SRAM区域:在链接脚本中为缓存区预留固定大小的SRAM,例如定义.cache_buffer段。
  3. 实现缓存替换算法:对于软件缓存,可采用LRU(最近最少使用)或FIFO策略;对于硬件缓存,配置Cache Line大小和替换策略。
  4. 处理缓存一致性:在DMA写入外部存储后,执行缓存失效操作;在CPU修改缓存数据后,执行缓存回写操作。
  5. 监控命中率:通过性能计数器或调试工具测量缓存命中率,调整缓存大小和策略。

案例拆解:外部Flash代码加载到SRAM执行

某电机控制项目使用STM32H743(内部Flash 2MB,SRAM 1MB),由于算法复杂度高,代码体积达到2.4MB,超出内部Flash容量。设计采用外部QSPI NOR Flash(32MB)存放全部代码,启动时由Bootloader将代码从外部Flash加载到内部SRAM执行。关键参数如下:

  • 外部Flash读取速度:QSPI 100MHz,四线模式,理论带宽50MB/s;
  • 代码加载时间:2.4MB / 50MB/s ≈ 48ms(实际考虑协议开销约60ms);
  • SRAM占用:代码段2.4MB + 数据段0.3MB + 堆栈0.1MB = 2.8MB,超出1MB SRAM容量。

解决方案:将代码分为常驻段(中断服务程序、实时控制循环)和覆盖段(初始化代码、自检代码)。常驻段约800KB,加载到SRAM;覆盖段约1.6MB,保留在外部Flash,通过XIP方式执行。系统运行时,覆盖段代码的访问频率较低,XIP的等待周期(约5个周期)对实时性影响可接受。通过这种方式,SRAM占用降至1MB以内,同时保证了关键代码的零等待执行。

复盘总结:外部存储加载模型的核心权衡在于“加载时间”与“运行性能”之间。全量加载到SRAM可提供最佳运行性能,但受限于SRAM容量;XIP方式节省SRAM但引入等待周期。实际设计应根据代码的实时性要求进行分区,将高优先级代码放入SRAM,低优先级代码保留在外部Flash。

2.4 常见误区与设计要点

误区一:忽视外部存储的写入寿命

许多工程师在设计日志记录或参数存储时,未考虑NAND Flash的擦写寿命限制。例如,某设备每10秒写一次日志,使用NAND Flash的同一块区域,按10万次擦写寿命计算,仅11.5天即达到寿命上限。正确做法是采用磨损均衡算法,将写入分散到不同块,或使用NOR Flash(寿命相同但写入粒度更细)并配合环形缓冲区。

误区二:缓存策略一刀切

硬件Cache并非在所有场景下都能提升性能。对于顺序访问的大块数据(如视频帧),Cache的预取机制可能失效,反而因Cache Miss导致额外开销。此时应使用DMA直接传输到SRAM,绕过Cache。反之,对于随机访问的小块数据(如查找表),Cache能显著提升命中率。

误区三:忽略外部存储控制器的时序配置

外部存储的时序参数(如读/写恢复时间、片选建立时间)必须根据存储芯片的数据手册精确配置。配置过紧会导致数据错误,配置过松则降低性能。建议在初始化代码中增加时序自检功能,通过读写测试模式验证配置正确性。

设计要点 推荐做法 需避免的做法
存储选型 根据容量、速度、XIP需求综合评估 仅凭成本选择NAND,忽略XIP需求
缓存管理 区分硬件Cache与软件缓存,按需使用 盲目启用Cache,不处理一致性问题
加载策略 代码分区,关键代码SRAM常驻 全部代码XIP,导致实时性下降
可靠性 NAND使用坏块管理和ECC校验 忽略坏块,导致数据丢失