一、多核内存管理概述
各位同学好,我是老周。做嵌入式十几年了,从单核8051一路做到现在的多核STM32。说实话,多核编程最让人头疼的,不是任务调度,而是内存管理。你想想看,两个核同时访问同一块内存,谁先谁后?数据会不会乱?这些问题要是没搞清楚,调试起来真是欲哭无泪。
这一章我们先打个基础。把Cortex-M7、M4、M0+的内存架构差异理清楚,再看看多核共享内存到底怎么玩,最后聊聊内存一致性问题——嗯,这是多核调试的"万恶之源"。
1.1 Cortex-M7/M4/M0+ 内存架构对比
先看一张对比表,把三个核心的内存特性摆在一起,一目了然:
| 特性 | Cortex-M7 | Cortex-M4 | Cortex-M0+ |
|---|---|---|---|
| 总线架构 | 哈佛架构 + 多总线(AXI/AHB) | 哈佛架构(AHB) | 冯·诺依曼(AHB-Lite) |
| 缓存 | L1 Cache(I/D 可配) | 无 | 无 |
| TCM | ITCM + DTCM(紧耦合内存) | 无 | 无 |
| MPU | 8/16 区域 | 8 区域 | 8 区域(可选) |
| 原子操作 | LDREX/STREX + 双字 | LDREX/STREX | 无(需关中断) |
| 内存屏障 | DMB/DSB/ISB 全支持 | DMB/DSB/ISB 全支持 | 仅 DSB |
这张表我建议你存下来。我在项目里吃过亏——用M7的Cache没配好,结果M4写的数据M7读出来是旧的,查了两天才发现是Cache一致性问题。
关键点:M7有Cache和TCM,这是它性能强悍的原因,但也是多核调试的"坑"。M0+没有原子指令,多核共享数据时必须用关中断来保护,否则数据竞争跑不掉。
1.2 多核共享内存模型
多核共享内存,说白了就是两个或多个核共用一块RAM。STM32H7系列里,M7和M4共享SRAM1/SRAM2,还有D2域的内存。模型大概分三种:
- 主从模型:M7做主,M4做从。M7分配任务,M4执行。共享内存放任务队列和状态标志。
- 对等模型:两个核地位平等,各自跑独立任务,通过共享内存交换数据。
- 管道模型:用环形缓冲区(Ring Buffer)做数据流传输,一个核写,一个核读。
我个人习惯用主从模型,简单可靠。对等模型看着很美,但调试起来两个核互相等锁,死锁问题能让你怀疑人生。
下面这张图展示了典型的多核共享内存结构:
我的经验:共享内存的地址分配一定要在链接脚本里明确划分。我曾经见过一个项目,M7和M4的堆栈重叠了,结果两个核互相踩内存,跑几个小时就死机。查了三天才发现是链接脚本没配好。
1.3 内存一致性问题简介
内存一致性,说白了就是:一个核写的数据,另一个核什么时候能读到?
为什么会这样?因为现代处理器不是老老实实一条指令一条指令执行的。M7有Cache,有写缓冲,有乱序执行。你写了一个变量,可能还在Cache里,没刷到主存。另一个核去读主存,读到的当然是旧值。
我列几个常见的"坑":
- Cache一致性问题:M7的Cache里存了数据,M4直接写主存,M7读到的还是Cache里的旧数据。
- 写缓冲问题:M7写了一个标志位,但数据还在写缓冲里没落盘,M4读标志位发现变了,但数据还没到位。
- 编译器重排问题:编译器觉得你的代码顺序可以优化,把两条指令交换了,结果逻辑全乱。
避坑指南:我曾经在一个H743项目里,M7和M4通过共享内存传数据。M7写数据,再写一个"数据就绪"标志。M4轮询这个标志,发现置位就去读数据。结果呢?M4读到的数据有一半是错的。原因就是M7的写缓冲把"数据"和"标志"的顺序打乱了。解决方案是在写标志之前加一条DMB指令,强制刷写缓冲。
解决内存一致性问题,ARM提供了三样武器:
- 内存屏障指令:DMB、DSB、ISB。DMB保证数据访问顺序,DSB保证指令执行完成,ISB清空流水线。
- 原子操作:LDREX/STREX,实现无锁的原子读写。M0+没有这个,得用关中断替代。
- MPU配置:把共享内存区域配成"共享"属性,硬件自动帮你做Cache一致性维护。
嗯,这一章先讲到这里。后面的章节我们会深入每个知识点,手把手带你调试实际案例。记住一句话:多核调试,内存先行。基础打牢了,后面才能走得稳。