3. BootROM 的职责:芯片上电后第一段代码
芯片上电那一刻,发生了什么?
很多人以为第一个跑起来的是操作系统。其实不是。第一个执行的是固化在芯片内部的一段只读代码——BootROM。它被焊死在硅片上,你改不了,也删不掉。
我个人习惯把 BootROM 比作「芯片的出厂设置」。它不需要外部存储,不需要初始化内存,甚至不需要时钟稳定——它就在那里,上电即跑。
3.1 BootROM 到底干了什么?
说白了,BootROM 就三件事:
- 初始化最基础的硬件——比如 CPU 核心、一级缓存、中断向量表。
- 确定启动介质——从哪加载下一段代码?NAND Flash?eMMC?SD 卡?还是 SPI NOR?
- 加载并跳转——把下一级 bootloader(比如 SPL、U-Boot 的第一阶段)从介质里读出来,然后跳过去执行。
嗯,这里要注意:BootROM 不会初始化所有外设。它只碰那些「必须碰」的东西。比如 DDR 内存,它通常不碰——那是下一级 bootloader 的事。
核心原则:BootROM 追求最小化。代码体积越小,芯片成本越低。你想想看,BootROM 占用的 die area 可是真金白银。
3.2 启动介质的选择策略
BootROM 怎么知道从哪启动?
常见做法有几种:
| 策略 | 原理 | 典型场景 |
|---|---|---|
| 固定引脚采样 | 上电时读取几个 GPIO 电平,组合成启动索引 | NXP i.MX、TI AM335x |
| 烧写 fuse | 芯片出厂时一次性熔断 eFuse,固化启动顺序 | 安全要求高的设备 |
| 轮询尝试 | 按固定顺序逐个介质尝试,直到读到有效头部 | Allwinner、Rockchip |
| 回退机制 | 主介质失败后自动切到备份介质 | 工业级、车载设备 |
我在项目中遇到过一种情况:某款芯片的 BootROM 默认从 SPI NOR 启动,但板子上焊的是 NAND Flash。结果上电后 BootROM 读不到有效数据,直接卡死。后来查手册才发现,需要拉高一个特定引脚才能切换介质。嗯,这种坑,踩过一次就记住了。
避坑指南:我曾经因为没仔细看 BootROM 的介质检测时序,导致板子启动成功率只有 70%。后来用示波器抓了 SPI 总线才发现,BootROM 在检测介质时会发送一个特定的「读 ID」命令,如果介质响应超时,它就跳过了。解决办法是在 Flash 的 WP# 引脚上加一个上拉电阻,确保 BootROM 能正确识别。
3.3 BootROM 的典型执行流程
下面这张图展示了 BootROM 从上电到跳转的完整路径:
3.4 介质头部格式:BootROM 怎么认代码?
BootROM 读到介质上的原始二进制数据后,怎么知道这是不是合法的 bootloader?
答案是:介质头部。BootROM 会检查一个固定偏移处的魔数(magic number),以及一些关键字段。
以我调试过的某款 ARM Cortex-A7 芯片为例,它的 BootROM 期望的介质头部格式如下:
偏移 0x000:魔数 0xEA0000FE(ARM 跳转指令)
偏移 0x004:镜像总长度(字节)
偏移 0x008:加载地址(DDR 目标地址)
偏移 0x00C:入口地址(跳转目标)
偏移 0x010:校验和(累加和或 CRC32)
偏移 0x014:版本号 / 配置标志
偏移 0x018 - 0x1FF:保留 / 填充
BootROM 读到这些字段后,会做几件事:
- 校验魔数是否正确
- 计算校验和是否匹配
- 检查镜像长度是否在合理范围内
全部通过,才把代码从介质拷贝到 SRAM 或指定的加载地址,然后跳转。
注意:BootROM 通常只有很小的内部 SRAM 可用(几 KB 到几十 KB)。所以它加载的下一级 bootloader 必须足够小,能塞进这片 SRAM。这就是为什么很多芯片有 SPL(Secondary Program Loader)——它负责初始化 DDR,然后加载完整的 U-Boot。
3.5 一个实际案例:NAND Flash 启动的坑
我记得有一次调试一块板子,BootROM 能从 SPI NOR 正常启动,但切到 NAND Flash 就死活不行。
用逻辑分析仪抓了 NAND 总线,发现 BootROM 发送了读 ID 命令(0x90),NAND 也正确返回了厂商 ID 和设备 ID。但 BootROM 接下来发送读页命令时,NAND 返回的数据全是 0xFF。
查了半天,问题出在 NAND 的 坏块管理 上。BootROM 默认从第 0 块开始读,但第 0 块恰好是坏块。BootROM 没有坏块跳过能力——它太简单了,不会去读 OOB 区域检查坏块标记。
解决办法:在烧录时强制把 bootloader 写到第 1 块,并在第 0 块填充一个无效的魔数,让 BootROM 跳过它。
经验总结:如果你用 NAND Flash 做启动介质,一定要确认 BootROM 是否支持坏块跳过。大部分 BootROM 不支持。所以要么保证第 0 块是好的,要么在烧录工具里做重映射。
3.6 BootROM 的局限性
BootROM 虽然重要,但它不是万能的。我列几个常见的限制:
- 不支持文件系统——BootROM 只读裸数据,不认识 FAT32、ext4 这些。它按固定偏移读固定长度的数据。
- 不支持复杂校验——大部分 BootROM 只做简单的累加和或 CRC32,没有 RSA 签名验证。安全启动需要额外硬件模块。
- 不支持动态配置——BootROM 的行为在芯片出厂时就固定了。你不能通过软件更新它。
- 调试困难——BootROM 执行期间,串口通常还没初始化。你没法用 print 调试。我一般用 GPIO 翻转来测量执行时间。
嗯,说到这里,你应该明白了:BootROM 是芯片启动的起点,但它只做最必要的事。真正的重头戏,在下一级 bootloader 里。