5-4. RTFM (Real Time For the Masses)

RTFM for ARM Cortex-Mは、リアルタイムシステムを構築するための並行処理フレームワークです。Real-time for the masses, step 1: Programming API and static priority SRP kernel primitives.というリアルタイムシステム構築の論文を、Rustで実装しています。RTOSほどの機能はありませんが、小規模なリアルタイムシステム構築に向いています。

機能一覧

  • 並行処理の単位tとしてタスクが定義されています。タスクはイベントトリガ、もしくは、アプリケーションからspawnすることができます。
  • タスク間でメッセージ送受信が可能です。
  • ソフトウェアタスクをスケジュールするタイマキューがあります。周期タスクを実装するために利用できます。
  • 優先度付きのタスク、および、プリエンプティブマルチタスキングを提供します。
  • 優先度に基づいたクリティカルセクション制御により、効率的でデータ競合のないメモリ共有が可能です。
  • コンパイル時にデッドロックが発生しないことが保証されます。
  • スケジューラは最小限のソフトウェアで実装されており、スケジューリングオーバーヘッドは最小です。
  • 全てのタスクが1つのコールスタックを共有しており、極めて効率的にメモリを利用します。
  • 全Cortex-Mデバイスをサポートしています。

アプリケーション実装方法

cortex-m-rtクレートとPeripheral Access Crate (PAC) に、初期化、タスク、優先度、共有リソースの概念が追加されます。

#[app(device = lm3s6965)]
const APP: () = {
    #[init]
    fn init() {
        // Cortex-M peripherals
        let _core: rtfm::Peripherals = core;

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

        // Pends the UART0 interrupt but its handler won't run until *after*
        // `init` returns because interrupts are disabled
        rtfm::pend(Interrupt::UART0);

        hprintln!("init").unwrap();
    }

    #[idle]
    fn idle() -> ! {
        // interrupts are enabled again; the `UART0` handler runs at this point

        hprintln!("idle").unwrap();

        rtfm::pend(Interrupt::UART0);

        debug::exit(debug::EXIT_SUCCESS);

        loop {}
    }

    #[interrupt]
    fn UART0() {
        static mut TIMES: u32 = 0;

        // Safe access to local `static mut` variable
        *TIMES += 1;

        hprintln!(
            "UART0 called {} time{}",
            *TIMES,
            if *TIMES > 1 { "s" } else { "" }
        )
        .unwrap();
    }
};

#[app(..)]アトリビュートは、device引数を使って、svd2rustで生成されたPACのパスを指定します。#[init]アトリビュートが指定された関数は、アプリケーションとして実行される最初の関数です。この関数は、割り込み禁止状態で実行します。

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

コラム〜RTFMの実装〜

RTFMソースコードを覗いてみると、その多くが手続きマクロによる静的検査と、コードジェネレータであることがわかります。cortex-m-rtクレートを使用する場合、割り込みとメイン関数間でのデータ共有に制限があります。常に、全ての割り込みを無効化するcortex_m::interrupt::Mutexを使わなければなりません。しかし、全ての割り込みを無効化することは、常に求められる条件ではありません。

例えば、2つの割り込みハンドラがデータを共有する場合、両者の優先度が同じで、プリエンプションが発生しないとすると、ロックは不要です。RTFMでは、ソースコードを静的に解析することで、不要なロックをせずに、共有データにアクセスできるようになっています。このような解析が可能な理由は、appアトリビュート内にアプリケーションの実装を、全て書くためです。

また、RTFMでは、動的な割り込み優先度の変更をサポートしていません。そのため、全ての割り込みハンドラ間の優先度は静的に決定します。これがうまいこと生きており、ある共有データを使用する割り込みハンドラ同士で、最も優先度の高いハンドラはロックを取得しなくても共有データにアクセスできます。

RTFMは、機能性と安全性を両立するアプローチです。しかし、複雑な手続きマクロで実装されているため、自分で機能を追加したり、アプリケーションをデバッグするのは、骨が折れそうです。

more information

RTFMのドキュメントにRTFMの使い方がまとめられています。

低レイヤ強くなりたい組み込みやさんのブログで、RTRMについていくつかエントリを書きました。タスクの利用方法や共有リソースの管理方法について気になった方は、こちらを参照下さい。