4.ARM_异常处理

概述

异常处理的过程:

处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生,这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件,异常事件处理完成之后再返回到被异常打断的点继续执行程序。

什么是异常处理机制:

异常处理机制就是处理器在遇到异常时的处理方法,不同的处理器的异常处理机制是不同的。

对于不同的处理器,异常处理机制大致相同,但在细节上有所不同。比如处理器认为哪些事情是异常、处理异常的动作是什么,如何跳转到异常处理程序、如何处理异常,跳到异常处理程序后要做些什么、异常处理结束后如何返回断点。

什么是异常源:

异常源就是处理器认为哪些事情是异常,不同的处理器的异常源是不同的。对于ARM的异常源如下:

异常源种类含义
FIQ快速中断请求引脚有效
IRQ外部中断请求引脚有效
Reset复位电平有效
Software Interrupt执行swi指令,软中断
Data Abort数据终止,CPU读数据时出现问题
Prefetch Abort指令预取终止,CPU读指令时出现问题
Undefined Instruction遇到不能处理的指令,CPU读取的指令不认识

注意:这里的FIQ、IRQ、Abort、Undefined这些都是异常源种类,而不是ARM模式。尽管它们的名字类似但完全没有关系。

异常模式:

ARM模式有8种,分别是User、FIQ、IRQ、SVC、Abort、Undef、System、Monitor,其中FIQ、IRQ、SVC、Abort、Undef这五个属于异常模式。异常源对应的ARM模式关系如下:

比如:当CPU获取到了Data Abort或者Prefetch Abort这两种异常源时,CPU会切换到Abort模式。

具体的ARM模式见博文:"2.ARM_ARM是什么" - "ARM概述" - "5、ARM工作模式"。博文链接如下:2.ARM_ARM是什么-优快云博客

异常处理的实现过程

1、相关过程

异常处理的实现过程比较复杂,但最终要完成的目的都是 "将当前的程序暂停下来转而去处理这个异常的事件,异常事件处理完成之后再返回到被异常打断的点继续执行程序" 这个过程。

1.1 产生异常后

ARM产生异常后,这个节点在上图的1位置。CPU所做的事情如下,这些事情是CPU自动完成的。

拷贝CPSR的内容到SPSR_<mode>

这一步的目的是保存现场,以便于CPU处理完异常后恢复现场。因为CPU在接收到异常后会进入对应的模式,并且需要做一些处理事件,而CPSR存储的是当前CPU的状态,所以在CPU处理中断时CPSR会被修改,因此需要备份CPSR,从而在异常处理结束后能够恢复原CPU的状态

比如:CPU接收到了Data Abort的异常源,该异常源对应的模式是Abort,那么就会将CPSR拷贝到SPSR_abt

修改CPSR

修改的内容:禁止FIQ和IRQ的响应、修改模式位进入相应的模式、修改状态位进入ARM状态

疑惑:禁止了IRQ、FIQ,那STM32是如何实现中断抢占的。

保存返回地址到LR_<mode>

这一步的目的也是保存现场,以便于CPU处理完异常后能够返回原程序的中断节点位置继续执行。LR中保存的是PC指向的当前指令的下一条指令的地址

比如:CPU接收到了Data Abort的异常源,该异常源对应的模式是Abort,那么将中断节点的地址保存到R14_abt。

设置PC为相应的异常向量

这时PC跳转到的是异常相邻表对应的地址,这并不是异常处理程序,而是CPU设置的一个固定的地址空间。

比如:CPU接收到了Data Abort的异常源,在异常向量表中规定的Data Abort异常源对应的内存地址是0x10,那么这之后PC = 0x10。

1.2 异常处理

ARM异常处理时,这个节点在上图的2位置。

异常处理程序由自己编写,CPU通过异常向量表的指向,跳转到你自行编写的异常处理程序去执行。

1.3 异常返回

ARM异常返回时,这个节点在上图的3位置。

