ハードウェア
ここまでで、ツールと開発プロセスにある程度慣れたはずです。このセクションでは、実際のハードウェアに切り替えます。 開発プロセスは、ほとんど同じままです。飛び込みましょう。
ハードウェアを知る
始める前に、プロジェクトの設定に利用するターゲットデバイスのいくつかの特徴を確認する必要があります。
- ARMコア、例えばCortex-M3です。
- そのARMコアはFPUを搭載していますか?Cortex-M4FとCortex-M7Fは、搭載しています。
- ターゲットデバイスに搭載されているフラッシュメモリとRAMの容量はいくらですか? 例えば、フラッシュは256KiBでRAMは32KiBです。
- フラッシュメモリとRAMは、アドレス空間のどこにマッピングされていますか? 例えば、RAMは、通常
0x2000_0000番地に位置します。
これらの情報は、デバイスのデータシートかリファレンスマニュアルに掲載されています。
このセクションでは、私たちのリファレンスハードウェアであるSTM32F3DISCOVERYを使用します。 このボードは、STM32F303VCT6マイクロコントローラを1つ搭載しています。このマイクロコントローラは以下のものを持っています。
- 単精度FPUを含むCortex-M4Fコアが1つ
- 0x0800_0000番地に配置された256KiBのフラッシュメモリ
- 0x2000_0000番地に配置された40KiBのRAM。(別のRAM領域もありますが、説明の簡単化のため、取り扱
設定
テンプレートの新しいインスタンスを使って、スクラッチから書いていきましょう。 cargo-generateを使用しない方法については、前セクションのQEMUを参照して下さい。
$ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
Project Name: app
Creating project called `app`...
Done! New project created /tmp/app
$ cd app第一ステップは、.cargo/configにデフォルトコンパイルターゲットを設定することです。
tail -n5 .cargo/config.toml# 以下のコンパイルターゲットから1つを選びます
# target = "thumbv6m-none-eabi" # Cortex-M0およびCortex-M0+
# target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4およびCortex-M7 (no FPU)
target = "thumbv7em-none-eabihf" # Cortex-M4FおよびCortex-M7F (with FPU)
Cortex-M4Fコアを対象とするものとして、thumbv7em-none-eabihfを使います。
NOTE: As you may remember from the previous chapter, we have to install all targets and this is a new one. So don’t forget to run the installation process rustup target add thumbv7em-none-eabihf for this target.第二ステップは、memory.xファイルにメモリ領域の情報を入力することです。
$ cat memory.x
/* Linker script for the STM32F303VCT6 */
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
FLASH : ORIGIN = 0x08000000, LENGTH = 256K
RAM : ORIGIN = 0x20000000, LENGTH = 40K
}NOTE: If you for some reason changed thememory.xfile after you had made the first build of a specific build target, then docargo cleanbeforecargo build, becausecargo buildmay not track updates ofmemory.x.
We’ll start with the hello example again, but first we have to make a small change.
debug::exit()の呼び出しが、コメントアウトされているか削除されていることを確認して下さい。 これは、QEMUで実行する時のみ、使用します。
#[entry]
fn main() -> ! {
hprintln!("Hello, world!").unwrap();
// QEMUを終了する
// 注記、ハードウェア上で実行しないで下さい。OpenOCDの状態を破壊する可能性があります。
// debug::exit(debug::EXIT_SUCCESS);
loop {}
}
これまでやってきた通り、cargo buildでプログラムをクロスコンパイルし、 cargo-binutilsでバイナリを調べることができます。 cortex-m-rtクレートは、チップを動作させるために必要な、全てのおまじないを処理します。 便利なことに、ほとんど全てのCortex-M CPUが同じ方法で起動します。
cargo build --example helloデバッグ
デバッグ方法は少し違います。実際、最初のステップは、ターゲットデバイスによって異なります。 このセクションでは、STM32F3DISCOVERY上で実行しているプログラムをデバッグするために必要となる手順を説明します。 これは、参考の役目を果たします。デバイス固有のデバッグ情報は、 the Debugonomiconを参照して下さい。
以前と同様に、リモートデバッグを行います。クライアントがGDBプロセスであることも同様です。 しかし、今回、サーバはOpenOCDになります。
インストールの確認セクションでやったように、ノートPCまたはPCをdiscoveryボードに接続し、 ST-LINKヘッダが設定されていることを確認して下さい。
discoveryボードのST-LINKに接続するために、端末でopenocdを実行して下さい。 このコマンドは、テンプレートプロジェクトのルートディレクトリから実行して下さい。 openocdは、どのインタフェースファイルとターゲットファイルを使うか、が記述されているopenocd.cfgファイルを見つけます。
cat openocd.cfg# STM32F3DISCOVERY開発ボード用のOpenOCD設定サンプル
# 持っているハードウェアのリビジョンに応じて、これらのインタフェースのうち、1つを選んで下さい。
# 常に、1つのインタフェースがコメントアウトされているべきです。
# リビジョンC (新しいリビジョン)
source [find interface/stlink.cfg]
# リビジョンAとB(古いリビジョン)
# source [find interface/stlink-v2.cfg]
source [find target/stm32f3x.cfg]
注記 インストールの確認セクションで、古いバージョンのdiscoveryボードを持っていることが判明している場合、interface/stlink-v2.cfgを使うようにopenocd.cfgファイルを修正する必要があります。
$ openocd
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 2.913879
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints別の端末で、GDBを実行します。こちらも、テンプレートプロジェクトのルートディレクトから実行して下さい。
gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/helloNOTE: like before you might need another version of gdb instead of gdb-multiarch depending on which one you installed in the installation chapter. This could also be arm-none-eabi-gdb or just gdb.
次に、TCP 3333ポートで接続待ちしているOpenOCDに、GDBを接続します。
(gdb) target remote :3333
Remote debugging using :3333
0x00000000 in ?? ()それでは、loadコマンドを使って、マイクロコントローラにプログラムを書き込んで下さい。
(gdb) load
Loading section .vector_table, size 0x400 lma 0x8000000
Loading section .text, size 0x1518 lma 0x8000400
Loading section .rodata, size 0x414 lma 0x8001918
Start address 0x08000400, load size 7468
Transfer rate: 13 KB/sec, 2489 bytes/write.プログラムがロードされました。このプログラムはセミホスティングを使用します。そこで、 セミホスティングを呼び出して何かを行う前に、OpenOCDにセミホスティングを有効にするように、 指示する必要があります。
(gdb) monitor arm semihosting enable
semihosting is enabledmonitor helpコマンドを実行することで、全てのOpenOCDコマンドを見ることができます。以前のように、ブレイクポイントとcontinueコマンドを使用することで、mainまでスキップすることができます。
(gdb) break main
Breakpoint 1 at 0x8000490: file examples/hello.rs, line 11.
Note: automatically using hardware breakpoints for read-only addresses.
(gdb) continue
Continuing.
Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello.rs:11
11 #[entry]NOTE If GDB blocks the terminal instead of hitting the breakpoint after you issue thecontinuecommand above, you might want to double check that the memory region information in thememory.xfile is correctly set up for your device (both the starts and lengths).
Step into the main function with step.
(gdb) step
halted: PC: 0x08000496
hello::__cortex_m_rt_main () at examples/hello.rs:13
13 hprintln!("Hello, world!").unwrap();翻訳が古くなっています この時点で、OpenOCDコンソールに、他のものと入り混じって「Hello, world!」と表示されるはずです。
$ openocd
(..)
Info : halted: PC: 0x08000502
Hello, world!
Info : halted: PC: 0x080004ac
Info : halted: PC: 0x080004ae
Info : halted: PC: 0x080004b0
Info : halted: PC: 0x080004b4
Info : halted: PC: 0x080004b8
Info : halted: PC: 0x080004bcThe message is only displayed once as the program is about to enter the infinite loop defined in line 19: loop {}
You can now exit GDB using the quit command.
(gdb) quit
A debugging session is active.
Inferior 1 [Remote target] will be detached.
Quit anyway? (y or n)Debugging now requires a few more steps so we have packed all those steps into a single GDB script named openocd.gdb. The file was created during the cargo generate step, and should work without any modifications. Let’s have a peek:
cat openocd.gdbtarget extended-remote :3333
# print demangled symbols
set print asm-demangle on
# detect unhandled exceptions, hard faults and panics
break DefaultHandler
break HardFault
break rust_begin_unwind
monitor arm semihosting enable
load
# start the process but immediately halt the processor
stepi<gdb> -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/helloを実行することで、GDBはすぐにOpenOCDに接続し、 セミホスティングを有効化し、プログラムをロードした上で、プロセスを開始します。
別の方法として、<gdb> -x openocd.gdbをカスタムランナーにして、cargo runでプログラムをビルドし、 さらにGDBセッションを開始することもできます。このランナーは、.cargo/configに含まれていますが、 コメントアウトされています。
head -n10 .cargo/config.toml[target.thumbv7m-none-eabi]
# ここのコメントアウトを外すと、`cargo run`はQEMUでプログラムを実行します
# runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# 3つの選択肢のうち、1つのコメントアウトを外すと、`cargo run`はGDBセッションを開始します。
# どの選択肢を使うか、は対象システムによって異なります。
runner = "arm-none-eabi-gdb -x openocd.gdb"
# runner = "gdb-multiarch -x openocd.gdb"
# runner = "gdb -x openocd.gdb"
$ cargo run --example hello
(..)
Loading section .vector_table, size 0x400 lma 0x8000000
Loading section .text, size 0x1e70 lma 0x8000400
Loading section .rodata, size 0x61c lma 0x8002270
Start address 0x800144e, load size 10380
Transfer rate: 17 KB/sec, 3460 bytes/write.
(gdb)