4. AURIX启动流程与分区选择

在双分区 OTA 方案中,真正决定“这次上电到底运行哪个分区”的,不是应用层的升级逻辑,而是芯片上电后最先执行的那段固化代码。AURIX(以 TC3xx 为例)的启动链路可以概括为:SSW 固件执行 → 读取 BMHD → 校验 BMHD → 选择启动模式 → 定位并校验用户代码 → 跳转到有效分区。理解这条链路,才能设计出可靠的 A/B 分区切换与回滚机制。

SSW:上电后的第一段代码

SSW(Startup Software)是固化在芯片 Boot ROM 中的启动固件,用户无法修改,但可以通过配置影响它的行为。上电复位释放后,CPU 从固定入口进入 SSW,SSW 依次完成基础时钟与 PLL 初始化、必要 RAM 的自检、看门狗相关寄存器的初始状态设置,然后进入最关键的一步:读取 BMHD(Boot Mode Header)。

SSW 会从 Flash 的固定地址读取 BMHD 结构。TC3xx 通常提供多个 BMHD 副本(例如 BMHD0 到 BMHD3),SSW 按顺序检查每个副本的合法性,选择第一个通过校验的 BMHD 作为本次启动依据。这个“多副本 + 顺序检查”的机制本身就是一种冗余保护:只要有一个副本完好,芯片仍能正常启动。

BMHD 校验:CRC、密码与启动模式字段

BMHD 是一个带校验的头部结构,核心字段包括:

  • BMI(Boot Mode Index):决定启动模式,例如内部 Flash 启动、外部启动、串口引导(ASC Bootstrap)等。
  • BMHDID:标识该头部是否有效,通常为固定值。
  • STAD(User Code Start Address):用户代码入口地址,SSW 校验通过后会跳转到该地址。
  • CRC 与 CRC 反转值:SSW 对 BMHD 内容计算 CRC,并与存储的 CRC 及其反码比对,两者必须同时匹配。
  • 密码字段(PW):部分模式下用于解锁调试或保护配置。

校验逻辑可以简化为:CRC_calc == CRC_stored 且 CRC_stored == ~CRC_inverted。任一条件不满足,该 BMHD 副本即被判为无效,SSW 继续检查下一个副本。如果所有副本都无效,芯片会进入错误处理状态(通常表现为反复复位或停留在 Boot ROM),此时只能通过调试接口或引导模式恢复。

注意:BMHD 的 CRC 覆盖范围、字节序和计算初值必须严格按芯片手册实现。OTA 过程中若在擦写 BMHD 所在扇区时掉电,可能导致该副本损坏;因此工程上通常保留至少两个有效副本,且不在同一次擦写操作中同时更新。

启动模式选择与分区有效性判断

BMHD 校验通过后,SSW 根据 BMI 决定启动模式。对于双分区 OTA,最常用的是“内部 Flash 启动”模式,此时 SSW 会读取 STAD 指向的地址,并进一步检查用户代码头部的有效性信息。这里存在两层判断:

  1. BMHD 层判断:决定“从哪个入口地址开始找代码”。
  2. 分区层判断:决定“这个分区里的代码是否可信、是否完整”。

分区层判断通常由用户自定义的头部结构完成,例如在分区起始处放置一个 PartitionHeader,包含 magic、版本号、长度、CRC 和状态标志。SSW 本身不解析这个自定义头部,因此实际工程中常见两种做法:

  • 方案 A:SSW 直接跳转到固定入口,由该入口处的 Bootloader 读取分区头,判断 A/B 分区有效性,再跳转到真正的应用分区。
  • 方案 B:通过 BMHD 的 STAD 指向当前有效分区,由 OTA 流程在切换时更新 BMHD,使 SSW 直接跳到目标分区。

方案 A 更灵活,适合需要复杂回滚策略的场景;方案 B 切换更直接,但每次切换都要擦写 BMHD,风险更高。实际项目中常采用“Bootloader + 分区头”的混合方式:BMHD 始终指向 Bootloader,Bootloader 负责分区选择。

