2.4 ARM体系结构中的异常

非预知的引发处理器暂时脱离正常指令序列并转到另外的程序段去运行的现象,称之为异常(Exceptions),所运行的程序段称为异常处理程序。当异常处理程序结束之后,多数情况下处理器需要返回脱离点继续执行后续的程序。

ARM体系结构中的异常与X86体系结构的中断和异常有很大的相似之处,但异常与中断的概念并不完全等同。

异常和中断都是因某种因素(内部或外部)导致程序流暂时脱离当前指令序列转到另外的一个指令序列去执行的现象。中断及其响应过程通常是由电信号(处理器内部或外部的引脚信号)触发的,属硬件触发,而且通常可以通过对中断屏蔽位的设置禁止或允许对其的响应;而异常则是因指令的执行而产生的,属于软件触发,且大部分是不可屏蔽的。由于在处理器内部对于中断或异常的管理都采用了相同的机制,所以统称为异常。

为了保证处理器执行完异常处理程序后能够正确的返回脱离点,必须要在进入异常处理程序前保存好处理器先前的工作环境,即处理器内部可能被异常处理程序使用的那些寄存器内容,它们是操作系统中称为上下文(Context)的重要部分。当异常处理完成之后,还要将这些寄存器内容恢复到原来的寄存器内,从而保证处理器在一个正确的环境下运行。处理器允许多个异常同时发生,但任意时刻只能处理一个。不同的异常具有不同的优先级,处理器会在多个异常同时发生的情况下,选择优先级最高的一个加以处理。

2.4.1 ARM体系结构所支持的异常类型

ARM体系结构所支持的异常及具体含义如表2-9所示。

表2-9 ARM体系结构所支持的异常

2.4.2 ARM处理器对异常的响应过程

当一个异常出现以后,ARM微处理器会按图2-15所示的流程进入异常处理程序。

图2-15 ARM处理器自动完成的异常响应过程

ARM微处理器对异常的响应过程也可用伪码描述为:

R14_<Exception_Mode>=Return Link      ;自动将当前PC值-4后存入LR寄存器
SPSR_<Exception_Mode>=CPSR            ;自动将CPSR存入本异常模式对应的SPSR
CPSR[4:0]=Exception Mode Number       ;自动将CPSR中的工作模式设为本异常模式
CPSR[5]=0                             ;自动将CPSR[5]清零,异常只能运行于ARM状态
If <Exception_Mode>==Reset or FIQ then ;判断是否为复位或FIO异常
CPSR[6]=1                              ;当响应FIQ异常时,禁止响应新的FIQ异常
   else CPSR[6]=不变                      ;当响应IRQ异常时,允许嵌套响应FIQ异常
CPSR[7]=1                              ;当响应某个异常时,禁止响应IRQ异常
   PC=Exception Vector Address            ;最后将PC设为本异常对应的异常向量地址

说明:对R14_xxx内地址值的设置,根据不同的异常而不同,具体见表2-10。

表2-10 各种异常对各自R14值的设置

2.4.3 异常向量表

与X86中断向量表不同的地方是,ARM的异常向量(Exception Vectors)表内的每个表项内容不是处理程序的入口地址,而是一条跳转到异常处理程序的跳转指令。要使处理器转入异常处理程序去运行,只要将PC值设置为异常向量对应的地址值,处理器执行里面的调转指令即可进入异常处理程序运行。ARM处理器一共只有8个异常向量表项,有定义的为7个,另一个为保留项。与X86中断向量表相同的地方是,ARM的异常向量也固定的设置在存储空间起始地址区域内,一共32B,比X86的1KB少许多。具体的向量分配见表2-11。

表2-11 异常向量表

2.4.4 从异常返回

异常处理完毕之后,ARM微处理器会执行以下几步操作从异常返回。

①将连接寄存器LR的值直接送到PC或者减去相应的偏移量后送到PC中。

