mruby/c を、Nucleo-F091RC (STM32F091RC) へ移植する

[しまねソフト研究開発センター](https://www.s-itoc.jp)（略称 ITOC）にいます、東です。

先日来、STマイクロエレクトロニクス製のマイコンボード、「Nucleo-F401RE」と、開発環境「STM32CubeIDE」を使って、mruby/c の移植などを行ってきました。
ITOCには、他にNucleoシリーズの「Nucleo-F091RC」と、「Nucleo-L476RG」を所有しています。せっかくなので、これらのデバイスにも mruby/c の移植を行い、その手順を公開します。

# 予定

## Nucleo-F091RC

前回までターゲットにしていた機種と比較して、より小規模のMPU搭載機種です。
 CPU: Cortex-M0
 RAM: 32KB

こちらは、mruby/c のバイトコードを C言語の配列として書き込む方法（[この記事](https://qiita.com/HirohitoHigashi/items/439494cde09da6350bde) の方法2）をやってみます。
すなわち、[mruby/c移植チュートリアル記事](https://qiita.com/HirohitoHigashi/items/7ef01d074baa27a3b2e7) を、機種を Nucleo-F091RC に替えて実施することとなります。

## Nucleo-L476RG

こちらは、より大規模のMPU搭載機種です。

 CPU: Cortex-M4
 RAM: 128KB

この機種では、次回以降に別記事として、バイトコードのみを書き込む方法（[この記事](https://qiita.com/HirohitoHigashi/items/439494cde09da6350bde) の方法1）を実施する計画です。


# 準備するもの

STM32マイコン評価ボード Nucleo-F091RC
(https://www.st.com/ja/evaluation-tools/nucleo-f091rc.html)
開発環境　STM32CubeIDE (ver 1.15.1)
(https://www.st.com/ja/development-tools/stm32cubeide.html)

![202410-1_Nucleo-F091RC.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/08daec00-5bdb-5a13-8283-842c1be1f2ec.jpeg)



# 作業手順

STM32CubeIDE 上で、MPUの種類を変更する方法が無いか探したのですが、おそらくメーカーが想定しているGUI操作の範囲内では用意は無さそうです。従って、新規にプロジェクトを作って、チュートリアルの手順を最短で組み入れる方法をとりました。


## 新規プロジェクトの作成

別記事[「mruby/cをSTM32マイコンで動かす Chapter01: 環境構築」](https://qiita.com/HirohitoHigashi/items/7ef01d074baa27a3b2e7) に書いた方法と同様の手順でプロジェクトを作ります。

Board Selector をクリックしてボードセレクタタブに変更し、Commercial Part Number 欄に、"F901" と入力すれば NUCLEO-F091RC が検索されますので、ボードリスト欄で選べばOKです。

![202410-1_scan14.39.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/a01293d3-8ef9-3bd2-5ef9-ba466319d159.png)

プロジェクト名は、任意でかまいません。今回私は、"F091RC_mrubyc" とつけました。


## デフォルトデバイスの確認

プロジェクトが作成されましたら、デフォルトでマップされた以下のデバイスを確認します。

  * オンボードLEDのポート番号　（前回F401REでは、PA5）
  * オンボードスイッチのポート番号　（前回F401REでは、PC13）
  * USBシリアルにマップされたUART番号　（前回F401REでは、UART2）

幸いなことに、今回のボード F091RC は、チュートリアルで使ったボード F401RE と同じポート番号、UART番号でした。よって、ポーティングは最小限の手順で行う事ができます。

## ソースコードのダウンロードとマージ

「チュートリアル Chapter04: halの仕上げ」あたりのソースコードが一番使いやすいと思うので、こちらをつかいます。

https://github.com/HirohitoHigashi/mrubyc-tuto-stm32/tree/chapter04

このページから、Code > Download ZIP 等でダウンロードし、展開しておきます。

展開したソースツリーから、以下のディレクトリを今回のプロジェクトのCoreディレクトリへコピーします。
  * Core/mrubyc
  * Core/mrubyc_src

![202410-1_scan14.47.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/94d35910-fa0b-4853-1a6e-d6e968193dde.png)


## 自動生成されたソースコードへ追記

Core/Src/ 以下のファイルを編集し、以下のコードを追記します。

mruby/c の開始指示

```C:Core/Src/main.c
  /* USER CODE BEGIN 2 */
  void start_mrubyc(void);
  start_mrubyc();
  /* USER CODE END 2 */
```

割り込み処理の追加

```C:Core/Src/stm32f0xx_it.c
  /* USER CODE BEGIN SysTick_IRQn 0 */
  void mrbc_tick(void);
  mrbc_tick();
  /* USER CODE END SysTick_IRQn 0 */
```

## ビルドプロパティーの変更

メニューから、Project > Properties を選び、ダイアログを開きます。

ダイアログ左ペインの C/C++ Build > Settings をクリックします。

右ペインの　Configuration: を、All configurations に変更します。

右ペインの　Tool Settings タブをクリックし、MCU Settings をクリックします。

Use float with printf from newlib-nano 欄のチェックをつけます。

![202410-1_scan14.54.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/1ddc3fd1-9580-f0f1-7b8e-f0fd502458dc.png)

右ペインの Build Steps タブをクリックします。

Pre-build steps の Command: 欄へ、以下の通り入力します。

```
cd ..\\Core\\mrubyc; make
```

![202410-1_scan14.55.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/252a6cb9-85d7-e6f5-3e20-e91ae1a2d051.png)

## CPU Cortex-M0 に対応

Cortex-M0 は、メモリアクセスに32bit アライメントを要求するので、vm_config.h でコメントアウトしてある以下の定義を有効化します。

```C:Core/mrubyc_src/vm_config.h
#define MRBC_REQUIRE_32BIT_ALIGNMENT
```

もしくは、IDEのビルドプロパティーで定義しても、おそらく問題ないでしょう。

## mruby/c プログラムが使うワークメモリの縮小

チュートリアルでは、mruby/c プログラムが使うワークメモリに30KBほど確保するよう記述しました。一方このMPUの内蔵メモリは、トータルで32KBしかないので、このままだとリンクエラーになります。従って、以下のように約半分の16KBに縮小します。

```C:Core/mrubyc/start_mrubyc.c
/* mruby/c プログラムが使うワークメモリの確保 */
#define MRBC_MEMORY_SIZE (1024*16)
```

これでOKです。


# 動作確認

ビルドしてターゲットに書き込みます。
TeraTerm 等を使い、ターゲットボードの COM 番号を開いて、"Hello, mruby/c ..." 等と表示されればOKです。



# その他の変更点

## オンボードLEDのポート番号が違う場合

オンボードLEDの割り当てポートが PA5 ではない場合は、c_led_write 関数を変更します。以下の例は、PB15に割り当てる例です。

```C:Core/mrubyc/start_mruby.c
/*! オンボードLED ON/OFF メソッドの実装
*/
static void c_led_write(mrbc_vm *vm, mrbc_value v[], int argc)
{
  int on_off = GET_INT_ARG(1);
  HAL_GPIO_WritePin( GPIOB, GPIO_PIN_15, on_off );
}
```

## UART 番号が違う場合

UARTが 2番ではない場合は、hal_write 関数を変更します。以下の例は、UART3に割り当てる例です。

```C:Core/mrubyc/start_mruby.c
int hal_write(int fd, const void *buf, int nbytes)
{
  extern UART_HandleTypeDef huart3;
  HAL_UART_Transmit( &huart3, buf, nbytes, HAL_MAX_DELAY );

  return nbytes;
}
```

## mruby/c ソースコードの入れ替え

バージョンアップやバグ対応等で、mruby/c VM のソースコードを更新したい場合の手順を記します。基本的には mrubyc_src ディレクトリの内容を入れ替えるだけです。

 1. Core/mrubyc_src/hal.h ファイル、及び vm_config.h ファイルを任意の場所にバックアップする
 2. Core/mrubyc_src ディレクトリの内容を、最新のmruby/c VM ソースコードの src ディレクトリの内容に全て入れ替える
 3. バックアップした hal.h を mrubyc_src ディレクトリ内へ戻す
 4. バックアップした vm_config.h を最新の vm_config.h と比較し、マージする


# おまけ

https://qiita.com/HirohitoHigashi/items/a0acb20ec8b58a21a9d4 と同じ条件にして、ベンチマークを行ってみました。

| MPU | Core | clock(MHz) | 最適化 | 実行時間(ms) |
|-|-|-:|:-:|-:|
|STM32F401RET6　| ARM Cortex M4 | 84 | -O2   | 2303 |
|STM32F091RC    | ARM Cortex M0 | 48 | -O2   | 9055 |

クロックが約1.8倍程度の差に対して、実行時間は約4倍の差がありますので、クロックあたりの性能比較では、約2.2倍の差が観測できました。
ただし、Cortex M0では、32bit align 回避が、このベンチマークにおいてはかなり不利になっていると思われ、コア単体の計算速度は、そこまで差が出ないはずと考えています。


# おわりに

次回は、ペリフェラルライブラリとバイトコード書き込み機能を含め、もう一つの機種「Nucleo-L476RG」への移植にトライします。