割り込みレイテンシとジッタの計測

「割り込みだから即応答するはず」。この思い込みは、割り込み禁止区間や優先度設計の不備で簡単に裏切られます。リアルタイムシステムの設計で本当に必要なのは、割り込みレイテンシを数字で語ることです。結論から言うと、割り込みレイテンシは「GPIO+オシロスコープで真値」「DWTで常時監視」の2本立てで測り、必ず最悪条件の最大値で評価します。本記事では、この結論に至る過程として、レイテンシ・ジッタという指標の整理から、GPIOトグル+オシロスコープとDWTサイクルカウンタという2つの計測方法、最大値の取り方までを解説します。

用語の整理:平均ではなく最大値で語る

  • 割り込みレイテンシ:割り込み要因(interrupt source)の発生から、ハンドラ先頭(最初の命令)が実行されるまでの時間。ハンドラの実行時間は含みません。割り込み禁止(PRIMASK/BASEPRIなど)や、同じか高い優先度の割り込みの実行中は、要因が発生してもハンドラの開始が遅れます。
  • ハンドラ実行時間:ハンドラ先頭から復帰までの時間。
  • ジッタ:レイテンシのばらつき(最大値と最小値の差、またはヒストグラム上の分布として表せます)。周期処理の品質(モータ制御や波形生成の精度)を直接左右します。

リアルタイム性の評価で意味を持つのは平均値ではなく最大値です。1万回に1回でも締切を超えれば製品としては不良です。「最悪条件で・長時間・最大値を記録する」が計測の基本姿勢になります。

レイテンシを延ばす主因は多くの場合ソフトウェアにある

プロセッサ自体の応答(ハードウェアのサイクル数)は、メモリがゼロウェイトであるなどの理想条件では小さく、ほぼ一定です。実機でレイテンシが延びる主因は、多くの場合ソフトウェア側にあります。ただしメモリ階層やバス競合、フラッシュのウェイト、割り込み入力の同期といったハードウェア要因や、システム構成(メモリ・バス・低消費電力設定)でも増えます。

  • 割り込み禁止区間:排他のために割り込みを止めている時間は、そのままレイテンシの上乗せになります。
  • 優先度の高い・同じ割り込みの先行実行:優先度設計とネスティングの問題です(「割り込み優先度の設定とネスティング」参照)。
  • メモリのウェイト:低速フラッシュからのベクタ・ハンドラ読み出しにウェイトが入る構成では、その分が応答に乗ります。

つまりレイテンシの計測は、自分のコードの割り込み禁止区間と優先度設計の品質を見える化する作業でもあります(直前に挙げたハードウェア要因も同時に効いてきます)。

計測法1:GPIOトグル+オシロスコープ(最も確実な方法)

要因発生のタイミングが信号として観測できる場合(外部トリガ、通信エッジなど)、その信号と「ハンドラ先頭で立てるGPIO」をオシロスコープの2チャネル(2ch)で見れば、時間差がそのままレイテンシです。配線からコアまで系全体を通した真値が見えること、ファームウェアに入れる仕掛けがGPIOトグル1行で済むことが強みです。注意点は次の2点です。①トグルはベンダ提供のライブラリ関数(HAL:ハードウェア抽象化レイヤ)を介さずレジスタ直書きにして、計測自体のオーバーヘッドを最小にすることです。②波形の最大値を捉えるために、オシロスコープの無限残光(パーシスタンス)表示や最大値計測機能を使うことです。なお、この方法で見えるのはGPIO書き込み命令からバス経路を経てピンが動くまでの遅延を含んだ値で、CPUの例外エントリ単体のサイクル数とは一致しません。ただし実システムでの観測値としては最も確実です。

計測法2:DWTサイクルカウンタ(ソフトウェアだけでクロック精度)

この方法が有効なのは、ソフトウェアが要因を起こせる場合です(外部信号のエッジ起因のレイテンシは、計測法1かタイマの入力キャプチャで測ります)。多くの組み込みマイコン(Cortex-M3/M4/M7/M33など)のコアには、デバッグ機構DWTの中にCPUクロックを数えるサイクルカウンタ(CYCCNT)があり、ソフトウェアだけでクロック分解能の時間計測ができます。ただし、コアや製品によってはDWTやCYCCNTが実装されていません(とくにCortex-M0/M0+系など)。使うには有効化が必要です。なお、デバイスや設定によってはDWTレジスタが書けないことがあります。動作しない場合は、デバイスのデバッグ/トレース設定とセキュリティ設定を確認してください。

