Files

8.7 KiB
Raw Permalink Blame History

tags, create time
tags create time
嵌入式系统
复习
ARM异常
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 的一张跳转表,每种异常对应一个固定的偏移地址:

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处理器自动执行以下步骤:

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返回:

SUBS PC, LR, #4    ; LR_irq/fiq - 4 = 被中断指令的下一条地址

从SWI/Undefined返回:

MOVS PC, LR        ; LR_svc/und 直接就是返回地址

从Data Abort返回:

SUBS PC, LR, #8    ; LR_abt - 8 = 需要重新执行那条失败的指令

[!warning] 关键点:MOVS和SUBS中的"S"后缀 返回指令必须使用带S后缀的版本(MOVS/SUBS),因为这会自动将SPSR的值恢复到CPSR。如果用MOV而不是MOVS,SPSR不会被恢复,处理器状态将不正确。

7. 中断嵌套

ARM7的中断支持嵌套。所谓嵌套,是指在一个中断处理过程中,更高优先级的中断可以打断当前处理。

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来管理栈帧
; 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 对比总结

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,减少了压栈/出栈的时间开销。

关联笔记