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

时间:2017-12-12 12:43:08

标签: c assembly exception-handling cortex-m faulthandler

到目前为止,我在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函数的汇编函数之间的区别。

有谁知道这是什么问题?

最终,我将使用一个会阻塞处理器的断言功能,但我希望它是可选的,直到我的决定。

1 个答案:

答案 0 :(得分: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 M 3 手册,您必须通过将三个EXC_RETURN值中的一个写入PC寄存器来退出异常处理程序。

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

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

我认为此时Cortex M 0 的工作方式与M 3 的工作方式相同。

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

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

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

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