wolfBootを使ってみた

# はじめに
[前回](https://qiita.com/AkimotoMasane/items/22b43a4ff49c52cd1097)は、wolfBootを使うための環境を整えました。
今回は、NUCLEO-F401REでwolfBootを動かすための準備を行いました。

# 使用機器
+ Windows11
+ WSL2
+ Ubuntu

# 手順
###  コンフィグレーションファイルの準備
STM32F401用のmakeで使用する設定ファイルになります。
コンフィグレーションファイルは、ファイル名を **.config** としてルートディレクトリにおきます。
コンフィグレーションファイルは、STM32F4用のものを基にして作成します。
ルートディレクトリにコピーします。
```:ターミナル
wolfBoot$> cp ./config/examples/stm32f4.config .config
```
STM32F4用のコンフィグレーションファイルをコピーしたので、STM32F401用とするために以下の変更を行います。
```:.config
（変更前）
TARGET?=stm32f4

（変更後）
TARGET?=stm32f401
```

### 機種依存ファイルの準備
機種ごとに必要となるペリフェラルのドライバとリンカスクリプトファイルをコピーします。
こちらもSTM32F4用のものを基にして作成します。
```:ターミナル
wolfBoot$> cd hal
wolfBoot/hal$> cp ./stm32f4.c ./stm32f401.c
wolfBoot/hal$> cp ./stm32f4.ld ./stm32f401.ld
```

stm32f401.cは、STM32F4用のファイルを基にしているためSTM32F401用に変更します。
ファイルの上から順に下記のように変更しました。
```c:wolfBoot/hal/stm32f401.c
// 変更点のみの抜粋となります。
// 変更をする際に、STM32F401のリファレンスマニュアル（RM0368）を参照します。

// stm32f4.cでは、PLLのソースクロックを外部発振子とする設定になっていますが、
// NUCLEO-F401REにはデフォルトで外部発振子は未実装のためPLLのソースクロックを
// 内臓のHSIオシレータクロックとします。
// RM0368 P.105 6.3.2章より
#define RCC_PLLCFGR_PLLSRC          (0 << 22) //(1 << 22)

// STM32F401REの動作周波数は84MHzとなるので分周比を修正します。
#define RCC_PRESCALER_DIV_2    4 //8


// 内蔵フラッシュのセクタの定義を修正します。
// RM0368 P.45 3.3章より
/* FLASH Geometry */
#define FLASH_SECTOR_0  0x0000000 /* 16 Kb   */
#define FLASH_SECTOR_1  0x0004000 /* 16 Kb   */
#define FLASH_SECTOR_2  0x0008000 /* 16 Kb   */
#define FLASH_SECTOR_3  0x000C000 /* 16 Kb   */
#define FLASH_SECTOR_4  0x0010000 /* 64 Kb   */
#define FLASH_SECTOR_5  0x0020000 /* 128 Kb  */
#define FLASH_SECTOR_6  0x0040000 /* 128 Kb  */
#define FLASH_SECTOR_7  0x0060000 /* 128 Kb  */
//#define FLASH_SECTOR_8  0x0080000 /* 128 Kb  */
//#define FLASH_SECTOR_9  0x00A0000 /* 128 Kb  */
//#define FLASH_SECTOR_10 0x00C0000 /* 128 Kb  */
//#define FLASH_SECTOR_11 0x00E0000 /* 128 Kb  */
#define FLASH_TOP       0x0100000

//#define FLASH_SECTORS 12
#define FLASH_SECTORS 8
const uint32_t flash_sector[FLASH_SECTORS + 1] = {
    FLASH_SECTOR_0,
    FLASH_SECTOR_1,
    FLASH_SECTOR_2,
    FLASH_SECTOR_3,
    FLASH_SECTOR_4,
    FLASH_SECTOR_5,
    FLASH_SECTOR_6,
    FLASH_SECTOR_7,
    //FLASH_SECTOR_8,
    //FLASH_SECTOR_9,
    //FLASH_SECTOR_10,
    //FLASH_SECTOR_11,
    FLASH_TOP
};


// stm32f4.cでは外部発振子を使用していますが、NUCLEO-F401REにはデフォルトで外部発振子は未実装のため
// クロックは内臓のHSIオシレータクロックを使用します。
static void clock_pll_on(int powersave)
{
    uint32_t reg32;
    uint32_t cpu_freq, plln, pllm, pllq, pllp, pllr, hpre, ppre1, ppre2, flash_waitstates;

    /* Enable Power controller */
    APB1_CLOCK_ER |= PWR_APB1_CLOCK_ER_VAL;

    // HSIオシレータクロックを使用して84MHzで動作させるためにクロックの設定を見直します。
    /* Select clock parameters (CPU Speed = 84MHz) */
    cpu_freq = 84000000; (void)cpu_freq; /* not used */
    pllm = 16; //8
    plln = 336;
    pllp = 4; //2
    pllq = 7;
    pllr = 0; (void)pllr; /* not used */
    hpre = RCC_PRESCALER_DIV_NONE;
    ppre1 = RCC_PRESCALER_DIV_2; //RCC_PRESCALER_DIV_4;
    ppre2 = RCC_PRESCALER_DIV_NONE; //RCC_PRESCALER_DIV_2;
    flash_waitstates = 3;

    flash_set_waitstates(flash_waitstates);

    /* Enable internal high-speed oscillator. */
    RCC_CR |= RCC_CR_HSION;
    DMB();
    while ((RCC_CR & RCC_CR_HSIRDY) == 0) {};

    /* Select HSI as SYSCLK source. */
    reg32 = RCC_CFGR;
    reg32 &= ~((1 << 1) | (1 << 0));
    RCC_CFGR = (reg32 | RCC_CFGR_SW_HSI);
    DMB();

#if 0
    外部発振子を有効にしていますが、NUCLEO-STM32F401REには外部発信子はデフォルトで未実装のため本処理は無効とします。
    RM0638 P.103 6.3.1章より
    /* Enable external high-speed oscillator 8MHz. */
    RCC_CR |= RCC_CR_HSEON;
    DMB();
    while ((RCC_CR & RCC_CR_HSERDY) == 0) {};
#endif

    /*
     * Set prescalers for AHB, ADC, ABP1, ABP2.
     */
    reg32 = RCC_CFGR;
    reg32 &= ~(0xF0);
    RCC_CFGR = (reg32 | (hpre << 4));
    DMB();
    reg32 = RCC_CFGR;
    reg32 &= ~(0x1C00);
    RCC_CFGR = (reg32 | (ppre1 << 10));
    DMB();
    reg32 = RCC_CFGR;
    reg32 &= ~(0x07 << 13);
    RCC_CFGR = (reg32 | (ppre2 << 13));
    DMB();

    /* Set PLL config */
    reg32 = RCC_PLLCFGR;
    reg32 &= ~(PLL_FULL_MASK);
    RCC_PLLCFGR = reg32 | RCC_PLLCFGR_PLLSRC | pllm |
        (plln << 6) | (((pllp >> 1) - 1) << 16) |
        (pllq << 24);
    DMB();
    /* Enable PLL oscillator and wait for it to stabilize. */
    RCC_CR |= RCC_CR_PLLON;
    DMB();
    while ((RCC_CR & RCC_CR_PLLRDY) == 0) {};

    /* Select PLL as SYSCLK source. */
    reg32 = RCC_CFGR;
    reg32 &= ~((1 << 1) | (1 << 0));
    RCC_CFGR = (reg32 | RCC_CFGR_SW_PLL);
    DMB();

    /* Wait for PLL clock to be selected. */
    while ((RCC_CFGR & ((1 << 1) | (1 << 0))) != RCC_CFGR_SW_PLL) {};

#if 0
    stm32f4.cでは、外部発信子を使用するためにHSIオシレータクロックを停止していますが、
    NUCLEO-STM32F401REでは、HSIオシレータクロックを使用するために本処理は無効とします。
    RM0368 P.103 6.3.1章より
    /* Disable internal high-speed oscillator. */
    RCC_CR &= ~RCC_CR_HSION;
#endif
}
```

リンカスクリプトファイルを変更します。
```:stm32f401.ld
/* 変更点のみの抜粋となります */

/* stm32f.ldを基にしているためSTM32F401に合わせてRAMサイズを変更します */
MEMORY
{
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = @BOOTLOADER_PARTITION_SIZE@
    /*RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x0001C000*/
    RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00018000
}
```


### テスト用のアプリケーションの準備
テスト用のアプリケーションファイルを準備します。
こちらもSTM32F4用のものを基にして作成します。
```:ターミナル
wolfBoot$> cd test-app
wolfBoot/test-app$> cp ./stm32f4.c ./stm32f401.c
```


### 各種ファイルの修正
/wolfBoot/test-app内の各種ファイルにある条件付きコンパイルの判定条件を変更します。
STM32F401向けの関数が不足してmakeでエラーとなるため、条件付きコンパイルの判定条件を修正しました。
本来であれば、STM32F401の関数を準備するほうがいいと思います。

```c:wolfBoot/test-app/system.c
// 変更点のみの抜粋となります。

//#if defined(PLATFORM_stm32f4) || defined(PLATFORM_stm32f7)
#if defined(PLATFORM_stm32f4) || defined(PLATFORM_stmf401) || defined(PLATFORM_stm32f7)

// 新規追加
/* STM32F401, 84 MHz */
#ifdef PLATFORM_stm32f401
#  define PLLM 8
#  define PLLN 336
#  define PLLP 2
#  define PLLQ 7
#  define PLLR 0
#  define TARGET_FLASH_WAITSTATES 5
#endif
```
```c:/wolfBoot/test-app/led.c
// 変更点のみの抜粋となります。

//#ifdef PLATFORM_stm32f4
#if defined(PLATFORM_stm32f4) || defined(PLATFORM_stm32f401)
```
```c:/wolfBoot/test-app/timer.c
// 変更点のみの抜粋となります。

//#ifdef PLATFORM_stm32f4
#if defined(PLATFORM_stm32f4) || defined(PLATFORM_stm32f401)
```


### makeファイルの修正
factory.binを作成するときに使用されるオフセット値を修正します。

```:arch.mk
ifreq ($(TARGET),stm32f4)
    ARCH_FLASH_OFFSET=0x08000000
    SPI_TARGET=stm32
endif

# 追加します。
ifreq ($(TARGET),stm32f401)
    ARCH_FLASH_OFFSET=0x08000000
    SPI_TARGET=stm32
endif
```

ARCH_FLASH_OFFSETは、wolfboot.binとimage_v1_signed.binをfactory.binにまとめるときに使用されるようです。
ARCH_FLASH_OFFSETを定義しないとデフォルト値は0（ゼロ）のようで、ゼロ番地にwolfboot.binを割り当てたfactory.binが作成されます。


### make
STM32F401向けの対応が完了したらmakeします。
```:ターミナル
wolfBoot$> make clean
wolfBoot$> make
```
下記のファイルが作成されれば準備は完了となります。
+ wolfBoot/wolfboot.elf
+ wolfBoot/factory.bin
+ wolfBoot/hal/stm32f401.o
+ wolfBoot/test-app/image.elf

# 編集後記
wolfBootをNUCLEO-F401REで動作させるために下記のファイル
+ /wolfBoot/test-app/system.c
+ /wolfBoot/test-app/led.c
+ /wolfBoot/test-app/test.c

についてはmakeでエラーとならいないようにするための対応のみ行いました。
必要なときが来たらちゃんと対応してみたいと思います。
次回は、STM32CubeIDEを使用して、wolfBootをNUCLEO-STM32F401で動作させたいと思います。