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执行流程:

Boot ROM执行流程图 Boot ROM 执行流程与设备选择状态机 上电复位 / POR 基础硬件初始化(时钟/堆栈) 读取Boot Mode引脚电平 根据引脚状态选择启动设备 eMMC NAND SPI Nor eMMC初始化与读取 NAND初始化+ECC SPI Nor初始化 加载SPL到内部SRAM 校验Magic/CRC并跳转

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中执行。
三种启动设备的ROM加载策略对比 Boot ROM 对不同启动设备的加载策略对比 eMMC 加载流程 1. 发送CMD0复位 2. 发送CMD2获取CID 3. 发送CMD7选中设备 4. 读取LBA 0 (512B) 5. 硬件ECC自动处理 6. 复制到SRAM NAND 加载流程 1. 发送FFh复位 2. 读取ID (90h) 3. 读取Page 0 (2KB) 4. 硬件ECC校验 5. 若失败,尝试Page 1 6. 复制到SRAM SPI Nor 加载流程 1. 发送0xAB唤醒 2. 读取JEDEC ID 3. 发送0x0B Fast Read 4. 从0x0读取数据 5. 无需ECC/坏块处理 6. 复制到SRAM 说明:NAND启动最复杂,需要处理ECC和坏块回退;eMMC启动最简洁,硬件封装了复杂性;SPI Nor启动最直接,但容量受限。

4.7 本章小结