2、硬件平台选型:支持双系统的SoC
做双系统开发,第一步就是选芯片。这事儿马虎不得。
我个人习惯,先看SoC是否原生支持硬件虚拟化。说白了,就是看它有没有ARM的TrustZone或者类似的安全扩展。没有这个基础,后面做内存隔离、启动切换,会非常痛苦。
2.1 主流SoC选型对比
目前市面上,我接触最多的就是NXP的i.MX8系列和瑞萨的R-Car系列。这两家,算是双系统方案的常青树。
| 特性 | i.MX8M Plus | R-Car H3 |
|---|---|---|
| CPU架构 | 4×Cortex-A53 + 1×Cortex-M7 | 4×Cortex-A57 + 4×Cortex-A53 + 双锁步R7 |
| 硬件虚拟化 | ARMv8-A EL2/EL3 | ARMv8-A + 硬件虚拟化支持 |
| GPU | GC7000L | PowerVR GX6650 |
| 典型应用 | 工业控制、边缘计算 | 车载信息娱乐、ADAS |
我在项目中遇到过用i.MX8做工业网关的案例。当时客户要求QNX跑实时控制,Linux跑应用层。嗯,i.MX8的异构架构正好合适。A53跑Linux,M7跑裸机或QNX,天然隔离。
核心观点:选SoC时,别只看主频。要关注它有没有独立的实时核(Cortex-R或M系列),以及硬件虚拟化扩展是否完整。这是双系统能否稳定运行的基础。
2.2 内存分区与隔离机制
内存隔离,是双系统协作的命门。你想想看,如果Linux崩了,把QNX的内存踩了,那整个系统就完了。
我建议的做法是:
- 硬件层面:使用ARM的TrustZone或IOMMU/SMMU做物理隔离。QNX跑在安全世界,Linux跑在非安全世界。
- 软件层面:在ATF(ARM Trusted Firmware)中配置内存区域。给QNX分配固定物理地址,Linux只能看到剩余部分。
举个例子,i.MX8M Plus的内存布局,我通常会这样划分:
// 内存分区示例(4GB DDR)
// 0x4000_0000 - 0x4FFF_FFFF: QNX实时系统 (256MB)
// 0x5000_0000 - 0xBFFF_FFFF: Linux系统 (1.75GB)
// 0xC000_0000 - 0xFFFF_FFFF: 共享内存区域 (1GB)
注意:共享内存区域一定要通过硬件MPU或MMU做访问控制。我曾经见过一个项目,因为共享内存没加保护,Linux的一个野指针直接写穿了QNX的调度队列,导致系统随机死机。排查了整整两周。
2.3 启动流程设计:SPL+ATF+双系统
启动流程,是双系统协作的「第一公里」。搞不好,连系统都起不来。
标准的启动链是这样的:
- SPL(Secondary Program Loader): 初始化DDR、时钟等基础外设。从Flash加载ATF。
- ATF(ARM Trusted Firmware): 进入EL3异常级别。初始化TrustZone,配置内存隔离。这是双系统的「总开关」。
- 双系统加载: ATF根据配置,决定先启动QNX还是Linux。通常先起QNX,因为它负责实时控制。
我习惯在ATF中加一个自定义的启动选择逻辑。比如通过GPIO电平判断:
// 伪代码:ATF启动选择
if (gpio_read(BOOT_SEL_PIN) == HIGH) {
// 启动QNX
load_image(QNX_IMAGE_ADDR);
smc_call(SWITCH_TO_QNX);
} else {
// 启动Linux
load_image(LINUX_IMAGE_ADDR);
smc_call(SWITCH_TO_LINUX);
}
个人经验:ATF阶段一定要做看门狗喂狗操作。我遇到过SPL加载ATF时,因为DDR训练时间过长,看门狗超时复位了。后来在SPL里加了提前喂狗的逻辑,才稳定下来。
2.4 避坑指南
做双系统启动,有几个坑我踩过,分享给你:
- DDR训练参数:不同温度下,DDR训练结果可能不同。量产时一定要做全温区测试。
- 中断路由:QNX和Linux的中断不能混在一起。用GIC的硬件中断分组,把实时中断分给QNX。
- 共享资源访问:比如UART、I2C,谁用谁锁。我建议用硬件信号量(Hardware Semaphore)做同步。
嗯,硬件平台选型这部分,其实还有很多细节。但核心就三点:SoC的虚拟化能力、内存隔离方案、启动流程设计。把这三点吃透了,双系统开发就成功了一半。