2、嵌入式系统启动基础:CPU复位向量、内存映射、启动介质(NOR/NAND/eMMC/SD)与启动模式

嵌入式系统上电后的第一行代码从哪里执行?CPU如何知道自己的程序放在哪里?为什么有的系统从NOR Flash启动,有的从eMMC启动,而有的却要先经过一个BootROM?这些问题的答案都指向本章的核心主题——嵌入式系统的启动基础。理解复位向量、内存映射、启动介质与启动模式,是掌握U-Boot移植与底层硬件初始化的前提。本章将从CPU的硬件行为出发,逐步拆解从“上电”到“U-Boot第一阶段执行”之间的完整链路。

嵌入式系统启动链路:复位向量 → 内存映射 → 启动介质 → 启动模式 ① CPU复位向量 • ARM Cortex-A: 0x00000000 或 0xFFFF0000 (HiVec) • RISC-V: 0x00001000 (M-mode) • x86: 0xFFFFFFF0 (Reset Vector) • 取指 → 执行第一条指令 • 通常是一条跳转指令 (b reset / j _start) ② 内存映射 • 地址空间划分(32位/64位) • ROM/RAM/外设寄存器地址 • 片选信号 CS0~CSn • 静态映射 vs 动态映射 • 启动时无需MMU,直接物理地址 • 关键:复位后CPU访问的 地址必须映射到非易失存储 ③ 启动介质 • NOR Flash: 支持XIP,容量小 • NAND Flash: 容量大,坏块管理 • eMMC: 内置控制器,块设备 • SD/MMC: 可移动,广泛使用 • SPI NOR: 小容量,低成本 • 介质决定启动方式 • 介质决定U-Boot镜像格式 ④ 启动模式 • BootROM • 外部NOR • 外部NAND • SD/eMMC • UART/USB • 由Boot引脚/ OTP/寄存器决定 典型启动流程(以i.MX6为例) BootROM → 读取启动设备 → 加载U-Boot SPL → SPL初始化DDR → 加载完整U-Boot → U-Boot加载内核 BootROM阶段 1. 固化在芯片内部ROM 2. 初始化时钟/串口/启动介质 3. 根据eFUSE/GPIO选择启动源 4. 加载SPL到SRAM (≤192KB) 5. 校验签名(安全启动) 6. 跳转到SPL入口 SPL阶段 1. 最小化U-Boot(~20-50KB) 2. 初始化DDR控制器 3. 初始化时钟/PLL 4. 从启动介质加载完整U-Boot 5. 跳转到DDR中的U-Boot 6. 完成硬件初始化 U-Boot完整阶段 1. 完整初始化外设 2. 建立内存映射(MMU) 3. 读取环境变量 4. 加载内核到DDR 5. 设置启动参数(ATAG/DTB) 6. 跳转到内核入口

上图展示了嵌入式系统从复位到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将取到无效指令导致启动失败。

关键理解:复位向量不是“软件配置”出来的,而是CPU硬件的固定行为。Bootloader的第一条汇编指令(通常是b reset或j _start)就存放在这个地址处。对于现代SoC,这个地址通常指向芯片内部的BootROM,而不是外部Flash。

判断方法与操作流程

在实际开发中,确认复位向量地址是移植U-Boot的第一步。可以通过以下流程确认:

  1. 查阅芯片手册(Reference Manual)中的“Reset”或“Boot”章节,找到复位向量地址。
  2. 检查U-Boot源码中对应的链接脚本(.lds文件),确认_start符号被链接到正确地址。
  3. 使用JTAG调试器连接目标板,复位后读取PC寄存器值,验证是否为预期地址。
  4. 如果使用安全启动,确认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已经占据了低地址空间。

常见误区:很多初学者误以为“复位向量=Flash起始地址”。在传统ARM9(如S3C2440)中确实如此,但在现代Cortex-A系列SoC中,复位向量通常指向内部BootROM,外部Flash的起始地址可能被映射到其他位置(如0x10000000或0x30000000)。移植U-Boot时必须查阅具体芯片手册,不能想当然。

2.2 内存映射:地址空间如何规划

核心原理

内存映射(Memory Map)定义了CPU地址空间中各个区域的用途。32位CPU拥有4GB地址空间,这些地址并非全部对应DRAM,而是被划分为多个区域:内部ROM、内部SRAM、外部存储控制器(NOR/NAND/SDRAM)、外设寄存器等。以i.MX6ULL为例,其内存映射大致如下:

