Cortex_M3之中断的具体行为

在本文中,如无特殊说明,“中断”与“异常”这两个术语都是同一个意思,可以互换使用。

中断/异常的响应序列

当CM3开始响应一个中断时,会在它看不见的体内奔涌起三股暗流:

  • 入栈: 把8个寄存器的值压入栈
  • 取向量:从向量表中找出对应的服务程序入口地址
  • 选择堆栈指针MSP/PSP,更新堆栈指针SP,更新连接寄存器LR,更新程序计数器PC

入栈

响应异常的第一个行动,就是自动保存现场的必要部分:依次把xPSR, PC, LR, R12以及R3‐R0由硬件自动压入适当的堆栈中:如果当响应异常时,当前的代码正在使用PSP,则压入PSP,即使用线程堆栈;否则压入MSP,使用主堆栈。一旦进入了服务例程,就将一直使用主堆栈。

假设入栈开始时,SP的值为N,则在入栈后,堆栈内部的变化如下表所示。又因为AHB接口上的流水线操作本质,地址和数据都在经过一个流水线周期之后才进入。另外,这种入栈在机器的内部,并不是严格按堆栈操作的顺序的——但是机器会保证:正确的寄存器将被保存到正确的位置,入栈下图所示。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
CM3在看不见的内部打乱了入栈的顺序,这是有深层次的原因的。先把PC与xPSR的值保存,就可以更早地启动服务例程指令的预取——因为这需要修改PC;同时,也做到了在早期就可以更新xPSR中IPSR位段的值。

取向量

当数据总线(系统总线)正在为入栈操作而忙得团团转时,指令总线(I‐Code总线)从向量表中找出正确的异常向量,然后在服务程序的入口处预取指。由此可以看到各自都有专用总线的好处:入栈与取指这两个工作能同时进行。

更新寄存器

在入栈和取向量的工作都完毕之后,执行服务例程之前,还要更新一系列的寄存器:

  • SP:在入栈中会把堆栈指针(PSP或MSP)更新到新的位置。在执行服务例程后,
    将由MSP负责对堆栈的访问。
  • PSR:IPSR位段(地处PSR的最低部分)会被更新为新响应的异常编号。
  • PC:在向量取出完毕后,PC将指向服务例程的入口地址,
  • LR:LR的用法将被重新解释,其值也被更新成一种特殊的值,称为“EXC_RETURN”,
    并且在异常返回时使用。EXC_RETURN的二进制值除了最低4位外全为1,而其最低4位则有另外的含义。

以上是在响应异常时通用寄存器的变化。另一方面,在NVIC中,也伴随着更新了与之相关的若干寄存器。例如,新响应异常的悬起位将被清除,同时其活动位将被置位。

异常返回

当异常服务例程执行完毕后,需要很正式地做一个“异常返回”动作序列,从而恢复先前的系统状态,才能使被中断的程序得以继续执行。从形式上看,有3种途径可以触发异常返回序列,触发中断返回的指令如下表所示;不管使用哪一种,都需要用到先前储的LR的值。
在这里插入图片描述
在这里插入图片描述

有些处理器使用特殊的返回指令来标示中断返回,例如8051就使用reti。但是在CM3中,是通过把EXC_RETURN往PC里写来识别返回动作的。因此,可以使用上述的常规返回指令,从而为使用C语言编写服务例程扫清了最后的障碍。

在启动了中断返回序列后,下述的处理就将进行:

  1. 出栈:先前压入栈中的寄存器在这里恢复。内部的出栈顺序与入栈时的相对应,堆栈指
    针的值也改回去。
  2. 更新NVIC寄存器:伴随着异常的返回,它的活动位也被硬件清除。对于外部中断,倘若
    中断输入再次被置为有效,悬起位也将再次置位,新一次的中断响应序列也可随之再次开始。

嵌套的中断

在CM3内核以及NVIC的深处,就已经内建了对中断嵌套的全力支持,开发工程师要做的就只是为每个中断适当地建立优先级,不用再操心别的。表现在:

第一、 NVIC和CM3处理器会为我们排出优先级解码的顺序。因此,在某个异常正在响应时,所有优先级不高于它的异常都不能抢占之,而且它自己也不能抢占自己。

第二、 有了自动入栈和出栈,就不用担心在中断发生嵌套时,会使寄存器的数据损毁,从而可以放心地执行服务例程。

另一个要注意的,是相同的异常是不允许重入的。因为每个异常都有自己的优先级,并且在异常处理期间,同级或低优先级的异常是要阻塞的,因此对于同一个异常,只有在上次实例的服务例程执行完毕后,方可继续响应新的请求。由此可知,在SVC服务例程中,就不得再使用SVC指令,否则将fault伺候。

咬尾中断

CM3为缩短中断延迟做了很多努力,第一个要提的,就是新增的“咬尾中断”(Tail‐Chaining)机制。

