1.1 中断向量表基础概念
1.1.1 什么是中断向量表
中断向量表(Interrupt Vector Table,IVT)是嵌入式系统中最核心的数据结构之一。它本质上是一个地址表,表中每个条目(向量)存储着对应中断或异常处理程序的入口地址。当CPU检测到中断或异常事件时,硬件自动从向量表中取出对应的地址,并跳转到该地址执行处理程序。
核心定义:中断向量表 = 一张由硬件定义起始位置、按中断号索引的地址跳转表。每个表项宽度通常为4字节(32位系统)或8字节(64位系统)。
| 术语 | 说明 |
|---|---|
| 向量表基地址 | 向量表在内存中的起始物理地址,由芯片复位后硬件固定或通过寄存器配置 |
| 向量表项 | 每个中断/异常对应的入口地址存储单元 |
| 中断号 | 硬件为每个中断源分配的编号,作为向量表的索引偏移 |
| 异常向量 | CPU内部异常(如复位、硬故障、缺页)对应的固定向量表项 |
1.1.2 向量表在内存中的位置
向量表的存放位置由芯片架构和启动配置共同决定。不同架构有不同约定:
- Cortex-M 系列(ARMv7-M/v8-M): 向量表默认位于地址
0x00000000,可通过SCB->VTOR寄存器重定位到任意地址(需对齐到 512 字节边界)。 - Cortex-A 系列: 异常向量表支持高向量模式(
0xFFFF0000)和低向量模式(0x00000000),通过系统控制寄存器SCTLR.V位选择。 - RISC-V: 通过
mtvec(机器模式)和stvec(监管模式)寄存器设置向量表基地址,支持直接模式和向量模式。 - x86: 中断描述符表(IDT)通过
LIDT指令加载,基地址可位于内存任意位置。
| 架构 | 默认基地址 | 重定位方式 | 对齐要求 |
|---|---|---|---|
| Cortex-M3/M4/M7 | 0x00000000 | VTOR 寄存器 | 512 字节 |
| Cortex-A | 0x00000000 / 0xFFFF0000 | SCTLR.V 位 | 无特殊要求 |
| RISC-V | 由 mtvec/stvec 指定 | 直接写 CSR | 4 字节(直接模式)/ 128 字节(向量模式) |
| x86 | 由 LIDT 加载 | LIDT 指令 | 8 字节对齐(IDTR 限长) |
常见陷阱: 向量表重定位后,如果新地址未按架构要求对齐,CPU 在取向量时可能发生对齐错误,导致系统直接进入硬件故障(HardFault)或死锁。Cortex-M 的 VTOR 寄存器要求低 9 位为 0,即基地址必须 512 字节对齐。
1.1.3 向量表与 CPU 启动流程的关系
向量表在 CPU 启动流程中扮演第一入口的角色。以典型的 Cortex-M 启动为例,硬件复位后的执行序列完全依赖向量表:
- 复位后硬件行为: CPU 从地址
0x00000000读取初始栈指针(MSP),从0x00000004读取复位向量(Reset_Handler 地址)。 - 执行启动代码: 跳转到 Reset_Handler,完成时钟配置、全局变量初始化、BSS 段清零等操作。
- 调用 main(): 启动代码最后调用 C 运行时库的
__main或直接跳转到main()。 - 中断使能后: 任何中断触发时,CPU 自动从向量表基地址 + (中断号 × 4) 取出处理函数地址并跳转。
工程启示: 如果向量表配置错误(如复位向量指向无效地址),CPU 在启动瞬间就会跑飞。调试时若发现仿真器无法连接或复位后 PC 指针异常,应首先检查向量表前两个表项(栈指针和复位向量)是否正确。
1.1.4 向量表初始化典型代码
以下是一个基于 Cortex-M 的向量表定义示例,展示了如何用 C 语言构造向量表并确保其位于正确位置:
/* 定义中断处理函数原型 */
void Reset_Handler(void);
void NMI_Handler(void);
void HardFault_Handler(void);
void SVC_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
/* 外设中断处理函数 */
void UART0_IRQHandler(void);
void TIMER0_IRQHandler(void);
/* 使用 __attribute__((section(".isr_vector"))) 将向量表放到指定段 */
__attribute__((section(".isr_vector")))
void (* const g_pfnVectors[])(void) = {
(void (*)(void))&__StackTop, /* 0x00000000: 初始栈指针 */
Reset_Handler, /* 0x00000004: 复位向量 */
NMI_Handler, /* 0x00000008: NMI */
HardFault_Handler, /* 0x0000000C: 硬故障 */
0, 0, 0, 0, /* 保留 */
SVC_Handler, /* 0x00000010: SVCall */
0, /* 保留 */
0, /* 保留 */
PendSV_Handler, /* 0x00000014: PendSV */
SysTick_Handler, /* 0x00000018: SysTick */
/* 外设中断从第16号开始 */
UART0_IRQHandler, /* 中断号16 */
TIMER0_IRQHandler, /* 中断号17 */
/* ... 后续中断向量 */
};
关键检查点:
- 向量表第一个表项必须是有效的栈指针地址(不能为 NULL 或无效 RAM 地址)。
- 第二个表项(复位向量)必须指向可执行的代码区域(Flash 或 RAM)。
- 所有未使用的中断向量应指向一个默认的
Default_Handler,避免意外触发时系统跑飞。 - 链接脚本中必须将
.isr_vector段放置在目标架构要求的起始地址。