--- 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|试题册索引]]