3、MCU内存模型:深入理解Flash、SRAM、寄存器、DMA等内存层次,以及不同MCU型号的差异

TFLite Micro 的量产部署,本质上是在一个极其有限的内存舞台上编排一场精密的舞蹈。许多开发者在 PC 或 Linux 环境下能轻松跑通的模型,一旦移植到 MCU 上便遭遇“内存不足”或“意外复位”的窘境,根源往往不在于算力,而在于对目标 MCU 内存模型的认知偏差。本章将深入剖析 MCU 内部的内存层次结构,厘清 Flash、SRAM、寄存器与 DMA 之间的协作与制约关系,并对比主流 MCU 内核(Cortex-M0+/M4/M7/RISC-V)在资源适配上的关键差异,为后续的算子优化和内存复用策略打下坚实的硬件认知基础。

非易失存储 易失存储 外设与数据通路 Flash 主存储 代码 + 只读权重 OTP / 选项字节 写保护 / 读保护 Flash 控制器 预取缓冲 等待周期 (WS) SRAM 阵列 激活值 / 中间结果 栈 / 堆 / 全局变量 DMA 描述符 / 缓冲区 SRAM 控制器 多总线主控 仲裁 / 位带 ECC 校验 DMA 引擎 内存↔外设搬运 外设寄存器 UART / SPI / ADC 总线矩阵 AHB / APB 外设时钟 中断控制器 I-Bus D-Bus DMA请求 寄存器映射 总线桥 外设总线 图:MCU 内存层次与数据通路(以 Cortex-M 为例) Flash 通过 I-Bus 取指,SRAM 通过 D-Bus 读写,DMA 独立搬运外设数据

上图展示了 MCU 内存系统的核心骨架。与 PC 的冯·诺依曼或哈弗架构不同,现代 MCU 普遍采用改进型哈弗架构,即指令总线(I-Bus)与数据总线(D-Bus)物理分离,但共享同一片物理存储空间。这意味着 CPU 可以同时从 Flash 取指、从 SRAM 读写数据,从而提升执行效率。然而,这种并行性也带来了新的瓶颈——总线仲裁、Flash 等待周期以及 DMA 与 CPU 争抢 SRAM 带宽。理解这张图,是掌握 TFLite Micro 内存优化的第一步。

3.1 核心原理:四大内存实体的角色与物理约束

在 MCU 中,内存并非一个统一的“大仓库”,而是由多个物理特性和访问路径完全不同的区域组成。对于 TFLite Micro 而言,我们最关心的是以下四个实体:

  • Flash(非易失闪存):存放代码段(.text)、只读数据(.rodata,包括模型权重和常量)、以及初始化后的全局变量副本。其特点是容量大(通常 256KB~2MB)、掉电不丢失,但写入速度慢(ms 级)且需要先擦除后写入,读取速度受限于等待周期(WS)。在 TFLite Micro 中,模型权重若声明为 const,则直接映射到 Flash 地址,不占用宝贵的 SRAM。
  • SRAM(静态随机存储器):存放可读写的全局变量(.data/.bss)、栈(Stack)、堆(Heap)以及 TFLite Micro 的 TensorArena。其特点是访问速度快(零等待)、可随机读写,但容量极小(通常 64KB~512KB)且掉电丢失。这是内存优化的主战场。
  • 寄存器(Register File & 外设寄存器):CPU 内部的通用寄存器(R0-R15)用于指令执行;外设寄存器(如 UART->DR、DMA->CCR)则映射到固定的地址空间(如 0x40000000 起)。TFLite Micro 的算子内核会频繁使用通用寄存器,而外设寄存器则用于驱动传感器或调试输出。
  • DMA(直接内存访问):一个独立于 CPU 的“搬运工”。它可以在外设与 SRAM、或 SRAM 与 SRAM 之间搬运数据,不占用 CPU 的寄存器资源。在 TFLite Micro 中,DMA 常用于加速传感器数据采集(如麦克风 PDM 数据)或模型输出结果的快速拷贝,但需要谨慎管理其与 CPU 对 SRAM 的并发访问。

关键认知:Flash 是“只读仓库”,SRAM 是“工作台”,寄存器是“双手”,DMA 是“传送带”。TFLite Micro 的优化目标就是:让模型权重尽可能留在 Flash 仓库中,让工作台上的临时变量(激活值)尽可能小,让双手(CPU)少做搬运,让传送带(DMA)多干重活。

3.2 判断方法:如何精准评估目标 MCU 的内存预算

在开始移植 TFLite Micro 之前,必须通过系统化的方法评估目标硬件的内存上限。以下是三步判断法:

  1. 查阅数据手册的 Memory Map 章节:确认 Flash 起始地址(通常 0x08000000)和 SRAM 起始地址(通常 0x20000000)。注意部分 MCU 有多个 SRAM 块(如 STM32F4 有 CCM RAM 和普通 SRAM),它们的访问速度不同,CCM RAM 只能由 CPU 访问,DMA 无法访问。
  2. 计算静态内存占用:使用 arm-none-eabi-size 工具查看编译后的 .text/.data/.bss 大小。其中 .data 和 .bss 必须加载到 SRAM 中。例如,一个模型权重为 300KB 的模型,若声明为 const,则占用 Flash 300KB,SRAM 仅占用 4KB(指针);若误声明为全局变量,则 SRAM 直接爆炸。
  3. 估算动态内存峰值:TFLite Micro 使用 TensorArena 作为所有中间张量的存储池。其大小取决于模型结构(如卷积的输入输出通道数、全连接层的维度)。可以通过 tflite::MicroInterpreter 的 arena_size 参数进行估算,或使用官方提供的 ModelAnalyzer 工具。
