硬件基础:CPU复位行为、复位向量表、内存映射与地址空间

做BootROM开发,第一件事不是看代码,而是搞清楚芯片上电那一刻,CPU到底在干什么。说白了,你得知道CPU醒来后第一眼往哪儿看,手里拿着什么,脚下踩着哪块地。

我刚开始做嵌入式时,以为复位就是“从头跑一遍”。后来调一个启动bug调了三天,才发现是复位向量表配错了。嗯,从那以后我再也不敢小看这个环节了。

CPU复位行为:上电后CPU的第一口呼吸

CPU复位,本质上是一个硬件状态机。当复位信号(比如POR、外部RESET引脚)被释放后,CPU内部寄存器会被强制设为预设值。最关键的是程序计数器(PC)——它决定了CPU从哪个地址取第一条指令。

核心要点:不同架构的复位PC值不同。ARM Cortex-M系列固定从0x00000000取向量表,而Cortex-A系列则从0x00000000或0xFFFF0000取第一条指令。x86则从0xFFFFFFF0开始。

我个人习惯把复位行为拆成三个阶段来看:

  1. 复位信号有效期:CPU内部逻辑全部冻结,所有寄存器回到默认值。
  2. 复位释放瞬间:CPU开始执行内部微码或硬件逻辑,加载复位向量。
  3. 第一条指令取指: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的内存映射图,你看一眼就明白了:

典型SoC内存映射结构图 典型SoC内存映射结构图 0x0000_0000 - 0xFFFF_FFFF (4GB地址空间) BootROM 0x0000_0000 片上SRAM 0x2000_0000 外设寄存器 0x4000_0000 片外DDR 0x8000_0000 说明: • BootROM通常映射在地址0x0000_0000,CPU复位后直接从这里取指令 • 片上SRAM速度极快但容量小,用于存放临时数据和堆栈 • 片外DDR容量大但初始化复杂,需要BootROM先配置控制器 复位后CPU视角 1. PC = 0x0000_0000 2. 读取向量表 → 获取SP和PC 3. 跳转到BootROM代码入口 BootROM初始化流程 1. 配置时钟和电源 2. 初始化DDR控制器 3. 从存储介质加载用户程序到DDR

你看,地址空间被划分成几个区域。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出发,一步步把系统拉起来。