[英]Recover from Hard Fault on Cortex M0+
到目前为止,我在向量表中定义了C语言的Hard Fault处理程序:
.sect ".intvecs"
.word _top_of_main_stack
.word _c_int00
.word NMI
.word Hard_Fault
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
.word Reserved
....
....
....
我们的一项测试通过写入不存在的地址触发了有意的硬故障。 测试完成后,处理程序将返回到调用函数,并且皮质将从故障中恢复。 值得一提的是处理程序没有任何参数。
现在,我正在编写一个真正的处理程序。 我为堆栈框架创建了一个结构,以便在出现故障的情况下可以打印PC,LR和xPSR:
typedef struct
{
int R0 ;
int R1 ;
int R2 ;
int R3 ;
int R12 ;
int LR ;
int ReturnAddress ;
int xPSR ;
} InterruptStackFrame_t ;
我在C中的硬故障处理程序定义为:
void Hard_Fault(InterruptStackFrame_t* p_stack_frame)
{
// Write to external memory that I can read from outside
/* prints a message containing information about stack frame:
* p_stack_frame->LR, p_stack_frame->PC, p_stack_frame->xPSR,
* (uint32_t)p_stack_frame (SP)
*/
}
我创建了一个汇编函数:
.thumbfunc _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
MRS R0, MSP ; store pointer to stack frame
BL Hard_Fault ; go to C function handler
POP {R0-R7} ; pop out all stack frame
MOV PC, R5 ; jump to LR that was in the stack frame (the calling function before the fault)
.endasmfunc
现在是说我没有操作系统的正确时机,因此我不必检查LR的bit [2],因为我确实知道我使用MSP而不是PSP。
该程序可以编译并正常运行,我使用JTAG来确保所有寄存器都恢复为所需值。 当执行最后一条命令( MOV PC, R5
)时,PC返回正确的地址,但是在某些时候,调试器指示M0锁定在硬故障中并且无法恢复。
我不理解使用C函数作为处理程序或调用C函数的汇编函数之间的区别。
有谁知道这是什么问题?
最终,我将使用将卡住处理器的assert函数,但是我希望它是可选的,并由我决定。
解释“ old_timer”的评论:
在Cortex上输入异常或中断处理程序时,LR寄存器具有特殊值。
通常,您可以通过简单地跳转到该值(通过将该值写入PC寄存器)来从异常处理程序中返回。
然后,Cortex CPU将自动从堆栈中弹出所有寄存器,并将重置中断逻辑。
但是,当直接跳转到存储在堆栈中的PC时,您将破坏一些寄存器,并且不恢复中断逻辑。
因此,这不是一个好主意。
相反,我会做这样的事情:
.thumbfunc _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
MRS R0, MSP
B Hard_Fault
编辑
使用B
指令可能不起作用,因为B
指令所允许的“距离”比BL
指令受到更多的限制。
但是,您可以使用两种可能的方法(不幸的是,我不确定这些方法是否一定可以使用)。
进入汇编器处理程序时,第一个将返回到LR
寄存器中已传递的地址:
.thumbfunc _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
MRS R0, MSP
PUSH {LR}
BL Hard_Fault
POP {PC}
第二个将间接进行跳转:
.thumbfunc _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
MRS R0, MSP
LDR R1, =Hard_Fault
MOV PC, R1
编辑2
您不能使用LR,因为它具有EXC_RETURN值。 ...您必须从堆栈中读取LR,并且必须从堆栈帧中清除堆栈,因为被中断的程序不知道已存储帧。
根据Cortex M 3手册,您必须通过将三个EXC_RETURN值之一写入PC寄存器来退出异常处理程序。
如果您只是跳转到存储在堆栈帧中的LR
值,则将保留在异常处理程序中!
如果在程序执行过程中发生了一些愚蠢的事情,CPU将假定异常处理程序内部发生了异常并挂起。
在这一点上,我假设Cortex M 0与M 3的工作方式相同。
如果要在异常处理程序期间修改某些CPU寄存器,则可以修改堆栈框架。 当您将EXC_RETURN值写入PC
寄存器时,CPU将自动从堆栈帧pop
所有寄存器。
如果要修改堆栈帧中不存在的寄存器之一(例如R5
),则可以在异常处理程序中直接对其进行修改。
这显示了您的中断处理程序的另一个问题:
POP {R0-R7}
指令会将寄存器R4
至R7
设置为与已中断程序不匹配的值。 R12
也将根据C代码被销毁。 这意味着在被中断的程序中,这四个寄存器突然更改,而程序没有为此做好准备!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.