割り込みコントローラ
SH7780では割り込みの受付時にINTEVTレジスタ(メモリ割り付けレジスタ)を読むことでどの要因で割り込みが発生したのか特定できるようになっている。割り込みコントローラ(ハードウェア)は、タイマーやDMACなどから割り込みの要求が入るとINTEVTにその要求に割り当てられた数字を書き込む。割り込みハンドラ(プログラム)はINTEVTレジスタを読み込み、スケジューリングやIRQの転送などの処理へ分岐していく。
割り込みの要因は四種類に大別できる。NMI、IRQ、IRL、内蔵モジュールである。NMIはNon Maskable Interruptで、マスクできない、つまり、プロセッサがどのような状態にあっても入ってくる割り込みである。IRQはInterrupt Requestで、一般に周辺機器を制御するための割り込みである。SH7780には8チャネルのIRQ端子があり、それぞれ独立して割り込みの状態を設定できる。IRLはIRQと同じように周辺機器を制御するための割り込みなのだが、割り込みをチャネルではなくレベル(あるいはチャネルの状態のパターン)で分類する。IRLを使うと効果的な場面はよくわからない。内蔵モジュールの割り込みは、タイマー、DMAC、シリアルインタフェースなどからの割り込みである。
割り込みには優先順位をつけることが出来る。レベル16が最高で、レベル1が最低となっている。レベル0では割り込みが受け付けられない。割り込みの優先順位は同時刻に発生した割り込みを開発者の思うとおりに制御するために必要である。たとえば、タイマー割り込みの優先度が高ければ、スケジューリングの遅延を避けることが出来、結果として、実時間性を向上させることが出来る。
割り込み要因の考慮
先述のように割り込みの要因は四つある。NMIはカーネルで処理する。したがって、ユーザー空間に転送しなければならない割り込みは、大別すると外部割り込み(IRQもしくはIRL)と内部割り込み(内蔵モジュール)の二種類になる。L4のAPIでは割り込みは一つの空間にまとめられている。IA32やARMでは割り込みの空間は一種類なので良かったが、二種類の割り込みの空間があるSH4Aではカーネルはどのように処理したらよいのだろうか。
カーネル内部ではIRQを管理する配列がある。IRQそれぞれに対し、一要素を割り当てる。SH4Aでは12ビットの例外コードによって、外部割り込み、内部割り込みに関わらず、要因を区別する。これらの割り込みは合計すると68種類にもなる。例外コードの内、下位5ビットは使われていない。つまり、ゼロだ。下位5ビットを算術シフト演算で取り除き、オフセット値を引けば、0からの通し番号に変換できる。しかし、例外コードをよく見てみると、所々番号が飛んでいるところがある。なんともいやらしいなぁ。
外部割り込みだけなら話は簡単だ。しかし、デバイスドライバの処理に必要なDMACやシリアルポートの割り込みは内部割り込みだ。
とりあえずの解法として、飛び石の番号を無視して配列を確保してしまうことにした。例外コードは0×200から始まり0xFE0までなので、0×20毎に例外コードがあるとすれば112要素必要になる。実際には例外コードは68種類しかないので54要素分は不要になる。各要素は12バイトのデータ構造なので、648バイトは無駄になる。
割り込みコントローラの設定
- 端子モードはIRQを使用する(IRLモードは使用しない。効果的な使い方を知らないので。)
- IRQ0からIRQ7はオフ(ユーザプロセスからの要求に従ってオン)
- TMU0はオン
SH4Aは二種類のタイマーを提供する。TMUは独立したチャンネルが六つあり、割り込みも独立して入る。CMTにもチャンネルはあるが、割り込みは共通している。特に理由はなくTMU0をスレッドディスパッチ用タイマーとして選択する。
カーネル内の割り込みハンドラ
例外処理ルーチンの先頭アドレスは各割り込みで共通である。なので、割り込み要因を識別するためにINTEVTをオフセットとして分岐する。
- IRQをIPCに変換する (IRQ0からIRQ7)
- スケジューリングする (TMU)
カーネル割り込みハンドラの中でやらなければいけないこと
1.IRQを配送する
IRQはユーザプロセスで処理されるので、そのためにカーネルは受け取った割り込み要求をユーザプロセスに配送しなければならない。例:simplesoc_handle_irq_rescheduleを呼び出す。(rescheduleの意味は何?)
2.スケジューリングする
handle_timer_interruptを呼び出す。
カーネル割り込みハンドラの中でやらないこと
シリアルインタフェースの割り込み処理など、その他の割り込み。