【備忘録】STM32 × RustでLチカしてみよう【IDEはEclipse】

# はじめに

以前[GR-PEACH × RustでLチカしてみよう【とにかくやってみる編】](https://qiita.com/yagisawa/items/fa203a8e07872ce93800)という記事を書いたのですが、その時「今度はスタートアップからスクラッチで書いてみたい」ということを書きました。
この記事は上記作業の備忘録となります。備忘録なので記載が雑な部分がありますがご容赦ください。
ちなみにボードはGR-PEACHのROMがシリアルフラッシュだったりとスタートアップの工程がめんどくさそうだったので、まずは簡単なSTMのNucleoボードでチャレンジしました。

本記事の環境は以下にまとめてあります。

https://github.com/kei-yagisawa/nucleo_blinky_startup/releases/tag/20210923

# 環境

* OS
    * Windows 10 Pro 2004(64bit)
* rustc・cargo
    * 1.55.0-nightly
* rustup
    * 1.24.3
* Eclipse
    * Pleiades All in One Eclipse 2020-12(Platform)
* Rust DT
    * 0.8.1.v201612291856
* OpenOCD
    * v0.11.0-1
* GNU Arm Embedded Toolchain
    * Version 9-2019-q4-major
* ボード
    * [NUCLEO-F401RE](https://www.st.com/ja/evaluation-tools/nucleo-f401re.html)


# 環境構築

## Eclipseのインストール

以下URLから取得します。

https://mergedoc.osdn.jp/

JavaでもC++でもないのでパッケージは`Platform`でOKです。私はちょっと古い`2020-12`版を使っていますが、最新版(私が確認したときは2021-04版)はダークテーマに背景も文字も真っ黒になってしまうバグがあり使っていないだけなので、おそらく最新版でも問題ないはずです。

## RustDTのインストール

以下URLを参考にインストールします。

http://rustdt.github.io/

## GNU Arm C/C++ OpenOCD Debuggingのインストール

Rust用のOpenOCDデバッグ環境はなさそうなので、C/C++用をインストールします。
最新の`Eclipse Embedded CDT`は[https://download.eclipse.org/embed-cdt/releases/6.1.2/p2/](https://download.eclipse.org/embed-cdt/releases/6.1.2/p2/)にあるのですが、ここからインストールしようとしたら、RustDTと相性が悪いようでRustDTをアンインストールするよう促されたので、仕方なく[http://sourceforge.net/projects/gnuarmeclipse/files/Eclipse/updates](http://sourceforge.net/projects/gnuarmeclipse/files/Eclipse/updates)から(古めのやつ？を)インストールしました。
インストール方法は先程のRustDTと同じく`新規ソフトウェアのインストール...`から行います。OpenOCDでデバッグしたいだけなので、インストールするのは`GNU Arm C/C++ OpenOCD Debugging`だけでOKです。

## OpenOCDのインストール

推奨インストール方法は`xpm`によるインストールのようですが、めんどくさかった(MSYS2がインストールされていなかった)ので[公式サイト](http://openocd.org/getting-openocd/)にリンクされていた以下の`非公式バイナリパッケージ`を取得しました。インストーラではないので適当なところに展開してください。

https://github.com/xpack-dev-tools/openocd-xpack/releases

余談ですが、過去にGR-PEACHのデバッグに`v0.10.0`を使っていたのでそれを使いまわそうとしたのですが、エラーで起動しなかったので最新版をインストールしました。

## GNU Arm Embedded Toolchainのインストール

GDBを使うのでインストールします。

https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads

## ST-Linkドライバのインストール

以下URLからドライバを取得します。

https://www.st.com/ja/development-tools/stsw-link009.html

# プロジェクト作成

`プロジェクト・エクスプローラーで右クリック → 新規作成 → プロジェクト → Rust → Rust Cargo Project`からプロジェクトを作成します。

# コーディング

以下のようなLチカコードを書きました。

```Rust:main.rs
#![no_main]
#![no_std]
#![feature(llvm_asm)]

#[link_section = ".vector_table.reset_vector"]
#[no_mangle]
pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;

#[no_mangle]
pub unsafe extern "C" fn Reset() -> ! {
    let rcc_ahb1enr = 0x4002_3830 as *mut usize;
    let gpioa_moder = 0x4002_0000 as *mut usize;
    let gpioa_odr   = 0x4002_0014 as *mut usize;

    set_bit(rcc_ahb1enr,  0, true);
    set_bit(gpioa_moder, 10, true);

    let mut flg: bool = true;
    loop {
        flg = !flg;
        set_bit(gpioa_odr, 5, flg);

        for _ in 0..24_000 {
            llvm_asm!("" :::: "volatile")
        }
    }
}

unsafe fn set_bit( addr: *mut usize, bit: usize, is_set: bool ) {
    if is_set {
        *addr |= 1 << bit;
    }
    else {
        *addr &= !(1 << bit);
    }
}


use core::panic::PanicInfo;
#[panic_handler]
fn panic(_panic: &PanicInfo<'_>) -> ! {
    loop {}
}
```


# ビルド

## ビルドコマンドの変更

`プロジェクトを右クリック → プロパティ → Rust Build Target → ビルド・ターゲット(build)`からビルドコマンドを変更できます。デフォルトだと`${CARGO_TOOL_PATH} test --no-run --message-format=json`(テスト用のコマンド)となっているので、`cargo build`に書換えます。

## ターゲットの追加

以下のコマンドでターゲットを追加します。

```shell
> rustup target add thumbv7em-none-eabihf
```

`STM32F401RE`マイコンのコアは`Cortex-M4F`なので、`thumbv7em-none-eabihf`を追加します。他のコアのマイコンを使用している場合は

* thumbv6m-none-eabi → M0, M0+
* thumbv7m-none-eabi → M3
* thumbv7em-none-eabi → M4, M7
* thumbv7em-none-eabihf → M4F, M7F

を追加してください。

## リリースチャネルをNightlyに変更

Eclipseのプロジェクトを作成したディレクトリで以下のコマンドを実行しNightlyに変更します。

```shell
> rustup override add nightly
```

## configファイルの作成

Eclipseのプロジェクトを作成したディレクトリに`.cargo`というディレクトリを作成し、その中に以下の`config`ファイルを作成します。

```config
[target.thumbv7em-none-eabihf]
rustflags = [
	"-C", "link-arg=-Tlinker.ld",
]

[build]
target = "thumbv7em-none-eabihf"
```

## リンカ・スクリプトの作成

Eclipseのプロジェクトを作成したディレクトリ直下に以下の`linker.ld`ファイルを作成します。

```
MEMORY {
    FLASH : ORIGIN = 0x08000000, LENGTH = 512K
    RAM   : ORIGIN = 0x20000000, LENGTH =  96K
}

ENTRY(Reset);
EXTERN(RESET_VECTOR);

SECTIONS {
    .vector_table ORIGIN(FLASH) : {
        LONG(ORIGIN(RAM) + LENGTH(RAM));        /* スタックポインタ */
        KEEP(*(.vector_table.reset_vector));    /* リセットベクタ   */
    } > FLASH

    .text : {
        *(.text .text.*);
    } > FLASH

    .rodata : {
        *(.rodata .rodata.*);
    } > FLASH

    /DISCARD/ : {
        *(ARM.exidx .ARM.exidx.*);
    }
}
```

`STM32F401RE`マイコンのROMは`0x08000000`から512KB、RAM`0x20000000`から96KBです。

## やっとビルド

Eclipseで作成したプロジェクトの`Build Targets`に`build` `check` `clean`という項目が並んでいると思います。`build`をダブルクリックするだけでビルドは完了します。Cのビルドに慣れていると一瞬で終わりすぎて、ちゃんとビルドできたのかとちょっと焦ります:sweat_smile:

# 書込み及びデバッグ

メニューから`実行 → デバッグ構成`を選択し、デバッグ構成ウィンドウを開きます。
次に`GDB OpenOCD Debuggingを右クリック → 新規作成`でデバッグ構成を作成します。

各タブの設定内容は以下のとおりです。

* メイン
    * プロジェクト
        * 通常なら紐付けるプロジェクトが選択できるのですが、C/C++プロジェクトではないのため選択肢がありません。したがって空欄にします。
    * C/C++ アプリケーション
        * ELFファイルを指定します。プロジェクトディレクトリ内の`target/thumbv7em-none-eabihf/debug/nucleo_blinky`にあって、拡張子はありません。ファイル名はプロジェクト名によって異なります。
* デバッグ
    * OpenOCD Setup
        * 実行可能ファイル
            * OpenOCDの実行ファイルを指定します。
            * 私の場合は`${openocd_path}/bin/${openocd_executable}`です。変数の値が期待してるものと違ってた気がするので、うまく実行できない場合は見直してください(多分絶対パスで指定するのが確実)。
        * Config options
            * ターゲットボードのコンフィグファイルを指定します。
            * 私の場合は`-s ${openocd_path}/scripts -f board/st_nucleo_f4.cfg`です。パスについては上記と同様です。
    * GDB Client Setup
        * 実行可能ファイル
            * GDBの実行ファイルを指定します。
            * 元々何かの変数が指定してあった気がしますが、なんかうまくいかなかったので私は絶対パスを指定してしまっています。
* 始動
    * Run/Restart Commands
        * Set breakpoint at
            * `main`が指定されていますが今回main関数は無いのでチェックを外しておきます。

以上で設定は完了(のはず)です。

`デバッグ`を押すとデバッグが開始され、`Reset`関数の先頭でブレークしているはずです。`F8`で実行します。

![blinky.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469259/bea7a8c9-4216-489b-c8ac-ea906652c6b3.gif)

# おわりに

以前の記事から1年近く経ってしまいましたがようやくスタートアップからスクラッチでLチカすることができました。
相変わらず割込み等使ってませんが、ようやく組込みRustっぽくなってきた気がします。`cortex-m-quickstart`テンプレートや`stm32f4`クレートを使えば一発なんだとは思いますが、一つ一つ理解しながらやっていこうと思います。
記事を書いている途中で`RustDT`はもう古い(更新が止まってる)ことに気付いたり、Eclipseの変数が全然使えなかったりと色々ありましたが、また時間ができたらアップデートしていこうと思います。
次回は割込みに挑戦しようと思います。