4. nRF54多核架构与Zephyr多核支持

nRF54H20 是 Nordic 首款面向高可靠、高实时场景的多核 SoC。它内部并不是“一个 Cortex-M33 加几个外设”的传统结构,而是由多个可独立运行固件的处理单元组成:应用核为 Arm Cortex-M33,配套的还有 RISC-V 协处理器(PPR、FLPR 等),以及负责安全与系统管理的 Secure Domain。理解这些核的定位、启动顺序和通信方式,是使用 Zephyr 开发 nRF54H20 的基础。

一、nRF54H20 的核布局与职责划分

从开发视角看,nRF54H20 中最常打交道的处理单元有三类:

  • Application Core(Cortex-M33):主应用核,运行 Zephyr 主镜像,负责协议栈、业务逻辑、外设驱动与系统调度。它是开发者最常编写代码的目标核。
  • PPR(Peripheral Processor RISC-V):外设协处理器,通常承担低层外设的实时处理、射频时序配合等任务,可由 Nordic 提供的固件或用户自定义固件驱动。
  • FLPR(Fast Lightweight Processor RISC-V):轻量级 RISC-V 核,适合运行对时延敏感、逻辑简单但需要快速响应的任务,例如高速 GPIO 响应、传感器采样触发等。

这些核共享部分内存与外设资源,但各自有独立的指令空间和运行状态。它们之间不是主从轮询关系,而是通过 IPC、共享内存和中断协同工作。下图给出 nRF54H20 多核之间的资源与通信关系示意:

nRF54H20 多核资源与通信关系 Application Core Cortex-M33 Zephyr 主镜像 / 协议栈 业务逻辑 · 外设驱动 PPR RISC-V 协处理器 外设实时处理 射频时序配合 FLPR RISC-V 轻量核 高速 GPIO / 采样触发 低时延响应任务 Secure Domain 安全启动 · 密钥管理 · 系统控制 核间权限与资源仲裁 共享内存 + IPC 通道 + 中断 核间消息传递 · 数据缓冲区 · 事件通知 Zephyr IPC 服务 / MBOX / 共享内存区 IPC IPC

二、Zephyr 多核启动流程

在 nRF54H20 上,Zephyr 的启动并不是“一上电就跑 main”。上电后首先由 Secure Domain 执行安全启动流程,验证并加载各核固件。Application Core 的 Zephyr 镜像被加载后,从复位向量进入 Zephyr 的启动代码,依次完成:

  1. 早期架构初始化:设置栈、时钟、异常向量,完成 Cortex-M33 的 MPU/SAU 基础配置。
  2. SoC 与板级初始化:配置时钟树、电源域、引脚复用,并初始化 IPC、MBOX 等核间通信硬件。
  3. 驱动与子系统初始化:按 Zephyr 的 init 优先级依次初始化设备驱动、日志、内核对象。
  4. 启动从核:通过 IPC 或专用启动寄存器通知 PPR/FLPR 加载并运行各自固件,从核进入自己的 main 或任务循环。
  5. 进入主调度:Application Core 创建主线程与用户任务,内核开始调度。

需要特别注意的是,从核的固件通常独立编译、独立加载,Zephyr 主镜像只负责“启动它”和“与它通信”,并不把从核当作普通线程来调度。因此,多核任务管理的核心是划分职责与定义接口,而不是把所有逻辑塞进一个镜像。

三、核间通信(IPC)机制

nRF54H20 的核间通信主要依赖三类机制:

  • 共享内存:多个核可访问同一块 SRAM 区域,用于传递数据缓冲区。需要约定好地址、长度、读写顺序和缓存一致性策略。
  • IPC/MBOX 中断:一个核向另一个核发送事件通知,接收核在中断中处理消息。适合传递短消息、命令和完成通知。
  • Zephyr IPC 服务:Zephyr 提供 IPC 服务框架,可在共享内存之上封装消息队列、信号量和回调,简化核间通信代码。

一个典型的通信流程是:Application Core 把命令写入共享内存缓冲区,然后通过 IPC 向 PPR 发送中断;PPR 在中断服务中读取命令、执行外设操作,完成后把结果写回共享内存,再通过 IPC 通知 Application Core。整个过程是异步的,双方通过消息 ID 和状态字段保证顺序与完整性。

共享内存通信必须明确“谁写、谁读、何时读”。建议为每个方向定义独立缓冲区,并用序号或标志位标记数据有效性,避免读写竞争。

四、在 Zephyr 中管理多核任务

在 Zephyr 中管理 nRF54H20 多核任务,通常遵循以下实践:

  • 按实时性划分:硬实时、低时延任务放到 FLPR 或 PPR;复杂协议栈和业务逻辑留在 Cortex-M33。
  • 接口先行:先定义核间消息格式(命令码、参数、返回码、缓冲区布局),再分别实现两端固件。
  • 使用设备树描述:在 Zephyr 设备树中声明 IPC、MBOX、共享内存区域,使驱动和 IPC 服务能正确初始化。
  • 同步与超时:核间调用应设置超时和错误返回,避免某一核异常导致另一核永久阻塞。
  • 日志分离:各核日志通过独立通道输出,便于定位问题出在哪个核。

下面是一个简化的核间消息结构示例,用于 Application Core 向 PPR 发送“启动采样”命令:

/* 共享内存中的消息结构(示例) */
struct ipc_cmd {
    uint16_t cmd_id;      /* 0x01: 启动采样 */
    uint16_t seq;         /* 序号,用于匹配请求与响应 */
    uint32_t param;       /* 采样周期,单位 us */
    uint32_t status;      /* 0: 未处理, 1: 成功, 2: 失败 */
};

/* Application Core 侧发送 */
cmd->cmd_id = 0x01;
cmd->seq = ++g_seq;
cmd->param = 1000;
cmd->status = 0;
ipc_send(IPC_PPR, IPC_CMD_SAMPLE_START);

/* PPR 侧处理完成后回写 status 并通知 */
cmd->status = 1;
ipc_send(IPC_APP, IPC_CMD_SAMPLE_DONE);

这段代码的关键不在语法,而在于它体现了多核协作的基本约定:命令有编号、有参数、有状态、有完成通知。只要这个约定稳定,Application Core 和 RISC-V 协处理器就可以各自独立开发、独立测试,最后通过 IPC 联调。

不要假设从核一定已经就绪。Application Core 在发送第一条 IPC 命令前,应确认从核固件已加载并进入可接收状态,否则消息可能丢失。

总结来说,nRF54H20 的多核架构为 Zephyr 开发带来了更细的任务划分能力:Cortex-M33 负责系统与业务,RISC-V 协处理器负责实时与外设,Secure Domain 负责安全与启动。Zephyr 通过启动流程、设备树和 IPC 服务把这些核组织起来。掌握核间消息格式、共享内存约定和超时处理,是写出稳定多核应用的关键。