中断函数、函数调用 --- 出栈入栈原理分析

子函数或者中断函数的最后,一般的语句都是BX LR 返回调用点,但是两者的含义略有不同

中断函数

当系统发生中断/异常的时候,CM3 处理器会:

1、入栈:将 8 个寄存器的值压入栈,其中 LR 的值是中断前的值;

2、取向量:从向量表中获取对应中断的 ISR 入口地址;

3、取出MSP,更新到栈指针 SP 中,根据进入中断前的状态更新链接寄存器 LR为EXC_RETURN,更新 PC为中断函数;

入栈就是在进入中断/异常服务程序之前的现场保存,硬件自动将 xPSR、PC、LR、R12、R3、R2、R1、R0 压入栈,如果当中断/异常发生时刻,正在使用 PSP,则压入 PSP;否则压入 MSP;一旦进入 ISR,那就一直使用 MSP;

 

 

R0~R3 和 R12 自动入栈了,那其他的 R4~R11 呢, 依据 ARM 的 C 语言标准函数调用约定(AAPCS),如果真的用到了 R4~R11,C语言编译为汇编时,编译器生成代码来入栈它们,因为AAPCS是约束C语言到汇编,如果中断函数是用汇编写的,则需要自己编写汇编代码入栈;

当中断最后调用BX LR把EXC_RETURN送给 PC,CPU检测到向PC中载入的是这个特殊值时,就知道是中断返回,于是做中断返回的动作(与压入动作相反:从堆栈中弹出核心寄存器的值,恢复到线程模式或Handler模式等)

ExceptionEntry()伪代码,描述的就是进中断函数前的操作

 

 

子函数

BL调用子函数,LR保存的是调用函数后的下一个指令地址。

调用子函数,编译器知道在哪一行代码中进入子函数,所以C语言编译为汇编代码后,在子函数使用的寄存器,如果父函数也使用了,在父函数调用前压栈,子函数返回前会出栈。所以进入子函数前,是否压栈,压栈什么寄存器,是由编译器决定的,编译器依据AAPCS标准

中断是随时随刻都可能触发,编译器无法提前判断,所以进入中断前的入栈,和退出中断前的出栈,也是依据AAPCS,不过入栈出栈是硬件自动实现

子函数最后执行BX LR,相当于MOV LR, PC(PC = LR) 

 

 

参考资料

《Armv7-M Architecture Reference Manual

 

posted @ 2023-09-29 00:01  流水灯  阅读(276)  评论(0编辑  收藏  举报