启动模式与硬件基础:ARM Cortex-M/NXP i.MX 等常见 MCU 的启动模式、复位向量表、启动介质选择
做 Bootloader 开发,第一件事不是写代码,而是搞清楚芯片上电后到底干了什么。说白了,你得知道 CPU 从哪里开始跑,它凭什么决定从 Flash 启动还是从 SD 卡启动。这些基础搞不清楚,后面调试起来会非常痛苦。
我个人习惯,拿到一款新 MCU,第一件事就是翻它的 System Control 和 Boot ROM 章节。别急着看外设驱动,先把启动流程吃透。今天我们就来聊聊 ARM Cortex-M 系列以及 NXP i.MX 这类应用处理器,它们的启动模式到底是怎么回事。
复位向量表:CPU 的第一口奶
ARM Cortex-M 内核上电后,硬件会自动做两件事:
- 从地址 0x00000000 加载主栈指针 (MSP) 的值
- 从地址 0x00000004 加载复位向量,也就是程序入口地址
这就是所谓的“复位向量表”。Cortex-M 的向量表跟老式 ARM7 不一样,它里面存的是地址,不是指令。你想想看,CPU 一醒来,直接拿到 PC 指针的值,跳过去执行,干净利落。
关键点: 向量表起始地址在 Cortex-M 里是可以重映射的。通过设置 SCB->VTOR 寄存器,你可以把向量表搬到 RAM 或者 Bootloader 所在的区域。这是实现 Bootloader 跳转应用的核心机制之一。
我在项目中遇到过一个问题:芯片上电后死活跑不起来,用调试器一看,PC 指针指向了一个奇怪的值。后来发现是向量表里的复位向量地址算错了,低位的 Thumb 标志位没置 1。Cortex-M 强制要求地址的最低位必须是 1,表示 Thumb 模式。这个坑,我踩过一次就再也没忘过。
启动介质选择:谁来决定从哪里启动?
不同的 MCU,启动介质的选择方式差别很大。我把它分成两类来讲:
1. 纯 Cortex-M MCU(如 STM32、NXP LPC 系列)
这类芯片通常通过 BOOT 引脚 的电平状态来决定启动介质。上电时,芯片内部的 Boot ROM 会采样这些引脚,然后决定从 Flash、System Memory(内置 Bootloader)还是 SRAM 启动。
| BOOT0 | BOOT1 | 启动介质 |
|---|---|---|
| 0 | X | 主 Flash 启动 |
| 1 | 0 | System Memory(内置 Bootloader) |
| 1 | 1 | SRAM 启动 |
小技巧: 调试 Bootloader 时,我经常把启动模式设成 SRAM。这样可以直接在 RAM 里下载和调试 Bootloader 代码,不用反复擦写 Flash,省了不少时间。
2. 应用处理器(如 NXP i.MX 系列)
i.MX 这类芯片就复杂多了。它没有简单的 BOOT 引脚,而是通过 eFUSE 或者 GPIO 上拉/下拉 来配置启动设备。上电后,内部的 Boot ROM 会按照一个预设的优先级列表去尝试启动:
- 检查 eFUSE 是否烧录了启动配置
- 如果没有,则采样 GPIO 电平,确定启动设备(比如 SD 卡、NAND Flash、SPI NOR Flash 等)
- 从选中的设备读取启动镜像,加载到内部 SRAM 或 DDR 中
我记得第一次调 i.MX6 的启动时,被它的启动流程图吓到了。分支多,优先级复杂,还涉及到不同的镜像头部格式。后来我画了一张图,才把整个流程理清楚。
启动介质的选择策略
做产品设计时,启动介质的选择直接影响 Bootloader 的复杂度。我个人总结了几条经验:
- NOR Flash: 支持 XIP(就地执行),代码可以直接在 Flash 里跑。适合做 Bootloader 的存放介质,启动速度快。
- NAND Flash: 容量大、价格低,但不能 XIP。需要先把启动代码拷贝到 RAM 里再执行。Bootloader 里必须包含 NAND 控制器驱动和坏块管理。
- SD/eMMC: 常用于应用处理器。启动时 Boot ROM 会从 SD 卡读取镜像。我曾经遇到过 SD 卡初始化时序不对,导致启动失败的情况,后来加了延时才解决。
- SPI Flash: 引脚少,适合小封装芯片。但速度慢,通常只用来存 Bootloader,应用代码放在外部 DRAM 里。
注意: 有些芯片的 Boot ROM 对启动设备的时序要求非常严格。比如 NXP i.MX RT 系列,从 QSPI Flash 启动时,Flash 的 Read ID 命令响应时间必须在规定范围内。否则 Boot ROM 会认为设备不存在,直接跳过。我曾经因为这个原因,换了好几种 Flash 型号才搞定。
Bootloader 在启动流程中的位置
不管启动介质怎么选,Bootloader 的最终目标只有一个:接管硬件控制权,完成初始化,然后跳转到应用代码。具体来说,Bootloader 需要做这几件事:
- 设置系统时钟,让 CPU 和外设跑在正确的频率上
- 初始化必要的硬件(如 DDR 控制器、Flash 控制器)
- 从启动介质中读取应用镜像
- 验证镜像的完整性(CRC 或签名校验)
- 设置向量表偏移,跳转到应用入口
嗯,这里要注意一点:跳转之前,一定要把中断关掉,把外设恢复到复位状态。否则应用代码一启动,可能就被一个未处理的中断给搞死了。我见过有人跳转后系统立马 HardFault,查了半天发现是某个定时器还在跑,中断向量指向了 Bootloader 的地址。
小结
启动模式是 Bootloader 的地基。搞清楚了复位向量表、启动介质选择逻辑,你才能知道代码该放哪里、怎么放、CPU 怎么找到它。下一块内容,我们会深入到具体的启动代码实现,看看汇编那一小段到底在干什么。