从Cortex M0 +上的硬故障中恢复

问题描述 投票:0回答:1

到目前为止,我在C中有一个我在向量表中定义的硬故障处理程序:

.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的位[2],因为我肯定知道我使用MSP而不是PSP。

程序编译并正常运行,我使用JTAG确保所有寄存器恢复到所需的值。当执行最后一个命令(MOV PC, R5)时,PC返回到正确的地址,但在某些时候,调试器指示M0被锁定在硬故障中并且无法恢复。

我不明白使用C函数作为处理程序或调用C函数的汇编函数之间的区别。

有谁知道这是什么问题?

最终,我将使用一个会阻塞处理器的断言函数,但我希望它是可选的,取决于我的决定。

c assembly exception-handling cortex-m faulthandler
1个回答
0
投票

解释“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 M3手册,您必须通过将三个EXC_RETURN值中的一个写入PC寄存器来退出异常处理程序。

如果您只是跳转到存储在堆栈帧中的LR值,您将保留在异常处理程序中!

如果在程序期间发生了一些愚蠢的事情,CPU将假定异常处理程序内发生异常并且它会挂起。

我假设Cortex M0在这一点上与M3的工作方式相同。

如果要在异常处理程序期间修改某些CPU寄存器,可以修改堆栈帧。当您将EXC_RETURN值写入pop寄存器时,CPU将自动PC堆栈帧中的所有寄存器。

如果要修改堆栈框架中不存在的寄存器之一(例如R5),可以在异常处理程序中直接修改它。

这显示了中断处理程序的另一个问题:

指令POP {R0-R7}将寄存器R4设置为R7,使其值与已被中断的程序不匹配。根据C代码,R12也将被销毁。这意味着在程序被中断时,这四个寄存器突然改变,而程序没有为此做好准备!

© www.soinside.com 2019 - 2024. All rights reserved.