vault backup: 2026-06-10 11:17:10
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
---
|
||||
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|试题册索引]]
|
||||
Reference in New Issue
Block a user