在博客[2]Cortex-M系列: 软件中断和硬件中断中,主要写的是要怎么配置中断并产生中断,而本篇主要将CPU是如何识别我们的代码并运行中断,同时不破坏程序的上下文的。文中用一个例子解释了惰性压栈的原理。在发生中断嵌套时,Cortex-M处理器将使用出栈抢占、末端连锁、延迟到达等机制来优化响应速度,同时降低了功耗[6]。理解这部分原理,一方面有利于处理在中断中出现的BUG,另一方面是有利于深入理解嵌入式操作系统(这部分算是基础)。
在阅读本文前,推荐先阅读[4]Cortex-M系列:非中断、特权模式下的汇编语言,因为:调用者(函数)+被调用者(函数)的关系和进程(或在无操作系统中的main函数)与中断的上下文关系是类似的。
本文是在Stm32H743单片机下进行测试的,对Cortex-M4和Cortex-M7适用,对CortexM3不一定适用。
目录
2.2 从中断向量表中取出异常向量( vector fetch)
1 接收异常请求
在博客[2]中的硬件中断请求设置完成后,要能够接收异常请求还有以下条件(虽然大多是情况下是满足的):
1、处理器正在运行(未被暂停或处于复位状态)[7.7.1]
2、异常处于使能状态
3、异常的优先级高于当前优先级
4、异常未被屏蔽
2 异常进入
参考[7.7.2],[2.3.7]
当有一个具有足够优先级的挂起异常时发生异常项,并且满足一下其中一个条件:
•处理器处于线程模式。
•新异常的优先级高于正在处理的异常,在这种情况下,新异常会抢占原始异常。
当一个异常抢占另一个异常时,异常被嵌套。
注:2.1,2.2,2.3的工作是同步进行的,并不是顺序关系。
2.1 寄存器压栈(Stacking)
先解释下上下文(context),这里的上文指中断前程序所在的地方,下文指中断后的中断服务程序所在的地方,先不考虑中断嵌套的情况。另外文中不特别区分中断和异常两个概念的区别,而当做一个意思。
2.1.1 栈帧
寄存器压栈分为短栈帧压栈和长栈帧压栈。
短栈帧由不保护浮点寄存器的调用者保护寄存器组成:R0~R3,R12,返回地址,LR,xPSR组成,共8个字。[8.1.3]
注:栈帧包括返回地址。这是中断程序中的下一条指令的地址。此值在异常返回时恢复到PC寄存器,以便中断的程序恢复运行.

长栈帧由包含浮点寄存器的调用者保护寄存器组成:R0~R3,R12,返回地址,LR,xPSR,S0~S15,FPSCR组成,共26个字。
长短栈帧的选择由:CONTROL.FPCA位决定。该位为1时表示该进程或主函数使用了浮点运算,则需要保护浮点寄存器上文,因此选用长栈帧。反之使用短栈帧。

2.1.2 MSP/PSP
选择保存的栈的位置由:CONTROL.SPSEL位决定。该位为1时使用进程栈指针所指向的地址为栈入口,反之(默认)以主栈指针所指向的指针作为栈入口。
注:有操作系统时,当前任务选择使用进程栈指针(PSP),OS内核及异常处理使用MSP。没有操作系统时,默认始终使用MSP。
2.1.3 双字对齐
最后还需要考虑双字对齐:该特性在SCB.CCR.STKALLGN中设置,Cortex-M4和Cortex-M7是默认开启的。压栈前,程序会判断所需要的栈帧压栈后是否满足双字对齐,如果不满足,则将xPSR.bit9写为1,并在压栈后,附加一个栈顶空位来实现对齐。
另外,“惰性压栈”将在2.1+中讲解。
2.1+ 一个例子
现在我们实测下长栈帧的情况:无操作系统的最小系统程序进入主程序后就进入”while(1);“中,程序只开启一个定时器2中断,定时器2中除了清除定时器状态寄存器的状态标志,无其他操作。中断前寄存器为下图(FPU部分比较长,就没截图了):


中断后在memory找到如下长栈帧:

疑问:上文没使用浮点运算也是长栈帧,浮点单元中读取的值和Keil寄存器中观察的值不一致。
答:

本文详细介绍了Cortex-M系列处理器如何处理中断和异常,包括寄存器压栈、异常向量表、异常分类及优化机制等内容。通过具体实例解释了惰性压栈原理,以及在中断嵌套时Cortex-M处理器如何优化响应速度。

2107

被折叠的 条评论
为什么被折叠?



