ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

双核MCU架构解析:如何兼得高性能与增强安全

双核MCU架构解析:如何兼得高性能与增强安全 1. 双核 MCU 的架构逻辑性能与安全为什么能“兼得”这几年做嵌入式项目明显感觉到双核 MCU 已经不是“旗舰专属”了。随手翻一下主流厂商的产品线意法半导体的 STM32H7 系列里带双核的型号、恩智浦的 i.MX RT1170、瑞萨的 RA6M5 / RZ 系列还有 Silicon Labs 带 Secure Vault 的双核无线 SoC几乎都在强调同一件事Dual-Core MCUs Blend High Performance and Enhanced Security。也就是说双核不再是单纯堆算力而是把“跑得快”和“防得住”放到同一个芯片里解决。这里说的“高性能”和“增强安全”并不是简单地一个核干活、另一个核闲着。对真正做产品的人来说双核意味着你可以用架构手段同时满足实时控制、复杂通信、安全启动、安全通信这几类互相打架的需求。如果你正在纠结“一个高性能单核跑 Linux 实时扩展”还是“两颗 MCU 分开管”那么双核 MCU 很可能是一个更优的中间态。这篇文章我会从架构、软件分工、安全设计、实际落地和调试经验几个层面把这东西讲透。1.1 对称多核与异构多核的根本区别双核 MCU 和多核应用处理器有个关键差异大多数双核 MCU 走的是异构多核AMPAsymmetric Multi-Processing路线而不是我们平时听说的对称多核SMP。SMP 的意思是两个一模一样的核心共同共享一份内存和一套操作系统调度器。Linux 的 SMP 就是这种多核之间通过调度器自动分配任务代码写起来相对“无感”。但 MCU 领域的双核尤其强调异构比如 Cortex-M7 配 Cortex-M4或者 Cortex-M33 配 Cortex-M55。这类芯片两个核心的主频可能一样但性能差异明显例如 M7 带缓存和双发射M4 则更省电、更可预测。它们在硬件上就不平衡所以软件层面也做不到“随便哪个核跑都行”。AMP 架构下每个核心运行独立的执行环境可以跑不同的 RTOS、甚至一个裸机一个 RTOS。两个核心之间通过硬件邮箱Mailbox、共享内存、中断来通信。这种设计的好处在于核心之间天然隔离一个核崩了或者被攻击另一个核还能继续工作。这和安全设计的目标高度重合。1.2 “性能核”与“安全核”的分工模式双核 MCU 上最常见的分工是“性能核跑业务安全核跑安全”。用一颗大核处理以太网协议栈、GUI、复杂算法或者电机控制环路另一颗小核专门负责安全启动校验、密钥管理、安全协议比如 TLS 握手运算、安全存储等。这种做法在行业里有个很形象的名字叫“安全岛”Secure Island。性能核即使被恶意数据打穿攻击者拿到的也只是这个核的内存空间。密钥、证书、安全存储区域如果放在另一个核对用的安全内存里并且在硬件层面做了访问权限隔离那么攻击者就没法顺着总线直接偷到密钥。再加上安全核往往配有独立的硬件加解密引擎和真随机数发生器TRNGTLS 握手开销也不会拖累业务核。这样的分工不是拍脑袋想出来的而是安全需求层层倒逼的结果。比如你要做一款支持 MQTT/TLS 的工业网关业务核如果要同时跑 Modbus 主站、边缘协议转换和 TLS负担会非常重。而把 TLS 记录层处理放在安全核让业务核通过 IPC 调用安全服务整个系统的实时抖动反而更低安全边界也更清晰。1.3 选型时先看内核安全扩展双核 MCU 不是只要有两个核就够了安全增强很大程度上取决于内核本身支持哪些安全扩展。Cortex-M33 和 Cortex-M55 的核心自带 TrustZone-M 扩展可以在硬件层面把内存、外设、中断划分为安全和非安全两个世界。Cortex-M7 和 M4 则没有 TrustZone只能靠 MPU 做相对粗粒度的保护。RISC-V 双核方案则要看是否带有 PMPPhysical Memory Protection和校验扩展比如某些厂家采用一个带 P 扩展的高性能核 一个带有安全特性的小核。选型时建议先把“安全需求等级”定下来如果只是防误操作、防程序跑飞MPU 级别就够了如果产品需要合规比如 PSA Certified Level 2 或更高那就尽量选带 TrustZone 或者独立安全子系统的芯片。不要只看主频和 Flash安全特性往往是双核 MCU 拉开产品档次的地方。2. 把双核性能真正榨干的软件设计双核 MCU 的软件架构和单核完全不是一个套路。很多人拿到双核芯片第一反应是“两个核跑两个点灯程序”然后发现根本没有发挥出双核的价值。真正有用的做法是从系统工程角度重新规划任务归属和核间通信。2.1 任务划分的原则别把双核当单核用我在做项目时任务划分会按照三个维度来评估实时性要求、计算密度、安全等级。实时性要求高的任务比如电流环、编码器采样、PWM 输出应该放在延迟可预测的核上最好代码路径短、无动态内存分配、没有不可屏蔽中断长时间占用。计算密度高的任务比如图像处理、FFT、加密算法放在带 DSP 扩展或主频更高、带缓存的大核。安全等级高的任务比如固件验证、密钥派生放到安全核或安全世界中单独处理。举个例子一个双轴伺服控制器使用 M7 M4 架构我把两个电流环都放在 M4 上运行 16kHz 控制频率代码量不大但周期极短M7 则跑 EtherCAT 从站协议栈、状态机和上位机命令解析。有人会问M7 明明更快为什么不用 M7 跑控制环因为控制环对确定性要求极高M4 没有复杂缓存Cache miss 引入的抖动远比 M7 小。这是嵌入式里“更快不等于更合适”的典型场景。2.2 IPC 与共享内存双核通信的“高速公路”两个核之间通信最底层的方式是硬件 Mailbox 加中断。Mailbox 本身只能传一个很短的寄存器值一般用来发事件通知。真正传数据要走共享内存。共享内存的设计有几个关键点第一数据 buffer 通常用无锁环形队列Ring Buffer避免两个核心使用同一个锁那个锁放哪里都会成为瓶颈和死锁来源。环形队列的读写指针用原子操作更新配合内存屏障基本可以做到无锁。第二要小心缓存一致性。M7 这类带缓存的大核在访问共享内存时默认行为可能造成数据不同步。常见做法是把共享内存区域配置成非缓存Normal Non-cacheable或者使用硬件一致性接口。最稳妥的搞法是查参考手册单独为共享内存区建立 MPU 或 TrustZone 配置并明确使用 non-cacheable 属性。否则就会出现“A 核写了一个值B 核隔了好久才看到”这种极其难查的问题。第三通信协议不要设计得太复杂。我一般会在共享内存头部放一个 version 字段、一组数据长度和序列号然后紧跟有效载荷。B 核收到 Mailbox 中断后先读取序列号校验长度再处理数据。这样即使 A 核更新频率很快B 核也能通过丢帧策略主动丢弃旧数据保证实时性。2.3 双核启动与同步双核 MCU 的启动流程和单核很不一样。大多数芯片上电后只有一个核心从 BootROM 开始运行另一个核心则处于复位状态或是在等待被释放。比如 STM32H747 内部默认是 M7 上电运行M4 保持复位i.MX RT1170 则可以根据启动配置选择哪个核作为主核。软件上要遵循“主核先起从核后起”的顺序。主核需要完成全局时钟、电源域、DDR/外部存储器初始化把从核的固件从 Flash 加载到指定的 RAM 地址然后释放从核的复位并从核的入口地址编译时需要固定到对应地址。两个核心开始运行后要定义一套“握手协议”。最常见的方式是从核初始化完自己的外设后向共享内存中写入版本号然后给主核发一个 Mailbox 中断主核收到中断后再把自己的状态同步过去。不要一上来就交互数据先确认两个核心的时钟、外设都稳定否则后面出问题很难排查。2.4 性能调优负载均衡不等于平均分不少人在双核上做负载均衡以为两核跑 50% 50% 就是完美。实际经验是稳态的系统更怕的是瞬态过载。我一般会保证主核的最高负载不超过 70%从核不超过 80%剩下一点余量用来处理突发任务和安全服务调用。如果某个核长期跑满考虑把一部分非实时任务重新分配或者用消息队列缓存起来而不是硬塞给另一个核。另外中断分配要格外重视。两个核虽然可以各自接收到外设中断但每个外设的中断只能配置到某一个核。设计时要先画一张表格把外设、中断、DMA、所在核全部列清楚。如果中断目标核配置错了轻则性能下降重则因两个核同时访问同一外设而冲突。这个表在调试时也会非常有价值。3. 增强安全不是“加壳”而是系统级设计很多人理解的“安全”是最后给代码加个加密算法或者固件里加个密码校验。真正的产品级安全是从信任根、启动、运行、更新、存储全链路设计出来的。双核 MCU 给了你一个难得的硬件基础但如果你不会用等于白买。3.1 信任根与安全启动安全启动是系统安全的第一道门。它的目标很简单确保芯片上跑的固件是开发者签名发布的版本不是被篡改的版本。双核场景下启动链通常比单核多一层。拿 M7 M4 的组合举例流程是BootROM 加载并验证一级 Bootloader 的签名一级 Bootloader 验证 M7 应用固件M7 应用固件去加载 M4 固件前仍然需要先验证 M4 固件的签名。注意这一步不能省不能让 M7 直接读取未经验证的 M4 固件到共享内存。否则攻击者可以通过更新 M4 固件绕过整个安全体系。签名验证一般使用非对称算法比如 ECDSA P-256 或 RSA-2048。公钥放在 OTP一次性可编程区域或者带读保护的安全 Flash 中硬件上禁止调试接口读出。私钥永远只存在于构建服务器上不能出现在工程师本地电脑里。这里的核心是建立“信任链”从芯片 BootROM 这个不可变信任根开始每一级都验证下一级任何一环断开系统都不进入正常执行。3.2 安全分区与权限隔离安全启动保证“进来的代码是可信的”但运行时还要防止恶意数据或者内存越界破坏关键数据。双核 MCU 提供了两种隔离手段MPU 和 TrustZone。MPU 是每个 Cortex-M 核心都有的内存保护单元可以按区域配置访问权限。MPU 能挡住普通软件 bug 和无意的非法访问但它的配置是由 CPU 自己管理的CPU 跑飞后 MPU 也形同虚设。TrustZone 则是硬件强制的隔离安全世界和非安全世界的总线事务被物理隔离非安全代码根本没法访问安全内存和设备。如果你的双核芯片支持 TrustZone我强烈建议把安全核本身放在安全世界把业务核放在非安全世界。再配合“安全内存区域只在安全世界可见”的配置业务核即使被攻破也拿不到安全密钥。如果你用的是不带 TrustZone 的双核 M7/M4至少要保证安全核的私有内存、密钥区通过 MPU 配置成特权模式访问同时关闭调试口的非法访问。3.3 硬件加密引擎与密钥存储双核 MCU 上几乎所有安全通信都要用到硬件加密引擎比如 AES、SHA、RSA/ECC、真随机数发生器TRNG。这些外设的使用要注意一点优先把它们分配给安全核或者通过 TrustZone 配置成安全外设。业务核需要加密服务时不直接操作硬件加密寄存器而是调安全核提供的 API通过 IPC 把明文发过去再拿回密文。这样设计的好处是业务核即使被恶意代码控制也没法直接读取硬件加密引擎里的密钥。密钥存储在安全核私有的 Key Storage 区域通常是 OTP 里的自定义字段或者 Secure Flash 区域。每次上电用设备唯一密钥对密钥进行解密放在安全 RAM 中。全程不让业务核接触。在 TLS 连接场景我看到很多产品直接把证书私钥放在主控的外部 Flash 里。这在双核安全架构里一定是反面教材。正确做法私钥存放在安全存储区域TLS 握手时由安全核完成签名/解密操作或者使用硬件 crypto 库调用完成。如果芯片支持把密钥放在 Security Subsystem 中连安全核 CPU 都读不到只能通过硬件调用完成加解密。3.4 安全固件更新“产品能升级”是一件既必需又危险的事。缺少安全机制的升级通道等于给攻击者开了一个后门。双核设备升级时要注意两个核的固件必须作为一个整体处理不能出现“主核升级了新版本从核还跑着旧版本”的不一致状态。我一般会把两个固件打包成一个升级镜像包含版本号、哈希列表、签名。升级前整体校验再分别写入两个核的备份区。回滚保护也很重要。如果新版本被发现有安全漏洞攻击者可能试图往旧版本回滚因为旧版本可能存在已知漏洞。解决方式是在 OTP 中记录“最低可接受版本号”Bootloader 启动时如果发现镜像版本小于这个值就拒绝执行。3.5 运行时防护双核互为看门狗安全不仅是“启动时安全”还要在运行时保持。双核架构有一个有价值的安全技巧让两个核互相监控。比如安全核可以周期性给业务核发 Ping 指令业务核收到后返回一个递增序列号。安全核如果连续 N 次没有收到正确回复就认为业务核已经跑飞主动触发复位或切换到安全状态。反过来业务核也可以监控安全核的状态避免安全核因为死锁导致整个安全服务停摆。这个机制比单纯用硬件看门狗更灵活因为看门狗只能检测“有没有喂狗”无法确认“程序逻辑是否正常”。双核互检可以在应用层做到逻辑级别的监控。我甚至在一个安全要求很高的项目里让安全核定期检查业务核内存里几个关键变量的 CRC一旦发现异常直接进入安全停机流程。4. 一个真实项目双核工业控制 安全通信模块的落地复盘这一章拿我最近做的一个项目来复盘。需求是做一个工业边缘控制器既要支持本地高速 IO 控制和 PLC 协议又要支持带 TLS 的 MQTT 通信上报数据到云端。控制器还要求支持远程固件升级不能让调试接口暴露密钥。整体选型最终落在了带 TrustZone 的双核 MCU 上。4.1 需求与选型为什么是双核而不是“外挂安全芯片”刚开始方案评审时有两个备选一是单核 MCU 外部安全芯片二是双核 MCU 一个核跑业务、一个核做安全。外部安全芯片的优势是安全认证成熟比如 ATECC608A但问题是它和业务核之间走 I2C 通信传输速率有限。TLS 握手需要频繁使用安全芯片做签名成了性能瓶颈。双核 MCU 内部安全核和业务核走共享内存加 Mailbox带宽高几个数量级而且省掉一路 I2C 和一颗芯片的 BOM 成本。最终选的型号是带有 Cortex-M33 和 Cortex-M55 双核的 MCUM33 支持 TrustZone作为安全核M55 主频更高带 Helium DSP 扩展作为业务核。两颗核都有 512KB 以上的本地 SRAM共享一段 64KB 的 I/D RAM 作为共享通信区。4.2 软件架构搭建步骤整个软件架构分四层硬件层双核、内存映射、Mailbox、硬件加密引擎、TrustZone 配置。基础层两个核各自的 SDK 和 RTOS。M55 跑 ThreadXM33 跑裸机 安全服务框架因为安全核的任务相对简单裸机能最大程度减少运行时代码也让安全审计更简单。服务层定义了一套 IPC 接口包括安全启动检查、随机数获取、TLS 握手、固件校验等。应用层M55 上跑 Modbus TCP 从站、边缘数据处理、MQTT 客户端。M33 上跑证书管理、TLS 记录层处理、安全升级校验。搭建顺序建议从下往上先配置时钟和内存映射确保两核能独立启动然后实现 Mailbox 驱动和共享内存区跑通最简单的“ping-pong”帧再实现 IPC 服务调用框架接着移植业务核上的 RTOS把应用模块挂上去最后一步步把安全服务从业务核抽到安全核。不要一上来就迁 TLS先把 IPC 和基础服务跑稳不然出问题都不确定是通信问题还是安全逻辑问题。4.3 共享内存和 Mailbox 的核心实现共享内存采用无锁环形队列队列长度 64 项每项最大 256 字节。#define SHM_BUF_SIZE 256 #define SHM_QUEUE_SIZE 64 typedef struct { uint32_t magic; uint32_t seq; uint32_t length; uint8_t payload[SHM_BUF_SIZE]; } shm_message_t; typedef struct { volatile uint32_t head; volatile uint32_t tail; shm_message_t msg[SHM_QUEUE_SIZE]; } shm_ring_t;M55 写入时只修改 headM33 读取时只修改 tail。内存区域在两边都配置成 non-cacheable避免缓存一致性问题。Mailbox 只用于发通知void m55_send_to_secure(mailbox_regs_t *mb, uint32_t data) { while (mb-SR MAILBOX_SR_FULL); mb-DR data; }在 M33 侧收到 Mailbox 中断后就立即从共享内存队列里取一条消息解析消息类型执行对应的安全服务。整个过程确定性强毫秒级完成。4.4 参数计算与资源分配双核项目的关键参数要提前算清楚。我一般会列一张资源分配表资源分配位置大小说明M55 应用固件Flash 0x08000000512KBModbus、MQTT 相关代码M33 安全固件Flash 0x08100000128KB安全服务、密钥处理共享消息队列AHB SRAM64KB两个核都能访问安全存储区OTP Flash32KB存放公钥、设备证书任务周期和 IPC 负载也需要估算。假设业务核每秒需要建立一次新的 MQTT 连接每次 TLS 握手需要安全核执行约 20 次 RSA 操作每次操作平均 5ms那么安全核每秒需要投入 100ms 处理握手负载大约 10%。平时非握手阶段安全核负载低于 2%。这个余量完全够用。共享内存大小要按峰值消息率计算。假如业务核最高每秒发送 2000 条控制消息每条平均 128 字节那每秒要传输 256KB。64KB 环形队列能缓存约 500 条消息足够应对瞬态突发。如果估算发现排队延迟超过任务周期就要考虑增大队列或降低消息频率而不是盲目提高传输带宽。4.5 实测结果实测下来业务核 M55 跑 Modbus TCP 和 MQTT 的同时CPU 负载大约 45% 左右。安全核 M33 除了处理 TLS 握手还要做固件校验平均负载约 20%。整个系统相比之前单核方案吞吐量提升了接近一倍而且 TLS 握手时间因为不再被业务任务抢占反而更稳定了。安全验证方面我们模拟了几种攻击场景通过调试口直接读取 Flash、篡改应用固件、在 M55 上注入恶意代码尝试访问 M33 私有内存均无法成功。这也验证了双核 TrustZone 的架构确实能挡住常见物理和远程攻击路径。5. 双核调试与踩坑实录双核项目的开发难度很大一部分在调试。两个核同时跑日志乱、断点乱、数据乱一不留神就是几天查不出原因。下面这些坑我基本都踩过。5.1 双核 Debug 的常见坑第一个坑是日志输出。两个核共用同一个 UART 打印日志不加保护时会出现字符交错。处理方法是在日志驱动里加互斥锁或者给两个核分配不同 UART再或者在日志帧里加入核 ID 和时间戳便于事后分析。第二个坑是断点。你用 IDE 连上双核调试器默认可能只停在主核上。从核还在跑然后你在共用的库函数里下了断点两个核同时停IDE 直接卡死。建议调试时先用一段时间只在单一核上打断点确认一个核逻辑没问题再启用多核同步调试。很多调试器支持 cross-trigger两个核心可以互相触发暂停但上手复杂建议前期不要开。第三个坑是共享内存查看。如果开了缓存你在调试器窗口看到的共享内存值可能不是最新值。调试前先确认内存属性或者在调试器里用内存访问命令绕过缓存。5.2 共享资源的竞争与死锁一个典型的死锁场景是两个核都要访问同一个 SPI Flash各自驱动里都有软件锁结果 M7 持锁等待 M4 通过 Mailbox 返回结果而 M4 又持锁等待 M7 释放 SPI 总线两边互相等待系统卡死。这种问题设计阶段就要避免。合理的做法是每个外设明确指定唯一的“归属核”其他核要使用该外设时必须通过 IPC 请求归属核代为完成。不要让两个核同时初始化同一个外设。如果必须共享优先使用无锁方案或带超时的获取锁机制同时记录持锁核 ID方便死锁后从复位信息里排查。5.3 缓存一致性问题用带缓存的大核比如 M7/M55访问共享内存最常见的现象是M55 写了一个消息M33 收到 Mailbox 中断后读到的却是旧数据。原因就是 M55 的 Cache miss 回写规则导致数据还停留在 Cache 里没有真正落到 SRAM。解决办法有三个把共享内存区域配置成 non-cacheable最简单适合消息量不大且不追求极致性能的场景。在写共享内存之后主动执行 Clean 操作在读取共享内存之前执行 Invalidate 操作。适合不想降低缓存性能的场景。使用硬件一致性接口有些芯片支持 AXI SRAM 与 CPU 缓存的一致性配置后硬件自动处理但可用区域有限需要查手册。我在项目里推荐优先使用 non-cacheable因为嵌入式共享内存的数据量通常不大牺牲一部分缓存收益换取可预测性和代码简单性非常划算。5.4 常见问题速查表现象可能原因解决方法从核启动后立即 HardFault从核固件烧录地址错误确认从核链接脚本中固件入口与启动地址一致两个核通信偶发丢帧Mailbox 中断优先级过低或队列溢出提高 Mailbox 中断优先级检查队列 head/tail 更新是否被调度打断共享内存读到旧数据缓存未失效/清写配置 non-cacheable或手动 Cache Clean/InvalidateTx/Rx 同时断点导致 IDE 崩溃多核断点同步未配置只在一个核上断点或开启 cross-trigger安全服务调用偶尔超时主核频繁访问安全核导致排队采用异步 IPC 加超时回退不要阻塞等待升级后设备变砖固件镜像没有做整体校验打包双核镜像统一校验加 A/B 备份升级最后再分享一个小技巧做双核项目两年多我最大的体会是“双核”听起来是硬件问题实际是软件问题。真正把一个双核 MCU 用好不是简单地开两个工程、写两套 main而是要在一开始就把硬件资源归属、内存映射、核间通信、安全边界全部规划好。如果你正准备做双核项目一定先画一张“双核资源分配表”把每个外设、每块内存、每个中断、每条 IPC 通道的归属核标清楚再动手写代码。这个表就是你后续所有架构决策的基础。最后再补充一个屡试不爽的调试策略双核项目不要追求“一步到位”。先让两个核各自跑最简程序互相之间只发一个 Mailbox 帧确认“我活着”再把共享内存逐渐加大最后才接入安全服务和复杂协议。每一步都在双核协同的状态下验证过再去开发新功能。靠这个笨办法我避开了很多不可复现的诡异 bug。希望这篇总结能让你少走一些弯路。
返回列表