说明:异常返回是通过直接向PC中注入返回地址实现的,但由于不同的异常需要返回的地址不一样。有的异常在LR中保存的就是返回地址值,而有的异常则需要在LR中保存的地址值基础上再减去1或2条指令的偏移量才是真正的返回地址。表2-12是各类异常的返回地址设置表。关于返回所需设置的PC值为什么不同,请参见后续内容。

表2-12 异常返回时需要由程序向PC值设置的地址值

②将SPSR复制回CPSR中。

③若在进入异常处理时设置了中断禁止位,要在此清除。

由于程序总是从复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。

2.4.5 各类异常有关说明

1.快中断(Fast Interrupt Request,FIQ)

FIQ异常是为了支持外设的快速响应而设计的,利用在ARM状态下配备较多的私有寄存器,可在保存和恢复现场信息时避免对慢速的存储器操作,减少系统上下文切换时间。

若将CPSR的F位置为1,则会禁止FIQ中断,若将CPSR的F位清零,处理器会在指令执行时检查FIQ的引脚输入,如果有请求信号将给予响应。可由外部通过对处理器上的nFIQ引脚输入低电平产生FIQ。注意只有在特权模式下才能改变F位的状态。

当处理器即将响应中断时,由于取指令流水线的并行操作,程序指针PC的内容已经是当前指令后第3条指令的地址,即当前指令地址加12(对于Thumb指令则是当前指令地址加6)。在进入中断时处理器会自动将PC内容减4保存到R14_fiq中。该地址是当前指令地址后第2条指令的地址,所以在返回断点时还需要将R14_fiq的值减4,如图2-16所示。

图2-16 ARM指令中断产生和返回地址示例图

可以采用以下指令实现断点的返回。

SUBS  PC, R14_fiq,#4

该指令将寄存器R14_fiq的值减去4后,复制到程序计数器PC中,从而实现从异常处理程序中的返回,同时将SPSR_fiq寄存器的内容恢复到当前程序状态寄存器CPSR中。

2.普通中断(Interrupt Request,IRQ)

IRQ异常属于正常的中断请求,可通过对处理器的nIRQ引脚输入低电平产生,IRQ的优先级低于FIQ,即当IRQ和FIQ同时产生时先响应FIQ。当程序执行进入FIQ异常时,IRQ将被屏蔽,禁止在FIQ内嵌套IRQ。

若将CPSR的I位置为1,则会禁止IRQ中断,若将CPSR的I位清零,处理器会在指令执行完之前检查IRQ的输入。注意只有在特权模式下才能改变I位的状态,用户模式则不可以。

如同图2-16所示FIQ的中断响应过程一样,IRQ将采用如下指令返回断点。

SUBS PC , R14_irq , #4

该指令将寄存器R14_irq的值减去4后,复制到程序计数器PC中,从而实现从异常处理程序中的返回,同时将SPSR_irq寄存器的内容复制到当前程序状态寄存器CPSR中。

例如,整个地址空间的起始位置(地址0x00000000开始)有以下指令,一旦发生外部中断请求,处理器首先自动保存当前状态(PC-4送R14,CPSR送SPSR),进入外部中断模式,接着执行地址0x00000018处的指令(b IRQ_SVC_HANDLER)跳转到标号IRQ_SVC_HANDLER处开始执行。

b SYS_RST_HANDLER                  ;地址=0x00000000
b UDF_INS_HANDLER                  ;地址=0x00000004
b SWI_SVC_HANDLER                  ;地址=0x00000008
b INS_ABT_HANDLER                  ;地址=0x0000000c
b DAT_ABT_HANDLER                  ;地址=0x00000010
b .                                ;地址=保留
b IRQ_SVC_HANDLER                  ;地址=0x00000018
b FIQ_SVC_HANDLER                  ;地址=0x0000001c

通常情况下IRQ_SVC_HANDLER处的代码为:

IRQ_SVC_HANDLER
    sub lr, lr, #4                 ;修正返回地址值
    stmfd sp!, {r0-r3, lr}         ;保存r0,r1,r2,r3及lr的值
    ldr r0,=IRQ_SVC_Vector         ;将中断处理程序地址的指针值存入r0
    ldr pc, [r0]                   ;从IRQ_SVC_Vector存储单元取地址值送pc

