#[app] 属性と RTIC アプリケーション
app 属性の要件
すべての RTIC アプリケーションは app 属性(#[app(..)])を使用します。この属性は、RTIC アプリケーションを含む mod アイテムにのみ適用されます。
app 属性には、値として パス を取る必須の device 引数があります。これは、svd2rust v0.14.x 以降を使用して生成された ペリフェラルアクセスクレート(PAC)を指す完全なパスでなければなりません。
app 属性は適切なエントリポイントに展開されるため、cortex_m_rt::entry 属性の使用を置き換えます。
構造とゼロコスト並行性
RTIC の app は、シングルコアアプリケーション向けの実行可能なシステムモデルであり、init、idle、ハードウェア タスク、ソフトウェア タスクの集合によって操作される local および shared リソースの集合を宣言します。
initはほかのどのタスクよりも前に実行され、localリソースとsharedリソースを返します。- タスク(ハードウェア、ソフトウェアの両方)は、それぞれに関連付けられた静的優先度に基づいてプリエンプティブに実行されます。
- ハードウェアタスクは、対応するハードウェア割り込みに束縛されます。
- ソフトウェアタスクは、非同期エグゼキュータの集合によってスケジュールされます。各ソフトウェアタスク優先度ごとに 1 つのエグゼキュータがあります。
idleは最も低い優先度を持ち、バックグラウンド処理に使用したり、何らかのイベントによって起床されるまでシステムをスリープさせたりできます。
コンパイル時に、タスク/リソースモデルは Stack Resource Policy(SRP)のもとで解析され、次の優れた特性を持つ実行コードが生成されます。
- 単一の共有スタック上で、競合のないリソースアクセスとデッドロックのない実行が保証されます。
- ハードウェアタスクのスケジューリングはハードウェアによって直接行われます。
- ソフトウェアタスクのスケジューリングは、アプリケーション向けに自動生成された async エグゼキュータによって行われます。
総じて、生成されるコードは手書きの実装と比較して追加のオーバーヘッドを生じさせないため、Rust の用語で言えば、RTIC は並行性に対するゼロコスト抽象化を提供します。
優先度
RTIC における優先度は、#[task] 属性に渡される priority = N(N は正の数)引数を使用して指定します。すべての #[task] は優先度を持つことができます。タスクの優先度が指定されていない場合は、デフォルト値の 0 に設定されます。
RTIC の優先度は、値が大きいほど重要であるという方式に従います。たとえば、優先度 2 のタスクは優先度 1 のタスクをプリエンプトします。
RTIC アプリケーションの例
RTIC の雰囲気をつかんでもらうために、次の例にはよく使われる機能が含まれています。 以降のセクションでは、各機能を詳しく見ていきます。
//! examples/common.rs
#![no_main]
#![no_std]
#![deny(warnings)]
#![deny(unsafe_code)]
#![deny(missing_docs)]
use panic_semihosting as _;
#[rtic::app(device = lm3s6965, dispatchers = [UART0, UART1])]
mod app {
use cortex_m_semihosting::{debug, hprintln};
#[shared]
struct Shared {}
#[local]
struct Local {
local_to_foo: i64,
local_to_bar: i64,
local_to_idle: i64,
}
// `#[init]` cannot access locals from the `#[local]` struct as they are initialized here.
#[init]
fn init(_: init::Context) -> (Shared, Local) {
foo::spawn().unwrap();
bar::spawn().unwrap();
(
Shared {},
// initial values for the `#[local]` resources
Local {
local_to_foo: 0,
local_to_bar: 0,
local_to_idle: 0,
},
)
}
// `local_to_idle` can only be accessed from this context
#[idle(local = [local_to_idle])]
fn idle(cx: idle::Context) -> ! {
let local_to_idle = cx.local.local_to_idle;
*local_to_idle += 1;
hprintln!("idle: local_to_idle = {}", local_to_idle);
debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
// error: no `local_to_foo` field in `idle::LocalResources`
// _cx.local.local_to_foo += 1;
// error: no `local_to_bar` field in `idle::LocalResources`
// _cx.local.local_to_bar += 1;
loop {
cortex_m::asm::nop();
}
}
// `local_to_foo` can only be accessed from this context
#[task(local = [local_to_foo], priority = 1)]
async fn foo(cx: foo::Context) {
let local_to_foo = cx.local.local_to_foo;
*local_to_foo += 1;
// error: no `local_to_bar` field in `foo::LocalResources`
// cx.local.local_to_bar += 1;
hprintln!("foo: local_to_foo = {}", local_to_foo);
}
// `local_to_bar` can only be accessed from this context
#[task(local = [local_to_bar], priority = 1)]
async fn bar(cx: bar::Context) {
let local_to_bar = cx.local.local_to_bar;
*local_to_bar += 1;
// error: no `local_to_foo` field in `bar::LocalResources`
// cx.local.local_to_foo += 1;
hprintln!("bar: local_to_bar = {}", local_to_bar);
}
}