在项目开发中,有些设备在接收到相关指令或者遇到某种中断触发后需要重启。如果从程序入口处进行启动,会有一段比较长的时间进行初始化工作,相当浪费时间。重启的位置可以从初始化后的特定位置重新运行,这就需要把当前的程序计数器PC及堆栈SP指针进行备份。
下面的程序不涉及到进程堆栈,备份的断点位置是在线程模式下。首先需要声明两个变量:
// Global Variables to track test progress
unsigned int g_labelBP; //BP
unsigned int g_MSP; //MSP backup
对断点位置进行备份用C语言无法实现,需要用汇编语言实现。
//backup PC value
__asm void BackupPC(void)
{
IMPORT g_labelBP
IMPORT g_MSP
;把断点位置下一条指令的PC指针存入变量g_labelBP
LDR r0, =g_labelBP
PUSH {lr}
LDR r1, [sp, #0x0]
STR r1, [r0]
;把断点位置的主堆栈指针MSP存入变量g_MSP
LDR r0, =g_MSP
mrs r1, msp
adds r1, #0x04
STR r1, [r0]
;返回断点位置的下一条指令继续执行
pop {pc}
}
如果遇到某种中断触发后进行重启,回到备份的端点位置,下面首先讲两个知识点。
1、中断发生后入栈后堆栈内的内容
2、EXC_RETURN寄存器
EXC_RETURN为架构定义的特殊值,用于异常返回机制,这个值在异常被接受并且压栈完成后会自动存储到链接寄存器中(LR或 R14)。 EXC_RETURN为32位数值,并且高28位置1 ,第0位到第3位则提供了异常返回机制所需的信息(见下表)。
Cortex-M0 中EXC_RETURN的 bit0保留,且必须为 1。
EXC_RETURN的 bit2 表示出栈恢复寄存器时使用的是主栈(MSP)还是进程栈 (PSP)。
EXC_RETURN的 bit3 表示处理器要返回线程模式还是处理模式。
下表列出了Cortex-M0处理器使用的EXC_RETURN的合法值。
由于 EXC_RETURN 的值在异常入口处被自动加载到 LR 中,异常处理会把它当成普通的返回地址。如果返回地址无须保存在栈上,异常处理也可以像普通函数一样,通过执行 “BX LR” 来触发异常返回并且返回到中断前的程序。另外,如果异常处理需要执行函数调用,就需要将 LR 压栈.在异常处理的最后,已经压栈的 EXC_RETURN 值将会通过 POP 指令直接加载到 PC ,这样就能触发异常返回流程并且返同到中断前的程序。
汇编程序如下:
//jump from interrupt service program to the back point of the main program
__asm void rtnBackup(void)
{
IMPORT g_labelBP
IMPORT g_MSP
;发生中断后MSP=g_MSP-0x20
LDR r0, =g_MSP
LDR r1, [r0] ;r1=backup MSP addr
SUBS r1,r1,#0x20
msr MSP,r1
;r1=backup pc addr
LDR r0, =g_labelBP
LDR r1, [r0] ;r1=backup pc addr
;更改中断发生后堆栈中PC的值,以便中断能正常返回到断点位置
mrs r2,MSP
LDR r3,=0x18
add r3,r2 ;msp+18, pc
STR r1,[r3]
;更改中断发生后堆栈中xPSR的值(线程模式),以便中断能正常返回到断点位置
LDR r3,=0x1c
add r3,r2 ;msp+1c, xPSR
LDR r1, =0x01000000
STR r1,[r3] ;xPSR=0x01000000, Thumb state bit = 1
;设置r2为特定值,使其从中断中返回到中断前的位置
ldr r2,=0xfffffff9 ;return to thread mode
bx r2
}