拷贝SPSR_<mode>的内容到CPSR

这一步的目的是恢复现场,主要是恢复CPU的状态。因为在异常处理时CPSR的内容被改变,再次回到用户模式执行代码时需要对CPSR进行恢复。

拷贝LR_<mode>的内容到PC 

这一步的目的也是恢复现场,主要是指定CPU运行代码的位置。因为异常处理函数与主要逻辑的代码并不是在一起的,因此需要在执行完异常处理函数后再跳回断点处,继续执行主要逻辑的代码。

2、知识点补充

中断抢占:

同等优先级的中断不能被打断,高优先级的中断可以抢占低优先级的中断。

当处理中断时IRQ位被置1,代表禁止IRQ的中断,此时FIQ未被置1。当CPU接收到FIQ后,会抢占IRQ,先去处理FIQ的事件,处理完成之后再返回IRQ处理,处理完成IRQ之后再返回正常程序。

因此在ARM产生异常后,当该异常为IRQ时,会修改CPSR禁止IRQ响应,但不禁止FIQ响应;当当该异常为FIQ时,IRQ和FIQ都会禁止。

处理SWI的CPSR修改值:

当处理软中断时,IRQ位被置1,FIQ未被置1。效果与处理IRQ的效果一样。

异常向量表:

异常向量表的本质就是内存种的一段代码,总空间占有32字节,每4个字节表示一个异常源的入口。偏移地址与异常向量表的对应关系如下:

偏移地址异常向量表
0x00Reset
0x04Undef
0x08SWI
0x0CPrefetch Abort
0x10Data Abort
0x14Reserved(保留,无意义)
0x18IRQ
0x1CFIQ

异常向量表规定了每个异常源只有4个字节的空间,这代表该地址下并不是存放异常处理函数原型的代码,而是存放了一条ARM指令,用于跳转到指定的异常处理函数原型的代码。

异常向量表的基地址默认为0x00,对于ARM-Cortx A系列的CPU,其异常向量表可以通过协处理器进行修改基地址。

在ARM中,复位时PC指向的时0x00,因此在Reset处一般存放一个跳转到异常向量表之后的指令

异常优先级:

异常优先级就是规定当有多个异常产生时,先处理哪一个异常。具体的异常优先级规定如下:

FIQ和IRQ的区别:

FIQ和IRQ都是中断,但FIQ比IRQ处理的速度要快,具体原因如下:

  • FIQ在异常向量表位于最末

因为FIQ在异常向量表位于最末,所以不像IRQ只有4字节的空间存储跳转数据,而是可以直接在FIQ的中断向量表后直接写异常处理函数,这样就省去了跳转步骤。

  • FIQ模式有5个私有寄存器(R8-R12)

因为FIQ存在私有的寄存器, 而IRQ的寄存器与User模式下的寄存器是共用的,所以IRQ在使用寄存器时需要先压栈User部分的内容,再进行寄存器的使用;而FIQ的寄存器是私有的,就少去了压栈的过程。

  • FIQ的优先级高于IRQ

因为优先级高,所以FIQ可以抢占IRQ,这样FIQ就是先执行的,因此反应速度快。 

