2. 嵌入式系统启动流程

嵌入式系统的启动过程是一个从硬件复位到操作系统内核运行的完整链条。理解这一流程是 Bootloader 开发的核心基础。本章将详细拆解启动的各个阶段,并明确 ROM 代码、一级 Bootloader、二级 Bootloader 的角色划分。

2.1 启动流程总览

典型的嵌入式系统启动流程可分为以下五个主要阶段:

  1. 上电复位与硬件初始化:CPU 从复位向量地址取指,执行固化在 ROM 中的启动代码。
  2. ROM 代码执行:完成最底层的硬件配置,加载一级 Bootloader 到内部 SRAM。
  3. 一级 Bootloader (SPL/MLO):初始化 DDR 等关键外设,加载二级 Bootloader 到 DDR。
  4. 二级 Bootloader (U-Boot/ATF):完成复杂外设初始化、加载操作系统内核与设备树。
  5. 操作系统启动:内核接管系统,挂载根文件系统,启动用户空间。
关键概念: 复位向量是 CPU 上电后第一条指令的地址,通常位于 ROM 或 Flash 的起始位置(如 0x00000000 或 0xFFFF0000)。这是整个启动流程的物理起点。
嵌入式系统启动流程总览 嵌入式系统启动流程总览 上电复位 复位向量取指 ROM 代码 加载 SPL 一级 Bootloader 初始化 DDR 二级 Bootloader 加载内核 OS 启动 内核运行 存储介质:ROM / Flash / SD 卡 / eMMC / NAND 代码逐级加载,从片上存储到外部 DDR,最终进入操作系统

2.2 复位向量与硬件初始化

CPU 上电后,硬件逻辑自动将程序计数器 (PC) 设置为复位向量地址。该地址通常指向 ROM 或 Flash 中的一段固定代码。此阶段完成以下操作:

  • 设置栈指针 (SP):为后续 C 语言运行环境提供栈空间。
  • 配置时钟与 PLL:将 CPU 和总线时钟从默认低频切换到目标频率。
  • 初始化关键外设:如看门狗定时器、中断控制器、内存控制器。
  • 跳转到 ROM 主函数:执行更复杂的初始化逻辑。
开发提示: 复位向量处的代码通常用汇编语言编写,因为此时 C 运行环境尚未建立。务必确保栈指针在跳转到 C 函数前已正确设置,否则会导致不可预知的崩溃。

2.3 ROM 代码的角色与职责

ROM 代码是固化在芯片内部 ROM 中的一段不可修改的启动代码,由芯片厂商提供。其主要职责包括:

功能模块 具体职责 典型实现
启动介质检测 根据 Boot Pin 或 eFuse 配置,选择从 NAND、eMMC、SD 卡、UART 或 USB 启动 读取 GPIO 电平状态
一级 Bootloader 加载 从选定的存储介质读取 SPL/MLO 到内部 SRAM NAND 控制器、MMC 控制器驱动
安全校验 验证一级 Bootloader 的签名或哈希值 SHA-256、RSA 验签
跳转执行 将控制权交给加载到 SRAM 中的一级 Bootloader 修改 PC 寄存器
注意事项: ROM 代码不可修改,因此其支持的启动介质和协议是固定的。如果芯片 ROM 不支持某种存储介质(如 SPI NAND),则必须通过一级 Bootloader 的辅助代码来弥补。

2.4 一级 Bootloader (SPL/MLO)

一级 Bootloader 通常被称为 SPL (Secondary Program Loader) 或 MLO (Mmc Loader)。它由 ROM 代码加载到内部 SRAM 中执行,受限于 SRAM 大小(通常 64KB~256KB),其功能非常精简:

  • 初始化 DDR 控制器:配置 DDR 时序、频率和地址映射,使外部 DDR 可用。
  • 初始化存储控制器:为加载二级 Bootloader 准备存储访问能力。
  • 加载二级 Bootloader:从存储介质(如 eMMC、NAND)读取 U-Boot 或 ATF 到 DDR。
  • 可选安全启动:验证二级 Bootloader 的完整性。
  • 跳转到 DDR:将控制权交给二级 Bootloader 的入口点。
