Files

236 lines
8.7 KiB
Markdown
Raw Permalink Normal View History

2026-06-10 11:17:10 +08:00
---
tags: [嵌入式系统, 复习, ARM异常]
create time: 2026-06-10 11:11
---
# ARM异常与中断机制
## 概述
本文档详细讲解ARM处理器的异常与中断机制,包括7种异常类型、异常向量表、异常响应流程、FIQ/IRQ中断处理以及中断嵌套。这是理解嵌入式系统实时响应能力的关键。
## 正文
### 1. 什么是异常
**异常(Exception)** 是指处理器在执行正常程序过程中,遇到的需要特殊处理的事件。异常是同步事件,由当前执行的指令触发。
**中断(Interrupt)** 是一种特殊的异常,属于异步事件——由外部硬件信号触发,与当前执行的指令无关。
> [!tip] 异常与中断的关系
> 中断是异常的子集。所有中断都是异常,但不是所有异常都是中断。IRQ和FIQ是中断,而Reset、SWI、Data Abort等是其他类型的异常。
### 2. 七种异常类型
ARM7定义了7种异常类型,每种异常对应一种处理器模式:
| 异常类型 | 触发原因 | 进入模式 | 优先级 |
|----------|----------|----------|--------|
| Reset | 复位信号 | SVC | 最高(1) |
| Undefined Instruction | 无法识别的指令 | UND | 最高(2) |
| SWI(软件中断) | SWI指令执行 | SVC | 最高(3) |
| Prefetch Abort | 指令预取失败 | ABT | 最高(4) |
| Data Abort | 数据访问失败 | ABT | 最高(5) |
| IRQ | 外部IRQ中断请求 | IRQ | 较低(6) |
| FIQ | 外部FIQ中断请求 | FIQ | 最低(7) |
> [!question] 为什么Reset的优先级最高?
> Reset是最高优先级异常,因为它代表系统需要从头开始。无论处理器当前在做什么,复位信号都必须立即响应,将系统拉回到初始状态。
### 3. 异常向量表
异常向量表是位于地址 **0x00000000** 的一张跳转表,每种异常对应一个固定的偏移地址:
```mermaid
graph TD
subgraph vectorTable["Exception Vector Table"]
V0["0x00000000: Reset"] --> V4["0x00000004: Undefined Instruction"]
V4 --> V8["0x00000008: SWI"]
V8 --> VC["0x0000000C: Prefetch Abort"]
VC --> V10["0x00000010: Data Abort"]
V10 --> V14["0x00000014: Reserved"]
V14 --> V18["0x00000018: IRQ"]
V18 --> V1C["0x0000001C: FIQ"]
end
style V0 fill:#ef9a9a
style V4 fill:#ffcc80
style V8 fill:#fff176
style VC fill:#a5d6a7
style V10 fill:#90caf9
style V14 fill:#e0e0e0
style V18 fill:#ce93d8
style V1C fill:#f48fb1
```
每个向量表项只占**4个字节**(一条ARM指令的大小),通常放置一条跳转指令(B或LDR PC)跳转到对应的异常处理程序。
> [!warning] 0x00000014是保留位置
> 0x00000014是Reserved,没有定义任何异常。这是因为每种异常间隔4个字节,而从Data Abort到IRQ之间需要一个间隔。
> [!question] 为什么FIQ的向量地址是0x0000001C,排在最后?
> 把FIQ放在向量表末尾有一个巧妙的好处:FIQ的处理程序可以直接从0x0000001C开始写,而不需要一条跳转指令。因为从0x0000001C往后到向量表结束还有空间,足以放置几条关键指令,从而节省了一个跳转周期。
### 4. 异常响应流程
当异常发生时,ARM处理器自动执行以下步骤:
```mermaid
flowchart TD
A["Exception Occurs"] --> B["Step1: Save CPSR to SPSR of target mode"]
B --> C["Step2: Set CPSR mode bits to target mode"]
C --> D["Step3: Set CPSR T bit to 0 ARM state"]
D --> E["Step4: Set CPSR I/F bits disable interrupts"]
E --> F["Step5: Save return address to LR of target mode"]
F --> G["Step6: Set PC to vector address"]
G --> H["Execute exception handler"]
style A fill:#ef9a9a
style H fill:#a5d6a7
```
具体来说:
**Step 1:保存CPSR**
- 将当前CPSR的值复制到**目标异常模式的SPSR**
- 例如:IRQ发生时,CPSR → SPSR_irq
**Step 2:切换处理器模式**
- 修改CPSR的M[4:0]位,切换到异常对应的模式
**Step 3:切换到ARM状态**
- 清除CPSR的T位(T=0),确保在ARM状态下执行异常处理程序
- 这意味着即使当前在Thumb状态,异常处理也使用ARM指令
**Step 4:禁止中断**
- 根据异常类型,设置I位或F位来禁止中断
- IRQ异常设置I位(禁止IRQ),FIQ异常设置I和F位(禁止所有中断)
**Step 5:保存返回地址**
- 将程序计数器的值保存到**目标模式的LR**
- 不同异常的返回地址偏移不同(见下表)
**Step 6:跳转到向量表**
- 将PC设置为对应异常的向量地址
### 5. 返回地址的偏移
每种异常保存到LR中的返回地址有特定的偏移:
| 异常类型 | LR保存的值 | 说明 |
|----------|------------|------|
| Reset | 未定义 | 无需返回 |
| Undefined | PC + 4 | 指向未定义指令的下一条 |
| SWI | PC + 4 | 指向SWI指令的下一条 |
| Prefetch Abort | PC + 4 | 指向预取失败指令的下一条 |
| Data Abort | PC + 8 | 指向数据访问失败指令的下一条 |
| IRQ | PC + 4 | 指向被中断指令的下一条 |
| FIQ | PC + 4 | 指向被中断指令的下一条 |
> [!question] 为什么Data Abort的偏移是+8而其他是+4?
> 因为ARM7三级流水线中,当Data Abort被检测到时,流水线已经多预取了指令。Data Abort在执行阶段才被发现,此时PC已经前进了8个字节(两条指令之后)。
### 6. 异常返回
异常处理完成后,需要返回到被中断的程序。返回方式取决于异常类型:
**从IRQ/FIQ返回**:
```arm
SUBS PC, LR, #4 ; LR_irq/fiq - 4 = 被中断指令的下一条地址
```
**从SWI/Undefined返回**:
```arm
MOVS PC, LR ; LR_svc/und 直接就是返回地址
```
**从Data Abort返回**:
```arm
SUBS PC, LR, #8 ; LR_abt - 8 = 需要重新执行那条失败的指令
```
> [!warning] 关键点:MOVS和SUBS中的"S"后缀
> 返回指令必须使用带S后缀的版本(MOVS/SUBS),因为这会自动将SPSR的值恢复到CPSR。如果用MOV而不是MOVS,SPSR不会被恢复,处理器状态将不正确。
### 7. 中断嵌套
ARM7的中断**支持嵌套**。所谓嵌套,是指在一个中断处理过程中,更高优先级的中断可以打断当前处理。
```mermaid
sequenceDiagram
participant Main as Main Program
participant IRQ as IRQ Handler
participant FIQ as FIQ Handler
Main->>IRQ: IRQ interrupt occurs
Note over IRQ: CPSR saved, IRQ enabled
IRQ->>FIQ: FIQ interrupt occurs
Note over FIQ: CPSR saved, all IRQ disabled
FIQ-->>IRQ: Return from FIQ
IRQ-->>Main: Return from IRQ
```
实现中断嵌套的关键:
1. 进入中断处理程序后,需要**手动重新使能IRQ**(清除CPSR的I位)
2. 保存必要的寄存器到栈上
3. 使用STMFD/LDMFD来管理栈帧
```arm
; IRQ中断处理程序模板
IRQ_Handler:
SUB LR, LR, #4 ; 修正返回地址
STMFD SP!, {LR} ; 保存返回地址
MRS R14, SPSR ; 保存SPSR
STMFD SP!, {R14} ; 将SPSR压栈
STMFD SP!, {R0-R3, R12} ; 保存可能被破坏的寄存器
MSR CPSR_c, #0x13 ; 切换到SVC模式,使能IRQ
STMFD SP!, {LR} ; 保存SVC模式的LR
; ... 在SVC模式下处理中断 ...
LDMFD SP!, {LR} ; 恢复SVC的LR
MSR CPSR_c, #0x92 ; 切回IRQ模式,禁止IRQ
LDMFD SP!, {R0-R3, R12} ; 恢复寄存器
LDMFD SP!, {R14} ; 恢复SPSR到R14
MSR SPSR_cxsf, R14 ; 写回SPSR
LDMFD SP!, {PC}^ ; 恢复PC,同时恢复CPSR
```
### 8. FIQ vs IRQ 对比总结
```mermaid
graph TD
subgraph comparison["FIQ vs IRQ Comparison"]
direction LR
subgraph fiqProps["FIQ Properties"]
FP1["Higher priority"]
FP2["Private R8-R14"]
FP3["Vector at 0x1C"]
FP4["Fastest response"]
end
subgraph irqProps["IRQ Properties"]
IP1["Lower priority"]
IP2["Private R13-R14 only"]
IP3["Vector at 0x18"]
IP4["Needs register save"]
end
end
style FP1 fill:#f48fb1
style IP1 fill:#ce93d8
```
| 特性 | FIQ | IRQ |
|------|-----|-----|
| 优先级 | 高 | 低 |
| 私有寄存器 | R8-R14(7个) | R13-R14(2个) |
| 向量地址 | 0x0000001C | 0x00000018 |
| 响应速度 | 快(无需保存R8-R12) | 慢(需要保存R0-R12) |
| 嵌套 | 支持 | 支持 |
| 典型用途 | 高速数据传输(DMA) | 一般外部设备中断 |
> [!question] 如果系统中只有一个中断源,应该选择FIQ还是IRQ?
> 如果只有一个中断源且对响应速度有要求,建议使用FIQ。因为FIQ有更多私有寄存器,中断处理程序不需要保存/恢复R8-R12,减少了压栈/出栈的时间开销。
## 关联笔记
- [[嵌入式系统/试题册/index|试题册索引]]