如何在 IAR Embedded Workbench 中调试 LPC1788 的 "undefined instruction" 故障?

How can I debug "undefined instruction" faults for an LPC1788 in IAR Embedded Workbench?

我正在为 LPC1788 (Cortex-M3) 微控制器开发应用程序。这个应用涉及到发送和接收CAN报文,我发现当我给它施加重负载并用拇指转动30-60分钟时,会出现hardfault。

我正在通过 IAR Embedded Workbench 6.60.1.5104 调试固件,当发生此硬故障时,它会在 startup_LPC177x_8x.s 中定义的弱链接默认硬故障处理程序处中断:

        PUBWEAK HardFault_Handler
        SECTION .text:CODE:REORDER(1)
HardFault_Handler
        B HardFault_Handler

不幸的是,调用堆栈只包含此处理程序的地址,而不包含调用它的代码的任何部分(发生错误的地方)。

我能够收集到的唯一有用信息来自 NVIC:CFSR 寄存器中设置的 UNDEFINSTR 位。来自文档:

When this bit is set, the PC value stacked for the exception return points to the undefined instruction. An undefined instruction is an instruction that the processor cannot decode. Potential reasons:

a) Use of instructions not supported in the Cortex-M device.
b) Bad or corrupted memory contents.

我读到在执行指令时程序计数器的值存储在异常堆栈寄存器之一中,但我不确定如何从 IAR 访问这些寄存器。

如果有任何帮助,我附上了包含一些调试细节的屏幕截图(right click -> view image 对于较大的版本):

我通过修改找到的代码找到了解决方案 from this blog.

那里的用户 "Ramon" 很有帮助地发布了代码,使我走上了正确的轨道,获得了在 IAR 中编译的东西(我以前从未尝试过在 IAR Embedded Workbench 中编写原始程序集)。

这是我使用的硬故障处理代码:

#include "lpc177x_8x.h"   

static volatile unsigned long stacked_r0 = 0;
static volatile unsigned long stacked_r1 = 0;
static volatile unsigned long stacked_r2 = 0;
static volatile unsigned long stacked_r3 = 0;
static volatile unsigned long stacked_r12 = 0;
static volatile unsigned long stacked_lr = 0;
static volatile unsigned long stacked_pc = 0;
static volatile unsigned long stacked_psr = 0;
static volatile unsigned long _cfsr = 0;
static volatile unsigned long _hfsr = 0;
static volatile unsigned long _dfsr = 0;
static volatile unsigned long _afsr = 0;
static volatile unsigned long _bfar = 0;
static volatile unsigned long _mmar = 0;

void hardfault_handler( void )
{
  __asm("tst lr, #4");
  __asm("ite eq \n"
        "mrseq r0, msp \n"
        "mrsne r0, psp");
  __asm("b hard_fault_handler_c");
}

void hard_fault_handler_c(unsigned long *hardfault_args){
  stacked_r0 = ((unsigned long)hardfault_args[0]);
  stacked_r1 = ((unsigned long)hardfault_args[1]);
  stacked_r2 = ((unsigned long)hardfault_args[2]);
  stacked_r3 = ((unsigned long)hardfault_args[3]);
  stacked_r12 = ((unsigned long)hardfault_args[4]);
  stacked_lr = ((unsigned long)hardfault_args[5]);
  stacked_pc = ((unsigned long)hardfault_args[6]);
  stacked_psr = ((unsigned long)hardfault_args[7]);

  // configurable fault status register
  // consists of mmsr, bfsr and ufsr
  _cfsr = (*((volatile unsigned long *)(0xe000ed28)));

  // hard fault status register
  _hfsr = (*((volatile unsigned long *)(0xe000ed2c)));

  // debug fault status register
  _dfsr = (*((volatile unsigned long *)(0xe000ed30)));

  // auxiliary fault status register
  _afsr = (*((volatile unsigned long *)(0xe000ed3c)));

  // read the fault address registers. these may not contain valid values.
  // check bfarvalid/mmarvalid to see if they are valid values
  // memmanage fault address register
  _mmar = (*((volatile unsigned long *)(0xe000ed34)));
  // bus fault address register
  _bfar = (*((volatile unsigned long *)(0xe000ed38)));

  __asm("bkpt #0\n"); // break into the debugger
}

为了对此进行测试,我创建了以下函数以确保 "division by zero" 使用错误会被捕获,但使用错误会自动升级为硬故障:

void configureFaultHandling()
{
  // Catch all possible faults.

//  SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk
//    | SCB_SHCSR_BUSFAULTENA_Msk
//    | SCB_SHCSR_USGFAULTENA_Msk;

  SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk
    | SCB_CCR_UNALIGN_TRP_Msk;
}

我在 main 的开头添加了以下内容:

void main()
{
  configureFaultHandling();

  int a = 5 / 0;
}

运行 这个,程序很快停在 void hardfault_handler( void ),我可以通过这个进入 hard_fault_handler_c 并检查寄存器的值。

我发现这些值对应于 IAR 的 View -> Stack -> Stack 1 窗格中显示的值。事后看来这是有道理的,因为文档指出某些寄存器的值在发生故障时被压入堆栈。然而,编写这个函数帮助我弄清楚堆栈中的哪些值对应于哪些寄存器。

为了参考自己和其他可能有类似问题的人,我发现"stack 1"中的第7个值(即索引6)对应于发生异常时的程序计数器值。这就是我的样子(right click -> view image 放大):

这样做可以让您找到硬故障的来源,而无需覆盖默认的硬故障处理程序,只要调试器在发生硬故障时自动中断即可。

希望这也有助于确定 "undefined instruction" 故障。