硬件基础:CPU复位行为、复位向量表、内存映射与地址空间
做BootROM开发,第一件事不是看代码,而是搞清楚芯片上电那一刻,CPU到底在干什么。说白了,你得知道CPU醒来后第一眼往哪儿看,手里拿着什么,脚下踩着哪块地。
我刚开始做嵌入式时,以为复位就是“从头跑一遍”。后来调一个启动bug调了三天,才发现是复位向量表配错了。嗯,从那以后我再也不敢小看这个环节了。
CPU复位行为:上电后CPU的第一口呼吸
CPU复位,本质上是一个硬件状态机。当复位信号(比如POR、外部RESET引脚)被释放后,CPU内部寄存器会被强制设为预设值。最关键的是程序计数器(PC)——它决定了CPU从哪个地址取第一条指令。
核心要点:不同架构的复位PC值不同。ARM Cortex-M系列固定从0x00000000取向量表,而Cortex-A系列则从0x00000000或0xFFFF0000取第一条指令。x86则从0xFFFFFFF0开始。
我个人习惯把复位行为拆成三个阶段来看:
- 复位信号有效期:CPU内部逻辑全部冻结,所有寄存器回到默认值。
- 复位释放瞬间:CPU开始执行内部微码或硬件逻辑,加载复位向量。
- 第一条指令取指:PC指向复位向量表指定的地址,开始执行。
避坑指南:我曾经在一个项目中,复位信号释放太快,电源还没稳定CPU就开始跑,结果PC读到的是垃圾数据。后来我加了一个电源监控芯片,确保VDD稳定后再释放复位。记住:复位时序不是越快越好。
复位向量表:CPU的“第一本字典”
复位向量表,说白了就是一张地址表。CPU复位后,会从这个表里读出各个异常或中断的入口地址。对于BootROM来说,我们最关心的是复位向量——也就是第0号向量。
以ARM Cortex-M为例,向量表的前两个条目是:
| 偏移 | 内容 | 说明 |
|---|---|---|
| 0x00 | 初始栈指针(MSP) | CPU复位后自动加载到SP寄存器 |
| 0x04 | 复位向量(Reset_Handler) | CPU复位后第一条指令的地址 |
| 0x08 | NMI向量 | 不可屏蔽中断入口 |
| 0x0C | HardFault向量 | 硬件错误处理入口 |
注意:向量表必须对齐到地址边界。Cortex-M要求向量表基址按128字节对齐(即低7位为0)。我曾经见过有人把向量表放在非对齐地址,结果CPU取到的复位向量全是错的——芯片直接“死”在启动阶段。
你想想看,如果复位向量指向的地址里没有有效代码,CPU会怎样?它会取到随机数据,然后执行非法指令,最终触发HardFault。所以BootROM的第一件事,就是确保复位向量正确,并且指向的代码段已经就绪。
内存映射与地址空间:芯片的“地图”
内存映射,就是芯片内部各个模块在CPU地址空间中的分布。CPU通过地址总线访问外设、SRAM、DDR等,每个模块都被分配了一段连续的地址范围。
我画了一张典型SoC的内存映射图,你看一眼就明白了:
你看,地址空间被划分成几个区域。BootROM通常放在最低地址,因为CPU复位后默认从0x00000000开始取指。片上SRAM在另一个地址段,速度极快但容量有限。片外DDR容量大,但需要初始化后才能用——这就是BootROM存在的意义之一。
片上SRAM与片外DDR:BootROM的“工作台”和“仓库”
片上SRAM和片外DDR,是BootROM执行过程中最重要的两个存储区域。我习惯把SRAM比作“工作台”,DDR比作“仓库”。
| 特性 | 片上SRAM | 片外DDR |
|---|---|---|
| 访问速度 | 极快(1-2个CPU周期) | 较慢(几十个周期,取决于频率和时序) |
| 容量 | 小(几KB到几MB) | 大(几十MB到几GB) |
| 初始化 | 无需初始化,上电即可用 | 需要配置控制器、时序、刷新等 |
| 用途 | BootROM代码运行、堆栈、临时数据 | 存放用户程序、大块数据 |
关键点:BootROM本身通常放在片上ROM或Flash中,但它运行时需要SRAM作为数据空间。而DDR在BootROM执行初期是不可用的——BootROM必须先初始化DDR控制器,然后才能把用户程序从Flash/NAND/eMMC等介质加载到DDR中。
我记得有个项目,BootROM代码里忘了初始化DDR的ZQ校准,结果DDR在低温下读写不稳定,用户程序加载到一半就挂了。后来我在BootROM里加了一段DDR自检流程,跑完自检才继续加载。嗯,这种坑踩过一次就记住了。
总结一下
这一章我们聊了CPU复位后怎么“睁眼”,向量表怎么指路,地址空间怎么划分,以及SRAM和DDR各自扮演什么角色。说白了,BootROM就是在这张地址地图上,从0x00000000出发,一步步把系统拉起来。