RustのRTFM (Real Time For the Masses)を試してみる①

はじめに

組込みRust界の伝説japaric氏が実装しているReal Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollersを試してみます。

github.com

RTFM自体は、Real-time for the masses, step 1: Programming API and static priority SRP kernel primitives.という論文が大本のようです。

ドキュメントもしっかり整備されています。見習わないといけないですね。

japaric.github.io

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関数は、アプリケーションとして実行される最初の関数です。 この関数は、割り込み禁止状態で実行します。

coredeviceという変数があり、この変数を通して、Cortex-Mとペリフェラルにアクセスできます。

        // Cortex-M peripherals
        let _core: rtfm::Peripherals = core;

        // Device specific peripherals
        let _device: lm3s6965::Peripherals = device;

appinitアトリビュートあたりがマクロ展開されて定義されるのでしょう。