一,前言
每次看os代码,涉及到汇编总是要折腾一把,原因是每次看的时候汇编指令含义又忘记了,另外一个问题就是我之前弄不清哪些是程序员要手工写的,哪些是编译器自动的,哪些是硬件自动的。所以今天准备再搞搞清楚,虽然我只要有看懂汇编的水平即可,但是弄清楚后,我就可以自己写中断相关的汇编了。基于cortexM4内核,做了调试验证。
二,编译器自动出栈入栈验证
对应没有os的工程,我们都是c代码,那么入栈和出栈寄存器保护和恢复都不需要程序去写代码。那么保护的到底是8个还是16个呢?
找了一个timer中断函数,平时就是delay延时等待timer中断到后led闪烁的工程。
下面的情景是在延时函数中触发中断,自动入栈保护现场(8个寄存器),执行中断,恢复现场(8个寄存器还原),r4~r11看起来没有自动恢复的原因是,根本没有用到,用到的话也会保存和恢复。所以是16个。其中r12是可以不被保存的。
-
跳入中断前,一直在0x080005F0~0x080005F6间运行,因为是一个do while循环,做超时等待用的。
-
进入中断前会自动入栈保护现场,按入栈地址顺序检查下msp寄存器的内容,与进入中断前一致。
这里解释下LR寄存器会被更新为0xFFFFFFE9的原因是。
cortexM3M4在进入异常服务程序后, LR的值被自动更新为特殊的EXC_RETURN,这
是一个高28位全为1的值,只有[3:0]的值有特殊含义。当异常服务例程把这个
值送往PC时,就会启动处理器的中断返回序列。这个返回序列就是说的恢复动作。
0xFFFF_FFF1 返回handler模式
0xFFFF_FFF9 返回线程模式,并使用主堆栈(SP=MSP)
0xFFFF_FFFD 返回线程模式,并使用线程堆栈(SP=PSP)
事实上,在中断嵌套时,更深层ISR所看到的LR总是0xFFFF_FFF1。因为中断和异常都是出于handler模式。 -
退出中断后,可以恢复现场。和入栈前的8个寄存器值一样,说明正确。r4~r11没有用到,无需保护和恢复。
再说下需要手工恢复的r4~r11。为什么要手工恢复呢?很容易理解,就是为了保存这个task中的临时变量。
r0-r3 用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。
被调用函数在返回之前不必恢复 r0-r3。如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。
r4-r11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。
r12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。
在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复 r12。
13 是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。
r14 是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复
r15 是程序计数器 PC。它不能用于任何其它用途。
-
进入中断和进入函数一样的,都是要进行中断保护的,那么我继续拿一个没os的工程,进入一个函数来验证下R4 ~ R11是否进行了自动保存。(r0 ~ r3及另外4个由于不是进入中断,所以不需要入栈保护)
进入myfun函数前,可以观察r4 ~ r11
进入myfun函数后R0 ~ R11都被修改过,R0 ~ R11保存了传入参数,正常来说输入的参数是放在R0 ~ R3寄存器中的,但是我故意将传入的参数数量变大,这样确保可以使用到R4 ~ R11寄存器来验证它的恢复。
从myfun函数返回后,可以看出r4 ~ r11已经恢复,说明编译器在函数调用返回的时候会进行自动恢复。
-
同样是myfun函数,我在最后一句加入了while(1),等待中断来打断。
退出中断后,r4 ~ r11都还原了。而且r0 ~ r3及另外4个寄存器也还原了,只有从中断退出才会恢复r0 ~ r3和PC,lr等一共8个寄存器的。App调用是不会恢复r0 ~ r3等8个寄存器的。
三,关于硬件,编译器和程序员对中断代码的分工
今天的调试都是编译器自动生成的汇编语言起到的入栈和出栈保护。前2天分析的FreeRTOS任务切换源码分析--Apple的学习笔记和FreeRTOS的第一个任务跳转源码分析—Apple的学习笔记里面都是__arm函数中程序员自己写的汇编语言,所以关于出栈和入栈及跳转都要用汇编命令来实现。<Cortex M3权威指南>中写的用汇编,BX LR就可以不用程序员去保存r0 ~ r3,lr,pc等8个内核寄存器。关于r4 ~ r11若用到的话,则程序员要手工保存。昨天看的都是bx r3(保存的值和lr一样)照样在中断中断不需要程序员去保存和恢复r0 ~ r3,lr,pc等寄存器,说明bx指令就启动了序列恢复,而进入中断cortexM硬件自动时候启动了入栈保存。
cotexM3内核手册中关于跳转指令,cortexM4也类似的。
四,总结
今天算是弄的比较清楚了,对于c代码,程序员不需要做出入栈保护。对于编译进出中断程序员需要保护r4 ~ r11的8个寄存器(若中断中用到的话),而R0 ~ R3,R12,LR,PC,xPSR是cortexM硬件自动在中断触发的时候保存的,在pop {xx,lr}或者BX lr的时候硬件自动恢复的。