4. 启动ROM与一级引导:片上ROM代码行为、启动设备选择(eMMC/NAND/SPI Nor)
系统上电后,CPU首先执行的不是u-boot,而是固化在芯片内部的启动ROM(Boot ROM)代码。这段代码是芯片出厂时写入的只读程序,负责最底层的硬件初始化和启动设备选择,是整个启动时序的第一棒。本章深入分析Boot ROM的行为、启动设备的选择逻辑以及一级引导(SPL)的加载过程。
4.1 启动ROM的核心职责
Boot ROM是SoC内部的一段不可修改的固件,其执行流程高度依赖于芯片厂商的设计。其主要职责包括:
- 基础硬件初始化:设置CPU模式、关闭中断、初始化堆栈指针(SP)、配置系统时钟(PLL)到安全频率。
- 启动设备检测:根据硬件引脚电平(Boot Pin)或eFuse配置,扫描并确定第一启动设备。
- 加载一级引导程序:从选定的启动设备读取固定大小(通常为8KB~128KB)的代码到内部SRAM(On-Chip SRAM)。
- 校验与跳转:对加载的代码进行简单的完整性校验(如CRC或Magic Number),校验通过后跳转到SRAM中的入口地址执行。
关键概念: Boot ROM的执行环境极其受限——此时DDR尚未初始化,CPU只能使用内部SRAM(通常只有几十KB)。因此,一级引导程序必须足够精简,能够在SRAM中完成DDR初始化,并加载完整的u-boot到DDR中。
4.2 启动设备选择机制
Boot ROM通过读取芯片特定引脚的逻辑电平(Boot Mode Pins)来决定从哪个设备启动。常见的启动设备包括eMMC、NAND Flash和SPI Nor Flash。下表对比了这三种主流启动介质的特性:
| 特性 | eMMC | NAND Flash | SPI Nor Flash |
|---|---|---|---|
| 接口类型 | MMC/SD协议(8位并行) | NAND协议(8/16位并行) | SPI串行(1/2/4线) |
| 存储密度 | 高(4GB~256GB) | 高(1GB~8TB) | 低(1MB~512MB) |
| 读取速度 | 快(顺序读约200MB/s) | 中(顺序读约50MB/s) | 中(QSPI模式约80MB/s) |
| 坏块管理 | 内置(硬件处理) | 需要软件处理(UBI/MTD) | 无需(无坏块问题) |
| 启动复杂度 | 低(Boot ROM直接读取) | 高(需处理ECC和坏块) | 低(XIP或直接映射) |
| 典型应用 | 手机、平板、高端嵌入式 | 工业控制、网络设备 | BIOS、路由器、低功耗设备 |
设计建议: 对于量产产品,建议优先使用eMMC作为启动设备,其内置的坏块管理和硬件ECC可以显著降低Boot ROM和SPL的软件复杂度。如果成本敏感且容量需求低(< 32MB),SPI Nor Flash是更可靠的选择。
4.3 Boot ROM执行流程详解
Boot ROM的执行并非简单的“读取-跳转”,而是包含多个阶段的状态机。下图展示了典型的Boot ROM执行流程:
4.4 一级引导程序(SPL)的加载细节
Boot ROM加载到SRAM中的程序通常被称为SPL(Secondary Program Loader)或MLO(MMC Loader)。SPL是u-boot源码编译出的一个精简版本,其大小受限于SRAM容量(典型值:64KB~128KB)。
4.4.1 SPL的加载约束
- 大小限制:必须小于内部SRAM大小减去Boot ROM占用空间。例如,若SRAM为128KB,Boot ROM占用32KB,则SPL最大为96KB。
- 存储位置:在eMMC中通常位于第1个分区(Boot Partition)的固定偏移(如0x0);在SPI Nor中通常位于0x0偏移;在NAND中通常位于第1个块(Block 0)。
- 格式要求:必须包含Boot ROM识别的头部信息(Magic Number、加载地址、大小、校验值)。
常见陷阱: NAND Flash的启动最为复杂。由于NAND出厂时可能包含坏块,且需要ECC校验,Boot ROM通常只能读取NAND的第1个块(Block 0)。如果Block 0损坏,系统将完全无法启动。因此,许多SoC支持“冗余启动”——如果第1个块校验失败,自动尝试第2个块。
4.4.2 SPL的典型代码结构
以下是一个典型的SPL启动代码片段(基于ARM架构),展示了从Boot ROM跳转到SPL后的初始化流程:
// arch/arm/mach-sunxi/board.c (简化示例)
void spl_board_init(void)
{
/* 1. 关闭看门狗 */
watchdog_disable();
/* 2. 配置系统时钟到更高频率(如 600MHz) */
clock_init();
/* 3. 初始化串口用于调试输出 */
uart_init();
/* 4. 初始化DDR控制器(关键步骤) */
dram_init();
/* 5. 从启动设备加载完整的u-boot到DDR */
spl_load_image(CONFIG_SYS_UBOOT_BASE);
/* 6. 跳转到u-boot入口 */
jump_to_image_no_args(&spl_image);
}
4.5 启动设备选择的硬件实现
Boot ROM通过读取一组专用引脚(Boot Mode Pins)的二进制组合来确定启动顺序。这些引脚在芯片复位期间被采样,采样结果决定了启动设备的优先级。下表展示了典型的4位引脚配置:
| BOOT[3:0] 引脚电平 | 第一启动设备 | 第二启动设备 | 回退策略 |
|---|---|---|---|
| 0000 | eMMC (Boot Partition 1) | SPI Nor | 若eMMC失败,尝试SPI Nor |
| 0001 | NAND Flash (Block 0) | eMMC | 若NAND失败,尝试eMMC |
| 0010 | SPI Nor (0x0) | UART (FEL模式) | 若SPI Nor失败,进入USB下载模式 |
| 0011 | UART (FEL模式) | 无 | 强制进入USB下载模式(用于烧录) |
调试技巧: 在开发阶段,可以将Boot Mode引脚配置为“UART下载模式”(如0011),这样系统上电后不会从Flash启动,而是等待主机通过USB或UART发送SPL镜像。这对于没有JTAG调试器的场景非常有用。
4.6 不同启动设备的ROM加载策略对比
Boot ROM针对不同存储介质的特性,采用了差异化的加载策略:
- eMMC:ROM直接发送CMD0/CMD1/CMD2序列初始化设备,然后读取固定LBA(逻辑块地址)0x0处的数据。eMMC内部硬件处理ECC和坏块,ROM无需关心。
- NAND:ROM需要发送NAND复位命令(FFh),读取ID,然后使用硬件ECC引擎读取第1页(Page 0)。如果第1页校验失败,ROM会尝试读取第2页或第2个块。
- SPI Nor:ROM通过SPI接口发送READ命令(0x03或Fast Read 0x0B),从地址0x0开始读取数据。SPI Nor支持XIP(Execute In Place),但Boot ROM通常仍将代码复制到SRAM中执行。