【问题标题】:Recover from Hard Fault on Cortex M0+从 Cortex M0+ 上的硬故障中恢复
【发布时间】:2017-12-12 12:43:08
【问题描述】:

到目前为止,我在向量表中定义了一个 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 的 bit[2],因为我肯定知道我使用的是 MSP 而不是 PSP。

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

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

有谁知道问题出在哪里?

最终,我将使用一个断言函数来卡住处理器,但我希望它是可选的并且由我决定。

【问题讨论】:

  • 您能否验证C函数void Hard_Fault(InterruptStackFrame_t* p_stack_frame)的地址是偶数地址?查看地图文件。当处理器进入锁定状态并冻结时,还要检查S_LOCKUP 位的值。
  • 为什么不从异常中正常返回并让逻辑恢复寄存器(看起来您正在丢弃寄存器,但不确定您使用的是什么 C 编译器或约定)。
  • @Gaurav 我验证了 Hard_Fault 的地址是偶数(以 d0 结尾)。 S_LOCKUP 的值为 0:00000000000000110000000000000011 只有 S_HALT、S_REGRDY_ST、C_HALT 和 C_DEBUGEN 处于开启状态(读取调试故障状态寄存器 - 0xE000EDF0)​​

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


【解决方案1】:

解释“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 值写入PC 寄存器时,CPU 会自动将堆栈帧中的所有寄存器 pop

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

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

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

【讨论】:

  • 我希望。这是我尝试的第一件事,但我收到了这个错误:错误!在第 49 行:[E0001] 地址必须在当前部分中定义
  • 我上面描述的那个。它不允许我使用分支,因为这两个函数不在同一部分,尽管它们都在“.text”部分下
  • @DrorNohi 那么问题是汇编程序不接受B 指令?如果这是您的问题,我编辑了我的答案。
  • 已经尝试过(没有“=”符号)。它给了我同样的错误:错误!在第 49 行:[E0001] 地址必须在当前部分中定义。我的编译器不熟悉使用 LDR 的“=”符号。
  • @DrorNohi 那么push {lr}, pop {pc} 方法呢?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-18
  • 1970-01-01
  • 2013-09-09
  • 2017-06-20
  • 2016-12-18
  • 1970-01-01
  • 2020-11-12
相关资源
最近更新 更多