RustのRTFM (Real Time For the Masses)を試してみる①
はじめに
組込みRust界の伝説japaric
氏が実装しているReal Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollersを試してみます。
RTFM自体は、Real-time for the masses, step 1: Programming API and static priority SRP kernel primitives.という論文が大本のようです。
ドキュメントもしっかり整備されています。見習わないといけないですね。
RTFM brief
ドキュメントの機能一覧を抜粋します。
- タスク管理
- タスク間メッセージ
- タイマキュー
- プリエンプティブマルチタスキング
- 効率的でデータ競合のないメモリ共有
- デッドロックしない実行
- 最小限のスケジューリングオーバーヘッド
- 極めて効率的なメモリ使用
最小限のリアルタイムコアシステム、といった印象を受けます。
initサンプル
qemu-system-arm
があれば、動かせるサンプルが多く用意されています。
QEMUをターゲットに、少しサンプルを動かしてみましょう。
$ git clone https://github.com/japaric/cortex-m-rtfm.git $ cd cortex-m-rtfm $ cargo run --example init Finished dev [unoptimized + debuginfo] target(s) in 0.07s Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel target/thumbv7m-none-eabi/debug/examples/init` init # サンプルで出力した文字列
単純に文字を出力するだけのサンプルのようです。 アプリケーションのソースコードを見てみましょう。
ソースコード
#![deny(unsafe_code)] #![deny(warnings)] #![no_main] #![no_std] extern crate panic_semihosting; use cortex_m_semihosting::{debug, hprintln}; use rtfm::app; #[app(device = lm3s6965)] const APP: () = { #[init] fn init() { static mut X: u32 = 0; // Cortex-M peripherals let _core: rtfm::Peripherals = core; // Device specific peripherals let _device: lm3s6965::Peripherals = device; // Safe access to local `static mut` variable let _x: &'static mut u32 = X; hprintln!("init").unwrap(); debug::exit(debug::EXIT_SUCCESS); } };
まず、良いなと思ったのは、最初に#![deny(unsafe_code)]
しています。
組込みRust普及活動をする上で、unsafe
だらけのコードになるのでは?という質問を良く受けますが、アプリケーションレベルでは、unsafeを許さないこのアトリビュートを設定できます。
後は、それだけこのRTFMフレームワークが上手にunsafeを閉じ込めた実装になっている、というのもこの1行からわかります。
見慣れないのは、#[app(device = lm3s6965)]
アトリビュートと、#[init]
アトリビュートです。
#[app(..)]
アトリビュートは、device
引数を使って、svd2rust
で生成されたperipheral access crate (PAC)を指すパスを指定します。
#[app(..)]
アトリビュートは、const itemに適用しなければなりません。これは、nightlyを使わないようにするための制限です。
app
アトリビュートは、init
アトリビュートでマーキングされた初期化関数があることを想定します。
init
アトリビュートは、[unsafe] fn()
をシグネチャに持つ関数につけることができます。
init
関数は、アプリケーションとして実行される最初の関数です。
この関数は、割り込み禁止状態で実行します。
core
とdevice
という変数があり、この変数を通して、Cortex-Mとペリフェラルにアクセスできます。
// Cortex-M peripherals let _core: rtfm::Peripherals = core; // Device specific peripherals let _device: lm3s6965::Peripherals = device;
app
かinit
アトリビュートあたりがマクロ展開されて定義されるのでしょう。