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+双系统

启动流程,是双系统协作的「第一公里」。搞不好,连系统都起不来。

标准的启动链是这样的:

  1. SPL(Secondary Program Loader): 初始化DDR、时钟等基础外设。从Flash加载ATF。
  2. ATF(ARM Trusted Firmware): 进入EL3异常级别。初始化TrustZone,配置内存隔离。这是双系统的「总开关」。
  3. 双系统加载: 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的虚拟化能力、内存隔离方案、启动流程设计。把这三点吃透了,双系统开发就成功了一半。