跳转逻辑与回滚触发条件

以 Bootloader 方案为例,其跳转逻辑可归纳为:读取 A、B 两个分区的头部 → 分别校验 magic 与 CRC → 得到每个分区的“有效/无效”状态 → 结合升级标志和尝试计数决定启动目标。一个典型的判断顺序是:

1. 读取 A 分区头,校验 magic + CRC → validA
2. 读取 B 分区头,校验 magic + CRC → validB
3. 若存在“待验证升级标志”且目标分区有效:
      跳转到目标分区,并递增尝试计数
4. 若目标分区无效或尝试计数超限:
      清除升级标志,回滚到另一有效分区
5. 若两个分区都无效:
      进入恢复模式(等待串口/CAN 升级)

这里的“尝试计数”是回滚的关键。新固件第一次启动后,若在限定时间内未调用“确认接口”清除升级标志,Bootloader 会在下次复位时判定升级失败并回滚。示例参数:尝试计数上限设为 3,确认超时设为 5 秒。这些值需根据实际启动时间和看门狗策略调整,属于工程示例而非固定值。

AURIX 启动链路与双分区选择逻辑 SSW 固件 时钟/RAM/看门狗初始化 读取 BMHD 副本 BMHD0 → BMHD1 → … BMHD CRC 校验 CRC + 反码双重比对 BMI 启动模式选择 内部Flash / 外部 / 引导 BMHD 无效 检查下一副本 / 错误处理 Bootloader 分区选择 读取 A/B 分区头,校验 magic + CRC 分区 A Header: magic / CRC / 版本 状态: 有效 / 无效 分区 B Header: magic / CRC / 版本 状态: 有效 / 无效 目标分区 有效? 是 否 回滚到另一有效分区 清除升级标志 / 计数复位 跳转到应用入口 递增尝试计数 / 启动应用 启动固件 有效路径 异常/回滚

工程实现中的关键约束

把上述逻辑落到代码时,有几个约束必须遵守。第一,BMHD 的擦写必须原子化:如果一次擦写会同时影响两个副本,应改为先擦一个、写一个、校验通过后再处理另一个。第二,分区头的 CRC 必须覆盖整个应用镜像,包括向量表和常量区,否则部分损坏可能被漏检。第三,跳转前要关闭看门狗或重设其超时,因为应用初始化时间可能超过 Bootloader 阶段的看门狗窗口。第四,尝试计数应存储在非易失区域(如独立的 Flash 扇区或带备份的 DFlash),避免掉电丢失导致无限重试。

一个可参考的分区头结构示例如下(字段为示例,实际按项目定义):

typedef struct {
    uint32_t magic;        // 0x50415254 ("PART")
    uint32_t version;      // 分区版本号
    uint32_t image_size;   // 应用镜像字节数
    uint32_t image_crc;    // 镜像 CRC32
    uint32_t header_crc;   // 本头部 CRC32
    uint8_t  state;        // 0=无效 1=有效 2=待验证
    uint8_t  reserved[3];
} PartitionHeader;

Bootloader 读取该结构后,先校验 header_crc,再校验 image_crc,两者都通过才认为分区有效。若 state == 2(待验证),则启动后由应用调用确认接口将其改为 1;若超时未确认,Bootloader 在下次复位时将其改回 0 并回滚。这套机制与 SSW/BMHD 的分工是清晰的:SSW 保证“能启动到 Bootloader”,Bootloader 保证“启动到正确的应用分区”。

调试启动问题时,可先确认 BMHD 是否被正确烧录(读回 CRC 字段比对),再确认 Bootloader 是否被 SSW 正确跳转(在 Bootloader 入口翻转一个 GPIO 或用调试器设断点),最后确认分区头校验结果。按这个顺序排查,能快速定位问题落在哪一层。