在多核 MCU 上,“所有核都跑起来”并不等于“系统已经正确初始化”。真正决定系统是否可靠的,是谁负责全局运行时、各核按什么顺序接管资源、共享数据何时可见,以及进入周期调度前是否建立了可验证的跨核契约。
本文以 AURIX TC275 的三核启动结构为背景,分析一种常见的自研基础软件架构:它沿用 MCAL、CDD、RTE 等 AUTOSAR 分层思想,但没有采用标准 AUTOSAR OS 的 Task、Alarm 和 ScheduleTable,而是通过共享令牌完成串行初始化,进入 RUN 后再由各核独立中断驱动周期逻辑。
边界说明:本文结论来自静态结构分析和通用工程推演,不代表已经完成上板或实车验证。文中的函数、变量和模块名称均为简化伪代码,不对应任何具体项目符号,也不涉及安全配置、口令、私有诊断值或内存地址。
一、背景与总体结论
这类系统可以概括为“CPU0 主导、三核串行初始化、RUN 后各核独立调度”。CPU0 是全局启动所有者:它建立主核运行环境,初始化全局 C 运行时,准备共享数据,再释放 CPU1 和 CPU2。其余两核只初始化本核上下文和本核资源,不重复执行全局 BSS 清零或 data 复制。
- 启动期:通过单个共享令牌把三核约束为确定的阶段顺序,降低并发初始化同一外设或共享数据的风险。
- 运行期:每核拥有独立 STM 周期中断;CPU1 还承担更高优先级的电机 PWM ISR。
- 内存模型:三核分别使用自己的 DSPR,但系统状态、启动令牌和普通 RTE 全局变量主要由 CPU0 DSPR 承载,并通过全局地址被其他核访问。
- 主要风险:状态发布早于初始化完成、共享变量缺少快照协议、非原子信号量、软件中断缺少确认、启动等待无超时,以及状态发布前缺少显式内存屏障。
问题的重点不在于继续拆文件或增加抽象层。现有启动代码若职责内聚、依赖关系清楚,可以保持结构不变,优先补齐跨核可见性、所有权、超时和失败语义。
二、三核复位入口:全局运行时只能建立一次
CPU0 复位后首先设置 BTV、BIV 和 SDA,随后执行全局 C 运行时初始化:清零 BSS,并把只读镜像中的 data1、data2 复制到运行地址。只有这些工作完成后,CPU0 才启动 CPU1 和 CPU2。
// CPU0:唯一的全局启动所有者
SetupVectorAndSmallData();
ClearGlobalBss();
CopyInitializedData();
StartSecondaryCore(CPU1);
StartSecondaryCore(CPU2);
EnterStagedInitialization();
这个顺序非常关键。若从核在全局 BSS 尚未清零、已初始化数据尚未复制时开始读取共享变量,后续即使令牌协议正确,也可能基于不确定初值运行。
CPU1 和 CPU2 被释放后,各自建立本核栈、PSW、PCXI 与 CSA,配置本核 BTV/BIV、SDA、缓存和看门狗,然后进入共享令牌等待。PCXI/CSA 属于 TriCore 上下文切换的基础资源,不能简单继承主核状态;每个核都必须拥有独立且匹配链接布局的上下文区域。
| 启动对象 | 主要职责 | 不应重复执行的工作 |
|---|---|---|
| CPU0 | 建立全局运行时、准备共享数据、启动从核、主导阶段推进 | 无 |
| CPU1 | 建立本核上下文、缓存、向量表、看门狗和本核外设 | 全局 BSS 清零与 data 复制 |
| CPU2 | 建立本核上下文、缓存、向量表、看门狗和本核外设 | 全局 BSS 清零与 data 复制 |
三、五阶段令牌式初始化
共享启动令牌把阶段和核号编码在一个字节中:
token = (stage << 4) | cpu
高半字节表示阶段,低半字节表示当前获得执行权的核。期望序列为:
00 -> 01 -> 02
-> 10 -> 11 -> 12
-> 20 -> 21 -> 22
-> 30 -> 31 -> 32
-> 40 -> 41 -> 42
这种设计的优点是顺序直观,调试器中只看一个变量就能判断启动卡在哪个核、哪个阶段。但它同时把系统可用性绑定在单个令牌上:任意一步没有推进,其他核都会永久等待。因此令牌不应只有“下一个值”,还应配套阶段超时、失败原因和可诊断的降级路径。
| 阶段 | CPU0 主要工作 | CPU1/CPU2 主要工作 |
|---|---|---|
| INIT | 读取复位信息,初始化软件中断和跨核同步基础 | 建立本核同步入口并确认就绪 |
| MCAL | 初始化数据/标定管理、Mcu、STM、GTM、Fls、Fee、I/O、CAN、SPI 等 | 初始化本核 STM 等核私有资源 |
| SRV | 初始化系统基础芯片驱动、IoHwAb、NvM、CSPRNG、CanIf、CCP、UDS、测试服务和系统控制 | 完成本核服务依赖或保持等待 |
| CMPLX | 维持全局依赖和共享资源就绪状态 | CPU1 依次建立代码映射、普通 I/O、电流采样、旋变采样、三相 PWM 与故障捕获等电机 CDD |
| RUN | 进入本核周期调度 | 各核进入自己的周期或快速中断调度 |
四、MCAL、服务层与电机 CDD 的真实依赖
阶段名只是外壳,真正决定初始化能否交换顺序的是数据和硬件依赖。例如,CPU0 的数据/标定管理模块会先复制标定区,并为三个核配置 OVC。CPU1 的电机 CDD 随后才使用映射后的代码或标定视图,因此 OVC 不是一个可延后的“公共服务”,而是电机初始化的前置条件。
CPU0:
CopyCalibrationImage();
ConfigureOverlayFor(CPU0, CPU1, CPU2);
PublishStageReady();
CPU1:
WaitForStageReady();
InitCodeMapping();
InitMotorIoAndSampling();
InitMotorPwmAndFaultCapture();
这也说明串行阶段并非单纯为了“代码好看”,而是在编码真实依赖:先建立全局数据视图,再启动消费该视图的 CDD。审查时应逐项回答“谁生产、谁消费、何时可见、失败后谁负责”,而不是只对照模块列表。
五、RUN 后的三核任务调度
进入 RUN 后,三个核分别使用本核 STM 产生 1 ms 和 10 ms 回调。它们是中断回调,不是 AUTOSAR OS Task,也没有标准 OS 提供的优先级天花板、资源协议或任务间事件语义。CPU1 还存在优先级更高的电机 PWM ISR,用于执行快速控制环。
| 执行上下文 | 典型职责 | 并发关注点 |
|---|---|---|
| CPU0 STM 1 ms | 快速基础服务与输入输出调度 | 可能抢占 CPU0 10 ms 初始化状态机 |
| CPU0 STM 10 ms | 系统状态机与延后应用初始化 | 状态发布与初始化函数的先后关系 |
| CPU1 STM | 本核周期服务 | 与高优先级 PWM ISR 共享数据 |
| CPU1 PWM ISR | 电机快速控制主循环 | 必须在初始化完成后才能消费控制状态 |
| CPU2 STM | 预留周期调度入口 | 当前为空时仍应保留可观测性和预算 |
三核 STM 也不应被假定为严格同相位。令牌序列反映的是阶段握手点,而从实际控制流看,STM 的启用顺序可能表现为 CPU2、CPU0、CPU1。即使三个核都配置相同周期,启动时刻不同也会形成固定或漂移的相位差。需要成组采样的数据不能依赖“周期相同”来获得一致快照。
应用初始化被进一步延后:CPU0 的 10 ms 状态机从 BOOT、INIT、PREDRIVE 迁移到 DRIVE 时,才调用电机与车辆控制的初始化入口。这种做法能让底层驱动先就绪,但也把“系统进入 DRIVE”与“应用对象完成初始化”放进了同一个中断调度窗口,必须严格处理发布顺序。
六、代码放在 CPU1 段,不代表调用发生在 CPU1
RTE 宏或普通函数调用不会自动跨核。一个函数即使链接在 CPU1 可执行代码区域,只要 CPU0 通过普通调用指令进入它,执行上下文仍然是 CPU0:使用 CPU0 的栈、寄存器上下文和当前中断状态。代码放置决定“从哪里取指”,调用者决定“由哪个核执行”。
因此,延后调用的电机初始化入口若由 CPU0 直接调用,它仍运行在 CPU0;只有由 CPU1 PWM ISR 调用的快速控制主循环才真正运行在 CPU1。若架构要求初始化必须在 CPU1 上下文执行,应使用显式跨核请求、CPU1 本地状态机或核间消息,而不是依赖链接段名称。
七、DSPR 与跨核共享数据
CPU0、CPU1、CPU2 各自使用独立 DSPR,这有利于降低本地栈和高频变量的访问延迟。共享变量则主要放在 CPU0 DSPR,并通过全局地址供其他核访问。物理上“可以访问”只解决寻址问题,并不自动解决所有权、原子性、缓存一致性或业务快照一致性。
| 共享对象 | 当前常见形式 | 需要补充的契约 |
|---|---|---|
| 启动令牌 | volatile 标量 | 单写者、显式屏障、超时、失败码 |
| 系统运行状态 | 普通非 volatile 全局变量 | 所有权、发布顺序、编译器与硬件可见性 |
| RTE 数据 | 普通全局变量和直接赋值宏 | 快照、序号、双缓冲或消息队列 |
| 测试状态 | 跨核直接读写 | 生产与测试访问的仲裁及生命周期限制 |
volatile 只能约束编译器对该对象的部分优化,不等价于原子操作,也不构成完整的跨核内存屏障。对多字段控制命令,普通 RTE 读写宏如果只是直接赋值,就没有队列、快照、版本号或成组一致性保证。读者可能得到“新目标值 + 旧模式位”的混合视图。
接口已经定义也不等于数据链已经接通。CAN 到电机命令、电机到 CAN 反馈即使存在 RTE 端口或全局变量,还需要确认生产者确实写入、消费者确实读取、更新周期匹配,并且在超时或无效数据时有明确降级行为。
八、信号量和 GPSR 软件中断的边界
1. “观察空闲后写入核号”不是锁
一种常见自研信号量会先读取共享变量,看到空闲后再写入自己的核号。这是两个独立总线操作:CPU0 和 CPU1 可以同时读到“空闲”,随后都写入并认为自己获得了锁。它还缺少同核嵌套的递归计数,内层释放可能提前解除外层临界区。
// 有竞态的伪代码
if (lock == FREE) {
lock = CurrentCpu();
return ACQUIRED;
}
更可靠的方案是复用芯片库已有的 cmpswap 或 SpinLock:通过原子比较交换完成“仅当仍为空闲时写入”,并明确禁止递归或维护所有者与递归计数。锁的内存序、最大持有时间和中断策略也应写入接口契约。
2. GPSR 中断触发不等于从核已经停下
CCP 在线标定可能通过 GPSR 高优先级软件中断,让其他核进入暂停 ISR,再修改共享标定数据。但发起核写入请求后,如果没有等待从核确认已进入 ISR,就不能严格保证多字节修改期间其他核已经停止。目标核可能仍在更高优先级中断、关中断区或指令窗口中运行。
Requester: publish request -> trigger GPSR -> wait all acknowledgements
Targets: enter ISR -> publish acknowledgement -> wait release
Requester: modify shared calibration snapshot -> publish release
完整协议至少需要 request、ack、release 三个阶段,并为等待确认设置超时。若标定数据支持双缓冲或 OVC 页切换,更理想的做法是准备完整新视图后一次性切换版本,缩短暂停窗口。
九、已识别的初始化竞态
最需要优先处理的竞态是:CPU0 的 10 ms 状态机先发布 DRIVE,再调用电机和车辆控制初始化。若 ISR 内允许重新开中断,CPU1 PWM ISR 或 CPU0 1 ms ISR 可能在初始化尚未完成时看到 DRIVE,并开始读取尚未建立完成的对象或共享数据。
// 风险顺序
SystemState = DRIVE;
InitMotorApplication();
InitVehicleControl();
// 推荐顺序
InitMotorApplication();
InitVehicleControl();
PublishMemoryBarrier();
ApplicationReady = true;
SystemState = DRIVE;
状态是对其他执行上下文的承诺。发布 DRIVE 应表示所有 DRIVE 消费者依赖的数据、对象和硬件都已就绪。若初始化必须拆成多个周期,应增加明确的 INITIALIZING/READY 握手或就绪位,消费者只在状态与对应版本同时满足时运行。
此外还应审查三个启动薄弱点:启动等待没有超时;启动从核的返回值被忽略;推进令牌和发布系统状态前未见显式内存屏障。这些问题在正常路径上可能长期不暴露,但在单核启动失败、调试器暂停某核或高优化等级下会显著放大。
十、调试验证方法
静态分析给出的是待验证假设。上板调试时可以围绕“顺序、核身份、周期和先后关系”建立最小证据链:
- 记录启动令牌:对令牌设置写监视点或轻量追踪,确认严格出现 00 到 42 的预期序列,并记录每一步的核身份与时间戳。
- 核对 CPU_CORE_ID:在三核入口、各阶段函数和关键 ISR 中读取核 ID,确认“代码放置”和“实际执行核”没有被混淆。
- 观察 STM 计数:分别记录三核 1 ms/10 ms 回调的首次触发时间和周期抖动,验证 CPU2、CPU0、CPU1 的实际启动相位。
- 验证 Init/Main 顺序:在应用初始化完成点和 PWM 快速主循环入口设置追踪,确认任何 Main 都不会早于对应 Init 完成。
- 注入启动故障:让一个从核不推进令牌、让从核启动返回失败,确认系统能超时、记录原因并进入受控状态,而不是永久忙等。
- 验证共享快照:让生产者快速更新多字段命令,消费者检查版本号与校验字段,确认不会读到跨周期拼接的数据。
- 验证暂停协议:延迟目标核响应 GPSR,确认发起核会等待 ack,并在超时后放弃修改而不是继续写共享数据。
测量应使用调试器追踪、GPIO 标记或低开销环形日志,具体阈值根据系统周期预算确定。不要把调试器单步下的顺序直接当作自由运行时序结论。
结语
TC275 三核基础软件的核心难点不在“启动三个核”,而在把启动期的串行依赖和运行期的并发访问分开治理。共享令牌可以建立清晰的启动顺序,但必须有超时、失败诊断和内存可见性;独立 STM 与 PWM ISR 可以提供确定的执行入口,但必须以初始化完成和一致快照为前提;DSPR 的全局可寻址能力可以承载共享数据,却不能替代原子操作和所有权协议。
系列导航:ECU 软件工程系列总纲 | 下一篇:ECU 系统运行状态机设计:从上电启动到休眠、唤醒与 Bootloader
评论