前言
在上一篇文章中,我们深入分析了函数调用返回、中断调用返回使用的LR和EXC_TURN,也简单说明了函数调用流程、中断调用流程。在FreeRTOS中,任务的调度是通过SysTick + PendSV 中断来实现的,尤其在PenSV中断中进行任务上下文切换原理,是理解FreeRTOS任务调度原理的重中之重。而SysTick和PendSV都是中断,所以我们有必要深入分析一下在ARM CM3/4中中断的完整处理流程。
中断处理完整流程概述
整体流程如下:
- 主程序或应用程序正在运行过程中,中断发生。
- CPU自动保存现场寄存器,包括R0~R3, R12, R14(LR), 返回地址(PC), xPSR ,将这8个寄存器值存放到堆栈中,具体是任务堆栈,还是主堆栈,取决于发生中断前正在使用的是MSP还是PSP。
(1) 假如发生中断前,正在运行OS的某个任务,即此时使用的是PSP,则将现场的这8个寄存器值使用PSP存储到线程的堆栈中。
(2)假如发生中断前,正在使用MSP(裸机程序或中断嵌套),则将现场的8个寄存器值使用MSP存放到主堆栈中。
注:这些工作都是CPU自动完成的,硬件实现,不需要程序员实现。这8个寄存器的值就是栈帧- CPU根据发生中断前的状态,比如正在使用MSP或PSP,当前模式是线程模式或handler模式等,计算查表获取EXC_RETURN值。然后将EXC_RETURN值赋值给LR,这里注意,步骤2中保存的是LR的值,这里是将EXC_RETURN的值更新到LR寄存器,不要混淆。
注:这些工作是CPU自动完成的,硬件实现,不需要程序员实现。步骤2和3都是进入中断前的准备工作,也就是stacking阶段。- 进入中断服务程序,此时假如步骤2和3中使用的是PSP,则CPU会自动切换使用MSP,因为一旦进入中断,CPU的工作模式就变成了Handler模式,而Handler模式默认是使用MSP的。
- 将LR寄存器和其他高级寄存器(R4~R11根据需要选择)压栈。
注:该步骤必须由程序员来实现,当然了,IDE或交叉编译器会帮我们实现。我们必须要将LR压栈,因为返回的时候需要LR的值EXC_RETURN.- 执行中断服务程序。
- 中断服务程序执行完毕后,调用pop {r4, PC}, 由于步骤4中,是将r4和LR压栈,本步骤出栈,则是将LR的值(EXC_RETURN)赋值给了PC。
- CPU判断PC的值,发现PC的值在EXC_RETURN值范围,该范围本身在CPU的地址架构中定义就是“non-excutable”,所以CPU就知道了,原来这是中断要退出的信号,同时会更新SP,变成步骤2中使用的SP,比如由MSP切换到PSP。
- 执行步骤2的反操作,即将进入中断前存储的现场R0~R3, R12, R14, 返回地址(PC)、xPSR 恢复现场,此时PC就被赋值为了真正的返回地址。
注:该步骤和步骤7中,出现了2次赋值给PC,但是第一次只是通知CPU,让CPU知道要中断执行完毕,需要退出,然后让CPU自动执行步骤8和9. 这样设计的目的是与函数调用流程一致,但是又能让CPU能够区分函数返回和中断返回。- 由于步骤9中已经实现了恢复现场的效果,所以应用程序继续执行。
附录引用:中断处理流程详解
接下来我们直接引用《The definitive guide to ARM Cortex-M3/4》第8章节关于中断处理详情的介绍的几个经典流程图
1. 中断发生时,CPU自动压栈帧+中断向量表查找时序图
2. 中断发生时,CPU自动压栈栈帧顺序图
3.1 中断发生时,CPU使用MSP自动压栈图
这种情况,一般对应于裸机程序或正在处理中断1的时候,又有高优先级中断2发生了,所以使用MSP进行压栈。
上图中的Stacking就是指自动压栈,这个图画的非常好,stacking是上升沿,在应用程序之后,在中断服务程序之前,是两个阶段的过渡,CPU自动执行。
3.2 中断发生时,CPU使用PSP自动压栈图
这种情况,一般对应于:发生中断前正在执行OS的某个任务,正在使用PSP,发生中断后,CPU就会使用PSP自动压栈帧。
上图中,还包括了,在处理中断1的时候,又有高优先级中断2触发,这个时候,就出现了中断嵌套,则中断2中,CPU会继续使用中断1运行过程中使用的MSP压栈帧。
上图中的Stacking就是指自动压栈,stacking是上升沿,是两个阶段的过渡,CPU自动执行。
4.1 线程模式下(使用MSP)发生中断,然后中断退出全流程
上述流程,对应的是裸机程序下,发生中断嵌套的全流程处理过程。
上图中的Stacking就是指自动压栈,stacking是上升沿,是两个阶段的过渡,CPU自动执行。
4.2 线程模式下(OS系统使用PSP)发生中断,然后中断退出全流程。
上述流程,对应的就是含有RTOS系统的程序,发生中断前正在执行某个task,然后发生了中断嵌套的全流程处理过程。
上图中的Stacking就是指自动压栈,stacking是上升沿,是两个阶段的过渡,CPU自动执行。
5. 中断退出,CPU根据EXC_RETURN自动出栈恢复现场流程
上述流程,主要是通过对EXC_RETURN的分析,中断退出是,到底是使用PSP还是MSP来进行出栈,恢复现场。
小结
中断处理的全流程,涉及了很多细节知识点,这些知识点对于纯应用开发者来说,基本上不需要关注,因为IDE或交叉编译器已经帮我们自动实现了,尤其CPU的中断机制巧妙的让我们可以直接使用C语言来写中断服务程序了。但是对于想要深刻理解嵌入式RTOS的人来说,中断处理的全流程知识点,就必须要掌握了,因为RTOS最核心的任务调度、上下文切换都是基于中断细节实现的。后续我们会基于这些知识点来分析RTOS底层的调度原理、上下文切换原理。