处理器将通用寄存器和返回地址压入堆栈,接着跳转到外部中断请求的中断服务程序中。IRQ_SVC_Vector为外部中断请求的中断向量。

3.指令预取中止(Instruction Prefetch Abort)

指令预取中止是由存储器系统向ARM处理器发出的存储器中止(Abort)信号触发的,产生的原因是由于处理器读取的是一个无效的存储单元,这将导致存储系统将取到的内容标记为无效指令。但只有当处理器试图执行该指令时,指令预取中止异常才会发生。如果指令的前一条指令是转移指令则不会发生预取指令中止。指令预取中止在异常返回后通常将继续读取该指令。该过程如图2-17所示。

图2-17 ARM指令集指令预取中止产生和返回地址示意图

指令预取中止异常处理程序可执行以下指令从异常返回,该指令同时实现从SPSR_abt恢复CPSR内容的功能。

SUBS PC, R14_abt, #4

4.数据中止(Data Abort)

数据中止也是由存储器系统向ARM处理器发出的存储器中止(Abort)信号触发的,产生的原因是由于正在执行的指令访问的(采用Load/Store指令)是一个无效的存储单元,这将导致存储系统将取到的数据标记为无效数据。数据异常会在后续指令或异常改变CPU状态前产生。由于数据中止异常处理程序通常会重新建立有效的数据,所以返回后将继续执行原指令。该过程如图2-18所示。

图2-18 ARM指令集数据中止异常产生和返回地址示意图

如果希望异常返回后继续执行原来产生数据中止的指令,异常处理程序可安排执行以下指令从异常返回,该指令同时实现从SPSR_abt恢复CPSR内容的功能。

SUBS PC, R14_abt, #8 ;继续执行原产生数据中止的指令

但是如果不需要再次执行原先产生数据访问中止的指令,则可按以下指令返回。

SUBS PC, R14_abt, #4 ;从原先产生数据访问中止指令的下条指令开始执行

5.软件中断(Software Interrupt Instruction,SWI)

软件中断指令(SWI)用于实现由用户模式进入管理模式,以获得那些由特权模式才能使用的资源和服务。由于SWI指令是在指令译码时就被识别并做好了准备,一旦前条指令执行完就转入异常处理程序,所以返回地址是其下一条指令。该过程如图2-19所示。

图2-19 ARM指令集软件中断产生和返回地址示意图

软件中断处理程序无论是在ARM状态还是Thumb状态都可执行以下指令从SWI对应的软件中断服务程序返回SWI的下一条指令。

MOV PC , R14_svc

6.未定义指令(Undefined Instruction)

未定义指令异常发生在ARM处理器外部配置了协处理器的系统结构中。当ARM处理器执行了一条协处理器指令并得不到外部协处理器正确的应答时将产生未定义指令异常。

未定义指令异常也可以用于在没有协处理器的系统中通过软件模拟协处理器的功能,或者用于扩展ARM或Thumb指令集。未定义指令由于异常的产生在指令译码阶段就被识别并在前条指令执行完后马上进入异常。而异常返回后需要跳过该指令,所以返回地址是未定义指令的下条指令地址。该过程如图2-20所示。

图2-20 ARM指令集未定义指令异常产生和返回地址示意图

在仿真未定义指令后可执行以下程序返回。

MOVS PC, R14_und

以上指令恢复PC(从R14_und)和CPSR(从SPSR_und)的值,并返回到未定义指令后的下一条指令。

2.4.6 异常优先级(Exception Priorities)

由于ARM处理器内的异常都是相互独立的,所以可能会碰到同时发生多个异常的情况。由于处理器任意时刻只能处理一个异常请求,故将这些异常设置了如表2-13所示的固定优先级,处理器总是先处理优先级最高的异常。

表2-13 异常优先级