![kelly的头像 kelly的头像](https://cdn.eetrend.com/files/styles/picture200/public/pictures/picture-233-1470101630.jpg?itok=1zaKJM2x)
本节主要介绍Cortex-M0的异常处理模型。
1、异常的状态
每个异常都处于以下状态之一:
未激活(Inactive)
异常没有被激活也没有被挂起。
挂起(Pending)
异常正等待被处理器服务。
来自外设或软件的中断请求,能让相应中断变为挂起状态。
激活(Active)
异常正在被处理器服务,并且服务尚未结束。
注意:一个异常处理能中断另一个异常处理,在这种情况下,两个异常都处于激活态。
激活且挂起(Active and pending)
异常正在被处理器服务时,又出现了来自同一异常源的异常。
2、异常类型
异常的类型有:
Reset
复位(Reset)由加电或热复位引起,异常模型把复位当作一个特殊形式的异常。当复位信号有效时,无论指令执行到什么位置,处理器都停止当前的指令。复位信号失效之后,从复位异常规定的入口地址处开始执行,按Thread模式工作。
NMI
不可屏蔽中断(NMI)可由外设引起或软件触发,NMI是除Reset之外,最高优先级的异常,NMI被永久允许,拥有固定的优先级-2。
NMI不能被:
• 任何别的异常屏蔽或阻止。
• 除复位之外的任何异常抢占。
HardFault
硬故障(HardFault)是指在正常情况或异常处理时出现错误(error)所引起的一种异常,硬故障有固定的优先级-1,表明硬故障比任何一个可以配置优先级的异常的优先级都高。
SVCall
超级管理员调用(SuperVisorCall,SVC)是一种由SVC指令触发的异常,在操作系统环境下,应用程序能使用SVC指令访问操作系统内核或设备驱动程序。
PendSV
PendSV是一种中断驱动的请求,用于面向系统级服务。在操作系统环境下,当没有别的异常激活时,使用PendSV进行上下文切换。
SysTick
如果处理器实现系统滴答定时器,那么SysTick是由系统滴答定时器计数到0时所产生的一种异常。软件也能产生SysTick异常,在操作系统环境下,处理器能使用这一异常作为系统滴答计时。
中断(IRQ)
中断(IRQ)是由外设或软件请求而产生的异常,所有的中断对指令的执行是异步进行的。在计算机系统中,外设通过中断与处理器通信。
![](http://mm32.eetrend.com/files/2016-08/wen_zhang_/100002434-7747-111.jpg)
a、为简化软件层,CMSIS仅使用IRQ号,因此采用负数作为异常的编号,IPSR返回异常编号,详见第2-7页的中断程序状态寄存器。
b、有关更多的信息,请看中断向量表。
c、如果处理器没有系统滴答定时器,则异常15保留。
d、IRQ中断号是在处理器实现时定义的,范围是1-32。未实现的IRQ异常编号被保留,例如,如果某处理器仅实现一个IRQ,则异常号17及以上的编号均被保留。
e、见下文的中断优先级寄存器。
f、增长步长为4。
对于异步异常,除Reset外,处理器在异常被触发到异常开始被处理的这段时间内,仍能继续执行指令。
特权软件可以禁止表1中所列出的具有可配置优先级的异常。
3、异常处理
处理器使用以下方式处理异常:
ISRs
中断服务子程序(ISRs)处理IRQ中断异常。
Fault handler
故障处理程序(Faulthandler)处理硬故障。
System handlers
系统处理程序(System handlers)处理系统异常:NMI,PendSV,SVCallSysTick和硬故障。
4、异常向量表
异常向量表包含堆栈指针的复位值,以及起始地址。堆栈指针的复位值和起始地址一起被称为异常向量,用于所有异常处理。图2列出了异常向量表中的异常向量的次序。每一个向量的最低位必须为1,表明异常处理是用Thumb代码实现的。
![](http://mm32.eetrend.com/files/2016-08/wen_zhang_/100002434-7749-222.jpg)
向量表起始地址固定于0x00000000。
5、异常优先级
如上文中的表1所示,所有的异常都有一个优先级。
• 优先级数值越小,优先级越高。
• 除Reset,HardFault和NMI之外,其余所有异常都能配置其优先级。
如果软件没有配置优先级,那么所有可配置优先级的异常的优先级为0。
注意:可配置优先级的数值范围是0-192,步长为64。Reset、HardFault和NMI是具有负数值的固定优先级异常,比其它异常具有更高的优先级。
给IRQ[0]分配一个较大的优先级数值,而给IRQ[1]分配一个较小的优先级数值,就意味着IRQ[1]比IRQ[0]的优先级高,如果IRQ[1]和IRQ[0]都被触发,则IRQ[1]比IRQ[0]先执行。
如果多个挂起的异常具有同等的优先级,那么具有较小异常号的异常优先执行。例如,如果IRQ[0]和IRQ[1]具有相同的优先级,且都挂起,则IRQ[0]比IRQ[1]优先执行。
当处理器在执行一个异常处理时,如果有更高优先级的异常发生,则该异常可以被抢占。如果发生的异常与正在执行的异常具有相同的优先级,则不管其异常号为多少,正在执行的异常都不会被抢占,而新发生的异常的状态变为挂起。
6、异常处理的进入与返回
描述异常处理的一些术语:
抢占(Preemption)
当处理器在执行一个异常处理时,另一个异常处理可以抢占这个正在被执行的异常处理,只要其优先级比正在被处理的异常优先级高。
一个异常处理抢占另一个异常处理,被称为异常嵌套。
返回(Return)
当没有以下情况时,异常处理结束之后就异常返回:
• 没有挂起的异常需要服务。
• 所完成的异常处理不是一个迟到异常。
处理器从堆栈弹出数据,现场恢复到中断发生之前的状态。
尾链(Tail-chaining)
该机制加速了异常服务。当一个异常处理刚好完成时,若此时有一个挂起的异常满足进入执行的条件,则从堆栈弹出数据的操作就跳过,直接转到这个新的异常处理。
迟到(Late-arriving)
该机制加速抢占。当一个异常处理正在保存状态时,如果出现了一个更高优先级的异常,则处理器切换去执行这个更高优先级的异常,为新异常初始化预取向量。迟到异常并不影响向量的保存,因为两个异常需要保存的状态是一致的。在迟到异常返回时,正常的尾链规则依然有效。
异常进入
有一个具有足够高优先级的挂起异常,以及以下条件之一,则可以进入异常:
• 处理器处于Thread模式。
• 新的异常比正在处理的异常具有更高的优先级。这种情况下,新异常抢占正在执行的异常。
当一个异常抢占另一个异常时,将出现异常嵌套。
足够高优先级的含义是,该异常比屏蔽寄存器的任一有限集的优先级都高,见第2-8页的异常屏蔽寄存器。而比有限集的优先级低的异常就挂起,不执行。当处理器处理一个异常时,除非该异常是一个尾链或迟到的异常,处理器把信息压入当前堆栈,这一操作称为入栈,而这个8字的数据结构被称为一个堆栈帧,堆栈帧包含如下的信息:
![](http://mm32.eetrend.com/files/2016-08/wen_zhang_/100002434-7750-444.jpg)
入栈后,堆栈指针指向帧的最低地址,堆栈帧是双字地址对齐的。
地址,该地址值保存在堆栈帧信息的PC中,因此被中断程序能恢复现场。处理器从向量表中读取异常处理程序的起始地址,当保护现场的入栈操作完成后,处理器开始执行异常处理程序。与此同时,处理器会将一个EXC_RETURN值写到LR寄存器,这能指明与堆栈信息帧相应的堆栈指针,以及进入异常之前处理器所处的操作模式。
如果在进入异常时没有发生更高优先级的异常,则处理器开始执行该异常处理,并自动把该异常的状态由挂起修改为激活。
如果在进入异常时发生了另一个更高优先级的异常,则处理器将执行高优先级的异常处理,先前的异常的挂起状态保持不变,这属于迟到异常的情况。
异常返回
当处理器处于Handler模式,且执行以下之一指令将PC的值置为EXC_RETURN的值,则发生异常返回:
• 执行POP指令,加载PC寄存器。
• 执行BX指令(任何寄存器均可)。
在异常进入时,处理器把EXC_RETURN的值保存到LR寄存器。处理器根据此数值来决定异常处理完成时的动作。EXC_RETURN值的[31:4]位是0xFFFFFFF,当处理器加载的值与之匹配时,处理器将检测到这不是一个正常的分支操作,而是异常结束。因此,处理器将开始异常返回操作。
EXC_RETURN值的[3:0]位指出所需的返回堆栈以及处理器模式,如表2所示。
![](http://mm32.eetrend.com/files/2016-08/wen_zhang_/100002434-7751-555.jpg)