内存区域 典型地址范围 容量示例 访问速度 TFLite Micro 用途
Flash 0x08000000 512KB - 1MB ~25MHz (WS=1) 模型权重、代码、常量表
SRAM 0x20000000 128KB - 256KB ~100MHz (零等待) TensorArena、栈、堆、全局变量
CCM RAM (仅M4/M7) 0x10000000 64KB ~100MHz (零等待) 关键中间结果(CPU独占)
外设寄存器 0x40000000 — APB1/APB2 时钟 DMA 配置、调试串口

经验法则:对于 256KB SRAM 的 MCU,TensorArena 通常只能分配 100KB~150KB。如果模型中间结果峰值超过此值,必须考虑算子内复用(如 in-place 激活)或模型剪枝。

3.3 案例拆解:STM32F746 与 RP2040 的内存适配实战

我们以两个典型 MCU 为例,展示内存模型差异如何影响 TFLite Micro 的部署策略。

案例背景:需要部署一个语音唤醒词模型(输入 16000Hz 音频,特征提取后为 10x40 的 MFCC 特征,两层 LSTM + 一层全连接,权重总量约 180KB)。

目标硬件 A:STM32F746(Cortex-M7,1MB Flash,320KB SRAM,带 64KB CCM RAM)。

  • 初始现象:直接使用默认配置编译,TensorArena 分配 200KB,导致 SRAM 溢出(.bss + 栈 + 堆 超过 320KB)。
  • 分析过程:查看链接脚本发现,.bss 段占用了 40KB(包含一个 16KB 的调试日志缓冲区),栈分配了 8KB。剩余可用 SRAM 为 272KB。但 LSTM 的中间状态(cell state 和 hidden state)需要 4 * 10 * 64 * 4B = 10KB,加上 MFCC 特征缓冲区,峰值需求约 220KB。
  • 操作步骤:① 将模型权重声明为 const,使其驻留 Flash,节省 180KB SRAM。② 将 TensorArena 大小调整为 180KB,并利用 CCM RAM 存放 LSTM 的 cell state(因为 DMA 不需要访问它)。③ 将调试日志缓冲区缩减至 2KB。
  • 结果对比:优化后 SRAM 占用为 180KB(Arena)+ 10KB(CCM)+ 10KB(栈/堆)= 200KB,远低于 320KB 上限,且 CPU 访问 CCM RAM 零等待,LSTM 推理速度提升 15%。

目标硬件 B:RP2040(Cortex-M0+,2MB Flash,264KB SRAM,无 CCM)。

  • 关键差异:M0+ 内核没有 Flash 预取指令缓存,且 Flash 访问需要 2 个等待周期。如果模型权重放在 Flash,CPU 取指和取数会争抢 Flash 控制器,导致性能下降。
  • 操作步骤:① 将 TensorArena 设为 200KB,但将 LSTM 的权重(约 80KB)在初始化时从 Flash 拷贝到 SRAM 中(使用 memcpy),因为 LSTM 是计算密集型的,频繁访问 Flash 会拖慢速度。② 将 MFCC 特征提取的查找表(如汉明窗系数)放在 Flash,因为它是顺序访问,预取缓冲可以覆盖。
  • 风险与代价:拷贝 80KB 权重到 SRAM 需要额外 80KB 空间,导致 TensorArena 缩减至 120KB。但通过算子内复用(LSTM 的 gate 计算可以 in-place),最终峰值内存控制在 180KB 以内。

重要警示:不要盲目将大权重拷贝到 SRAM。对于 Cortex-M7 这类带指令缓存(I-Cache)和预取缓冲的高性能内核,Flash 读取速度足够快,拷贝反而浪费 SRAM 和启动时间。务必通过 perf counter 或示波器测量实际瓶颈。

3.4 常见误区与总结复盘

在多年的 MCU 部署支持中,以下三个误区最为常见,值得反复强调:

  • 误区一:混淆 Flash 与 SRAM 的容量概念。 认为“1MB Flash 很大,模型随便放”。实际上,TFLite Micro 的模型权重虽然可以放 Flash,但所有中间计算必须在 SRAM 中完成。一个 500KB 的模型可能只需要 50KB SRAM 存放权重指针,但激活值可能高达 200KB。
  • 误区二:忽视 DMA 与 CPU 的 SRAM 争抢。 当 DMA 正在搬运音频数据到 SRAM 缓冲区时,CPU 访问同一块 SRAM 区域会被插入等待周期。在实时性要求高的场景(如关键词检测),这会导致推理时间抖动。解决方案是使用双缓冲(Ping-Pong Buffer),让 DMA 写入缓冲区 A 时,CPU 处理缓冲区 B。
  • 误区三:忽略 Flash 等待周期对性能的影响。 在低主频 MCU(如 48MHz 的 M0+)上,Flash 等待周期可能高达 3 个周期。如果模型权重未对齐或访问模式随机,CPU 将大量时间花在等待上。此时,将热点权重(如第一层卷积)放入 SRAM 是明智的。

总结复盘:MCU 内存模型是 TFLite Micro 部署的“地基”。Flash 管容量,SRAM 管速度,寄存器管执行,DMA 管搬运。 不同 MCU 的差异不仅在于容量大小,更在于总线架构(是否有 CCM、Flash 预取深度、DMA 通道数)。在项目启动的第一天,就应该绘制一张目标 MCU 的内存地图,标注出每个区域的起始地址、大小、访问权限和等待周期。这张地图将指导你后续所有的算子优化决策——是选择 Flash 映射权重,还是 SRAM 拷贝权重;是使用 DMA 加速数据采集,还是用 CPU 直接轮询。