Files
final-exam/嵌入式系统/复习文档/ARM异常与中断机制.md
T

236 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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|试题册索引]]