2. 多核硬件平台与BSP适配
AMP(Asymmetric Multiprocessing,非对称多处理)与SMP最大的区别在于:每个CPU核运行独立的操作系统镜像或裸机程序,核与核之间没有统一调度器,因此“谁在哪个核上跑、从哪里启动、占用哪些外设”必须在BSP和硬件层面显式约定。本节围绕目标硬件选型、多核启动地址配置、BSP核间资源分配、串口与调试通道规划、硬件初始化顺序五个方面展开,目标是让读者能够针对一块具体板卡,规划出一套可落地的AMP启动与资源划分方案。
一、目标硬件选型:从核数、内存映射与启动方式入手
AMP平台选型不能只看“核多不多”,而要重点考察三件事:核间是否对称、每个核能否独立复位与启动、外设与内存能否被静态划分。常见候选平台如下:
| 平台 | 典型核结构 | AMP适配要点 |
|---|---|---|
| Xilinx Zynq-7000 / Zynq UltraScale+ | 双核Cortex-A9 或 4×A53 + 2×R5 | CPU0由FSBL引导,CPU1可通过SEV指令或复位向量独立启动;R5核适合跑硬实时任务,A53跑Linux或VxWorks |
| NXP T4240(QorIQ) | 12×e6500双线程核 | 核间通过CoreNet fabric互联,支持每核独立复位;需配置LAW窗口与DDR分区,避免多核争抢同一内存段 |
| NXP i.MX8(如i.MX8QM) | 4×A53 + 2×A72 + M4F | 异构明显,A核与M4F通过MU(Messaging Unit)通信;M4F可跑VxWorks或裸机,A核跑Linux |
选型时建议优先确认:目标核是否支持独立复位向量、是否有专用TCM或紧耦合内存、核间中断控制器(如Zynq的IPI、i.MX8的MU)是否可用。若这些条件不满足,AMP方案会退化为“伪AMP”,核间同步将非常脆弱。
二、多核启动地址配置:谁先跑、谁等谁
AMP启动的核心是“主核引导从核”。以Zynq-7000为例,上电后CPU0从BootROM开始执行FSBL,CPU1默认处于等待事件(WFE)状态。要让CPU1运行VxWorks镜像,需在CPU0的BSP中完成以下动作:
/* 示例:CPU0 唤醒 CPU1 并跳转到指定入口 */
#define CPU1_ENTRY_ADDR 0x00100000 /* CPU1镜像加载地址 */
#define CPU1_STACK_ADDR 0x001F0000
/* 1. 将CPU1镜像从Flash拷贝到DDR */
memcpy((void*)CPU1_ENTRY_ADDR, (void*)FLASH_CPU1_OFFSET, CPU1_IMAGE_SIZE);
/* 2. 写入CPU1入口地址到复位向量寄存器 */
Xil_Out32(CPU1_RESET_VECTOR, CPU1_ENTRY_ADDR);
/* 3. 通过SEV指令唤醒CPU1 */
__asm__ volatile("sev");
/* 4. CPU1启动后自行初始化MMU、Cache并进入VxWorks */
关键参数是入口地址与栈地址。入口地址必须与链接脚本中CPU1镜像的起始地址一致,栈地址要避开CPU0已使用的内存区。T4240上则通过RCW(Reset Configuration Word)和核复位控制寄存器实现类似功能,每个核有独立的复位向量偏移。i.MX8的M4F核通常由A核通过SCU(System Controller Unit)释放复位并设置入口。
三、BSP中核间资源分配:内存、外设与中断
AMP下资源必须静态划分,不能依赖操作系统动态申请。典型划分包括:
- 内存分区:CPU0使用0x00000000–0x0FFFFFFF,CPU1使用0x10000000–0x1FFFFFFF,中间留4KB共享内存用于核间通信。
- 外设归属:串口0归CPU0调试用,串口1归CPU1;以太网控制器按核分配,避免同一寄存器被两个核同时写。
- 中断路由:GIC中每个SPI中断需配置目标核,PPI中断默认归属本核。核间中断(IPI)用于通知对方“数据已就绪”。
在BSP的config.h或hwconf.c中,通常以宏定义方式固化这些划分,例如:
#define CPU0_MEM_BASE 0x00000000
#define CPU0_MEM_SIZE 0x10000000
#define CPU1_MEM_BASE 0x10000000
#define CPU1_MEM_SIZE 0x10000000
#define SHM_BASE 0x0FFFF000
#define SHM_SIZE 0x00001000
#define UART0_BASE 0xE0000000 /* CPU0调试串口 */
#define UART1_BASE 0xE0001000 /* CPU1调试串口 */
四、串口与调试通道规划
AMP调试最怕“两个核抢一个串口”。规划原则是:每个核至少独占一个UART,主核串口用于打印启动日志和异常,从核串口用于输出自身任务状态。若板卡UART数量不足,可用以下替代方案:
- 通过共享内存+IPI实现“从核日志转发到主核串口”,但会增加主核负担。
- 使用JTAG分别连接各核,通过调试器查看从核PC和寄存器。
- 在从核代码中预留一个内存日志缓冲区,主核定期读取并打印。
调试通道还应包括:核间心跳计数器(放在共享内存中,双方定期递增)、看门狗喂狗分工、以及从核异常时的复位策略。建议在共享内存头部定义固定结构体,便于双方解析。
五、硬件初始化顺序
AMP启动的初始化顺序必须严格分层,否则会出现“从核已跑但时钟未配”的随机故障。推荐顺序如下:
- 主核完成时钟、PLL、DDR控制器初始化,确保内存可用。
- 主核配置MMU和Cache,建立自身地址映射。
- 主核初始化共享内存区域,清零并建立通信结构。
- 主核加载从核镜像到DDR,刷新Cache。
- 主核配置从核的复位向量和栈指针,释放从核复位。
- 从核启动后先初始化自身MMU、Cache,再初始化专属外设。
- 双方通过IPI或共享内存标志位完成握手,进入各自主循环。
下面用一张信号链/时序图概括上述启动与资源划分关系:
图中左侧为主核CPU0的初始化序列,右侧为从核CPU1的启动序列,中间紫色块为共享内存与IPI通道。可以看到,主核在完成DDR和共享内存初始化后才唤醒从核,从核启动后先建立自身运行环境再写握手标志,双方通过共享内存和IPI完成同步。实际项目中,这套顺序和地址划分应固化在BSP配置文件中,并在板级初始化阶段通过打印日志逐条确认,避免“从核跑飞”后难以定位。