一、异常分类
异在ARM里,中断有三种,IRQ、FIQ、Software Interrupt;中断是属于异常的。包含中断在内的异常一共有8种。
(一)reset:重启异常: 该异常会让模式切换到SVC模式,
(二)undefined Instruction 指令未定义异常
(三)Software Interrupt (SWI) 软中断, 即用户模式级别使用代码发出的中断,系统调用就是使用软中断从用户模式切换到特权模式的
(四)Prefetch Abort 指令预取异常 对程序指令预取时产生的异常
(五)Data Abort 数据访问异常 访问合法空间的数据发现数据不存在,访问了非法的空间(访问了别一个程序的空间, 即越界了,比如访问0指针) 0地址空间开始即其后面少部分地址空间被系统用于去作为非法访问的标记,所以自己写的程序不能去访问这部分空间。
(六)Reserved 这种异常是一种保留异常,现在没有用,可能以后会扩展。
(七)IRQ:慢中断
(八)FIQ:快中断
通常说的异常主要来自cpu内部,abort,reset等;而中断主要来自外设。
二、异常的优先级 :
从高到底的顺序依次为: Reset,Data Abort, FIQ, IRQ,Prefetch Abort,SWI(软中断),undefined Instruction 高优先级的异常会终止底先级的异常
三、异常产生,异常处理与异常返回
(一)、当异常产生时,cpu会自动为我们完成下面的事
1、拷贝CPSR到SPSR_《MODE》 (异常产生后,会进入异常对应的模式中,而SPSR_《MODE》就是异常产生后进入的模式中的 SPSR; ARM中只有一个CPSR,也只有一个SP,被所有的模式共用)
2、设置适当的CPSR位: (1)改变处理器状态进入ARM状态,因为接下去的处理过程中使用的部分功能 只有ARM指令能完成, 而thumb指令完不成 (2)改变处理器模式,进入产生的异常对应的异常模式 (3)设置CPSR来禁止刚产生的同种类型的异常。 例:当IRQ产生后,ARM会自动调用CPSR的相应标识位来禁止再次产生IRQ,此时只有比IRQ优先级别更高的 异常(Reset,Data Abort, FIQ)产生才能打断IRQ的异常处理。
3、保存返回地址到LR_《MODE》;(LR是异常对应的模式中的LR,而不是异常产生之前的LR。)
4、设置PC为相应的异常向量;
四、异常返回需要在异常处理回调代码中最后使用代码实现,因为CPU不会自动做异常返回,异常处理需要:
1、从SPSR_《MODE》恢复CPSR 2、从LR_《MODE》恢复PC (上面的SPSR_《MODE》 、LR_《MODE》中的MODE,表示的是ARM模式, 即表示某种特权模式下的SPSR与LR,即异常产生后进入的模式中的SPSR与LR)
五、上面有句话“设置PC为相应的异常向量;”, 那么什么是异常向量呢?为什么要设置呢?
1、什么是异常向量?
每种异常都有一个4字节内存与之对应,一般Reset对应的内存地 址为0x00, UndefinedInstruction对应的内存地址为0x4,其它异常见上面的图。
由图可知,这些内存地连续的。并且由于每一个内存都刚好是4字节 (即32位),刚好能放一条ARM指令。 上面所说的异常向量地址是从0x00开始的,实际上有些是从0xFFFF0000 开始的,例如ARM720T、ARM9/10等。 为了更加灵活,在Cortex A8内核的ARM处理器的异常向量地址的 甚至可以使用程序设置。 假如说,设置开始为0xFFFF0004,那么Reset对应的向量内存地址为0xFFFF0004, Undefined Instruction对应的向量内存地址为0xFFFF0008,Software Interrupt 对应的向量内存地址为0xFFFF000C。
2、为什么设置pc为相应的异常向量? 其实是为了执行异常回调代码。当异常产生后,可以使用回调代码来处理异常。
3、异常产生及其使用回调代码整个过程为是什么?(共需要6步) 1)。异常产生 2).pc值设置为相应异常向量内存地址 3).ARM处理器执行pc指向的异常向量内存中的代码。 4)。异常向量内存中的代码使用为“修改PC值”,使PC指向回调代码的第一条 5)。执行回调代码
FIQ异常产生及其使用回调代码整个过程除了可以使用上面的步步骤之外, 还可以使用别外一种方式,即FIQ异常向量内存(32位)的代码写为回调代码 的第一条代码,再把FIQ异常向量内存的下一个32位写为回调代码的第二条 代码,以此类推。之所以可以这样做是因为FIQ异常向量内存的后面内存不再是 其它异常向量内存了。所以这种方式的步骤归纳为3步:
(A)异常产生后 (B)pc值设置为相应异常向量内存地址 (C)执行回调代码 正由于IRQ只要三步,其它的都要5步,这正好体现出快中断的快。
4、上面所讲解的“异常产生及其使用回调代码整个过程”的第4步中有”修改PC值”, 那么怎么去修改这个PC的值呢? 有三种方式: (1)B 标记--------不能使用BL,否则回调用代码执行完后又只能回到向量内存处了。 (2)MOV PC, 立即数 (3)LDR PC, [PC, 立即数]
根据B指令,MOV指令和LDR指令的限制我们可以发现这三种方式都有缺陷 (1)B 标记 ----- 地址范围在-32M到+32M (2)MOV PC, 立即数----- 只能是合法的立即数 (3)LDR PC,[PC,立即数] ----- 立即数最大只能是4k(4095)
六、异常自动返回呢,返回到哪里呢?
前面讲过异常处理返回依次做下面两个动作 1、从SPSR_《MODE》恢复CPSR 2、从LR_《MODE》恢复PC 首先要清楚reset异常是不需要返回的,因为返回后就相于又重启系统了。再来看其它异常返回的情况,处理异常代码的最后有以下几种情况来恢复CPSR和恢复PC。
1、undefined Instruction或Software Interrupt异常处理返回的办法为 MOVS PC, LR 上面一条MOVS指令有两个作用,说明如下 (1)MOVS中的S表示使用SPSR_《MODE》恢复CPSR。 MOV、SUB指令后面如果加了S,一般都是表示更新CPSR的标识位,但是如果目的操作数是PC,那么不是表示更新CPSR的标识位了,而是表示把指令执行时所属模式下的SPSR恢复CPSR。 (2)使用LR恢复PC(LR保存的是产生中断处的下一条指令地址)
2、FIQ,IRQ或Prefect Abort(预取异常)处理返回的办法为 SUBS PC, LR, #4 说明: (1)SUBS中S表示使用SPSR_《MODE》恢复CPSR; (2)LR减4的值赋给PC。为什么要减4以后才赋值给PC呢?通过下面图举的例子分析?
上图可知在执行ADD指令的1周期中刚好有IRQ发生,而在该周中,正在预取 SUB减法指令,在下一2周期中即2周期中EI(处理中断),而该2周期中,预取指令已经预取到了X指令,所以PC的值为0x00C,该值被备份到了LR中。当中断返回后因该执行的指令为MOV才对,如果返回时,直接把之前备份的LR恢复到PC,那么 就返回到了X。而把LR减4后恢复到PC,就可以返回到MOV指令,这才是程序正确 的结果。FIQ中断异常与IRQ中断异常是一样的道理。Prefect Abort(指令预取)异常返回时也要把LR减4后恢复到PC,但是原因与FIQ和IRQ不一样;Prefect Abort异常返回时不因该返回到该预取代码的下一条,而是因该继承去预取指令,直到预取成功才能执行下一条指令,如果直接把LR恢复到PC,则就跳过了预取指令了。(FIQ与IRQ是在别的指令执行的时候被打断的,而指令预取是指令在预取的时候产生的,不是执行的时候产生的)
3、Data Abort(数据异常)返回 数据异常是正在使用数据产生异常,而前一条一定是取数据,当数据异常处理返回时因该返回到发生数据异常指令的前一条指令(重新取数据)。因该让LR减8恢复到PC才能做到上面一点。
4、如果异常处理返回之前对LR进行了压栈,那么返回时因该出栈 采取的办法为: LDMFD sp! , {PC}^ 说明:(1)sp指向的栈顶的值恢复到PC (2)^表示使用SPSR《MODE》来恢复CPSR。(如果不加^,那么就不会恢复CPSR了) 根据上面的说明可以得出结论,一条LDMFD指令就既可以从栈中把LR的值恢复到PC,又可以把SPSR《MODE》恢复到CPSR (说明:使用ARM的程序员对异常返回的知识点,只要知道SWI(软中断)、FIQ和IRQ就可以了)
|