STM32 には出荷時点でブートローダーが入っていて、様々な方法ですぐ書きこめるようになっている。ブートローダーは、書き換えできない「システムメモリ」と呼ばれる領域に入っている。

ユーザーコードからでも、このシステムメモリにジャンプすればブートローダーのモードに入れる。入れるのだけど、このブートローダーに入る方法について AN2606 にはこう書かれている

In addition to patterns described above, user can execute bootloader by performing a jump to system memory from user code. Before jumping to bootloader user must:
• Disable all peripheral clocks
• Disable used PLL
• Disable interrupts
• Clear pending interrupts
System memory boot mode can be exited by getting out from bootloader activation condition and generating hardware reset or using Go command to execute user code.

https://www.st.com/content/ccc/resource/technical/document/application_note/b9/9b/16/3a/12/1e/40/0c/CD00167594.pdf/files/CD00167594.pdf/jcr:content/translations/en.CD00167594.pdf

つまりいろんなものを初期状態に戻さなければいけない。これは、まぁめんどうくさい……

同様の事例をググってみると、マジックコードをメモリに書きこんで (普通のグローバル変数でも良いし、メモリを節約したいなら、どうせリセットするのだし適当なアドレスに書きこんでいい)、システムリセットを起こし、(メモリは初期化されないので) マジックコードを検出して、リセット直後のあらゆるペリフェラルが初期化される前にブートローダ(システムメモリ)へジャンプをかけるという方法がひっかかる。

リセット直後にジャンプするほうがあきらかに楽。

ChibiOS では

board.c の __early_init() というのがスタック初期化直後に ChibiOS のブートストラップコードのアセンブリから呼ばれてくるので、ここにジャンプコードを実装してやる。

以下のようになった。いろいろ試したあげく、結局ほぼstackoverflowの内容と同じだけど、どうしてもうまくいかなかったため調べていたら、どうも __enable_irq() が必要ということがわかった。

STM32F072xB_SYSTEM_MEMORY の位置には初期スタックポインタのアドレスが入っているはず、、なんだけどリセットからくると(?)ダメみたいなので、定数値を入れている。STM32F072xB_SYSTEM_MEMORY+4 が実際のジャンプ先。

// board.c
void __early_init(void) {
  if ( *((unsigned long *)BOOT_FROM_SYTEM_MEMORY_MAGIC_ADDRESS) == BOOT_FROM_SYTEM_MEMORY_MAGIC ) {
    // require irq
    __enable_irq();
    *((unsigned long *)BOOT_FROM_SYTEM_MEMORY_MAGIC_ADDRESS) = 0;
    // remap memory. unneeded for F072?
    // RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    // SYSCFG->CFGR1 = 0x01;
    __set_MSP(SYSTEM_BOOT_MSP); 
    ( (void (*)(void)) (*((uint32_t *)(STM32F072xB_SYSTEM_MEMORY+4))) )();
  }

  //si5351_setup();
  stm32_clock_init();
}

定数はこのようになっている。STM32F072xB_SYSTEM_MEMORY は型番によって違うので調べる必要がある。リファレンスマニュアルに書いてある。

// board.h
#define STM32F072xB_SYSTEM_MEMORY 0x1FFFC800
#define BOOT_FROM_SYTEM_MEMORY_MAGIC_ADDRESS 0x20003FF0
#define BOOT_FROM_SYTEM_MEMORY_MAGIC 0xDEADBEEF
#define SYSTEM_BOOT_MSP 0x20002250

実際のジャンプするコードはこう。メモリにフラグを書いてリセットしているだけ。

  *((unsigned long *)BOOT_FROM_SYTEM_MEMORY_MAGIC_ADDRESS) = BOOT_FROM_SYTEM_MEMORY_MAGIC;
  NVIC_SystemReset();

ref

  1. トップ
  2. tech
  3. STM32F072 で、ユーザーコードから DFU モードに入る。