ARM中控制程序执行的三种方式:
初始化
设置中断源
设置中断控制器(中断屏蔽寄存器组、优先级)
设置CPU总开关,使能中断
执行正常程序
产生中断->中断信号发给中断控制器->CPU
CPU每执行完一条指令,都会检查有无中断/异常产生(硬件)
发现有异常/中断产生->开始处理:
参考资料:DDI0403E_B_armv7m_arm.pdf
、ARM Cortex-M3与Cortex-M4权威指南.pdf
、PM0056.pdf
参考资料:ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf
ldr pc, _irq
指令,执行后就跳转到_irq函数),在跳转到的函数中:
_start:
b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
上面启动代码来自:u-boot-arch-arm-lib-vectors.s
综上,两者区别:
举例分析:
上述4条汇编指令,它们被异常打断时,需要保证在异常处理完后,被打断的程序还能正确运行,需要保存的数据:
执行完第1条指令时程序被打断,恢复运行时,R0要保持不变
执行完第2条指令时程序被打断,恢复运行时,R0、R1要保持不变
执行完第3条指令时程序被打断,比较结果保存在程序状态寄存器里,恢复运行时,程序状态寄存器保持不变
上述指令要读取a、b内存,当被打断时无需保存,因为只要程序不越界,内存就保持不变。因此关键在于R0、R1、程序状态寄存器的保存:
根据ATPCS:C函数可以修改R0~R3、R12、R14(LR)及PSR寄存器(M3/M4中称为XPSR
,A7中称为CPSR
)。若C函数需要使用R4~R11,就应该把它们保存到栈里,在函数结束前在从栈中恢复它们。
因此,寄存器被拆分成2部分:
例如,函数A(调用者)调用函数B(被调用者)时:
对于函数A:
对于函数B:
函数B就可以视为异常/中断处理函数,在函数A被打断,执行异常/中断处理前,需要保存:
在异常入口处被压入栈空间的数据块为栈帧。对于Cortex-M3或不具有浮点单元的Cortex-M4处理器,栈帧都是8
个字(32字节)大小的,对于具有浮点单元的Cortex-M4则可能是8或26个字。
ATPCS要求栈指针的数值在函数入口和出口处是双字对齐的,即SP的值必须能被8整除,若在中断产生时栈帧未对齐到双字上,M3/4会自动插入一个字(栈本身即是字<4字节>对齐的),双字对齐特性可通过SCB->CCR
的STKALIGN位设置,若异常不符合ATPCS,则可将该特性关闭。(F1默认不开启,但启动文件使用PRESERVE8
做了8字节对齐)也可以通过伪指令REQUIRE8
和PRESERVE8
配置堆栈8字节对齐。
进入异常前,依次把xPSR, PC, LR, R12以及R3-R0由硬件自动压入适当的堆栈中(响应异常时,若当前代码正在使用PSP,则压入PSP,否则就压入MSP),其中,PC(SP-0x24
处)即为图中返回地址:
压栈时xPSR的bit9表示栈指针SP数值是否被调整过,上图为双字对齐,因此不会额外插入一个字,且xpsr bit9 = 0
。若使能了双字对齐特性,且SP数值未对齐到双字边界上,栈中会被插入一段空间,SP被强制对齐到双字地址,且xpsr bit9 = 1
,表明插入了一段区域:
在自动入栈的过程中,把寄存器写入堆栈内存的时间顺序,并不是与写入的空间顺序相对应的。但是机器会保证:正确的寄存器将被保存到正确的位置:
IPSR位段
的值。入栈操作由数据(系统)总线完成,而指令总线同时会从向量表中找出正确的异常向量,然后在服务程序的入口处预取指,即入栈和取指同时进行。
在入栈和取向量操作完成之后,执行服务例程之前,还要更新一系列的寄存器:
EXC_RETURN
此外,NVIC中也会更新若干个相关有寄存器。
M3/M4在调用异常处理函数前,把LR设置为一个特殊的值——EXC_RETURN
,当异常处理结束后,硬件将PC=EXC_RETURN
,触发异常返回机制:
EXC_RETURN
合法值如下:
如果主程序在线程模式下运行,并且在使用MSP时被中断,则在服务例程中LR=0xFFFF_FFF9;若使用PSP时被中断,则在服务例程中LR=0xFFFF_FFFD(主程序被打断前的LR已被自动保存至异常栈帧):
如果主程序在Handler模式(MSP)下运行,则在服务例程中LR=0xFFFF_FFF1,在嵌套时,更深层ISR所看到的LR总是0xFFFF_FFF1:
执行异常返回:
EXC_RETURN bit2
判断异常返回的栈帧使用的是MSP还是PSPEND