Wio Terminal で probe-run / defmt

はじめに

『基礎から学ぶ 組込みRust』では追加機材が不要な cargo hf2 を使ってファームウェアを書き込み、UART で文字を出力しました。 組込み Rust ではその他にも便利なツールがあります。 今回はそのうちの、probe-run と defmt を Wio Terminal で使ってみます (両方 knurling-rs の成果物です) 。

github.com

Runs embedded programs just like native ones

github.com

defmt ("de format", short for "deferred formatting") is a highly efficient logging framework that targets resource-constrained devices, like microcontrollers.

注意

本記事の内容を試すには、JLink か DAPLink かのデバッグアダプタを Wio Terminal に接続している必要があります。 Seeeduino XIAO を用いた DAPLink デバッグアダプタの環境構築は、基礎から学ぶ 組込みRust のサポートサイトに手順を掲載しています。

github.com

f:id:tomo-wait-for-it-yuki:20210503140523p:plain
JLink 接続

サンプルプロジェクトと実行方法

下に Wio Terminal で probe-run / defmt を試すためのサンプルプロジェクトを用意しました。

github.com

事前準備

probe-run をインストールします。

$ cargo install probe-run

Linux の場合は、libudev と libusb もインストールします。

sudo apt install libudev libusb

実行方法

Wio Terminal をデバッグアダプタと接続している状態で、プロジェクトを clone して、cargo run するだけです。

$ git clone https://github.com/tomoyuki-nakabayashi/wio-terminal-probe-run.git
$ cd wio-terminal-probe-run
$ cargo run

実行結果は次のようになります。

     Running `probe-run --chip ATSAMD51P19A --speed 100 target/thumbv7em-none-eabihf/debug/wio-terminal-probe-run`
  (HOST) INFO  flashing program (12.92 KiB)
  (HOST) INFO  success!
────────────────────────────────────────────────────────────────────────────────
       0 INFO  Hello, world!
└─ wio_terminal_probe_run::__cortex_m_rt_main @ src/main.rs:11
  (HOST) WARN  program has used at least 195528 bytes of stack space, data segments may be corrupted due to stack overflow
stack backtrace:
   0: lib::inline::__bkpt
        at ./asm/inline.rs:13
   1: __bkpt
        at ./asm/lib.rs:49
   2: wio_terminal_probe_run::exit
        at src/lib.rs:29
   3: wio_terminal_probe_run::__cortex_m_rt_main
        at src/main.rs:12
   4: main
        at src/main.rs:10
   5: ResetTrampoline
        at $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs:547
   6: Reset
        at $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs:550
   7: __DEFMT_MARKER_TIMESTAMP_WAS_DEFINED
   8: Reset
        at $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs:497
error: the stack appears to be corrupted beyond this point

実行時のログは、タイムスタンプ (サンプル実装は1ずつインクリメントされるただのカウンタ)、ログレベル、ソースコード上の位置付きで出力されます。

       0 INFO  Hello, world!
└─ wio_terminal_probe_run::__cortex_m_rt_main @ src/main.rs:11

bkpt 命令を呼び出したり、プログラムが panic するとバックトレースを出力します。

stack backtrace:
   0: lib::inline::__bkpt
        at ./asm/inline.rs:13
   1: __bkpt
        at ./asm/lib.rs:49
   2: wio_terminal_probe_run::exit
        at src/lib.rs:29
   3: wio_terminal_probe_run::__cortex_m_rt_main
        at src/main.rs:12
   4: main
        at src/main.rs:10
   5: ResetTrampoline
        at $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs:547
   6: Reset
        at $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs:550
   7: __DEFMT_MARKER_TIMESTAMP_WAS_DEFINED
   8: Reset
        at $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs:497
error: the stack appears to be corrupted beyond this point

少し解説

Cargo.toml

panic 時に probe-run 経由でバックトレースを出力する panic-probe を使うことができます。

panic-probe = "0.2.0"

defmt は、Cargo.toml に次の設定を追加します。features はこういうものだ、と思って下さい。

defmt = "0.2.0"
defmt-rtt = "0.2.0"

[features]
default = [
  "defmt-default",
]

defmt-default = []
defmt-trace = []
defmt-debug = []
defmt-info = []
defmt-warn = []
defmt-error = []

.cargo/config

probe-run と defmt を使うためにリンクオプションを2つ追加します。

[target.thumbv7em-none-eabihf]
rustflags = [
  "-C", "link-arg=-Tlink.x",
  "-C", "link-arg=--nmagic", # 追加
  "-C", "link-arg=-Tdefmt.x", # 追加
]
# runner を probe run に
runner = "probe-run --chip ATSAMD51P19A --speed 100"

src/lib.rs

アプリケーションから使えるように、lib.rs に部品をまとめておきます。

#![no_std]

use core::sync::atomic::{AtomicUsize, Ordering};

use defmt_rtt as _; // global logger
use panic_probe as _;
use wio_terminal as _;

use panic_probe as _;

// same panicking *behavior* as `panic-probe` but doesn't print a panic message
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
#[defmt::panic_handler]
fn panic() -> ! {
    cortex_m::asm::udf()
}

// defmt のタイムスタンプを実装します
// タイマを使えば、起動からの時間を表示したりできます
static COUNT: AtomicUsize = AtomicUsize::new(0);
defmt::timestamp!("{=usize}", {
    // NOTE(no-CAS) `timestamps` runs with interrupts disabled
    let n = COUNT.load(Ordering::Relaxed);
    COUNT.store(n + 1, Ordering::Relaxed);
    n
});

/// Terminates the application and makes `probe-run` exit with exit-code = 0
pub fn exit() -> ! {
    loop {
        cortex_m::asm::bkpt();
    }
}

src/main.rs

後は使うだけです。

#![no_std]
#![no_main]

use wio_terminal_probe_run;

use wio_terminal as wio;
use wio::entry;

#[entry]
fn main() -> ! {
    defmt::info!("Hello, world!");
    wio_terminal_probe_run::exit()
}

制限

次の issue に挙げられている通り、現在は speed を 100 khz にしないとうまく動きません。 そのため、少し動作が遅く感じます。

github.com

おまけ

probe-run は probe-rs を活かして実装されています。 probe-rs ではオプションの機能として、FTDI のデバッグアダプタをプローブする機能が実装されています。 そのうち、probe-run でも使えるようになるかもしれませんね。