地址范围大小用途
0x00000000 - 0x00017FFF96KB内部BootROM
0x00900000 - 0x0091FFFF128KB内部SRAM
0x10000000 - 0x1FFFFFFF256MB外部NOR Flash(CS0)
0x20000000 - 0x2FFFFFFF256MB外部NAND/其他(CS1)
0x80000000 - 0x9FFFFFFF512MBDDR3 SDRAM
0x40000000 - 0x4FFFFFFF256MB外设寄存器(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代码。
经验提醒:在U-Boot早期阶段(SPL),不要开启MMU,直接使用物理地址访问。MMU通常在U-Boot的board_init_r阶段才建立页表。过早开启MMU会导致地址映射混乱,增加调试难度。

2.3 启动介质:NOR/NAND/eMMC/SD的特性对比

核心原理

启动介质决定了Bootloader的存储方式和加载方式。四种主流介质各有优劣:

特性NOR FlashNAND FlasheMMCSD卡
接口并行/SPI并行MMC协议SD协议
支持XIP是否否否
容量1-64MB128MB-8GB4-256GB1-512GB
读取速度快(随机)快(顺序)快(块)中等
写入速度慢快(页)快中等
坏块管理无需需要(软件)内置(硬件)内置
成本高低中低
典型用途BIOS/U-Boot文件系统系统盘便携存储

XIP(Execute In Place,原地执行)是NOR Flash的独特能力,CPU可以直接从NOR Flash取指执行,无需先拷贝到RAM。但NOR容量小、写入慢,因此现代系统通常只在NOR中存放SPL或完整U-Boot,内核和文件系统放在其他介质。

判断方法与操作流程

选择启动介质时,需要评估以下因素:

  1. 启动时间要求:如果要求上电后100ms内启动,NOR Flash的XIP模式最快;如果允许1秒启动,eMMC的块读取效率更高。
  2. 存储容量需求:仅存放Bootloader选NOR;需要存放内核+文件系统选eMMC或NAND。
  3. 可靠性要求:NAND需要软件坏块管理,eMMC内置管理更可靠。
  4. 成本预算:NOR最贵,NAND最便宜,eMMC和SD居中。

案例拆解:NAND坏块对启动的影响

某工业设备使用NAND Flash存放U-Boot,批量生产时发现约2%的设备无法启动。分析发现,NAND出厂时存在随机坏块,而U-Boot镜像恰好被写入包含坏块的区域。解决方案是:在烧录时使用nand write.trimffs命令跳过坏块,并在U-Boot的NAND驱动中启用坏块跳过机制(BBT)。同时,在SPL阶段读取U-Boot时,也需要遍历坏块表。这个案例说明,使用NAND作为启动介质时,必须从设计之初就考虑坏块管理策略。

风险提示:NAND Flash的位翻转(Bit Flip)是常态,而非异常。U-Boot镜像在NAND中存放时,必须使用ECC校验。如果SPL阶段未初始化ECC引擎,可能导致读取的U-Boot镜像损坏。建议在SPL中尽早初始化NAND控制器和ECC。

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等。

判断方法与操作流程

在实际调试中,确认当前启动模式的流程:

  1. 检查原理图,确认BOOT_MODE和BOOT_CFG引脚的上下拉电阻配置。
  2. 使用串口连接目标板,观察BootROM打印的启动信息(通常包含启动设备名称)。
  3. 如果BootROM支持,通过USB或UART下载模式直接加载U-Boot到SRAM调试。
  4. 使用JTAG读取BootROM中的启动源选择寄存器,确认实际生效的配置。

案例拆解:SD卡启动的完整流程

以某基于i.MX6ULL的物联网网关为例,其启动介质为SD卡。BootROM的启动流程如下:

  1. BootROM初始化SDHC控制器,时钟频率设为400kHz(识别模式)。
  2. 发送CMD0进入空闲态,CMD8/SD_SEND_OP_COND确认SD卡版本。
  3. 读取SD卡第0扇区(512字节)的MBR,查找U-Boot镜像位置(通常偏移1KB处)。
  4. 将U-Boot SPL(最大192KB)加载到内部SRAM(0x00900000)。
  5. 校验SPL的DCD(Device Configuration Data)数据,初始化DDR控制器。
  6. SPL从SD卡读取完整U-Boot到DDR(0x87800000),跳转执行。

这个流程中,BootROM只负责加载SPL,而DDR初始化由SPL完成。这种两级加载(Two-Stage)设计是当前主流方案,因为BootROM无法预知外部DDR的型号和时序参数。

经验总结:在调试启动问题时,建议先通过BootROM的串行下载模式(如i.MX的USB下载模式)直接加载U-Boot到内存运行,排除BootROM和启动介质的问题。确认U-Boot本身正常后,再回头排查启动介质和BootROM配置。

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执行流,再检查启动介质配置,最后验证镜像内容。