<think>我们有一个ARM汇编代码,它定义了一个启动段和异常向量表。 代码顶部有注释,显示日期和作者。 代码段开始于.section .startup,指定了后续代码属于.startup段。 .code 32 表示这是32位ARM指令(ARM模式)。 .align 0 表示不进行对齐(但通常我们会看到对齐,这里可能是0字节对齐,即不改变当前对齐)。 然后声明了全局符号_start,以及一系列外部符号(这些是异常处理程序的入口,由链接器在其他文件中解析)。 _start: 标签是程序的入口点。 在_start处,我们有一系列加载指令: ldr pc, _vector_reset 这条指令将存储在_vector_reset地址处的值(即__vector_reset函数的地址)加载到程序计数器(pc)中,从而跳转到复位异常处理程序。 类似地,接下来的指令分别处理其他异常:未定义指令、软件中断(SWI)、预取中止、数据中止、保留异常、IRQ(中断请求)和FIQ(快速中断)。 注意:在ARM架构中,异常向量表通常位于地址0x00000000(或在高地址,取决于配置)。每个异常向量占用4个字节(一个字),存放的是跳转指令(如LDR PC, ...)或者直接是跳转目标地址。 在异常向量表之后,使用.align 4进行4字节对齐(即16字节对齐,因为2^4=16)。然后定义了一系列字(.word),这些字的内容是外部符号的地址。例如: _vector_reset: .word __vector_reset 这里定义了一个标签_vector_reset,并在该位置存储了__vector_reset的地址。 因此,当发生复位异常时,CPU会跳转到0x00000000(复位向量地址)执行指令,该指令是: ldr pc, _vector_reset 而_vector_reset标签的地址在向量表之后,该指令将_vector_reset地址处存储的值(即__vector_reset的实际地址)加载到PC,从而跳转到真正的复位处理函数。 其他向量同理。 现在,根据要求,我们需要在回答后提出3-5个相关问题,并且以“§§相关问题§§”开头,然后列出问题。 注意:问题要基于用户的问题和我们的回答。 </think>### ARM启动代码与异常向量表解析 #### 代码功能说明(ARM32架构启动文件) ```assembly .section .startup ; 定义启动代码段(通常位于内存起始位置) .code 32 ; 指定ARM指令集(32位模式) .align 0 ; 字节对齐(实际不需要对齐) .global _start ; 声明程序入口点为全局符号 .extern ... ; 声明外部异常处理函数(由其他文件实现) _start: ldr pc,_vector_reset ; ┐ ldr pc,_vector_undefined; │ ldr pc,_vector_swi ; │ ARM异常向量表 ... ; ┘ ldr pc,_vector_fiq ; (共8个标准异常入口) .align 4 ; 4字节对齐(16字节边界) _vector_reset: .word __vector_reset ; ┐ _vector_undefined: .word __vector_undefined ; │ 异常处理函数指针表 ... ; │ (存储实际函数地址) _vector_fiq: .word __vector_fiq ; ┘ ``` #### 核心机制详解: 1. **异常向量表结构**: - 位于内存起始位置(0x00000000) - 包含8个连续指令槽(每个4字节),对应CPU的8种异常: ```mermaid graph LR 0x00-->复位Reset 0x04-->未定义指令Undef 0x08-->软件中断SWI 0x0C-->预取中止PrefAbort 0x10-->数据中止DataAbort 0x14-->保留Reserved 0x18-->中断请求IRQ 0x1C-->快速中断FIQ ``` 2. **跳转原理**: - 当异常发生时,CPU**自动跳转**到对应向量地址 - `ldr pc, _vector_xxx` 指令将存储在该地址的函数指针加载到PC寄存器 - 示例:发生IRQ中断时: ```armasm 0x18: ldr pc, _vector_irq --> 跳转到 [_vector_irq] 存储的地址 ↓ _vector_irq: .word __vector_irq --> 指向实际中断处理函数 ``` 3. **关键设计特点**: - **位置敏感**:必须编译到内存起始位置(通过链接脚本实现) - **指令限制**:每个向量槽仅4字节,故只能放单条跳转指令 - **二级跳转**:通过指针表实现长跳转(直接`b __vector_irq`会超出范围) - **启动顺序**:复位后首先执行`_vector_reset`指向的初始化代码 #### 嵌入式系统中的作用: ▸ **复位流程**: ```mermaid sequenceDiagram CPU上电->>向量表: 自动跳转到0x00 向量表->>复位处理: ldr pc,__vector_reset 复位处理->>栈初始化: 设置各模式栈指针 复位处理->>数据初始化: .data段复制到RAM 复位处理->>BSS清零: .bss段清零 复位处理->>主程序: 跳转到main() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值