// 一级 Bootloader 伪代码示例
void spl_main(void) {
    // 1. 初始化 DDR
    ddr_init(CONFIG_DDR_SPEED, CONFIG_DDR_TYPE);
    
    // 2. 初始化存储设备
    mmc_init(0);  // 初始化 eMMC
    
    // 3. 读取二级 Bootloader 到 DDR 地址 0x80000000
    mmc_read(CONFIG_BL2_OFFSET, (void*)0x80000000, CONFIG_BL2_SIZE);
    
    // 4. 可选安全校验
    if (secure_boot_enabled()) {
        verify_signature((void*)0x80000000, CONFIG_BL2_SIZE);
    }
    
    // 5. 跳转到二级 Bootloader
    jump_to(0x80000000);
}

2.5 二级 Bootloader (U-Boot/ATF)

二级 Bootloader 是功能完整的引导加载程序,运行在 DDR 中,拥有更大的代码空间和更强的能力。常见的实现包括 U-Boot 和 ARM Trusted Firmware (ATF)。其核心职责如下:

功能 说明 典型命令/接口
外设初始化 初始化网络、USB、显示、文件系统等 dm_init(); eth_init();
环境变量管理 存储启动参数、内核命令行、启动顺序 printenv; setenv; saveenv
内核加载 从存储或网络读取内核镜像到 DDR fatload mmc 0:1 0x82000000 kernel.img
设备树传递 将设备树二进制 (DTB) 加载并传递给内核 fdt addr 0x83000000; bootz 0x82000000 - 0x83000000
启动内核 设置 CPU 寄存器,跳转到内核入口 bootm; bootz; booti
Bootloader 层级与内存布局 Bootloader 层级与内存布局 ROM 代码 (不可修改) 一级 Bootloader (SPL) 二级 Bootloader (U-Boot) 操作系统内核 内部 ROM (只读) 内部 SRAM (64KB~256KB) 外部 DDR (256MB~4GB) 外部 DDR (内核区域) 关键约束 • ROM 代码不可修改 • SRAM 大小限制 SPL 功能 • DDR 初始化后才能使用 • 二级 Bootloader 可交互 • 内核加载地址需对齐 • 设备树需与内核匹配 • 安全链逐级验证

2.6 角色划分总结

下表清晰对比了 ROM 代码、一级 Bootloader 和二级 Bootloader 的核心差异:

特性 ROM 代码 一级 Bootloader (SPL) 二级 Bootloader (U-Boot)
存储位置 芯片内部 ROM Flash / SD 卡 / eMMC Flash / SD 卡 / eMMC
执行位置 内部 ROM 内部 SRAM 外部 DDR
可修改性 不可修改(固化) 可修改(需重新烧录) 可修改(需重新烧录)
大小限制 通常 4KB~64KB 通常 64KB~256KB 通常 256KB~4MB
主要功能 启动介质检测、加载 SPL DDR 初始化、加载 U-Boot 外设初始化、加载内核
用户交互 无 通常无 命令行交互
安全启动 验签 SPL 验签 U-Boot 验签内核

2.7 启动流程中的关键时序

在实际开发中,启动时间是一个重要指标。以下是一个典型嵌入式系统的启动时序示例:

启动阶段                   典型耗时
----------------------------------------
上电复位 + ROM 代码        5 ms ~ 20 ms
一级 Bootloader (SPL)      10 ms ~ 50 ms
二级 Bootloader (U-Boot)   100 ms ~ 500 ms
内核启动 + 根文件系统      500 ms ~ 2 s
----------------------------------------
总启动时间                 615 ms ~ 2.57 s
优化建议: 如果系统对启动时间敏感,应优先优化二级 Bootloader 的初始化流程,例如裁剪不必要的驱动、使用快速存储接口、启用内核压缩等。一级 Bootloader 的优化空间有限,因其功能已非常精简。

2.8 本章小结