RustプログラムをNucleo-f401reボードで動かす

# 概要

組み込み系でRustを使ってみたいなと思っていろいろやってたらNucleo-f401reで動かせるサンプルがあったので試してみました。ここまでたどり着くのに時間はかかりましたが、これ自体はサクッと動いてくれました。

# 環境

- Ubuntu 18.04
- rustc 1.38.0
- VSCode 1.38.1

# 参考

開発環境は以下の3つのサイトを参考に構築しています。

[UbuntuにRustをインストールする](https://qiita.com/yoshiyasu1111/items/0c3d08658560d4b91431)
[UbuntuのVSCodeでRustの開発環境を作る](https://qiita.com/yoshiyasu1111/items/9ed8496e388164bd99bb)
[The Embedded Rust Book](https://tomoyuki-nakabayashi.github.io/book/intro/install.html)

クレートのGitHub

[nucleo-f401re](https://github.com/jkristell/nucleo-f401re)
[stm32f4xx-hal](https://github.com/stm32-rs/stm32f4xx-hal)

他のボード

[Rustで組み込みプログラミングの第一歩、LチカとHello Worldを試してみた](https://dev.classmethod.jp/hardware/rust-embedded-helloworld/)

# クロスコンパイラ

Nucleo-f401reに搭載されているのはCortex-M4Fなのでそれ用にコンパイルできる`thumbv7em-none-eabihf`をインストールしてきます。

```
$ rustup target add thumbv7em-none-eabihf
info: downloading component 'rust-std' for 'thumbv7em-none-eabihf'
info: installing component 'rust-std' for 'thumbv7em-none-eabihf
```

こんな感じの状態です。

```
$ rustup show
Default host: x86_64-unknown-linux-gnu
rustup home:  /home/yoshi/.rustup

installed targets for active toolchain
--------------------------------------

thumbv7em-none-eabihf
x86_64-unknown-linux-gnu

active toolchain
----------------

stable-x86_64-unknown-linux-gnu (default)
rustc 1.38.0 (625451e37 2019-09-23)
```

# クローン

```
$ git clone https://github.com/jkristell/nucleo-f401re.git
```


# ビルド

```
$ cargo build --example gpio_hal_blinky
    Updating crates.io index
   Compiling semver-parser v0.7.0
   Compiling typenum v1.11.2
   Compiling proc-macro2 v0.4.30
   Compiling unicode-xid v0.1.0
   Compiling rand_core v0.4.2
   Compiling syn v0.15.44
   Compiling stable_deref_trait v1.1.1
   Compiling cortex-m-rt v0.6.10
   Compiling vcell v0.1.2
   Compiling byteorder v1.3.2
   Compiling cortex-m v0.6.1
   Compiling r0 v0.2.2
   Compiling stm32f4 v0.7.1
   Compiling heapless v0.4.4
   Compiling nb v0.1.2
   Compiling void v1.0.2
   Compiling cortex-m-semihosting v0.3.5
   Compiling cortex-m v0.5.10
   Compiling cast v0.2.2
   Compiling cortex-m-rtfm v0.4.3
   Compiling aligned v0.2.0
   Compiling rand_core v0.3.1
   Compiling semver v0.9.0
   Compiling volatile-register v0.2.0
   Compiling embedded-hal v0.2.3
   Compiling rand v0.5.6
   Compiling tpa2016d2 v0.2.0
   Compiling segment-display v0.1.0
   Compiling hd44780-driver v0.3.0
   Compiling rustc_version v0.2.3
   Compiling hash32 v0.1.0
   Compiling quote v0.6.13
   Compiling bare-metal v0.2.5
   Compiling generic-array v0.12.3
   Compiling generic-array v0.11.1
   Compiling as-slice v0.1.0
   Compiling aligned v0.3.1
   Compiling panic-semihosting v0.5.3
   Compiling cortex-m-rt-macros v0.1.5
   Compiling owned-singleton-macros v0.1.0
   Compiling cortex-m-rtfm-macros v0.4.3
   Compiling owned-singleton v0.1.0
   Compiling stm32f4xx-hal v0.5.0
   Compiling nucleo-f401re v0.2.0 (/home/yoshi/rust/nucleo-f401re)
warning: use of deprecated item 'embedded_hal::digital::v1::OutputPin::set_low': Deprecated because the methods cannot return errors. Users should use the traits in digital::v2.
  --> examples/gpio_hal_blinky.rs:19:9
   |
19 |     led.set_low();
   |         ^^^^^^^
   |
   = note: `#[warn(deprecated)]` on by default

warning: use of deprecated item 'embedded_hal::digital::v1::OutputPin::set_high': Deprecated because the methods cannot return errors. Users should use the traits in digital::v2.
  --> examples/gpio_hal_blinky.rs:31:13
   |
31 |         led.set_high();
   |             ^^^^^^^^

warning: use of deprecated item 'embedded_hal::digital::v1::OutputPin::set_low': Deprecated because the methods cannot return errors. Users should use the traits in digital::v2.
  --> examples/gpio_hal_blinky.rs:38:13
   |
38 |         led.set_low();
   |             ^^^^^^^

    Finished dev [unoptimized + debuginfo] target(s) in 43.36s
```

# JTAG

openOCDで接続しておきます。

```
$ openocd -f discovery.cfg 
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 2000 kHz
adapter_nsrst_delay: 100
none separate
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : clock speed 1800 kHz
Info : STLINK v2 JTAG v29 API v2 SWIM v18 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 3.250593
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints

```

# 実行

OpenOCDを接続した端末とは別の端末から実行します。`arm-none-eabi-gdb`がないと怒られます。

```
$ cargo run --example gpio_hal_blinky
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `arm-none-eabi-gdb -q -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/gpio_hal_blinky`
error: could not execute process `arm-none-eabi-gdb -q -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/gpio_hal_blinky` (never executed)

Caused by:
  No such file or directory (os error 2)
```

## 修正

`.cargo/config`を修正します。デフォルトでは`arm-none-eabi-gdb`を使用するようになっていますが、Ubuntu18.04は`gdb-multiarch`に変更になっているので修正します。

```
- runner = 'arm-none-eabi-gdb -q -x openocd.gdb'
+ runner = 'gdb-multiarch -q -x openocd.gdb'
```

## 再実行

```
$ cargo run --example gpio_hal_blinky
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `gdb-multiarch -q -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/gpio_hal_blinky`
Reading symbols from target/thumbv7em-none-eabihf/debug/examples/gpio_hal_blinky...done.
0x00000000 in ?? ()
Breakpoint 1 at 0x80041d4: file /home/yoshi/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.10/src/lib.rs, line 555.
Function "UserHardFault" not defined.
Breakpoint 2 at 0x80036a2: file /home/yoshi/.cargo/registry/src/github.com-1ecc6299db9ec823/panic-semihosting-0.5.3/src/lib.rs, line 78.
semihosting is enabled
Loading section .vector_table, size 0x194 lma 0x8000000
Loading section .text, size 0x414e lma 0x8000194
Loading section .rodata, size 0x10b8 lma 0x80042f0
Start address 0x8004178, load size 21402
Transfer rate: 2 KB/sec, 5350 bytes/write.
Note: automatically using hardware breakpoints for read-only addresses.
```

LEDチカチカが無事できました。

# 感想

nucleo-f401reクレートは他のクレートをインポートしているだけみたいです。ボード依存のクレートといったところでしょうか。チップ依存(チップ=stm32f401)のクレートとしてstm32f4xx-halクレートを使用しています。参考に上げたサイト（[Rustで組み込みプログラミングの第一歩、LチカとHello Worldを試してみた](https://dev.classmethod.jp/hardware/rust-embedded-helloworld/)）ではボード依存に相当するクレートは使用せず、チップ依存に相当するクレート(stm32f1xx-hal)を使用しています。

記事には書いてないですが、stableでコンパイルできるようになったのは嬉しいけど、クレートの依存関係って意外とめんどくさいかったり、rustcのバージョンが上がるとnightly必要なかったり、使っていないオプション`-Z`だったり、まだ過渡的な時期だから仕方ないのでしょうかね。