ARM11异常&中断
参考
ARM异常与中断体系详解
Linux中断-ARM中断处理过程
ARM架构异常中断处理流程
Linux内核ARM架构异常向量表
以上这些是ARM11之前的比较老了,比较新的是ARMv8中的异常向量介绍
1.启动
程序映像存放在FLASH中,异常向量表位置在程序映像的最前面。由于FLASH不能运行代码,系统上电或者Resst的时候,内置的FLASH将访问控制接口,并将中断向量表和引导代码自动加载到内部SRAM(此时该SRAM 定位于起始地址空间0x00000000,容量为4KB),并且置PC值为0x00运行程序(这一切是有芯片内部的硬件逻辑完成的)。之后SRAM中的引导程序将FLASH中操作系统镜像加载到SDRAM中,操作系统就能够在SDRAM中运行。
ARM要求中断向量表必须放置在从0地址开始,连续8×4字节的空间内(ARM9后也支持从0xFFFF0000开始的高地址向量表)。
当异常或中断产生时ARM处理器强制把PC指针置为中断向量表中相对应的向量地址。因为每个中断向量在向量表中只有一个字节的存储空间,只能存放一条指令,所以通常存放跳转指令,使程序跳转到存储器的其他地方,再执行中断处理。
2.七种异常中断种类:
-
复位(Reset):当处理器的复位引脚有效时,系统产生复位异常中断,程序跳转到复位异常中断处理程序处执行。复位异常中断通常用在下面两种情况下:系统加电时,系统复位时,跳转到复位中断向量处执行,称为软复位。
-
未定义指令(Undefined):当ARM处理器或者是系统中的协处理器认为当前指令未定义时,产生未定义的指令异常中断。可以通过该异常中断机制仿真浮点向量运算。
-
软中断(software interrupt):这是一个由用户定义的中断指令。可用于用户模式下的程序调用特权操作指令。在实时操作系统(RTOS)中可以通过该机制实现系统功能调用。
-
指令预取中止:如果处理器预取的指令的地址不存在,或者该地址不允许当前指令访问,当该被预取的指令执行时,处理器产生指令预取中止异常中断。
-
数据访问中止:如果数据访问指令的目标地址不存在,或者该地址不允许当前指令访问,处理器产生数据访问中止异常中断。
-
外部中断请求(IRQ):当处理器的外部中断请求引脚有效,而且CPSR寄存器中的I控制位被清除时,处理器产生外部中断请求异常中断。系统中各个外设通常通过该异常中断请求处理器服务。
-
快速中断请求(FIQ):当处理器的外部快速中断请求引脚有效,而且CPSR寄存器中的F控制位被清除时,处理器产生外部中断请求(FIQ)异常中断。
3.七种工作模式:
-
用户模式(user):正常程序执行的模式
-
快速中断模式(FIQ): 用于高速数据传输和通道处理
-
外部中断模式(IRQ): 用于通常的中断处理
-
特权模式(SVC): 供操作系统使用的一种保护模式
-
中止模式(abort): 用于虚拟存储及存储保护
-
未定义指令模式(undefined): 用于支持软件仿真硬件的协处理器
-
系统模式(system): 用于运行特权级的操作系统任务
4.异常向量表:
在ARM体系中,异常中断向量表的大小为32个字节。其中,每个异常中断占据4个字节大小,保留4个字节空间。
ARM的异常中断向量表可以是高端向量表,也可以是低端向量表,两者取其一,区别是基地址不同。
高端向量是ARM架构可选配置,可以通过硬件外部输入管脚来配置是低端向量还是高端向量,不能通过指令来改变向量的位置,但如果ARM芯片内部有标准ARM协处理器,那么协处理器CP15的寄存器C1的bit13可以用来切换低端和高端向量地址,等于0时为低端向量,等于1时为高端向量。
Linux内核分用户空间、内核空间,通常32位处理器,用户空间0-3G,内核空间3-4G,所以Linux内核使用高端向量表。
处理器执行某个特定的异常中断的过程中,称为处理器处于特定的中断模式
指令预取中止、数据访问中止都处于-终止模式abort;
复位、软中断都处于-特权模式svc;
一般情况下则是运行在-用户模式user;
用于运行特权级的操作系统任务-系统模式sys
5.ARM寄存器
ARM寄存器有15个(ARM11及以前)通用寄存器和6个状态寄存器CPSR、SPSR_svc、SPSR_abt、SPSR_und、SPSR_irq、SPSR_fig。r13--sp堆栈指针,r14--lr连接寄存器,r15--pc程序计数器。
所有处理器模式下都可访问当前程序状态寄存器CPSR。在每种异常模式下都有一个对用的程序状态寄存器SPSR。
- CPSR中包含条件码标志、中断禁止位、当前处理器模式以及其他状态和控制信息。
- SPSR用于保存当异常出现时CPSR的状态,以便异常返回后恢复异常发生时的工作状态。
- SP每一种异常模式都有其自己独立的r13,它通常指向异常模式所专用的堆栈,也就是说五种异常模式、非异常模式(用户模式和系统模式),都有各自独立的堆栈,用不同的堆栈指针来索引。这样当ARM进入异常模式的时候,程序就可以把一般通用寄存器压入堆栈,返回时再出栈,保证了各种模式下程序的状态的完整性。
- LR有两种功能,一是保存子程序返回地址,通过把r14复制到PC来实现返回;二是当异常发生时,r14用来保存异常返回地址,将r14入栈可以处理嵌套中断。本质都是保存返回地址。
- PC是有读写限制的。当没有超过读取限制的时候,读取的值是当前指令的地址加上8个字节,经典的PC+8,因为流水线的"执行"在第三个阶段,"取指"在第一个阶段,一个阶段32÷8=4。
6.ARM对异常(中断)处理过程:
- 初始化
设置中断源,让它可以产生中断-->设置中断控制器(可以屏蔽某个中断,优先级)-->设置CPU总开关,(使能中断) - 执行其他程序:正常程序
- 产生中断:产生中断--->中断控制器--->CPU
- cpu每执行完一条指令都会检查有无中断/异常产生
- 发现有中断/异常产生,开始处理。对于不同的异常,跳去不同的地址执行程序。这地址上,只是一条跳转指令,跳去执行某个函数(地址),这个就是异常向量。如下就是异常向量表,对于不同的异常都有一条跳转指令。
- 第5步又可以细分为:
-
a 保存现场
-
(1).保存处理器当前状态、中断屏蔽位以及各条件标志位。这是通过将当前程序状态寄存器CPSR的内容保存到将要执行的异常中断对应的SPSR寄存器中实现的。各异常中断有自己的物理SPSR寄存器。
-
(2).设置当前程序状态寄存器CPSR中相应的位。包括设置CPSR中的位,使处理器进入相应的执行模式;如果是中断,则设置CPSR中的位,禁止IRQ中断,当进入FIQ模式时,禁止IRQ中断,因此初期的ARM是不支持中断嵌套和抢占,直到通用中断控制器GIC出现后(见第7节)。
-
(3).将寄存器lr_mode设置成返回地址,也就是把PC指针中地址的下一个地址放到LR指针。
-
(4).将程序计数器(PC)值,设置成该异常中断的中断向量地址,从而跳转到相应的异常中断处理程序处执行。
-
b 处理异常(中断)
-
c 恢复现场
-
(1).恢复被中断的程序的处理器状态,即将SPSR寄存器内容复制到当前程序状态寄存器CPSR中。
-
(2).返回到发生异常中断的指令的下一条指令处执行,即将lr_mode寄存器的内容复制到程序计数器PC中。
-
复位异常中断处理程序不需要返回。整个应用系统是从复位异常中断处理程序开始执行的,因为它不需要返回。实际上,当异常中断发生时,程序计数器PC所指的位置对于各种不同的异常中断是不同的。同样,返回地址对于各种不同的异常中断也是不同的。
7.GIC中断处理
- GIC决定该中断是否被使能,若没有被使能对GIC没有影响。
- 对于每个pending中断,GIC决定目标处理器。
- 对于每个处理器,Distributor根据他拥有的每个中断优先级信息决定最高优先级的挂起中断,将该中断传递给目标CPU interface。
- GIC Distributor将一个中断传递给CPUinterface后,该CPU interface决定该中断是否有足够的优先级将中断请求发送给CPU。
- 当CPU开始处理该异常中断,它读取GICC_IAR应答该中断。读取的GICC_IAR获取到中断ID,对于SGI,还有源处理器ID。中断ID被用来查找正确的中断处理函数。GIC识别读过程后,将改变该中断的状态:
- a.如果该中断在pending状态时,产生了另一次中断,中断状态将从pending转化为pending &active。
- b.否则,中断状态将从pending变为active。
- 当处理器完成中断处理后,它需要通知GIC处理已经完成。这个过程称为priority drop and interrupt deactivation:
- a.需要写EOIR(End of interrupt register)
- b.接着需要写GICC_DIR(deactivate interrupt register)
8.CPU的两种state:
1 ARM state:使用ARM指令集,每个指令4byte
2 Thumb state:使用的是Thumb指令集,每个指令2byte
比如同样是 mov R0, R1 编译后:
对于ARM指令集要占据4个字节:机器码
对于Thumb指令集占据2个字节:机器码
引入Thumb减少存储空间
ARM指令集与Thumb指令集的区别:
Thumb 指令可以看作是 ARM 指令压缩形式的子集,是针对代码密度的问题而提出的,它具有 16 位的代码密度但是它不如ARM指令的效率高 .
Thumb 不是一个完整的体系结构,不能指望处理只执行Thumb 指令而不支持 ARM 指令集.
因此,Thumb 指令只需要支持通用功能,必要时可以借助于完善的 ARM 指令集,比如,所有异常自动进入 ARM 状态.在编写 Thumb 指令时,先要使用伪指令 CODE16 声明,而且在 ARM 指令中要使用 BX指令跳转到 Thumb 指令,以切换处理器状态.编写 ARM 指令时,则可使用伪指令 CODE32声明.