OverView
Rustのマイコンを理解しようとしたこの記事で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用にプログラムします。
とりまgenerate
cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
プロジェクト名はappにしました。(gitとか他にも方法はあります)
About program
#![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
ターゲットに合わせた設定をしていく
[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)
こちらを参考に...
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でマイコンを学んでいく予定です。