4. 多核实时操作系统(RTOS)移植与配置
在RH850多核平台上运行RTOS,与单核移植最大的区别在于:RTOS不再只是“管理一个CPU上的任务”,而必须同时回答“哪个核跑哪些任务”“核间如何同步”“共享资源如何保护”“调度器如何跨核扩展”这四个问题。本节以FreeRTOS、AUTOSAR OS、μITRON三类典型RTOS为例,说明移植要点、核间任务分配、调度器扩展与共享资源保护,并给出一个核间任务同步的配置案例。
一、三类RTOS在RH850多核上的移植定位
FreeRTOS是源码开放、移植层清晰的轻量RTOS,其移植核心是portmacro.h、port.c与portASM.s三个文件。在RH850上,需要实现临界区进入/退出、任务上下文切换、SysTick或定时器中断、以及多核启动时的从核唤醒。FreeRTOS本身不提供多核调度,通常采用AMP(非对称多处理)方式:每个核运行独立的FreeRTOS实例,核间通过共享内存与IPI(核间中断)通信。
AUTOSAR OS面向汽车电子,天然支持多核(Multi-Core OS)。其移植要点是OS Application到核的映射、GetCoreID()与StartCore()的启动序列、以及Spinlock与IOC(Inter-OS-Application Communicator)机制。AUTOSAR OS的调度表(Schedule Table)和报警(Alarm)需要按核分别配置,跨核同步必须使用OS提供的Spinlock,而不能直接使用普通全局变量。
μITRON(如TOPPERS/ASP)在RH850上多用于工业控制,其移植重点是内核对象(任务、信号量、事件标志)的核间可见性,以及dis_dsp/ena_dsp等调度器状态在每核上的独立维护。μITRON通常也采用AMP方式,通过CRE_TSK指定任务所属核。
二、核间任务分配原则
核间任务分配不是“平均分”,而是按实时性、数据耦合度、负载均衡三个维度决策。以下是一个可参考的分配判断表:
| 任务类型 | 建议归属 | 理由 |
|---|---|---|
| 高频控制环(1ms以内) | 绑定到固定核,独占 | 避免迁移带来的缓存失效与抖动 |
| 与传感器强耦合的采集任务 | 靠近该外设所属核 | 减少跨核数据搬运 |
| 低速诊断、日志任务 | 集中到从核 | 不干扰主核实时性 |
| 共享资源访问密集的任务 | 尽量同核 | 降低Spinlock争用 |
在RH850上,若使用AUTOSAR OS,任务到核的映射通过OsTask的OsTaskCoreRef指定;若使用FreeRTOS AMP,则在每个核的main中分别创建本核任务,并通过共享内存队列传递数据。
三、调度器扩展与共享资源保护
单核RTOS的调度器只需维护一个就绪链表。多核下,若采用AMP,每个核有独立调度器,扩展点在于:核间中断触发从核调度、共享内存队列的原子操作、以及跨核信号量的实现。若采用SMP(对称多处理),则需要全局就绪队列、核间任务窃取(work stealing)和全局时钟同步,RH850的G3M等核在硬件上支持缓存一致性,但SMP RTOS移植复杂度显著高于AMP。
共享资源保护在多核下有三层手段:第一层是关中断,但只能保护本核,不能阻止其他核访问;第二层是Spinlock,适用于短临界区,RH850上可用LDLC/STC等原子指令实现;第三层是消息传递,用队列替代共享变量,从根本上避免竞争。实际工程中推荐“能消息传递就不共享,必须共享就用Spinlock,Spinlock临界区不超过几十个时钟周期”。
volatile,也只能防止编译器优化,不能防止两个核同时写。跨核共享变量必须配合Spinlock或原子指令。四、核间任务同步案例
下面给出一个FreeRTOS AMP场景下的核间同步案例。主核(CPU0)运行控制任务,从核(CPU1)运行采集任务。采集任务每1ms将数据写入共享缓冲区,控制任务读取并处理。使用一个共享内存结构体加一个核间信号量(通过IPI实现)。
/* 共享内存结构,放在非缓存或缓存一致性区域 */
typedef struct {
volatile uint32_t seq; /* 序号,用于判断新数据 */
volatile uint32_t data[8];
} SharedBuf;
SharedBuf g_buf __attribute__((section(".shared")));
/* CPU1 采集任务 */
void vAcquireTask(void *pv) {
uint32_t s = 0;
for (;;) {
s++;
g_buf.data[0] = read_adc(0);
g_buf.data[1] = read_adc(1);
g_buf.seq = s; /* 最后写seq,保证数据先可见 */
vTaskDelay(pdMS_TO_TICKS(1));
}
}
/* CPU0 控制任务 */
void vControlTask(void *pv) {
uint32_t last = 0;
for (;;) {
if (g_buf.seq != last) {
last = g_buf.seq;
process(g_buf.data[0], g_buf.data[1]);
}
vTaskDelay(pdMS_TO_TICKS(1));
}
}
该案例的关键点:seq最后写入,读侧先读seq再读数据,利用“写顺序”保证数据一致性;若数据量更大或需要严格同步,应改用FreeRTOS的StreamBuffer或AUTOSAR OS的IOC机制。判断同步是否成功,可观察seq是否单调递增、控制任务是否每次都能读到新数据、以及CPU1的采集周期是否稳定在1ms。
五、移植检查清单
完成上述配置后,建议按以下清单逐项确认,避免“单核能跑、多核就挂”的典型问题:
- 每个核的启动代码是否独立初始化了本核的栈指针、时钟和中断向量表;
- 共享内存段是否放在非缓存区,或已通过MPU配置为缓存一致性;
- 跨核共享变量是否全部使用Spinlock或原子指令保护;
- 核间中断(IPI)的优先级是否高于本核任务切换优先级;
- 每个核的SysTick或定时器是否独立配置,避免共用一个定时器导致节拍冲突;
- 调试时是否能用
GetCoreID()区分日志来源,确认任务确实运行在预期核上。
在多核RTOS移植中,最容易被忽视的是“共享资源的可见性”和“调度器状态的核间隔离”。只要把这两点处理清楚,FreeRTOS AMP、AUTOSAR OS多核、μITRON多核的移植路径就基本一致:先让每个核独立跑起来,再建立核间通信,最后用Spinlock或消息队列保护共享数据。