Rustでマイコン（QEMU編）

# OverView
Rustのマイコンを理解しようとした[この記事](https://qiita.com/motii8128/items/0253a0eb065bf1aa13d8)でQEMUをinstallしていたけど触れていなかったので追記。

今回もRust公式Docsにそってやったことを述べていきます。

# What is QEMU?
マイコンだとかコンピュータの挙動をソフトウェア的に再現できるみたいです。
エミュレーションとか呼ばれている気がします。(知識不足かも)

# Environment
Ubuntu22.04で動かしました。Cargoはv1.70.0。
とりあえずのinstallだけ
```
sudo apt install qemu-system-arm gdb-multiarch
```
    
# Usage
## Create project
Cortex-M3の[LM3S6965](https://www.ti.com/product/LM3S6965)用にプログラムします。
とりまgenerate
```
cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
```
プロジェクト名は**app**にしました。（gitとか他にも方法はあります）

## About program
```rust:src/main.rs 
#![no_std]
#![no_main]

// pick a panicking behavior
use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics
// use panic_abort as _; // requires nightly
// use panic_itm as _; // logs messages over ITM; requires ITM support
// use panic_semihosting as _; // logs messages to the host stderr; requires a debugger

use cortex_m::asm;
use cortex_m_rt::entry;

#[entry]
fn main() -> ! {
    asm::nop(); // To not have main optimize to abort in release mode, remove when you add code

    loop {
        // your code goes here
    }
}
```
`#![no_std]`
Rustの組み込み開発では標準crateであるstdではなく***core***というcrateにリンクするそうです

`#![no_main]`
先述した通りstdのない環境でmainインターフェースを扱うためにはnightlyというものが必要らしい。

`panic_halt`
プログラムにpanicが起こったときの挙動を定義しているらしい

`#[entry]`
標準のmainインターフェースを使用しないためエントリーポイントを示す必要があります。

プログラムを止めたくないからもろもろ書いてるぞ。（適当でごめんなさい）

## Set config
ターゲットに合わせた設定をしていく
```.cargo/config.toml
[build]
# Pick ONE of these default compilation targets
# target = "thumbv6m-none-eabi"        # Cortex-M0 and Cortex-M0+
target = "thumbv7m-none-eabi"        # Cortex-M3
# target = "thumbv7em-none-eabi"       # Cortex-M4 and Cortex-M7 (no FPU)
# target = "thumbv7em-none-eabihf"     # Cortex-M4F and Cortex-M7F (with FPU)
# target = "thumbv8m.base-none-eabi"   # Cortex-M23
# target = "thumbv8m.main-none-eabi"   # Cortex-M33 (no FPU)
# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU)
```

[こちら](https://lipoyang.hatenablog.com/entry/20170909/p1)を参考に...

## Build
ターゲットに合わせて
```
cargo build --target thumbv7m-none-eabi
```
とするのですがどうやらこのターゲットはデフォルトになっているらしいので
```
cargo build
```
だけでもOK

## Check
どうやら`cargo-binutils`をつかえばネイティブじゃないバイナリを確認できそう

最適化されたバイナリを確認するために`--release`を入れる
```
cargo size --bin app --release -- -A
```

以下の表示を確認することができた
```
app  :
section              size        addr
.vector_table        1024         0x0
.text                 668       0x400
.rodata                 0       0x69c
.data                   0  0x20000000
.bss                    0  0x20000000
.uninit                 0  0x20000000
.debug_loc            335         0x0
.debug_abbrev        1442         0x0
.debug_info          9313         0x0
.debug_aranges        688         0x0
.debug_ranges        1504         0x0
.debug_str          13855         0x0
.debug_pubnames      4870         0x0
.debug_pubtypes      1635         0x0
.ARM.attributes        50         0x0
.debug_frame         1448         0x0
.debug_line          6519         0x0
.comment               19         0x0
Total               43370
```

なおdebug情報などメタデータは正確に反映されているわけではなさそうなので
常にcargo-sizeしてねとのこと

バイナリをディスアセンブルできます。（まんま）
```
cargo objdump -- --disassemble-all
```

# Let's QEMU
hello exampleを使用します
```
cargo build --example hello
```

出力されたバイナリは`target/thumbv7m-none-eabi/debug/examples/hello`にあります

またQEMU上でバイナリを叩くには以下のコマンドを用います。
```
qemu-system-arm \
      -cpu cortex-m3 \
      -machine lm3s6965evb \
      -nographic \
      -semihosting-config enable=on,target=native \
      -kernel target/thumbv7m-none-eabi/debug/examples/hello
```

出力を確認できました。
```
Timer with period zero, disabling
Hello, world!
```

## Debug
以下のコマンドでQEMUをdebugモードで起動することができます。
```
qemu-system-arm \
      -cpu cortex-m3 \
      -machine lm3s6965evb \
      -nographic \
      -semihosting-config enable=on,target=native \
      -gdb tcp::3333 \
      -S \
      -kernel target/thumbv7m-none-eabi/debug/examples/hello
```
先程から追加された2行について
`-gdb tcp::3333`
QEMUがTCPポート(3333番)でデバッガーの接続を待ちます
`-S`
デバッガーを起動する前にプログラムが終わってしまうことを防ぎます。実際「hello, world!」とでていませんね。

別のターミナルでGDBを起動します。
```
gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello
```
そして接続してみるとこのような表示がでるはず
```
(gdb)target remote:3333
Remote debugging using :3333
cortex_m_rt::Reset () at src/lib.rs:497
497	pub unsafe extern "C" fn Reset() -> ! {
```
どうやらプロセスは停止していてReset関数を指していますね
これは最終的にmain関数を呼び出すことができて以下のようにスキップします
```
(gdb) break main
Breakpoint 1 at 0x484: file examples/hello.rs, line 11.
(gdb) continue
Continuing.

Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello.rs:11
11	#[entry]
```
先に進んでいきます
```
(gdb) next
[Inferior 1 (process 1) exited normally]
```
「プロセスが正常に終了したよ」と表示されましたね
ここでもう一度QEMU側のターミナルを覗いてみると
```
Hello, world!
```
出力されてますね

それではGDBも停止させましょう
```
(gdb)quit //ただの'q'でもokです
```

# おわりに
QEMUを使ってboardなしでdebugしてみました。家での作業が捗りそうです。
今後もRustでマイコンを学んでいく予定です。