2、嵌入式系统启动基础:CPU复位向量、内存映射、启动介质(NOR/NAND/eMMC/SD)与启动模式
嵌入式系统上电后的第一行代码从哪里执行?CPU如何知道自己的程序放在哪里?为什么有的系统从NOR Flash启动,有的从eMMC启动,而有的却要先经过一个BootROM?这些问题的答案都指向本章的核心主题——嵌入式系统的启动基础。理解复位向量、内存映射、启动介质与启动模式,是掌握U-Boot移植与底层硬件初始化的前提。本章将从CPU的硬件行为出发,逐步拆解从“上电”到“U-Boot第一阶段执行”之间的完整链路。
上图展示了嵌入式系统从复位到U-Boot执行的完整链路。下面我们将逐一深入每个环节的细节。
2.1 CPU复位向量:第一条指令从哪里来
核心原理
CPU复位向量(Reset Vector)是处理器在上电或复位后,硬件强制跳转到的第一个取指地址。这个地址是CPU架构层面定义的,不依赖任何操作系统或Bootloader。不同架构的复位向量位置差异很大:ARM Cortex-A系列默认从0x00000000开始取指,但可以通过设置CP15的V位(High Vector)将异常向量表重定位到0xFFFF0000;RISC-V架构在M模式下复位后从0x00001000开始执行;而x86架构则从0xFFFFFFF0(4GB地址空间的最后16字节)开始,那里通常存放一条跳转指令。
复位向量的本质是CPU硬件逻辑中一个固定的地址解码行为。当复位信号释放后,CPU的取指单元直接使用这个固定地址发起第一次内存访问。因此,系统设计者必须保证该地址处存在有效的非易失性存储(或BootROM),否则CPU将取到无效指令导致启动失败。
b reset或j _start)就存放在这个地址处。对于现代SoC,这个地址通常指向芯片内部的BootROM,而不是外部Flash。
判断方法与操作流程
在实际开发中,确认复位向量地址是移植U-Boot的第一步。可以通过以下流程确认:
- 查阅芯片手册(Reference Manual)中的“Reset”或“Boot”章节,找到复位向量地址。
- 检查U-Boot源码中对应的链接脚本(.lds文件),确认
_start符号被链接到正确地址。 - 使用JTAG调试器连接目标板,复位后读取PC寄存器值,验证是否为预期地址。
- 如果使用安全启动,确认BootROM中的ROM Code是否先执行了签名校验再跳转。
案例拆解:i.MX6ULL的复位向量
以NXP i.MX6ULL为例,该芯片内部集成96KB BootROM,复位后ARM Cortex-A7核心从0x00000000取指,但该地址被映射到BootROM。BootROM代码首先初始化时钟和DDR(如果启动介质需要),然后根据BOOT_CFG引脚或eFUSE值选择启动设备。如果选择SD卡启动,BootROM会读取SD卡特定扇区(通常偏移1KB处)的镜像到SRAM,校验通过后跳转执行。这个过程中,外部NOR Flash的0x00000000地址并不会被CPU直接访问,因为BootROM已经占据了低地址空间。
2.2 内存映射:地址空间如何规划
核心原理
内存映射(Memory Map)定义了CPU地址空间中各个区域的用途。32位CPU拥有4GB地址空间,这些地址并非全部对应DRAM,而是被划分为多个区域:内部ROM、内部SRAM、外部存储控制器(NOR/NAND/SDRAM)、外设寄存器等。以i.MX6ULL为例,其内存映射大致如下:
| 地址范围 | 大小 | 用途 |
|---|---|---|
| 0x00000000 - 0x00017FFF | 96KB | 内部BootROM |
| 0x00900000 - 0x0091FFFF | 128KB | 内部SRAM |
| 0x10000000 - 0x1FFFFFFF | 256MB | 外部NOR Flash(CS0) |
| 0x20000000 - 0x2FFFFFFF | 256MB | 外部NAND/其他(CS1) |
| 0x80000000 - 0x9FFFFFFF | 512MB | DDR3 SDRAM |
| 0x40000000 - 0x4FFFFFFF | 256MB | 外设寄存器(GPIO/UART/I2C等) |
启动阶段的关键在于:CPU复位后访问的地址必须映射到非易失存储。在U-Boot的SPL阶段,代码通常运行在内部SRAM中(因为DDR尚未初始化),此时需要将SPL镜像链接到SRAM地址。当DDR初始化完成后,完整版U-Boot被加载到DDR中运行。
判断方法与操作流程
在移植U-Boot时,内存映射的确认流程如下:
- 阅读芯片手册的“Memory Map”章节,记录所有关键地址区域。
- 检查U-Boot的
include/configs/<board>.h中的CONFIG_SYS_SDRAM_BASE、CONFIG_SYS_TEXT_BASE等宏定义。 - 确认链接脚本中代码段、数据段的加载地址(LMA)和虚拟地址(VMA)。
- 使用JTAG读取
0x00000000处的内容,确认是否为BootROM代码。
board_init_r阶段才建立页表。过早开启MMU会导致地址映射混乱,增加调试难度。
2.3 启动介质:NOR/NAND/eMMC/SD的特性对比
核心原理
启动介质决定了Bootloader的存储方式和加载方式。四种主流介质各有优劣:
| 特性 | NOR Flash | NAND Flash | eMMC | SD卡 |
|---|---|---|---|---|
| 接口 | 并行/SPI | 并行 | MMC协议 | SD协议 |
| 支持XIP | 是 | 否 | 否 | 否 |
| 容量 | 1-64MB | 128MB-8GB | 4-256GB | 1-512GB |
| 读取速度 | 快(随机) | 快(顺序) | 快(块) | 中等 |
| 写入速度 | 慢 | 快(页) | 快 | 中等 |
| 坏块管理 | 无需 | 需要(软件) | 内置(硬件) | 内置 |
| 成本 | 高 | 低 | 中 | 低 |
| 典型用途 | BIOS/U-Boot | 文件系统 | 系统盘 | 便携存储 |
XIP(Execute In Place,原地执行)是NOR Flash的独特能力,CPU可以直接从NOR Flash取指执行,无需先拷贝到RAM。但NOR容量小、写入慢,因此现代系统通常只在NOR中存放SPL或完整U-Boot,内核和文件系统放在其他介质。
判断方法与操作流程
选择启动介质时,需要评估以下因素:
- 启动时间要求:如果要求上电后100ms内启动,NOR Flash的XIP模式最快;如果允许1秒启动,eMMC的块读取效率更高。
- 存储容量需求:仅存放Bootloader选NOR;需要存放内核+文件系统选eMMC或NAND。
- 可靠性要求:NAND需要软件坏块管理,eMMC内置管理更可靠。
- 成本预算:NOR最贵,NAND最便宜,eMMC和SD居中。
案例拆解:NAND坏块对启动的影响
某工业设备使用NAND Flash存放U-Boot,批量生产时发现约2%的设备无法启动。分析发现,NAND出厂时存在随机坏块,而U-Boot镜像恰好被写入包含坏块的区域。解决方案是:在烧录时使用nand write.trimffs命令跳过坏块,并在U-Boot的NAND驱动中启用坏块跳过机制(BBT)。同时,在SPL阶段读取U-Boot时,也需要遍历坏块表。这个案例说明,使用NAND作为启动介质时,必须从设计之初就考虑坏块管理策略。
2.4 启动模式:BootROM如何选择启动源
核心原理
现代SoC几乎都内置BootROM,它是一段固化在芯片内部的只读代码。BootROM的作用是:初始化必要的硬件(时钟、启动介质控制器),根据启动模式选择(Boot Mode Select)从指定介质加载Bootloader到SRAM,然后跳转执行。启动模式的选择通常由以下方式决定:
- 硬件引脚:如i.MX6的BOOT_MODE[1:0]引脚和BOOT_CFG引脚。
- eFUSE/OTP:一次性可编程存储,用于安全启动或量产配置。
- 内部寄存器:某些芯片支持软件改写启动源。
以i.MX6ULL为例,BOOT_MODE[1:0]=01表示串行下载模式(USB/UART),10表示内部Boot模式(从配置的启动设备加载)。BOOT_CFG引脚进一步细分为:NOR、NAND、SD/eMMC、SPI NOR等。
判断方法与操作流程
在实际调试中,确认当前启动模式的流程:
- 检查原理图,确认BOOT_MODE和BOOT_CFG引脚的上下拉电阻配置。
- 使用串口连接目标板,观察BootROM打印的启动信息(通常包含启动设备名称)。
- 如果BootROM支持,通过USB或UART下载模式直接加载U-Boot到SRAM调试。
- 使用JTAG读取BootROM中的启动源选择寄存器,确认实际生效的配置。
案例拆解:SD卡启动的完整流程
以某基于i.MX6ULL的物联网网关为例,其启动介质为SD卡。BootROM的启动流程如下:
- BootROM初始化SDHC控制器,时钟频率设为400kHz(识别模式)。
- 发送CMD0进入空闲态,CMD8/SD_SEND_OP_COND确认SD卡版本。
- 读取SD卡第0扇区(512字节)的MBR,查找U-Boot镜像位置(通常偏移1KB处)。
- 将U-Boot SPL(最大192KB)加载到内部SRAM(0x00900000)。
- 校验SPL的DCD(Device Configuration Data)数据,初始化DDR控制器。
- SPL从SD卡读取完整U-Boot到DDR(0x87800000),跳转执行。
这个流程中,BootROM只负责加载SPL,而DDR初始化由SPL完成。这种两级加载(Two-Stage)设计是当前主流方案,因为BootROM无法预知外部DDR的型号和时序参数。
2.5 常见误区与复盘
在嵌入式启动基础的学习和实践中,以下误区值得特别注意:
| 误区 | 后果 | 正确做法 |
|---|---|---|
| 认为所有CPU复位向量都是0x00000000 | 移植U-Boot时链接地址错误 | 查阅具体芯片手册确认 |
| 忽略BootROM的存在 | 无法理解为何外部Flash地址不是0x0 | 明确BootROM占用低地址空间 |
| NAND启动不做坏块管理 | 批量生产出现随机启动失败 | 启用BBT和ECC校验 |
| SPL阶段开启MMU | 地址映射混乱,调试困难 | SPL阶段使用物理地址 |
| 忽视启动模式引脚配置 | BootROM从错误设备加载 | 核对原理图上下拉电阻 |
复盘一个典型的排障案例:某开发板从eMMC启动失败,串口无任何输出。排查步骤为:首先用JTAG确认CPU复位后PC值正确指向BootROM;其次检查BOOT_CFG引脚,发现eMMC的启动分区配置错误;最后通过修改eMMC的EXT_CSD寄存器,将Boot Partition 1设置为启动分区,问题解决。整个过程的关键在于:先确认CPU执行流,再检查启动介质配置,最后验证镜像内容。