MCU硬件基础:常见MCU架构、存储与外设对TinyML的影响
TinyML 的本质,是在功耗、成本与体积受限的 MCU 上运行机器学习推理。与服务器端的 GPU 集群不同,MCU 的算力、内存和存储资源往往只有几十到几百 KB 级别。要在这样的平台上实现高效的模型推理,工程师必须深刻理解 MCU 的硬件架构——尤其是 CPU 核心、存储层次、总线结构以及外设与 DMA 的工作方式。这些硬件特性直接决定了模型参数如何存放、中间激活值如何暂存、数据如何搬运,以及推理延迟的瓶颈在哪里。本章将从架构、存储、外设与 DMA 三个维度,系统拆解 MCU 硬件对 TinyML 工程实践的影响。
2.1 常见 MCU 架构:ARM Cortex-M 与 RISC-V 的工程差异
核心原理:指令集与微架构对推理的影响
当前 TinyML 领域最主流的 MCU 核心是 ARM Cortex-M 系列(M0+/M3/M4/M7/M33/M55)以及逐渐兴起的 RISC-V 内核(如 RV32IMC)。从指令集角度,Cortex-M4 及以上内核支持 DSP 指令(如 SIMD 的 SMLAD、SMLALD)和可选的单精度 FPU(FPv4-SP),这对于矩阵乘法和卷积运算有直接加速作用。Cortex-M7 采用六级流水线并支持双发射,在相同主频下比 M4 有更高的 IPC(每周期指令数)。Cortex-M33 基于 ARMv8-M 架构,增加了 TrustZone 安全扩展和可选的 DSP/FPU 扩展,适合需要安全隔离的端侧推理场景。
RISC-V 内核(如平头哥 E902/E906、SiFive E31)采用精简的 RV32IMC 指令集,其中 M 表示整数乘除法扩展,C 表示压缩指令。RISC-V 的优势在于指令集开放、可定制,部分厂商会添加自定义的 AI 加速指令(如玄铁系列)。但需要注意的是,RISC-V 的生态成熟度(编译器优化、CMSIS-NN 等库的适配)目前仍弱于 ARM,在部署量化模型时可能需要更多手工汇编优化。
| 特性 | ARM Cortex-M4 | ARM Cortex-M7 | ARM Cortex-M33 | RISC-V RV32IMC |
|---|---|---|---|---|
| 流水线 | 3级 | 6级双发射 | 3级 | 2-5级(厂商相关) |
| DSP扩展 | 支持 | 支持 | 支持 | 部分支持(P扩展) |
| FPU | 可选单精度 | 可选双精度 | 可选单精度 | 可选单精度 |
| SIMD | 16位 | 16/32位 | 16位 | 需自定义 |
| 典型主频 | 80-180MHz | 200-600MHz | 100-200MHz | 100-400MHz |
| CMSIS-NN支持 | 优秀 | 优秀 | 优秀 | 需移植 |
判断方法:如何选择适合 TinyML 的 MCU 核心
选择核心时,首先看模型的计算类型。如果模型以 8-bit 整数运算为主(量化后),Cortex-M4 的 DSP 指令已经足够,无需追求 M7 的高主频——因为 M7 的高性能需要更复杂的内存系统配合,否则会因 Flash 等待状态而性能下降。如果模型包含大量浮点运算(如异常检测中的高斯计算),则必须选择带 FPU 的核心,否则软件浮点库会让推理延迟增加 5-10 倍。其次看内存带宽:Cortex-M7 有独立的指令和数据缓存,但缓存未命中时的惩罚比 M4 更大,因此对 Flash 的随机访问性能要求更高。
案例拆解:Cortex-M7 上的 Flash 等待状态陷阱
某工业振动监测项目选用 STM32H743(Cortex-M7 @ 480MHz),部署一个 2 层 LSTM 异常检测模型。初始测试推理延迟为 38ms,远高于预期的 15ms。通过分析性能计数器(DWT->CYCCNT),发现 CPU 在等待 Flash 读取时浪费了约 60% 的周期。原因是 H743 的 Flash 接口为 128-bit 宽,但 LSTM 的权重访问模式是跳跃式的,导致缓存命中率仅 45%。解决方案:将权重数组从默认的 .rodata 段移动到 DTCM(Data Tightly Coupled Memory)区域,利用 TCM 的零等待访问特性。修改链接脚本后,推理延迟降至 19ms,同时将 Flash 预取缓冲区和 ART 加速器配置为自适应模式,最终延迟稳定在 16ms。这个案例说明,MCU 的存储架构(TCM、缓存、Flash 接口宽度)对推理性能的影响往往比 CPU 主频更大。
2.2 Flash、SRAM 与寄存器:容量、速度和功耗的三角博弈
核心原理:存储介质的物理特性决定工程策略
MCU 的 Flash 通常为 NOR 型,读取速度在 20-50ns(约 20-50MHz 的随机访问),但连续读取时可以利用内部预取缓冲区达到与 CPU 同频。Flash 的写入需要擦除操作,擦除块大小通常为 4KB-64KB,且擦写寿命约 1 万次——这意味着运行时权重更新(如联邦学习场景)必须谨慎设计。SRAM 的访问速度与 CPU 同频(零等待),但容量通常只有 Flash 的 1/8 到 1/16。寄存器是 CPU 内部的存储单元,访问速度最快(1 周期),但数量有限(通用寄存器通常 16-32 个)。
在 TinyML 推理中,Flash 存储模型权重和代码,SRAM 存储输入数据、中间激活值、输出缓冲。一个典型的 300KB 模型(INT8 量化)需要 300KB Flash 空间,而推理时的峰值激活值(如 224x224x32 的特征图)需要约 1.6MB SRAM——这远超大多数 MCU 的 SRAM 容量。因此,内存优化是 TinyML 的核心课题。
判断方法:内存预算的计算流程
- 权重空间:模型参数量 × 量化位数(INT8 为 1 字节/参数,INT4 为 0.5 字节)。
- 激活空间:逐层计算每层输出的特征图大小,取最大值。例如 Conv2D(32, 112x112) 的输出为 32×112×112×1 字节 = 401KB。
- 临时缓冲:算子实现中需要的额外缓冲区(如 im2col 转换、矩阵乘法中间结果)。
- 系统开销:RTOS 任务栈、DMA 描述符、通信协议缓冲区。
如果总需求超过 SRAM 容量,需要采用内存复用(in-place 计算)、算子融合(将 Conv+ReLU+Pool 合并为单次遍历)或权重流式加载(从 Flash 分段读取)。
__attribute__((section(".itcm"))) 将热点代码和权重放入 TCM,或使用 Flash 的连续读取模式(如 ST 的 ART 加速器)。
案例拆解:SRAM 容量不足时的分层优化
一个关键词唤醒(KWS)模型需要 200KB 权重和 150KB 激活值,但目标 MCU(STM32L4)仅有 128KB SRAM。初始方案直接编译失败。第一步优化:将权重从 INT8 量化为 INT4(使用 per-channel 量化),权重降至 100KB,但激活值仍为 150KB。第二步:分析模型结构,发现第一层卷积的输出(112×112×16)占用了 100KB,而后续层输出较小。采用内存复用——将第一层输出直接作为第二层输入,覆盖写入,避免同时保留两层输出。最终激活峰值降至 80KB。第三步:将部分中间结果暂存到外部 PSRAM(通过 QSPI 接口),但仅用于非实时路径。最终总内存占用 180KB(100KB 权重 + 80KB 激活),通过将权重放在 Flash 中按需读取,实际 SRAM 占用控制在 110KB 以内。推理延迟从 45ms 增加到 52ms(增加了 Flash 读取时间),但满足了内存约束。
2.3 外设与 DMA:数据搬运的延迟优化
核心原理:外设中断与 DMA 的协作机制
在 TinyML 系统中,传感器数据(麦克风、IMU、摄像头)通过 SPI、I2C、ADC 等外设输入。传统方式下,每次数据到达都会触发 CPU 中断,CPU 需要暂停当前推理任务去读取数据。如果采样率为 16kHz 的音频,每 62.5μs 产生一次中断,而一次推理可能需要 20ms——频繁的中断会严重干扰推理的实时性。DMA(Direct Memory Access)控制器可以在没有 CPU 参与的情况下,将外设数据直接搬运到 SRAM 指定缓冲区,搬运完成后仅产生一次中断。这极大减少了 CPU 的干预次数。
DMA 的另一个关键应用是双缓冲(Double Buffering):当 DMA 正在填充缓冲区 A 时,CPU 可以处理缓冲区 B 的数据;当 A 填满后,DMA 自动切换到 B,同时通知 CPU 处理 A。这种机制实现了数据采集与推理的流水线并行,隐藏了数据搬运的延迟。
判断方法:何时使用 DMA 以及如何配置
- 数据速率:如果外设数据速率超过 1KB/s,建议使用 DMA;低于此速率时,中断开销可接受。
- 数据块大小:DMA 适合块传输(≥16 字节),不适合单字节传输。
- 实时性要求:如果推理任务有硬实时截止期(如 10ms 内完成),必须使用 DMA 避免中断抖动。
- 通道优先级:将高频率外设(如 ADC 连续采样)配置为高 DMA 优先级,低频率外设(如按键)配置为低优先级。
// 示例:STM32 使用 DMA 双缓冲接收音频数据
DMA_InitTypeDef dma_init;
dma_init.PeriphAddr = (uint32_t)&ADC1->DR;
dma_init.Memory0Addr = (uint32_t)audio_buf_a;
dma_init.Memory1Addr = (uint32_t)audio_buf_b;
dma_init.Direction = DMA_PERIPH_TO_MEMORY;
dma_init.BufferSize = 512;
dma_init.Mode = DMA_CIRCULAR; // 循环模式 + 双缓冲
DMA_Init(DMA2_Stream0, &dma_init);
DMA_DoubleBufferModeConfig(DMA2_Stream0, (uint32_t)audio_buf_b, DMA_MEMORY_1);
DMA_DoubleBufferModeCmd(DMA2_Stream0, ENABLE);
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE); // 传输完成中断
案例拆解:DMA 双缓冲在语音唤醒中的实战
某智能家居语音唤醒设备使用 I2S 接口采集 16kHz/16-bit 单声道音频,每帧 20ms(320 个采样点)。CPU 为 Cortex-M4 @ 168MHz,推理模型为 3 层 CNN(约 12ms 推理时间)。初始方案:I2S 每收到 32 个采样点触发一次中断,CPU 在中断中读取数据并存入环形缓冲区。实测发现,中断服务程序(ISR)平均耗时 8μs,而音频采样间隔为 62.5μs,中断占用 CPU 约 12.8% 的带宽,导致推理偶尔超时。
改用 DMA 双缓冲后,I2S 数据直接由 DMA 搬运到两个 320 字节的缓冲区。当缓冲区 A 填满时,DMA 自动切换到 B,并触发一次完成中断。CPU 在中断中仅需切换缓冲区指针(约 2μs),然后立即返回推理任务。实测 CPU 占用率降至 3.2%,推理延迟稳定在 12.5ms 以内。进一步优化:将 DMA 中断优先级设为最低,确保推理任务不被抢占;同时利用空闲时间(推理完成后到下一帧数据到达前)进行模型权重的预取(Prefetch),将下一层权重从 Flash 加载到 SRAM 缓存,进一步减少推理中的 Flash 等待。
2.4 常见误区与总结复盘
误区一:只关注 CPU 主频,忽略存储瓶颈
很多工程师选择 MCU 时只看主频,但实际推理性能受限于 Flash 读取速度、SRAM 带宽和总线仲裁。一个 400MHz 的 M7 如果 Flash 等待周期为 5 个周期,实际有效访问速度仅为 80MHz,可能不如 168MHz 的 M4 配合零等待 TCM。正确做法是使用 DWT->CYCCNT 寄存器测量实际周期数,并分析 Flash 命中率。
误区二:所有数据都放入 SRAM
SRAM 容量有限且功耗较高。对于只读的权重数据,应尽量放在 Flash 中,利用 Flash 的连续读取模式或缓存。只有在推理热点(如当前层的输入输出)才放入 SRAM。部分 MCU 支持在运行时将 Flash 内容映射到 SRAM(如 STM32 的 ART 加速器),但需要权衡启动时间和功耗。
误区三:DMA 配置不当导致数据错位
DMA 双缓冲的常见错误是缓冲区大小与外设数据块不匹配,导致数据覆盖或丢失。例如,I2S 的 DMA 传输大小必须与音频帧对齐(如 320 字节),否则会出现半帧数据。此外,DMA 中断服务程序中不应执行耗时操作(如打印日志),否则会阻塞后续传输。
总结:硬件感知的 TinyML 设计流程
- 需求分析:确定模型类型、输入尺寸、延迟预算(如 50ms)和内存上限。
- 硬件选型:根据计算类型(INT8/FP32)选择带 DSP/FPU 的核心,根据内存需求选择 Flash/SRAM 容量。
- 内存规划:使用链接脚本将权重、激活、堆栈分配到不同存储区域,优先使用 TCM 和紧耦合内存。
- 外设配置:对高频数据流使用 DMA 双缓冲,对低频事件使用中断。
- 性能验证:使用周期计数器、Flash 命中率、DMA 传输完成时间等指标,定位瓶颈。
- 迭代优化:根据测量结果调整量化策略、算子实现和内存布局。