3、BootROM硬件基础:CPU复位向量、异常向量表、内存映射(Memory Map)、片上SRAM
好,咱们正式开始啃BootROM的硬件基础。
说实话,很多做嵌入式应用层的朋友,一听到「复位向量」、「异常向量表」这些词就头大。觉得这是芯片设计的事,跟自己没关系。我当年刚入行时也这么想,直到有一次调试一个上电后系统死活跑不起来的bug,折腾了三天,最后发现是复位向量地址配错了。嗯,从那以后我再也不敢小看这几行基础配置了。
3.1 CPU复位向量:芯片上电后的第一口饭
CPU复位向量是什么?说白了,就是芯片上电或复位后,CPU第一条指令从哪里取。
你想想看,CPU本身没有「记忆」,它不知道自己要干什么。所以硬件设计者给它定了个规矩:上电后,你直接去某个固定地址读指令,然后执行。这个固定地址,就是复位向量。
- ARM Cortex-M系列:复位向量通常位于地址0x00000000(或通过BOOT引脚映射到其他地址)
- ARM Cortex-A系列:复位向量可以是低地址(0x00000000)或高地址(0xFFFF0000),由硬件配置决定
- RISC-V架构:复位向量通常由硬件设计时固定,常见于0x00000000或0x80000000
我在项目中遇到过最典型的坑:某款国产MCU,手册上写复位向量在0x00000000,但实际硬件复位后CPU先去0x00000004读了一个「跳转指令」。后来翻勘误表才发现,芯片内部有个隐藏的BootROM,先执行了一段自检代码,再跳转到用户指定的复位向量。你说坑不坑?
3.2 异常向量表:中断和异常的「电话本」
异常向量表,你可以把它理解成一个电话本。CPU遇到各种异常(复位、硬fault、中断请求等),就去这个电话本里查对应的处理函数地址。
以ARM Cortex-M为例,异常向量表长这样:
; Cortex-M 异常向量表示例
; 地址 异常类型 处理函数
0x00000000 MSP初始值 __initial_sp
0x00000004 复位 Reset_Handler
0x00000008 NMI NMI_Handler
0x0000000C 硬fault HardFault_Handler
0x00000010 内存管理fault MemManage_Handler
0x00000014 总线fault BusFault_Handler
0x00000018 用法fault UsageFault_Handler
...
0x00000040 SVCall SVC_Handler
0x00000044 调试监视器 DebugMon_Handler
0x00000048 PendSV PendSV_Handler
0x0000004C SysTick SysTick_Handler
; 之后是外部中断向量
0x00000050 中断0 IRQ0_Handler
0x00000054 中断1 IRQ1_Handler
...
这里有个关键点:异常向量表的位置不是死的。很多芯片支持通过寄存器(如VTOR,Vector Table Offset Register)重定位向量表。为什么要重定位?因为BootROM阶段和用户应用阶段,向量表可能放在不同的位置。
3.3 内存映射(Memory Map):芯片的「城市地图」
内存映射,就是芯片内部各个模块(Flash、SRAM、外设寄存器等)在CPU地址空间中的分布。没有这张地图,CPU就不知道去哪里找代码、读数据、操作外设。
我习惯把内存映射比作一个城市的街区规划:
- Code区(0x00000000 - 0x1FFFFFFF): 相当于商业区,存放可执行代码。通常是Flash或BootROM
- SRAM区(0x20000000 - 0x3FFFFFFF): 相当于住宅区,存放运行时数据。就是咱们的片上SRAM
- 外设区(0x40000000 - 0x5FFFFFFF): 相当于政府机构,每个外设(UART、GPIO、TIMER等)都有自己的专属地址
- 系统区(0xE0000000 - 0xFFFFFFFF): 相当于交通指挥中心,系统控制寄存器、调试接口等
以STM32F4系列为例,典型的内存映射如下:
| 起始地址 | 结束地址 | 区域 | 说明 |
|---|---|---|---|
| 0x0000 0000 | 0x0000 FFFF | BootROM / Flash别名区 | 由BOOT引脚决定映射到Flash还是系统存储器 |
| 0x0800 0000 | 0x080F FFFF | 主Flash | 用户代码存放区,1MB |
| 0x1FFF 0000 | 0x1FFF 7FFF | 系统存储器 | 内置BootROM,出厂固件 |
| 0x2000 0000 | 0x2001 FFFF | SRAM | 128KB片上SRAM |
| 0x4000 0000 | 0x4002 3FFF | APB1外设 | 低速外设(UART2、I2C1等) |
| 0x4002 4000 | 0x4005 FFFF | APB2外设 | 高速外设(USART1、SPI1等) |
| 0xE000 0000 | 0xE00F FFFF | 系统控制块(SCB) | NVIC、SysTick、调试等 |
3.4 片上SRAM:BootROM的「临时工作台」
片上SRAM,就是集成在芯片内部的静态随机存取存储器。它速度快、延迟低,但容量小、掉电丢失。
在BootROM启动流程中,SRAM扮演什么角色?我总结了三件事:
- 栈空间:CPU执行C函数需要栈,栈必须放在可读写的内存里。BootROM阶段Flash不可写,所以栈只能放在SRAM
- 数据暂存:比如从Flash读取的配置参数、从外设采集的临时数据,都需要SRAM来存
- 代码搬运:很多BootROM会把用户代码从Flash或外部存储器搬到SRAM中执行,因为SRAM比Flash快得多
我记得有一次做高速数据采集项目,BootROM需要把FPGA的配置数据从SPI Flash搬到SRAM,再写到FPGA。SRAM只有64KB,而配置数据有80KB。怎么办?最后用了「乒乓搬运」——先把前64KB搬到SRAM,写到FPGA,再搬后16KB。虽然麻烦点,但总算解决了。
- SRAM的起始地址和大小,一定要从芯片手册确认。不同型号、不同封装,SRAM大小可能不同
- BootROM阶段不要用太多SRAM,要给用户应用留空间。我一般控制在SRAM总量的20%以内
- 注意SRAM的ECC(纠错码)功能。有些芯片支持SRAM ECC,但需要初始化ECC RAM后才能使用,否则会触发异常
3.5 知识体系总览
说了这么多,咱们用一张图把这几块内容串起来:
这张图把咱们今天讲的内容串起来了。你想想看,CPU上电后,先看复位向量找到第一条指令,然后初始化异常向量表准备响应中断,接着根据内存映射找到各个模块的地址,最后在SRAM里搭好栈和临时数据区。每一步都离不开这些硬件基础。
我个人觉得,理解这些硬件基础,比背一百个API函数都重要。API函数可以查手册,但硬件基础不懂,出了问题你连从哪里下手都不知道。
好了,这一章的内容就到这里。记住:复位向量是起点,异常向量表是保障,内存映射是地图,片上SRAM是工具。四者配合,BootROM才能顺利跑起来。