省RAM型BootLoaderの設計と実装

省RAM型BootLoaderとは

省RAM型BootLoaderは、外部メモリを使わずにファームウェア更新を完結させる方式である。 更新イメージはアプリケーションフラッシュ領域に直接書き込むため、RAM や外部ストレージを追加する必要がない。 一方で、更新失敗時にアプリケーションが消失するリスクがあり、失敗検知とリカバリ機構が必須となる。

省RAM型と二領域型の比較

観点省RAM型(本設計)二領域型
追加メモリ不要更新領域が必要
更新時間短い(直接書き込み)長い(コピー工程あり)
失敗時の挙動BootLoaderのみ起動旧アプリで動作継続
BootLoaderサイズ大きい(更新ロジック内包)小さい(コピーのみ)

コア機構

1. ジャンプ制御

アプリ ↔ BootLoader 間のジャンプはベクタテーブルを関数ポインタとして呼び出す。 Cortexでは次のように記述する(Cortex-M 系を想定)。

/* ジャンプ先アドレス(VTOR + 4 = Reset_Handler) */
constexpr uintptr_t APP_RESET_VTOR = 0x0800'8000UL + 4U;
using ResetFunc = void(*)();

void invoke_app()
{
    uint32_t reset_addr = *reinterpret_cast<uint32_t*>(APP_RESET_VTOR);
    ResetFunc jump = reinterpret_cast<ResetFunc>(reset_addr);

    /* 割り込み無効化・クロック初期化等を省略 */
    jump();   /* アプリへジャンプ */
}

2. 上位機(SOC)の電源維持

SOC の電源EN端子を MCU のGPIOで制御している場合、BootLoader では当該GPIOを Hi-Z にして初期化をスキップする。 これにより SOC がリセットされず、USB/UART 等の通信路が維持される。

3. 更新失敗検知

フラッシュ最終セクタに 32-bit ステータスワードを配置し、以下の状態を表現する。

  • 0xAAAAAAAA : 更新開始
  • 0x55555555 : 更新成功
  • 0xFFFFFFFF : 初期値(未更新/出荷状態)
  • その他 : 更新失敗

起動時にこのワードを読み、0x55555555 の場合のみアプリへジャンプする。 更新中に電源断が発生しても、次回起動時に BootLoader が検知し再更新を促す。

実装時の落とし穴

1. 誤リセット問題

BootLoader 内で FTM タイマを初期化する際、EXTTRIG.INITTRIGEN ビットを 1 にしていたところ、CNTIN 一致でタイマリセットが発生し、ウォッチドッグへのパットが遅延して MCU がリセットする事象が発生した。 対策として、INITTRIGEN を無効化し、CNTIN 一致割り込みを使用しない構成に変更した。

2. フラッシュ書き込み失敗

アプリ実行中にフラッシュを消去/書き込みしようとすると失敗するケースがあった。 原因は MCU が HSRUN モード(高周波)で動作しており、フラッシュタイミングが合わないため。 RUN モードにダウンスケールしてからフラッシュ操作を行い、完了後に再び HSRUN へ移行することで解決した。

void write_status_word(uint32_t value)
{
    enter_run_mode();               /* HSRUN → RUN */
    flash_unlock();
    flash_erase_page(STATUS_PAGE);
    flash_program_word(STATUS_ADDR, value);
    flash_lock();
    enter_hsrun_mode();             /* RUN → HSRUN */
}

まとめ

省RAM型BootLoaderは、リソース制約の厳しい組み込み機器において最小構成で OTA 更新を実現できる。 更新失敗時の安全性は犠牲になるが、フラグ管理とジャンプ制御を工夫することで、実用上十分な信頼性を確保できる。

タグ: BootLoader FlashProgramming MCU OTA EmbeddedC

7月19日 17:08 投稿