Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ハードウェア

ここまでで、ツール群と開発プロセスにはある程度慣れてきたはずです。 このセクションでは実際のハードウェアに切り替えます。プロセス自体は ほぼ同じです。では、始めましょう。

ハードウェアを把握する

始める前に、ターゲットデバイスのいくつかの特性を特定する必要があります。 これらはプロジェクトの設定に使用します。

  • ARM コア。例: Cortex-M3。

  • ARM コアに FPU は含まれていますか。Cortex-M4F および Cortex-M7F コアには含まれています。

  • ターゲットデバイスにはどれだけのフラッシュメモリと RAM がありますか。例: フラッシュ 256 KiB、RAM 32 KiB。

  • フラッシュメモリと RAM はアドレス空間のどこにマップされていますか。例: RAM は一般にアドレス 0x2000_0000 に配置されています。

この情報は、デバイスのデータシートまたはリファレンスマニュアルで確認できます。

このセクションでは、リファレンスハードウェアである STM32F3DISCOVERY を 使用します。このボードには STM32F303VCT6 マイクロコントローラーが搭載 されています。このマイクロコントローラーの仕様は次のとおりです。

  • 単精度 FPU を含む Cortex-M4F コア

  • アドレス 0x0800_0000 に配置された 256 KiB のフラッシュ。

  • アドレス 0x2000_0000 に配置された 40 KiB の RAM。(別の RAM 領域も ありますが、簡単のためここでは無視します)。

設定

新しいテンプレートインスタンスを使って、最初から始めます。 cargo-generate を使わずにこれを行う方法を思い出したい場合は、 previous section on 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.toml でデフォルトのコンパイルターゲットを 設定することです。

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(FPU なし)
target = "thumbv7em-none-eabihf" # Cortex-M4F および Cortex-M7F(FPU あり)

Cortex-M4F コアに対応しているため、thumbv7em-none-eabihf を使用します。

注記: 前の章で見たとおり、すべてのターゲットをインストールする必要が あり、これは新しいターゲットです。したがって、このターゲット向けに rustup target add thumbv7em-none-eabihf というインストール処理を 実行するのを忘れないでください。

2 つ目の手順は、メモリ領域の情報を memory.x ファイルに入力することです。

$ cat memory.x
/* STM32F303VCT6 用リンカスクリプト */
MEMORY
{
  /* 注 1 K = 1 KiBi = 1024 バイト */
  FLASH : ORIGIN = 0x08000000, LENGTH = 256K
  RAM : ORIGIN = 0x20000000, LENGTH = 40K
}

注記: 何らかの理由で、特定のビルドターゲットを最初にビルドした後に memory.x ファイルを変更した場合は、cargo build の前に cargo clean を実行してください。cargo build では memory.x の 更新が追跡されないことがあるためです。

再び hello の例から始めますが、まず小さな変更が必要です。

examples/hello.rs では、debug::exit() の呼び出しがコメントアウト されているか、削除されていることを確認してください。これは QEMU で 実行するときにのみ使用します。

#[entry]
fn main() -> ! {
    hprintln!("Hello, world!").unwrap();

    // QEMU を終了する
    // 注記: これはハードウェア上で実行しないでください。OpenOCD の状態を破損する可能性があります
    // debug::exit(debug::EXIT_SUCCESS);

    loop {}
}

これで cargo build を使ってプログラムをクロスコンパイルし、 以前行ったように cargo-binutils を使ってバイナリを調べられます。 役に立つことに、ほとんどすべての Cortex-M CPU は同じ方法でブートするため、 チップを動作させるために必要なあらゆる処理は cortex-m-rt クレートが 引き受けてくれます。

cargo build --example hello

デバッグ

デバッグの見た目は少し異なります。実際、最初の手順はターゲットデバイスに よって異なる場合があります。このセクションでは、STM32F3DISCOVERY 上で 動作するプログラムをデバッグするのに必要な手順を示します。これは参照用の 情報です。デバイス固有のデバッグ情報については the Debugonomicon を 参照してください。

前と同様にリモートデバッグを行い、クライアントは GDB プロセスになります。 ただし今回は、サーバーが OpenOCD です。

verify セクションで行ったように、discovery ボードをノート PC / PC に 接続し、ST-LINK ヘッダーが実装されていることを確認してください。

端末で openocd を実行して、discovery ボード上の ST-LINK に接続します。 このコマンドはテンプレートのルートから実行してください。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]

注記 verify セクションで discovery ボードが古いリビジョンであると 分かった場合は、この時点で openocd.cfg ファイルを変更して interface/stlink-v2.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/hello

注記: 前と同様に、インストールの章でどれをインストールしたかによっては、 gdb-multiarch ではなく別のバージョンの gdb が必要になる場合があります。 これは arm-none-eabi-gdb や単に gdb である可能性もあります。

次に、ポート 3333 で TCP 接続を待っている 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.

これでプログラムはロードされました。このプログラムは semihosting を 使用するため、semihosting の呼び出しを行う前に、OpenOCD に semihosting を有効にするよう伝える必要があります。monitor コマンドを 使って OpenOCD にコマンドを送信できます。

(gdb) monitor arm semihosting enable
semihosting is enabled

monitor 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]

注記 上の continue コマンドを実行したあと、ブレークポイントで停止する代わりに GDB が端末をブロックしてしまう場合は、memory.x ファイル内のメモリ領域情報が お使いのデバイス向けに正しく設定されているか(開始アドレス 長さの 両方)を再確認するとよいでしょう。

stepmain 関数の中に入ります。

(gdb) step
halted: PC: 0x08000496
hello::__cortex_m_rt_main () at examples/hello.rs:13
13          hprintln!("Hello, world!").unwrap();

next でプログラムを進めると、ほかにもいくつか出力がありますが、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: 0x080004bc

このメッセージが表示されるのは、プログラムが 19 行目で定義された無限ループ loop {} に入ろうとする直前の 1 回だけです

これで quit コマンドを使って GDB を終了できます。

(gdb) quit
A debugging session is active.

        Inferior 1 [Remote target] will be detached.

Quit anyway? (y or n)

ここからのデバッグにはもう少し手順が必要になるので、それらの手順を openocd.gdb という 1 つの GDB スクリプトにまとめてあります。このファイルは cargo generate の段階で作成されており、変更を加えなくても動作するはずです。中を見てみましょう:

cat openocd.gdb
target extended-remote :3333

# デマングルされたシンボルを表示
set print asm-demangle on

# 未処理の例外、ハードフォルト、パニックを検出
break DefaultHandler
break HardFault
break rust_begin_unwind

monitor arm semihosting enable

load

# プロセスを開始するが、ただちにプロセッサを停止する
stepi

<gdb> -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello を実行すると、GDB がただちに OpenOCD に接続し、semihosting を有効にして、プログラムをロードし、プロセスを開始します。

あるいは、<gdb> -x openocd.gdb をカスタムランナーにすると、 cargo run でプログラムをビルドし、さらに GDB セッションも開始できるようになります。このランナーは .cargo/config.toml に含まれていますが、コメントアウトされています。

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)