简体   繁体   English

从Cortex M0 +的硬故障中恢复

[英]Recover from Hard Fault on Cortex M0+

Until now I had a Hard fault handler in C that I defined in the vector table: 到目前为止,我在向量表中定义了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
....
....
....

One of our tests triggers a hard fault (on purpose) by writing to a non existing address. 我们的一项测试通过写入不存在的地址触发了有意的硬故障。 Once the test is done, the handler returns to the calling function and the cortex recovers from the fault. 测试完成后,处理程序将返回到调用函数,并且皮质将从故障中恢复。 Worth mentioning that the handler does not have any arguments. 值得一提的是处理程序没有任何参数。

Now I'm in the phase of writing a real handler. 现在,我正在编写一个真正的处理程序。 I created a struct for the stack frame so we can print PC, LR, and xPSR in case of a fault: 我为堆栈框架创建了一个结构,以便在出现故障的情况下可以打印PC,LR和xPSR:

typedef struct
{
    int     R0              ;  
    int     R1              ;  
    int     R2              ;  
    int     R3              ;  
    int     R12             ;
    int     LR              ; 
    int     ReturnAddress   ; 
    int     xPSR            ;

}   InterruptStackFrame_t  ;

My hard fault handler in C is defined: 我在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)
     */
}

I created an assembly function: 我创建了一个汇编函数:

        .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

This is the right time to say that I don't have an OS, so I do not have to check bit[2] of LR because I definitely know that I use MSP and not PSP. 现在是说我没有操作系统的正确时机,因此我不必检查LR的bit [2],因为我确实知道我使用MSP而不是PSP。

The program compiles and runs properly and I used JTAG to ensure that all registers restore to the wanted values. 该程序可以编译并正常运行,我使用JTAG来确保所有寄存器都恢复为所需值。 When executing the last command ( MOV PC, R5 ) the PC returns to the correct address, but at some point, the debugger indicates that the M0 is locked in a hard fault and cannot recover. 当执行最后一条命令( MOV PC, R5 )时,PC返回正确的地址,但是在某些时候,调试器指示M0锁定在硬故障中并且无法恢复。

I do not understand the difference between using a C function as a handler or an assembly function that calls a C function. 我不理解使用C函数作为处理程序或调用C函数的汇编函数之间的区别。

Does anyone know what is the problem? 有谁知道这是什么问题?

Eventually, I will use an assert function that will stuck the processor, but I want it to be optional and up to my decision. 最终,我将使用将卡住处理器的assert函数,但是我希望它是可选的,并由我决定。

To explain "old_timer"'s comment: 解释“ old_timer”的评论:

When entering an exception or interrupt handler on the Cortex the LR register has a special value. 在Cortex上输入异常或中断处理程序时,LR寄存器具有特殊值。

Normally you return from the exception handler by simply jumping to that value (by writing that value to the PC register). 通常,您可以通过简单地跳转到该值(通过将该值写入PC寄存器)来从异常处理程序中返回。

The Cortex CPU will then automatically pop all the registers from the stack and it will reset the interrupt logic. 然后,Cortex CPU将自动从堆栈中弹出所有寄存器,并将重置中断逻辑。

When directly jumping to the PC stored on the stack however you will destroy some registers and you don't restore the interrupt logic. 但是,当直接跳转到存储在堆栈中的PC时,您将破坏一些寄存器,并且不恢复中断逻辑。

Therefore this is not a good idea. 因此,这不是一个好主意。

Instead I'd do something like this: 相反,我会做这样的事情:

    .thumbfunc  _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
    MRS    R0, MSP
    B      Hard_Fault

EDIT 编辑

Using the B instruction may not work because the "distance" allowed for the B instruction is more limited than for the BL instruction. 使用B指令可能不起作用,因为B指令所允许的“距离”比BL指令受到更多的限制。

However there are two possibilities you could use (unfortunately I'm not sure if these will definitely work). 但是,您可以使用两种可能的方法(不幸的是,我不确定这些方法是否一定可以使用)。

The first one will return to the address that had been passed in the LR register when entering the assembler handler: 进入汇编器处理程序时,第一个将返回到LR寄存器中已传递的地址:

    .thumbfunc  _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
    MRS    R0, MSP
    PUSH   {LR}
    BL     Hard_Fault
    POP    {PC}

The second one will indirectly do the jump: 第二个将间接进行跳转:

    .thumbfunc  _hard_fault_wrapper
_hard_fault_wrapper: .asmfunc
    MRS    R0, MSP
    LDR    R1, =Hard_Fault
    MOV    PC, R1

EDIT 2 编辑2

You cannot use LR because it holds EXC_RETURN value. 您不能使用LR,因为它具有EXC_RETURN值。 ... You have to read the LR from stack and you must clean the stack from the stack frame, because the interrupted program doesn't know that a frame was stored. ...您必须从堆栈中读取LR,并且必须从堆栈帧中清除堆栈,因为被中断的程序不知道已存储帧。

According to the Cortex M 3 manual you must exit from an exception handler by writing one of the three EXC_RETURN values to the PC register. 根据Cortex M 3手册,您必须通过将三个EXC_RETURN值之一写入PC寄存器来退出异常处理程序。

If you simply jump to the LR value stored in the stack frame you remain in the exception handler! 如果您只是跳转到存储在堆栈帧中的LR值,则将保留在异常处理程序中!

If something stupid happens during the program the CPU will assume that an exception happened inside the exception handler and it hangs. 如果在程序执行过程中发生了一些愚蠢的事情,CPU将假定异常处理程序内部发生了异常并挂起。

I assume that the Cortex M 0 works the same way as the M 3 in this point. 在这一点上,我假设Cortex M 0与M 3的工作方式相同。

If you want to modify some CPU register during the exception handler you can modify the stack frame. 如果要在异常处理程序期间修改某些CPU寄存器,则可以修改堆栈框架。 Thc CPU will automatically pop all registers from the stack frame when you are writing the EXC_RETURN value to the PC register. 当您将EXC_RETURN值写入PC寄存器时,CPU将自动从堆栈帧pop所有寄存器。

If you want to modify one of the registers not present in the stack frame (such as R5 ) you can directly modify it in the exception handler. 如果要修改堆栈帧中不存在的寄存器之一(例如R5 ),则可以在异常处理程序中直接对其进行修改。

And this shows another problem of your interrupt handler: 这显示了您的中断处理程序的另一个问题:

The instruction POP {R0-R7} will set registers R4 to R7 to values that do not match the program that has been interrupted. POP {R0-R7}指令会将寄存器R4R7设置为与已中断程序匹配的值。 R12 will also be destroyed depending on the C code. R12也将根据C代码被销毁。 This means that in the program being interrupted these four registers suddenly change while the program is not prepared for that! 这意味着在被中断的程序中,这四个寄存器突然更改,而程序没有为此做好准备!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM