本帖最后由 classroom 于 2023-11-6 10:35 编辑
上图表述了两种压栈格式。这里我们只关注右边的压栈格式,后面试验不使用浮点存储。 我这里借助Cortex-M4内核的STM32G4芯片的TIM1/TIM2的更新中断来完成试验。二者建立主从同步启动关系,TIM1的抢占优先级高于TIM2的,均工作在单脉冲模式,即每次启动后都能且只能产生1次更新中断。通过给二者设置相应的溢出时间参数,来模拟实现上面的三种情形。我们从中关注栈帧和个别特定寄存器的内容。【注:本试验过程中仅开启了TIM1/TIM2更新事件中断,再无其它。】 对于发生抢占情形,我们通过在代码里设置断点,一方面可以看到2个中断的响应先后,另一方面可以看到因抢占动作导致栈帧内容的变动。 对于晚到情形和晚到情形,同样也会通过在代码里设置断点查看中断执行先后顺序。 对于晚到情形,申请压栈的是低优先级中断事件,先得到执行的则是高优先级中断,同时会发现虽然响应了2次中断,却只看到1次压栈,两次ISR运行时维持同一栈内容。对于咬尾情形在栈帧内容变化上跟晚到情形类似,但实现机理不同,它申请压栈的是高优先级或同级中断事件,做咬尾操作的是低优先级或晚发生中断请求的同级中断。 先介绍两个跟中断返回有关的寄存器LR(R14)和EXC_RETURN。纠正下,EXC_RETURN不是寄存器,是微处理器动态生成的跟中断返回有关的一个值。这个值有点神秘,也很重要。神秘的就是这个值怎么产生的、放在哪里的,似乎在ARM相关手册找不到具体说明。另外,这个值本身很特别,大大区别于通用程序运行地址。如果程序里不启用浮点存储,它的值可能是下面三个。 这里我们重点关注图中的前2个,后面试验过程中会见到这两个值。结合图中信息,如果该值等于0xfffffff1,ISR执行完毕后要返回Handle Mode和Main Stack,极大可能地发生了中断嵌套;如果该值等于0xfffffff9,ISR执行完毕后要返回Thread Mode和Main Stack,意味着当前中断是在线程中产生的,不用OS的话,即Main程序被打断。 它很重要,中断返回得仰仗它。没有它,中断返回可能就乱套了。 每当中断压栈申请完成后,这个EXC_RETURN值就被硬件根据中断发生时CPU运行状态、运行模式、所用栈帧模式给生成好了,并在开始运行ISR之前将该值主动赋给LR寄存器。硬件在ISR执行完毕即将退栈返回时又自动将LR的内容提供给PC寄存器。当PC寄存器发现这个特殊的值后会不会一脸懵逼,啥玩意?地址不像地址。我们可以把这个值理解成中断返回告知书,并非程序地址。EXC_RETURN值通过LR寄存器做中间人传达给PC,主要传达下面几个信息: 1、恭喜我们完美地处理了刚才的突发事件,要归队返回了; 2、我们清楚刚才处理事情时的状态和待遇,但更要清楚返回后的状态、模式,不得以刚才的模式或状态来套返回后的模式或状态,不能因出了趟差就不知回家后的姿态和责任; 3、记住上面提到的,具体返回路线会专人提供【即之前压栈的PC值经出栈提供】; 戏说下,知道大意即可,更多细节可以查看相关手册。退一步讲,个中细节我们旁人也真的难以知晓。 铺垫性的话题就聊到这里。下面具体看看针对中断抢占、中断晚到、中断咬尾的试验。 先看中断抢占的情形。下面截图是有关TIM1/TIM2时基参数的配置。 在前面提到的固定配置前提下,我将TIM2溢出周期比TIM1少21个脉冲【这个地方不是固定的,14~24应该都可以,具体自行验证】,二者同步启动。这样配置的目的就是确保TIM2一定是先进中断但又不至于它执行完毕了TIM1中断还没来,否则就没法看到抢占情形了。下图是TIM2首先进入中断时的情形:
|