ターゲットアーキテクチャ
Cortex-M デバイス
RTIC は現在すべての Cortex-M デバイスを対象にできますが、ユーザーが認識しておくべき
重要なアーキテクチャ上の違いがいくつかあります。具体的には、RTIC で使用される
ハードウェアによる優先度上限サポートに非常に適した Base Priority Mask Register (BASEPRI)
が ARMv6-M および ARMv8-M-base アーキテクチャには存在しないため、RTIC は代わりに
ソースマスキングを使用せざるを得ません。lock の各実装とその長所・短所の詳しい解説については、
src/export.rs 内の lock の実装を参照してください。
これらの違いはクリティカルセクションの実現方法に影響しますが、ARMv6-M/ARMv8-M-base では共有リソースを持つタスクを例外ハンドラにバインドできない点を除けば、機能は同じであるはずです。 これは、それらをハードウェアでマスクできないためです。
以下の表 1 は、Cortex-M プロセッサの一覧と、それぞれが採用するクリティカルセクションの 種類を示しています。
表 1: プロセッサアーキテクチャ別クリティカルセクション実装
| Processor | Architecture | 優先度上限 | ソースマスキング |
|---|---|---|---|
| Cortex-M0 | ARMv6-M | ✓ | |
| Cortex-M0+ | ARMv6-M | ✓ | |
| Cortex-M3 | ARMv7-M | ✓ | |
| Cortex-M4 | ARMv7-M | ✓ | |
| Cortex-M7 | ARMv7-M | ✓ | |
| Cortex-M23 | ARMv8-M-base | ✓ | |
| Cortex-M33 | ARMv8-M-main | ✓ |
優先度上限
これについては、この書籍の リソース ページで説明しています。
ソースマスキング
Nested Vectored Interrupt Controller (NVIC) において優先度上限を直接設定できる
BASEPRI レジスタがないため、RTIC は代わりに割り込みの無効化(マスキング)に依存する
必要があります。以下の図 1 を考えてみましょう。ここでは、A は B より高い優先度を持ちながら、
B とリソースを共有する 2 つのタスク A と B を示しています。
図 1: 共有リソースとソースマスキング
┌────────────────────────────────────────────────────────────────┐
│ │
│ │
3 │ Pending Preempts │
2 │ ↑- - -A- - - - -↓A─────────► │
1 │ B───────────────────► - - - - B────────► │
0 │Idle┌─────► Resumes ┌────────► │
├────┴────────────────────────────────────────────┴──────────────┤
│ │
└────────────────────────────────────────────────────────────────┴──► Time
t1 t2 t3 t4
時刻 t1 で、タスク B は共有リソースをロックするために、B とリソースを共有する任意の
タスクと同じかそれより低い優先度を持つ他のすべてのタスクを選択的に無効化します(NVIC を使用)。
この結果、BASEPRI のアプローチを模倣する仮想的な優先度上限が作られます。タスク A は、
タスク B とリソースを共有するそのようなタスクの 1 つです。時刻 t2 で、タスク A は
タスク B によって spawn されるか、あるいは割り込み条件によってペンディング状態になりますが、
優先度がより高いにもかかわらず、まだタスク B をプリエンプトしません。これは、タスク A が
無効化されているため、NVIC がその開始を防いでいるからです。時刻 t3 で、タスク B は
NVIC でタスク群を再度有効化することでロックを解放します。タスク A はペンディング状態であり、
かつタスク B より高い優先度を持つため、直ちにタスク B をプリエンプトし、データ競合の危険なく
共有リソースを使用できます。時刻 t4 で、タスク A は完了し、実行コンテキストを B に返します。
ソースマスキングは NVIC の使用に依存するため、HardFault、SVCall、PendSV、SysTick などのコア例外ソースは、他のタスクとデータを共有できません。
RISC-V デバイス
現在のすべての RISC-V バックエンドは、優先度上限を用いる Cortex-M デバイスと 同様の方式で動作します。したがって、この書籍の リソース ページがよい参考資料になります。ただし、これらのバックエンドの一部は完全な ハードウェア実装ではなく、物理的な割り込みコントローラをソフトウェアでエミュレート します。そのため、これらのバックエンドはハードウェアタスクを実装せず、必要なのは ソフトウェアタスクのみです。さらに、これらのターゲットではソフトウェアタスクの数は 利用可能な物理割り込みソースの数によって制限されません。
以下の表 2 では、利用可能な RISC-V バックエンドを比較しています。
表 2: プロセッサアーキテクチャ別クリティカルセクション実装
| バックエンド | 対応ターゲット | バックエンド固有の設定 | ハードウェアタスク | ソフトウェアタスク | タスク数が HW によって制限されるか |
|---|---|---|---|---|---|
riscv-esp32c3-backend | ESP32-C3 のみ | ✓ | ✓ | ✓ | |
riscv-mecall-backend | 任意の RISC-V デバイス | ✓ | |||
riscv-clint-backend | CLINT ペリフェラル搭載デバイス | ✓ | ✓ |
riscv-mecall-backend
RTIC がコンパイル時にそれらを生成するため、#[app] 属性でディスパッチャの一覧を
指定する必要はありません。優先度レベルは 0(idle タスク用)から 255 までです。
riscv-clint-backend
RTIC がコンパイル時にそれらを生成するため、#[app] 属性で dispatchers の一覧を
指定する必要はありません。優先度レベルは 0(idle タスク用)から 255 までです。
RTIC がアプリケーションを実行している HART を識別するために使用する ID 番号を
認識できるよう、#[app] 属性には backend 固有の設定を 必ず 含める必要があります。
たとえば、e310x チップでは、最小限のアプリケーションを次のように設定します。
#![allow(unused)]
fn main() {
#[rtic::app(device = e310x, backend = H0)]
mod app {
// ここにアプリケーションを記述します
}
}
このようにして、RTIC は常に HART H0 を参照します。