第二章:启动流程基础——CPU复位后的第一条指令
各位同学,今天我们来聊聊嵌入式系统里最基础、也最容易被忽视的部分——CPU复位后到底发生了什么。
说实话,我刚开始做嵌入式那会儿,总觉得启动流程就是个“黑盒子”。按下电源键,系统就跑了,谁管它中间经历了什么?直到有一次,我在调试一块新板子时,死活起不来系统。折腾了两天,最后发现是启动介质选择出了问题。嗯,从那以后,我就老老实实把启动流程啃了个透。
2.1 CPU复位后的第一条指令
CPU上电复位后,它做的第一件事是什么?
说白了,就是去一个固定的地址取指令。这个地址,就是所谓的“复位向量”(Reset Vector)。
不同的CPU架构,这个地址不一样:
- ARM Cortex-M系列:复位后从0x00000000取向量表,其中第一个字是栈指针,第二个字才是复位入口地址
- ARM Cortex-A系列:通常从0x00000000或0xFFFF0000开始执行
- x86架构:经典地址是0xFFFFFFF0(也就是4GB地址空间的最后16字节)
- RISC-V:复位地址通常是0x80000000或0x00000000,取决于具体实现
关键点:复位向量地址是硬件固化的,你改不了。但你可以通过修改BootROM代码,来决定它去加载什么。
我记得有一次调试一块基于Cortex-M3的MCU,客户说板子怎么都跑不起来。我一看,复位向量表里第一个字写的是0xFFFFFFFF——栈指针没初始化。CPU一复位就去取栈指针,结果取了个非法地址,直接HardFault。你说冤不冤?
2.2 启动介质选择
CPU拿到第一条指令后,接下来就要决定:我的程序从哪儿来?
这就涉及到启动介质的选择了。常见的启动介质有这几种:
| 介质类型 | 接口方式 | 读取速度 | 是否支持XIP | 典型应用 |
|---|---|---|---|---|
| NOR Flash | 并行/SPI | 较快 | ✅ 支持 | BIOS、BootROM、代码存储 |
| NAND Flash | 并行 | 快(但需ECC) | ❌ 不支持 | 大容量数据存储 |
| eMMC | MMC/SDIO | 很快 | ❌ 不支持 | 手机、平板、嵌入式Linux |
| SD卡 | SPI/SDIO | 中等 | ❌ 不支持 | 开发板、便携设备 |
| SPI Flash | SPI | 较慢 | 部分支持 | 低成本方案 |
这里有个概念很重要——XIP(Execute In Place,原地执行)。什么意思呢?就是CPU可以直接在Flash上执行代码,不用先把代码拷贝到RAM里。
NOR Flash支持XIP,所以很多BootROM直接放在NOR Flash里,CPU复位后就能直接跑。NAND Flash就不行,它只能按页读取,而且可能有坏块,所以必须先把BootROM的前几页读到内部SRAM里,再跳过去执行。
我的经验:选启动介质时,别光看速度。NOR Flash虽然贵,但调试阶段特别好用。我习惯在开发板上保留一个NOR Flash作为“救急启动”,万一NAND或eMMC里的系统坏了,还能从NOR启动来救砖。
2.3 启动设备优先级
一个系统里往往有多个启动介质,那CPU怎么知道该从哪个启动呢?
这就涉及到启动设备优先级了。说白了,就是BootROM里写死了一个“启动顺序表”。
我举个例子,以某款常用的i.MX系列处理器为例:
// 启动设备优先级配置(伪代码)
BootDevice boot_order[] = {
BOOT_DEVICE_NOR_FLASH, // 优先级最高
BOOT_DEVICE_NAND_FLASH, // 第二
BOOT_DEVICE_EMMC, // 第三
BOOT_DEVICE_SD_CARD, // 第四
BOOT_DEVICE_SPI_FLASH, // 最低
BOOT_DEVICE_USB, // 特殊:用于烧录
BOOT_DEVICE_SERIAL, // 特殊:串口下载
};
CPU复位后,BootROM会按照这个顺序,依次检查每个设备上是否有有效的启动镜像。怎么检查呢?通常是读设备的特定位置,看看有没有“启动签名”(Boot Signature)。
比如:
- NOR Flash:检查0x00000000处是否为有效的向量表
- eMMC:检查第0个扇区的MBR或GPT分区表
- SD卡:检查特定偏移处的启动签名(比如0x1FE处的0x55AA)
一旦找到有效的启动镜像,BootROM就把它加载到RAM里,然后跳过去执行。如果所有设备都检查完了还没找到,那就进入“死循环”或者“下载模式”。
避坑指南:我曾经遇到过一个坑——某款芯片的BootROM默认先检查eMMC,再检查SD卡。但客户的设计是SD卡里放了启动程序,eMMC是空的。结果每次上电,BootROM都去检查eMMC,发现没签名,然后...就卡住了。因为eMMC虽然空,但控制器返回的状态是“设备存在”,BootROM就傻傻地等它准备好。最后我只好改硬件,把SD卡的检测引脚拉高,让BootROM跳过eMMC检查。
2.4 启动流程核心逻辑图
下面我用一张图来总结整个启动流程的核心逻辑。这张图是我自己画的,希望能帮大家理清思路。
这张图展示了从CPU复位到最终执行启动代码的完整路径。你想想看,整个过程其实就三步:取向量、查设备、加载执行。但每一步里都有很多细节,稍不注意就会踩坑。
2.5 实际项目中的考量
在实际项目中,启动介质的选择和优先级配置,往往不是技术问题,而是成本和可靠性的权衡。
我个人习惯的做法是:
- 量产产品:用eMMC或NAND Flash做主存储,NOR Flash只放一个最小的BootROM(能初始化DDR和eMMC就行)
- 开发板:保留SD卡启动选项,方便调试时快速换系统
- 安全要求高的产品:把NOR Flash设为最高优先级,因为NOR Flash支持XIP,不容易被篡改
总结一下:启动流程看似简单,但它是整个系统的基石。CPU复位后的第一条指令、启动介质的选择、设备优先级的配置,这三者环环相扣。任何一个环节出问题,系统都跑不起来。
好了,这一章的内容就到这里。下一章我们会深入BootROM的内部实现,看看它到底是怎么初始化硬件、怎么加载启动镜像的。到时候我会拿一个实际项目的代码来讲解,保证干货满满。
课后小练习:找一块你手头的开发板,查一下它的芯片手册,看看复位向量地址是多少,支持的启动介质有哪些,优先级是怎么配置的。然后试着用示波器抓一下启动时的信号,看看实际流程和手册描述是否一致。
可可精品资料库