- 无论对于M3/M4还是A7,处理中断的第一步都是保存现场
- 现场就是指程序运行时的那些寄存器,保存现场就是把寄存器保存到栈中;恢复现场就是处理完中断后把栈中的值恢复到寄存器中
CPU内部由R0-R15、PSR等通用寄存器,难道发生中断时要保存全部的寄存器吗?
- 不需要,只需要保存用到了的寄存器
- ARM架构中,程序调用有些规则(ATPCS规则),根据规则可以知道那些寄存器可能会被使用
- 在C函数中,可以修改R0-R3、R12、R14(LR)以及PSR。
- 如果C函数要用到这些寄存器,就要把它们保存到栈里,在函数结束前在从栈中恢复它们。
- 这些寄存器被拆分成2部分:调用者保存的寄存器(R0-R3,R12,LR,PSR)、被调用者保存的寄存器(R4-R11)
比如函数A调用函数B,函数A应该知道:
- R0-R3是用来传参数给函数B的
- 函数B可以肆意修改R0-R3
- 函数A不要指望函数B帮你保存R0-R3
- 保存R0-R3,是函数A的事情
- 对于LR、PSR也是同样的道理,保存它们是函数A的责任
对于函数B:
- 我用到R4-R11中的某一个,我都会在函数入口保存、在函数返回前恢复
- 保证在B函数调用前后,函数A看到的R4-R11保存不变
假设函数B就是异常/中断处理函数,函数B本身能保证R4-R11不变,那么保存现场时,只需要保存这些:
- 调用者保存的寄存器(R0-R3,R12,LR,PSR)
- PC
- R0-R3是用来传参数给函数B的
实际上普通的函数调用中(不涉及模式切换、运行在用户模式时),调用者一般只是保存PC到LR中,然后再由被调用函数进行寄存器保存
- R0-R3用作传参,所以被调用函数应该会使用,不用保存
- R4-R11被用作被调用者的局部变量,所以被调用者需要将这些寄存器保存到栈中
- R12(IP)、R13(SP)、R14(LR)、R15(PC)这些寄存器依情况而定
- 还有CPSR状态寄存器也是需要保存的
- 涉及模式切换一般都是异常发生了(用户模式到特权模式),异常发生是不可预测的,可能发生在任何时候
- 在发生异常模式是,会去到异常向量表中调用对应的异常模式处理函数,异常模式处理函数中会判断具体是什么异常,进而调用具体的异常处理函数
- 在异常模式处理函数中,需要进行保存现场(保存)
- 这个保存现场需要保存的是R0-R3,R12,LR,PSR这些调用者需要保存的寄存器,而在具体的异常处理函数中再保存R4-R11这些被调用者要保存的寄存器
- A7中, 除了sys模式和用户模式公用一套寄存器外,其他异常模式几乎都有自己的SP和LR寄存器
- 不管发生了那种异常,硬件再切换到对应模式时都会将PC值保存到该模式的LR寄存器中(如:MOV LR_irq, PC)
- 每种模式都有自己的SP栈指针,所以用户模式的SP指针并不需要保存
- 异常处理的总入口函数中还会调用其他具体的处理函数,所以其模式独有的LR寄存器是要在保存现场的操作中进行保存的,因为再次调用其他函数时b/bl指令会更新LR寄存器的值
cortex-m3/m4
硬件保存现场:
调用函数时一般LR=返回地址,这在一般的函数调用中当然没有问题;但是在中断中,如果只是设置LR为返回地址的话,CPU到时跳转到返回地址去执行,寄存器的值并没由恢复到原来值,这该怎么办呢?
C函数执行完后,它返回LR所指示的位置。难道把LR设置为被中断的程序的地址就行了吗?如果只是返回LR所指示的地方,硬件帮我们保存在栈里的寄存器,怎么恢复?M3/M4在调用异常处理函数前,把LR设置为一个特殊的值,转给特殊的值被称为EXC_RETURN。
M3/M4的CPU硬件设计了一个特殊值,当返回得知LR(实际是要赋给PC)是这个特殊值时,CPU就明白了要先恢复现场;真正的返回地址随着此前保存现场被保存到栈中了,所以恢复现场后就可以跳转到实际的返回地址上执行程序了
- 当PC寄存器的值等于EXC_RETURN时,会触发异常返回机制,简单地说:会从栈里恢复R0-R3,R12,LR,PC,PSR等寄存器。
- EXC_RETURN有6个取值,这些地址值并不对应任何内存、也不对应任何flash;正常函数返回地址不可能是这些值,当CPU发现值是这些时就知道了这是异常的返回
- EXC_RETURN有写比较有意思的位,写程序时比较关心的是bit2(bit2表示用的是哪个栈,也就说保存现场时用的是sp_main还是sp_process)
补充2个知识点:
- 操作模式:M3/M4有两个操作模式
- 处理模式:执行中断服务程序等异常处理时,处于处理模式
- 线程模式:执行普通应用程序代码时,处于线程模式
- 处理模式:执行中断服务程序等异常处理时,处于处理模式
- M3/M4有连个SP寄存器:SP_process、SP_main
- 操作模式:M3/M4有两个操作模式
- 有些RTOS在运行用户程序时会使用SP_process,默认使用SP_main。
cortex-a7
- 对于A7寄存器,有多种操作模式,这些模式下都有其自身的SP寄存器;用户模式和系统模式共用一套寄存器
- 几乎每个模式下都有自己是SP寄存器,意味着这些模式下有自己的栈。
- 这意味着在这些模式下,我们都必须设置它们各自的栈sp
- 保存现场保存到的是对应异常模式下的栈(如: SP_irq)
当发生异常时,以IRQ为例:
- CPU会自动切换进入对应的模式,比如进入IRQ模式
- 并且会把被中断是的CPSR保存到SPSR_irq里
所以发生异常/中断时,在保存现场时,只需要保存:
- 调用者保存的寄存器(R0-R3,R12,LR)
- PC