/* DWTサイクルカウンタの有効化(DWT搭載コア) */
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; /* トレース機構を有効化 */
DWT->CYCCNT      = 0u;
DWT->CTRL       |= DWT_CTRL_CYCCNTENA_Msk;      /* カウント開始 */
/* レイテンシの記録:要因を起こす側で時刻を取り、ハンドラ先頭で差分 */
volatile uint32_t t_trigger;
volatile uint32_t lat_max = 0u;
volatile uint32_t lat_min = UINT32_MAX;
void trigger_event(void)
{
    t_trigger = DWT->CYCCNT;
    __DSB(); __ISB();                  /* 命令とメモリアクセスの順序を固定する(必要に応じて) */
    start_hw_event();                  /* 割り込み要因を発生させる */
}
void ExtInt_IRQHandler(void)
{
    uint32_t lat = DWT->CYCCNT - t_trigger;   /* 符号なしの差分 */
    if (lat > lat_max) { lat_max = lat; }
    if (lat < lat_min) { lat_min = lat; }
    clear_irq_flag();
    /* 本来の処理 */
}

差分計算は符号なし演算です。カウンタが一周をまたいでも、1周以内の区間なら正しい値が得られます(C言語仕様上、符号なし整数のラップアラウンドは定義済みの動作です。詳細は「整数オーバーフローと未定義動作」参照)。なお、CYCCNTは32ビットカウンタのため、最大で2³²カウント分の時間を計測できます。たとえば100 MHz動作なら、2³² / 10⁸ ≒ 42.95秒(約43秒)以内の区間が対象です。サイクル数を時間に直すにはCPUクロックで割ります。ただし、この方式が有効なのは割り込み要因をソフトウェアが起こす場合だけです。外部信号のエッジ(外部割り込みコントローラ。マイコンによってIRQ、INTCなどと呼ばれます)や周辺回路のイベント(いずれも割り込み要因の一種です)で割り込みが入る場合、t_triggerは真の要因発生時刻になりません。外部エッジ起因の真のレイテンシは、タイマの入力キャプチャで要因時刻をハードウェア的にラッチするか、計測法1のGPIOトグルとオシロスコープで観測します。なお、DWTのCYCCNTはコア実装やデバイスの設定によって利用できない場合があります(CYCCNTが非実装、デバッグ機能の制限、低消費電力モード中の停止など)。まずDWT_CTRLのNOCYCCNTビットが0であること(サイクルカウンタが搭載されていること)を確認し、DEMCR.TRCENA=1とDWT_CTRL.CYCCNTENA=1を設定してください。利用できない場合は、空いているタイマを最速で回して同じことをするか、タイマの入力キャプチャ機能で要因信号の時刻を取る方法で代替します。

ジッタの取り方:最悪条件で長く回す

上のコードのようにmin/maxを常時更新する方式なら、計測を入れたまま長時間運転できます。さらに分布が見たい場合は、レイテンシを数段階のビンに分けて回数を数える簡易ヒストグラムにします。重要なのは負荷条件です。アイドル状態という好条件で測った最大値は実際よりも小さく見えるため、実用上の意味がありません。通信のバースト、全チャネル動作、ログ出力、エラー処理経路など、割り込み禁止が最も多く走る最悪条件を意図的に作って測ります。計測結果は、CPU使用率(「RTOSのCPU使用率を測る」参照)と並ぶリリース判定の定量指標として記録に残すとよいでしょう。

数字が悪かったら:改善の定石

  • 割り込み禁止区間を測って削る:禁止から許可までの区間にも同じDWT計測を仕込み、長い区間から削減します。禁止のかわりに優先度マスクで「必要な優先度だけ止める」方法も有効です。
  • ハンドラを軽くする:重い処理はフラグやキューで主処理へ委譲します。
  • 優先度を見直す:締切の厳しい割り込みに高い優先度を与え、ネスティングを正しく設計します。

まとめ

割り込みレイテンシは「GPIO+オシロスコープで真値」「DWTで常時監視」の2本立てで測り、必ず最悪条件の最大値で評価します。計測の仕掛けを一度部品化しておけば、機能追加のたびに応答性の劣化を数字で検出でき、リアルタイム設計が経験と勘から計測にもとづく工学に変わります。

組み込みソフトの世界 トップへ戻る