0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RP2040のHard Faultハンドラを実装する

Posted at

1. はじめに

pico SDKには例愛発生時のハンドラを登録する仕組みが用意されています。
この機能を使用し、Hard Fault発生時にPCなどのレジスタし、発生k佐保を特定できる情報を表示するハンドラを作成しました。

2. Hard Faultとは

通常処理または例外処理中にエラーが発生したことによって、発生する例外です。Cortex-M0/M0+においては、以下のとうな要因で発生します。

  • アラインメントエラー
  • BKPT命令の実行(デバッガ未使用時)
  • 未定義命令の実行

3. 作成するハンドラの処理内容

ハンドラの処理内容は以下のようになっています。

  1. 例外発生時の情報を取得する
  2. ユーザが指定した関数に制御を移す

Hard Fault発生時の情報表示はユーザが作成するようにしました。これはArduino環境でもpico-SDK環境でも動作できるように考慮したためです。
例外から復帰させているのは、USBによるシリアル出力を可能とするためです。

4. 実装したコードとテストプログラム

実装したコード:
ヘッダファイル

HardFaultHanler.h
#pragma once

#ifdef __cplusplus
extern "C" {
#endif
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;
  uint32_t sp;
} fault_regs_t;

extern void registerHardfaultHandler(void (*func)(fault_regs_t *regs));

#ifdef __cplusplus
}
#endif

ハンドラ部

HardFaultHanler.c
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <hardware/exception.h>
#if PICO_RP2040
#include <RP2040.h>
#else
#include <RP2350.h>
#endif
#include "HardFaultHandler.h"

static fault_regs_t regs;
static void (*handler)(fault_regs_t *regs) = NULL;

static void
exceptionHandlerEntry(void)
{
  uint32_t *sp;
  uint32_t lr = (uint32_t)__builtin_return_address(0);
  if ((lr & 4) == 0) {
    sp = (uint32_t *)(__get_MSP() + 4);
  } else {
    sp = (uint32_t *)__get_PSP();
  }
  regs.r0  = sp[0];
  regs.r1  = sp[1];
  regs.r2  = sp[2];
  regs.r3  = sp[3];
  regs.r12 = sp[4];
  regs.lr  = sp[5];
  regs.pc  = sp[6];
  regs.psr = sp[7];
  regs.sp  = (uint32_t)sp;

  if ((regs.psr &0x1f) != 0) {
    // 例外処理中にfaultが発生」した場合は、
    // 表示ができない可能性があるため、ここで停止させる。
    for (;;) {
      ;
    }
  }
  // 例外からの戻りアドレスを変更
  sp[6] = ((uint32_t)handler) & 0xfffffffeU;
  // レジスタの値を格納した構造体のアドレスを第一引数として渡す
  sp[0] = (uint32_t)&regs;
}

void
registerHardfaultHandler(void (*func)(fault_regs_t *regs))
{
  exception_set_exclusive_handler(HARDFAULT_EXCEPTION, exceptionHandlerEntry);
  handler = func;
}

テスト用のプログラム

HardFaultHanler.ino

#include <Arduino.h>
#include <stdlib.h>
#include <hardware/exception.h>
#include "HardFaultHandler.h"

void
showFaultStatus(fault_regs_t *regs)
{
  Serial.printf("******** Hard fault ********\n");
  Serial.printf("r0: %08x r1:%08x r2:%08x r3: %08x\n", regs->r0, regs->r1, regs->r2, regs->r3);
  Serial.printf("r12:%08x lr:%08x pc:%08x psr:%08x\n", regs->r12, regs->lr, regs->pc, regs->psr);
  Serial.printf("******** Halted ********\n");
  for (;;) {
    delay(1000);
  }
}

void
setup()
{
  while (!Serial)
  {
    ;
  }
  delay(1000);
  Serial.printf("build [%s %s]\n", __DATE__, __TIME__);
  registerHardfaultHandler(showFaultStatus);
}

void
loop()
{
  __breakpoint();
}

5. 実行結果

動作確認は、rp2040で実施しました。
実行すると、以下のように例外発生時のレジスタ情報が表示されます。

build [Feb 24 2025 06:56:05]
******** Hard fault ********
r0: 100030c5 r1:1000320d r2:d0000150 r3: 20001d44
r12:2000026d lr:10003a1b pc:100032d8 psr:01000000
******** Halted ********

addr2lineコマンドを使えば、pcの値からソース上のどこで例外が発生したかを特定できますが、ちょっと手間がかかるのが難点です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?