3. 启动流程与BootROM:ARMv9标准启动流程、BootROM设计原理、启动模式选择
ARMv9的启动流程,说白了就是芯片上电后,从“什么都不会”到“跑起操作系统”的完整过程。我做了这么多年固件,见过太多启动阶段翻车的案例——有时候不是代码写错了,而是压根没走到该走的路径上。
这一章,我们就把ARMv9的启动流程掰开揉碎,看看BootROM到底在干什么,以及怎么选择启动介质。
3.1 ARMv9标准启动流程
ARMv9的启动流程,可以分成几个清晰的阶段。每个阶段都有明确的责任边界。
| 阶段 | 执行位置 | 主要任务 | 典型代码量 |
|---|---|---|---|
| BootROM | 芯片内部ROM | 初始化最小系统,加载下一级Bootloader | 4KB - 32KB |
| BL1 (Primary Bootloader) | SRAM或片上SRAM | 初始化DDR,验证BL2签名 | 8KB - 64KB |
| BL2 (Secondary Bootloader) | DDR | 加载ATF、OP-TEE、U-Boot等 | 64KB - 256KB |
| BL31 (ATF) | DDR | 提供运行时服务,管理异常等级切换 | 128KB - 512KB |
| BL32 (TEE) | DDR | 安全操作系统,处理安全敏感操作 | 256KB - 2MB |
| BL33 (U-Boot/EDK2) | DDR | 加载操作系统内核 | 512KB - 4MB |
你可能会问:为什么需要这么多阶段?直接一个Bootloader搞定不行吗?
嗯,这里有个关键点——安全。ARMv9强制要求安全启动链,每个阶段都要验证下一阶段的签名。如果只有一个Bootloader,那验证逻辑就太复杂了,而且一旦出问题,整个系统都起不来。
我在项目中遇到过,某个芯片厂商把BL1和BL2合并了,结果每次升级都要重新烧写整个Bootloader,风险极高。后来还是老老实实拆开了。
3.2 BootROM设计原理
BootROM是芯片上电后执行的第一段代码。它固化在芯片内部的ROM里,用户改不了。所以BootROM的设计必须极其谨慎——一旦流片回来发现Bug,那只能通过芯片版本迭代来修复。
3.2.1 BootROM的核心职责
- 最小系统初始化:设置CPU异常向量表、初始化栈指针、配置系统时钟。
- 启动介质检测:按照预设的优先级,轮询eMMC、NOR Flash、UART等介质。
- 加载并验证BL1:从检测到的介质中读取BL1镜像,验证其签名或哈希值。
- 跳转到BL1:验证通过后,跳转到BL1的入口地址。
关键设计原则:BootROM必须足够小、足够快、足够可靠。它不需要处理复杂逻辑,但必须保证在任何异常情况下都能给出明确的错误指示。
3.2.2 BootROM的典型工作流程
下面这张图,是我自己画的一个BootROM工作流程。你看完就能明白,为什么BootROM的代码量通常只有几KB。
3.2.3 BootROM的异常处理
BootROM如果遇到错误,比如介质损坏、签名验证失败,它会怎么做?
我个人习惯的做法是:让BootROM进入一个死循环,同时通过GPIO或UART输出错误码。这样调试时,用示波器量一下GPIO电平,或者接上串口看输出,就能知道问题出在哪。
小技巧:在BootROM中预留一个“恢复模式”入口。比如检测到某个GPIO被拉低,就强制进入UART下载模式。这样即使eMMC里的Bootloader坏了,也能通过串口救回来。
3.3 启动模式选择
ARMv9芯片通常支持多种启动介质。选择哪种,取决于你的产品需求。
3.3.1 eMMC启动
eMMC是目前最主流的启动介质。它速度快、容量大、支持分区管理。
- 优点:读写速度快,支持硬件分区(Boot Partition、User Partition),支持安全擦除。
- 缺点:需要初始化MMC控制器,BootROM代码量稍大。
- 典型场景:手机、平板、工业控制板。
我记得有一次,客户反馈说设备偶尔启动不起来。排查了半天,发现是eMMC的Boot Partition里写入了错误的数据。后来我们在BootROM里加了CRC校验,问题就解决了。
3.3.2 NOR Flash启动
NOR Flash支持XIP(eXecute In Place),也就是说,代码可以直接在Flash里运行,不需要拷贝到RAM里。
- 优点:支持XIP,启动延迟极低,可靠性高。
- 缺点:容量小(通常64MB以下),写入速度慢。
- 典型场景:路由器、物联网网关、车载ECU。
注意:NOR Flash的XIP模式虽然快,但访问速度远低于DDR。如果你的Bootloader代码量较大,建议还是先拷贝到SRAM里再执行。
3.3.3 UART启动
UART启动主要用于调试和工厂烧录。BootROM通过UART接收数据,直接写入SRAM或DDR。
- 优点:不需要额外的硬件,一根串口线就能搞定。
- 缺点:速度极慢(通常115200bps),不适合量产。
- 典型场景:开发调试、小批量烧录、紧急恢复。
我曾经在产线上遇到过一个问题:UART启动时,因为串口线太长,信号衰减导致数据错位。后来我们换成了带屏蔽的串口线,问题就解决了。嗯,有时候硬件问题比软件问题更隐蔽。
3.4 启动模式选择对比
| 特性 | eMMC | NOR Flash | UART |
|---|---|---|---|
| 启动速度 | 快(~100MB/s) | 极快(XIP模式) | 极慢(~11KB/s) |
| 容量 | 大(4GB - 256GB) | 小(1MB - 64MB) | 无限制(取决于上位机) |
| 可靠性 | 中等(有磨损均衡) | 高(无磨损问题) | 低(依赖外部设备) |
| 成本 | 中等 | 高 | 极低 |
| 适用阶段 | 量产 | 量产 | 开发/调试 |
3.5 实战:配置启动模式
在实际项目中,启动模式的选择通常通过芯片的Boot Pin来配置。比如,某款ARMv9芯片的Boot Pin配置如下:
// Boot Pin 配置示例
// BOOT_MODE[2:0] 选择启动介质
// 000: eMMC Boot Partition 1
// 001: eMMC Boot Partition 2
// 010: NOR Flash
// 011: UART
// 100: 保留
// 101: 保留
// 110: 保留
// 111: 恢复模式
#define BOOT_MODE_REG (*(volatile uint32_t *)0x10000000)
#define BOOT_MODE_MASK 0x07
uint32_t boot_mode = BOOT_MODE_REG & BOOT_MODE_MASK;
switch (boot_mode) {
case 0x00:
// eMMC Boot Partition 1
emmc_boot(0);
break;
case 0x01:
// eMMC Boot Partition 2
emmc_boot(1);
break;
case 0x02:
// NOR Flash
nor_flash_boot();
break;
case 0x03:
// UART
uart_boot();
break;
case 0x07:
// 恢复模式
recovery_mode();
break;
default:
// 默认尝试 eMMC
emmc_boot(0);
break;
}
避坑指南:我曾经遇到过,某个芯片的Boot Pin在复位瞬间有毛刺,导致BootROM读到了错误的模式。解决方案是在BootROM里加一个去抖逻辑——连续读三次,取多数值。
3.6 小结
ARMv9的启动流程,核心就是BootROM。它虽然小,但责任重大。理解了BootROM的设计原理,你就能明白为什么芯片上电后能自动找到Bootloader,以及为什么有时候启动会失败。
启动模式的选择,说白了就是根据产品需求,在速度、容量、成本之间做权衡。没有最好的方案,只有最合适的方案。