Cortex-M系列hardfault产生后防止宕机的方法

1 内核错误

Cortex-M系列内核有多个专用的故障状态寄存器,可以在发生故障时进行分析,以追踪问题所在。

1.1 错误状态寄存器

1.1.1 Configurable Fault Status Registers (CFSR) - 0xE000ED28

该寄存器包含了导致异常的故障的摘要。该寄存器由三个不同的状态寄存器组成 -- UsageFault、BusFault 和 MemManage:



注意:如果发生多个故障,则可能会设置与多个故障相关的位。 这些字段只能通过系统重置或写入 1 来清除。

1.1.2 BusFault Status Register (BFSR) - 0xE000ED29

该寄存器报告了与指令预取或内存访问失败相关的故障



  • BFARVALID - 表示BFAR(总线故障地址寄存器)的值是否有效

  • LSPERR - 表示FP lazy状态保存期间发生了BusFault故障

  • STKERR - 表示在中断压栈(进入)时发生了BusFault故障

  • UNSTKERR - 表示在中断出栈(退出)时发生了BusFault故障

  • IMPRECISERR - 表示发生了一个非精确(不能得到确切的位置)的数据访问错误。

  • PRECISERR - 表示发生了一个精确(可以得到确切的位置)的数据访问错误。

1.1.3 MemManage Status Register (MMFSR) - 0xE000ED28

该寄存器报告内存保护单元故障。

通常,仅有MPU启用时才会触发MPU故障。但是有时一些内存访问错误也会导致MemManage故障,例如尝试从系统地址范围(0xEXXXXXXX)执行代码



  • MMARVALID - 表示MMFAR(故障地址寄存器)的值是否有效

  • MLSPERR - 表示FP lazy状态保存期间发生了MemManage故障

  • MSTKERR - 表示在中断压栈(进入)时发生了MemManage故障

  • MUNSTKERR - 表示在中断出栈(退出)时发生了MemManage故障

  • DACCVIOL - 表示数据访问触发了MemManage故障

  • IACCVIOL - 表示尝试执行指令时触发了MPU或者从不执行(XN)故障

1.1.4 UsageFault Status Register (UFSR) - 0xE000ED2A

该寄存器报告了与内存访问失败无关的故障,例如执行无效指令或尝试进入无效状态。



  • DIVBYZERO - 执行了除法指令,其中除数为0,该故障可配置。

  • UNALIGNED - 发生了未对齐的访问操作。例如访问了为4字节对齐的uint32_t。低于4字节的未对齐访问是否生成故障是可配置的。

  • NOCP - 发出了协处理器指令,但协处理器不可用。

  • INVPC - EXC_RETURN 发生完整性检查错误

  • INVSTATE - 处理器执行具有无效Execution Program Status Register (EPSR) 值的指令,比如加载pc指令时,地址bit0的值不为1。

  • UNDEFINSTR - 执行了未定义的指令

有些UsageFault可以通过 Configuration and Control Register (CCR)-0xE000ED14 来配置

  • Bit 4 (DIV_0_TRP) - 控制除以零是否会触发故障。

  • Bit 3 (UNALIGN_TRP) - 控制未对齐访问是否始终会生成错误。

1.1.5 HardFault Status Register (HFSR) - 0xE000ED2C

该寄存器报告了触发HardFault异常的原因



  • DEBUGEVT - 表示在调试子系统未启用时发生了调试事件(即执行断点指令)

  • FORCED - 表示UsageFault, BusFault,MemManage升级为HardFault(如果上面的中断没有使能)

  • VECTTBL - 指示由于从向量表中的地址读取问题而发生故障。 这是非常不典型的,但如果向量表中存在错误地址并且发生意外中断,则可能会发生这种情况。

1.2 异常处理实例

1.2.1 恢复Call Stack

为了修复错误,我们需要确定错误发生时正在运行哪些代码。 为了实现这一点,我们需要得到异常进入时的寄存器状态。

当异常进入时,一些寄存器将始终自动保存在堆栈中。 根据是否使用 FPU,硬件将推送基本堆栈帧或扩展堆栈帧。

下面的例子我们仅观察基本堆栈帧,我们可以定义一个结构体:

typedef struct
{
  uint32_t r0;
  uint32_t r1;
  uint32_t r2;
  uint32_t r3;
  uint32_t r12;
  uint32_t lr;
  uint32_t pc;
  uint32_t psr;
}Exception_Type;

然后我们需要在startup.S文件中添加汇编的中断处理函数,并将该函数从defaultISR中去除

    .align  2
    .thumb_func
    .weak HardFault_Handler
    .type HardFault_Handler, %function
HardFault_Handler:
    tst LR, #4
    ite eq
    mrseq r0, msp
    mrsne r0, psp
    B Hardfault_Process

其中Hardfault_Process为c函数,内容如下:

void Hardfault_Process(Exception_Type *pFrame)
{
    SCB->CFSR |= (0x1UL << 9);
    pFrame->pc += 2; //PC指针跳过出错地址,往后执行
    pFrame->psr = 1 << 24; //重置psr状态,仅保留thumb instruction interworking位
}

这样,虽然产生了hardfault,但是我们的程序不会宕机啦,另外可以在这里对hardfault产生的栈信息进行记录,便于后期排查。

参考资料:

Cortex-M核心寄存器_m0 psr寄存器-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值