【Nerves】M5Stack Core MP135にポーティングしてみた(試行錯誤編)


[準備編](https://qiita.com/GeekMasahiro/items/dd214725bc1849b355ef)
[設定ファイル作成編](https://qiita.com/GeekMasahiro/items/17242bf1f030de91ed75)
[KernelのBuild編](https://qiita.com/GeekMasahiro/items/e1bb049144b314e0c413)
[試行錯誤編](https://qiita.com/GeekMasahiro/items/e26a185bf858b0b149df)　←いまココ

# Firmwareを試す

mix firmware
mix burn
を実行して、SDカードに書き込んで、M5Stack CoreMP135に挿入して、電源を入れます。

# トライアンドエラー

最初に作成したファームウエアで、電源をONしますが、何も表示されませんでした。
Core MP135には、LCDパネルがあるのですが、何も表示されません。HDMIの出力にも何も出ません。

やはり、簡単にはうまく行きませんね。
それに、何も表示されないので、どこが原因なのか全く分からず、どこから手をつけてよいかわかりませんでした。

M5Stack社の公式のbuildrootでは動いているという事実があるので、それを信じて、一歩一歩進めていきましょう。

次の2点を意識して、調査を進めました。

### どの段階まで動作しているのかを把握する

この判断には、以下の事を考慮しました。

- M5のbuild rootのファームは、U-bootの処理で、LCDパネルにロゴを表示している。これが表示されたかどうかで、U-bootが起動しているかどうかがわかる
- Linuxの起動パラメータにKernelのログをシリアルポートに出力するように設定して、コンソール出力で、カーネルが起動しているかどうかがわかる

U-bootもシリアルコンソール出力があるはずですが、確認できなかったので、手掛かりには使えませんでした。
UARTは複数のポートがあるので、もしかすると他のポートに出力されているかもしれません。

### 動作する構成との差分を特定する

M5のbuildrootで作成しバイナリーファイルと入れ替えてみたり、パーティション構成、設定ファイルの比較を行う事問題点の特定を行いました。

比較してみた一例を以下に示します。
M5のbuildrootのimageディレクトリにあるzImageのイメージを、削除して代わりに、
Nervesのビルドにある/image/zImageをM5のBuildrootのimage/zImageにコピーして、これを元にSDカードを作成します。
zImage以外は、動作実績のあるものなので、zImageに問題なければ、動作するはずです。
これで起動はできたのでzImage自体には問題がないと判断しました。

# ポーティングで問題になるポイント

buildrootでのファームウエアの構築ができている状態であれば、u-bootやKernelは基本的に動作するはずです。
Nervesのポーティングで問題になるのは、以下の点が多いと思います。

### fwup.confの設定

起動しない原因の多くは、fwup.confの設定に関するものでした。何か所か修正が必要でした。
今回の様に参考にしたM5のBuildrootとNervesではパーテイション構成が異なります。
ポーティングする際には大抵、異なっていると思います。
Nervesでは、fwup.confでパーティション構成を設定しているので、fwup.confの記述方法を理解して、オリジナルのburildrootのファームウエアが作成している内容を反映したものに修正する必要があります。

M5の設定では、Boot時にARM-TRUSTED-FIRMWAREを使っている為、MP1場合とは、必要になるファイルが異なっていて、fwup.confの設定をATFの場合の記述に修正する必要がありました。

### U-Bootの環境変数の保存場所

U-Bootの環境変数の保存場所は、M5の設定とNervesの設定どちらもSDカードの特定のセクターに保存する方法を取っていますが、この保存位置が異なっていました。
保存場所をNervesの設定に合わせる必要があります。

Nervesでは、```fwup.conf```の次の場所で保存場所を指定しています。

```fwup.conf
# Location where installed firmware information is stored.
# While this is called "u-boot", u-boot isn't involved in this
# setup. It just provides a convenient key/value store format.
uboot-environment uboot-env {
    block-offset = ${UBOOT_ENV_OFFSET}
    block-count = ${UBOOT_ENV_COUNT}
}
```

UBOOT_ENV_OFFSET、UBOOT_ENV_COUNTは、```fwup_include/fwup-common.conf```で定義されています。
u-bootの設定で、同じ場所を指定するようにします。u-bootの設定は、byte単位、fwup.confの設定は、セクター単位に変換して設定します。
```
CONFIG_ENV_OFFSET=0x100000
CONFIG_ENV_SECT_SIZE=0x10000
```

今回のポーティングでは、この部分の設定が間違っていた事が原因で、カーネルが起動しない問題が発生しました。
u-bootは起動時に、環境変数の領域を初期化します。環境変数の保存場所が誤ってカーネルのイメージファイルが保存されている、領域であった為に、カーネルのイメージファイル(zImage)を書き換えてしまっていて、起動できないという問題でした。
分ってしまえば、簡単な問題ですが、原因を特定するのに時間がかかりました。

### カーネルの設定

u-bootが正常に動作したら、カーネルが起動されます。
この段階の動作は、カーネルのログをシリアルコンソールに出力するように設定する事で、確認できると思います。
カーネルの起動パラメータは、```extlinux.conf```に記述されています。

```:extlinux.conf
label stm32mp135f-coremp135-buildroot
  kernel zImage
  devicetree stm32mp135f-coremp135.dtb
  append root=/dev/mmcblk0p5 rootwait console=tty1 console=ttySTM0,115200n8 quiet
```

今回使用したカーネルでは、シリアルコンソールのデバイスが```ttySTM0```なので、```console=ttySTM0```を指定しています。
この設定を行う事で、カーネル起動時のログがシリアルコンソールに出力され、状況を確認する事ができます。
このログが表示されない場合は、u-bootの設定が間違っている等、カーネル起動の前の段階で問題が発生している可能性が高いと思います。

今回のポーティングでは、カーネルが起動したら、iexのプロンプトが表示され使えるようになりました。

### ファイルシステムの書き込み

iexのプロンプトが表示され、Nervesが基本的に使えるようになりました。
しかし、起動するたびにSSHの鍵が変わるのです。
良く調べてみるとファイルシステムへの書き込みができていない事が原因でした。
カーネルの設定に[F2FS](https://www.kernel.org/doc/html/latest/filesystems/f2fs.html)が無い事が原因でした。

カーネルの設定は、M5の設定を使っていた為に、Nervesで前提としているものが足りていなかったのです。
```
CONFIG_F2FS_FS=y
```
このあたりを調整して解決しました。

# Nerves起動後の処理

Nerves起動後の処理には、ターゲット固有の部分は殆どなく、修正点は少ないと思います。
```nerves_system_m5stack_core_mp135/etc/rootfs_overlay```
ここに、nervesが参照する設定があるので、この項目を確認するとよいと思います。
今回のポーティングでは、次の点を修正しました。

### boardid

今回のボードには、ハードウエアにユニークなIDは無いので、boardidの生成ができません。
この為、'''-b force -f "0000"'''を追加して、u-boot環境変数に指定がない場合は0000とする設定を追加しました。

```:rootfs_overlay/etc/boardid.config
-b uboot_env -u nerves_serial_number
-b uboot_env -u serial_number
-b force -f "0000"
```

### erlinit.config

```erlinit.config```の```-c```によって、```iex>```のプロンプトを表示するコンソールを決める事ができますｓ。
UART（シリアルポート）をデフォルトとしたい場合は、```-c ttySTM0```等とします。
 
```:rootfs_overlay/etc/erlinit.config
#-c ttySTM0    # UART pins on the GPIO connector
-c tty1     # HDMI or LCD Display output
```

ターゲットのerlinit.configでデフォルトの挙動が決まる事になります。
この設定は、アプリケーションのmix configの記述で変更する事も可能です。

https://hexdocs.pm/nerves/advanced-configuration.html#overriding-erlinit-config-from-mix-config

道のりは長かったですが、カーネルの起動、Nervesの起動までたどり着く事ができました。

# まとめ


- Nervesについての知識が乏かった為、不具合の原因究明に時間を要したが、ポイントを押さえれば、ポーティングに必要な部分は非常に限られている。
- 特に、Buildrootでファームウエアが作成できる状態になっていれば、Nervesへの移植はそれほど難しくはない。
- u-boot関係は、ポーテイングする際に修正が必要になる
- Kernelが起動してから先は、ターゲット固有の部分はあまりない。

最近様々なSBCが販売されています。
NPUを搭載したものやFPGA等もあります。
このような特色あるボードにNervesをポーティングして組み込みシステムを作成してみてはいかがでしょうか。

[今回のポーティングのMP1からの差分](https://github.com/masahiro-999/nerves_system_m5stack_core_mp135/commit/5d0bea1c7d5e16e40037d68ee318cf65aad31d15)

参考

nerves_system_m5stack_core_mp135
https://github.com/masahiro-999/nerves_system_m5stack_core_mp135

STM32MP1 プラットフォームのブート
https://www.stmcu.jp/wp/wp-content/uploads/files/presentation-ja/STM32MP1/60_STM32MP1-Software-Platform-boot-(BOOT)_J.pdf

産業用堅牢ハードでElixir：e-RT3にNervesポーティング
https://qiita.com/pojiro/items/81f3aafb77eed47f3baa