割り込みハンドラ設計の鉄則
割り込みハンドラ(ISR:Interrupt Service Routine)は、組み込みシステムのリアルタイム性や信頼性を左右する重要な処理です。一方で、割り込みハンドラ内に多くの処理を実装すると、(1)割り込みレイテンシ増大、(2)他の割り込みハンドラの取りこぼし、(3)タスク起床遅延、(4)共有データ破壊などが起きやすく、システム全体の安定性を損なうおそれがあります。
この記事では、実務で求められる「短く・速く・安全な」割り込みハンドラ設計の基本原則から、タスクへの委譲方法や実装時の注意点までを、設計・実装の観点から解説します。
割り込みハンドラ設計の基本原則
割り込みハンドラの品質は、システム全体のリアルタイム性に直結します。まずは役割と設計の基本原則を理解しましょう。
割り込みハンドラの役割
割り込みハンドラの役割は、ハードウェアで発生したイベントに迅速に応答し、必要最小限の処理だけを実行することです。イベントの受付や状態の保存、割り込み要因のクリアなどにとどめ、時間のかかる処理は後続のタスクへ委譲することが基本です。
また、割り込み応答時間(レイテンシ)を考慮し、割り込みハンドラがCPUを占有する時間を抑えることが重要です。ただし、何ms/μsまで許容できるかは、システムのリアルタイム性や応答時間に関する要件によって異なります。
「短く・速く」が重要な理由
割り込みハンドラの実行時間が長いと、他の割り込み処理やタスクの開始が遅れ、通信や周期処理へ影響を与えます。CPU占有時間を最小限に抑えることは、リアルタイム性を維持するための基本原則です。
そのため、割り込みハンドラではイベントを受け付けるだけにとどめ、複雑な処理は後続のタスクへ委譲します。
タスクへ委譲する設計
たとえば、割り込みハンドラでは受信レジスタを読み出してリングバッファに格納し、タスクへ通知します。タスク側では、受信データの解析やプロトコル処理、ログ出力などを実施します。
リアルタイムOS(RTOS:Real-Time Operating System)では、キューなどでイベントを伝達できますが、割り込みハンドラから呼べるのは割り込みハンドラ対応API(*FromISR系など)に限られ、割り込み優先度にも制約があるため注意が必要です。割り込みハンドラとタスクの責務を分離することで、応答性を保ちながら処理を整理できます。
割り込みハンドラの実装で気を付けるべきポイント
基本原則を理解していても、実装方法を誤るとリアルタイム性や安定性を損ないます。ここでは、実装時に確認したいポイントを紹介します。
実行時間の短い処理だけを書く
割り込みハンドラでは、printf()やmalloc()などの処理は避けるべきです。これらの処理は、実行時間が不定(非決定)になりやすく、非ブロッキング性や再入性が保証されていない場合があるため、割り込み処理のジッタ要因になります。実装によっては内部ロックやメモリ領域の探索などが発生し、処理時間がさらに予測しにくくなることもあります。そのため、最悪実行時間(WCET:Worst-Case Execution Time)の増大を招き、リアルタイム性の確保が困難になります。また、長時間のループや複雑な演算も実行時間が延びる原因となります。
必要な情報だけを取得・保存し、その後の処理はタスクへ委譲することで、割り込み応答時間を短く維持できます。
排他制御の誤用を防ぐ
割り込みコンテキストでは、ブロックする可能性のあるミューテックスは原則使用しません。同期が必要な場合は、RTOSの割り込みハンドラ対応APIやクリティカルセクション、ロックフリー設計など、実行コンテキストに適した同期手段を利用します。
また、共有資源の設計では、優先度逆転やデッドロックが発生する可能性も考慮しておくことが重要です。
呼び出す関数は慎重に選択する
割り込みハンドラから呼び出す関数は、割り込みハンドラ対応APIであることを確認しましょう。
さらに、関数がリエントラントであるか、標準ライブラリがブロッキングしないかも確認し、割り込みコンテキストで安全に利用できる関数だけを使用することが重要です。
共有資源と排他制御の安全な設計
割り込みハンドラでは、タスクと共有するデータや周辺資源を扱うことがよくあります。排他制御を誤るとデータ不整合や予期しない動作につながるため、各手法の役割を理解して使い分けることが重要です。
volatileと排他制御の差異
「volatile」は、コンパイラによる最適化でメモリアクセスが省略・変更されることを抑止するためのものであり、排他制御や原子性を保証するものではありません。単純な読み書きが原子的に実行でき、整合性の要件を満たす場合は、必ずしもクリティカルセクションが必要とは限りません。
一方、インクリメント(++)のような複合操作や、一貫性を保つ必要がある共有データを扱う場合は、クリティカルセクション、アトミック操作、RTOSの同期機構などを利用して適切に保護する必要があります。必要に応じて、C11の_AtomicやCPU命令(LDREX/STREXなど)、RTOSの割り込みハンドラ対応APIを用います。
クリティカルセクションの適切な設計
クリティカルセクション(方式はRTOS/CPU依存)は共有資源を保護する有効な手段ですが、割り込み禁止時間は必要最小限に抑える必要があります。
また、ネスト時の動作やRTOSが提供するクリティカルセクションとの関係も理解し、OSの仕様に沿って利用することが重要です。
共有データの受け渡し方法
割り込みハンドラとタスク間では、リングバッファやFIFOを利用したデータの受け渡しが一般的です。受信データを一時的に蓄積し、タスクで処理することで、割り込みハンドラを短時間で終了できます。
また、オーバーランやアンダーランを防ぐため、バッファ容量やエラー処理もあらかじめ設計しておきましょう。
コンパイラ最適化とメモリアクセス
コンパイラの最適化によるメモリアクセスの並べ替えを抑止するには、コンパイラバリアを利用します。一方、プロセッサの弱いメモリモデルによってCPUがメモリアクセスの順序を変更する場合には、メモリバリアを利用して順序や可視性を保証します。メモリバリアの具体的な命令や実装は、アーキテクチャによって異なります。
特に、対称型マルチプロセッシング(SMP:Symmetric Multi-Processing)環境でCPU間のメモリアクセス順序を保証する場合、キャッシュや投機実行の影響を受ける場合、周辺レジスタへのアクセス順序が重要な場合には、原因に応じて適切なバリアを使用する必要があります。
RTOS環境での割り込みハンドラの設計手法
RTOSでは、割り込みハンドラとタスクが連携して動作します。割り込みハンドラ対応APIや優先度制御を適切に利用することで、高いリアルタイム性を維持できます。
割り込みハンドラ対応APIを利用
RTOSでは、割り込みハンドラからOSのAPIを呼び出す場合、通常のタスク用APIではなく割り込みハンドラ対応APIを利用します。一部のRTOSでは、割り込みハンドラからOSのAPIを呼び出せる割り込み優先度に上限制約があります。この制約を超える優先度の割り込みからOSのAPIを呼び出すと不具合の原因になるため、使用するRTOSの仕様を必ず確認してください。(Cortex-Mのように)数値が小さいほど高優先度の体系では、設定値より高い優先度(=数値が小さい)の割り込みハンドラからは割り込みハンドラ対応APIを呼べません。
また、割り込みハンドラからの通知によって待機中のタスクを即時に実行させる場合は、割り込みハンドラ側でコンテキストスイッチを要求します。たとえば、割り込みハンドラ対応APIが高優先度タスクの起床を示すフラグをtrueにした場合は、割り込みハンドラを抜ける前にコンテキストスイッチを要求する専用APIを呼び、割り込みハンドラ復帰直後に待機中の高優先度タスクへ切り換えます。
割り込み優先度の適切な設計
割り込み優先度はシステム全体の応答性に大きく影響します。
前述の優先度制約があるため、RTOSの仕様とマイコンの優先度体系を理解した上で設計し、設定ミスによる不具合を防ぐ必要があります。
リアルタイム性の評価
設計どおりの性能が得られているかを確認するには、割り込みハンドラの実行時間や割り込み応答時間を測定します。
さらに、安全性が求められるシステムでは最悪実行時間も評価し、十分な余裕があることを確認しておきましょう。
割り込みハンドラ設計の検証方法
割り込みハンドラは、実装後のレビューと性能評価によって品質を確認することが重要です。設計だけでなく、実機での検証も欠かせません。
レビューで確認すべき事項
コードレビューでは、割り込みハンドラ対応APIを使用しているか、実行時間が十分短いか、共有資源へのアクセス方法が適切かを重点的に確認します。
また、長時間処理や必要以上に長いクリティカルセクションがないかも確認しましょう。
デバッグと性能評価の方法
GPIOトグルを利用して、割り込みハンドラの実行時間や割り込み応答時間を測定します。ロジックアナライザやオシロスコープを活用すると、割り込みの発生から処理完了までの時間を波形から確認できます。
さらに、トレース機能を利用して、割り込みネストの有無やタスク起床までの遅延なども確認します。
設計品質を高めるためのチェックリスト
最後に、後処理をタスクへ委譲しているか、共有データへのアクセスは安全か、割り込み禁止時間は必要最小限かを確認しましょう。
これらをレビュー項目として標準化することで、設計品質を維持しやすくなります。