当处理器在响应某异常时,如果又发生其它异常,但它们优先级不够高,则被阻塞——这个我们已经知道。那么在当前的异常执行返回后,系统处理悬起的异常时,倘若还是先POP然后又把POP出来的内容PUSH回去,这不成了砸锅炼铁再铸锅,白白浪费CPU时间吗,可知还有多少紧急的事件悬而未决呀!正因此,CM3不会傻乎乎地POP这些寄存器,而是继续使用上一个异常已经PUSH好的成果,消灭了这种铺张浪费。这么一来,看上去好像后一个异常把前一个的尾巴咬掉了,前前后后只执行了一次入栈/出栈操作。于是,这两个异常之间的“时间沟”变窄了很多,异常咬尾示意图下图所示。
在这里插入图片描述

晚到(的高优先级)异常

CM3的中断处理还有另一个机制,它强调了优先级的作用,这就是“晚到的异常处理”。当CM3对某异常的响应序列还处在早期:入栈的阶段,尚未执行其服务例程时,如果此时收到了高优先级异常的请求,则本次入栈就成了为高优先级中断所做的了——入栈后,将执行高优先级异常的服务例程。可见,它虽然来晚了,却还是因优先级高而受到偏袒,低优先级的异常为它“火中取栗”。

比如,若在响应某低优先级异常#1的早期,检测到了高优先级异常#2,则只要#2没有太晚,就能以“晚到中断”的方式处理——在入栈完毕后执行ISR #2,晚到异常的处理模式图如下图示。如果异常#2来得太晚,以至于已经执行了ISR #1的指令,则按普通的抢占处理,这会需要更多的处理器时间和额外32字节的堆栈空间。

在ISR #2执行完毕后,则以刚刚讲过的“咬尾中断”方式,来启动ISR #1的执行

在这里插入图片描述

异常返回值

前面已经讲到,在进入异常服务程序后,LR的值被自动更新为特殊的EXC_RETURN,这是一个高28位全为1的值,只有[3:0]的值有特殊含义,如表9.3所示。当异常服务例程把这个值送往PC时,就会启动处理器的中断返回序列。因为LR的值是由CM3自动设置的,所以只要没有特殊需求,就不要改动它。

在这里插入图片描述
在这里插入图片描述

可以得出,合法的EXC_RETURN值共3个,如下表所示:

在这里插入图片描述
在这里插入图片描述

如果主程序在线程模式下运行,并且在使用MSP时被中断,则在服务例程中LR=0xFFFF_FFF9(主程序被打断前的LR已被自动入栈)。

如果主程序在线程模式下运行,并且在使用PSP时被中断,则在服务例程中LR=0xFFFF_FFFD(主程序被打断前的LR已被自动入栈)。

在这里插入图片描述

如果主程序在Handler模式下运行,则在服务例程中LR=0xFFFF_FFF1(主程序被打断前的LR已被自动入栈)。这时的“主程序”,其实更可能是被抢占的服务例程。事实上,在嵌套时,更深层ISR所看到的LR总是0xFFFF_FFF1,如下图所示。

在这里插入图片描述

由EXC_RETURN的格式可见,不能把0xFFFF_FFF0‐0xFFFF_FFFF中的地址作为任何返回地址。其实也并不用担心会弄错,因为CM3已经把这个范围标记成“取指不可区”了

异常响应期间的faults

Faults是运行时发生各种故障的表现,在中断响应期间的故障也不例外。中断响应的每一步骤都可以触发faults。

入栈期间

如果在入栈期间引起了总线fault,则本次入栈操作将被强行中止,并且把总线异常悬起或者在允许时立即响应。若除能了总线fault,则此次故障将成为“硬伤”——上访至硬fault。在总线fault被使能的情况下,如果它的优先级比正在响应的异常高,则抢占之,否则将悬起直到引起fault的异常执行完毕。这种情况被称为“入栈错误”(stacking error),由总线fault状态寄存器(BFSR,地址:0xE000_ED29)的STKERR位指示(位偏移:4)。如果入栈操作引起MPU访问违例,则产生存储管理fault,并且必须立即执行MemFault服务例程,否则将无条件上访成硬fault。在发生入栈时访问违例时,存储管理fault寄存器(MFSR,地址:0xE000_ED28)中的MSTKERR位(位偏移:4)被置位,用于指示该fault。入栈是自动完成的,因此不可能产生用法fault。

出栈期间

如果在中断返回时的出栈期间引起了总线fault,则本次出栈操作将被强行中止,并且把总线异常悬起或立即响应。若除能了总线fault,则此次故障将成为“硬伤”——上访至硬fault。其它情况下,只要总线fault的优先级比当前的高(也包括比当前最深嵌套的优先级高),则可以立即响应。这种情况称为“出栈错误”(unstacking error),由BFSR.3指示(UNSTKERR位)。类似地,如果是因MPU访问违例造成的MemManage fault,由MFSR.3(MUNSTKERR)指示。且MemManage fault的服务例程必须能立即执行,否则无条件硬fault。

取向量期间

若是在取向量期间发生总线fault,则比较罕见,这也是最严重的,因此直接上硬fault。这种情况,由硬fault状态寄存器(HFSR,地址:0xE000_ED2C)中的VECTTBL位(位偏移:1)来指示。

无效返回时

如果LR中的EXC_RETURN不是合法的值,则引起用法fault。如果用法fault被除能,也上访成硬fault。此时,用法Fault状态寄存器(UFSR,地址:0xE000_ED2A)中的INVPC位(位偏移:2),或者是INVSTATE位(位偏移:1)置位。

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值