armは割り込みは実際使うのは1本でそれをプログラマーブルインターラプトコントローラ(PIC)で複数にします。
armv7(?)でPICが標準化されていますが、それ以前はSOC個別のPICでした。
armv5tなRT1310の割り込みは以下のようになっています。
CPU側の割り込みのON/OFFとPICの割り込みマスクを使って制御します。
mipsでは5本(+タイマー)あってそのどれかにPICをつけたり、一つの割り込みで複数受け付けて5つでまにあわせるケースもありました。
デバイスがCPUに割り込みをあげると、決められた物理アドレスの命令に実行が強制的に移ります。その後ごにょごにょして、仮想アドレスのハンドラを呼び出して処理を行い、処理が終ったら強制的に移ったところに戻ります。
FreeBSDは割り込みは全アーキテクチャ共通で、実際の割り込みでの処理と、スレッドでの処理の二つに分けることができます。リアルタイムで処理しなくていいも物はスレッドで処理するようになっています。実際はどちらかしか使っていないケースが多いです。割り込みはCPU資源が大きいのでそうしているようです。このような構成からFreeBSDでは多重割り込みはサポートしてないと思います。
NetBSDではアーキテクチャ別に割り込み処理がありarmでは二つには分けていなくて、一つの処理として実行されるようです。また優先度の設定があるようです。この優先度によって多重割り込みを許可しているようです。
まず割り込みコントローラーのデバイスは最初にattachするように調整します。
デバイス側は、以下のように割り込みルーチンを登録します。
intr_establish(sc->sc_irq, IPL_SCHED, IST_LEVEL, sc->irq_handler, NULL);
この関数の実態は、sys/arch/arm/pic/pic.cにあります。
カーネルのビルドオプションでARM_INTR_IMPLでヘッダーファイルを指定して、その中でARM_IRQ_HANDLERで大本のハンドラの関数を指定します。
デバイスのハンドラの呼び出しはpic_dispatch()を使ったもの(apple,cortex,imx)と
pic_do_pending_ints()を使ったもの(apple,broadcom,cortex,gemini,imx,marvell,sunxi,ti)がありました。これより前はpicはなく独自実装だったようです。
ブート後に割り込みが有効になるのはarch/evbarm/autoconf.cのcpu_configure()の最後です。
NetBSDのコードがいけてないと思うのは以下のようなところです。
void
pic_do_pending_ints(register_t psw, int newipl, void *frame)
{
struct cpu_info * const ci = curcpu();
if (__predict_false(newipl == IPL_HIGH)) {
KASSERTMSG(ci->ci_cpl == IPL_HIGH, "cpl %d", ci->ci_cpl);
return;
}
#if defined(__HAVE_PIC_PENDING_INTRS)
pic_do_pending_ints()の中で#if defined(__HAVE_PIC_PENDING_INTRS)とか、なにをしたいのか、よく分からないです。
この割り込み処理は必要以上に複雑になってよくないと思います。最近の高速化したCPUにおいては出来るだけ割り込み処理は軽くするのがOSのセオリーなのに時代に逆行した実装にも見えます。
Comcertoの対応を実装していて、このSOCのPICには割り込み優先度の機能があり、ソフトでやる必要はなさそうです。
理想としてはプレーンな実装をまずつくり、必要なら優先度の処理がオプションで使えるようにするのでしょうか。
多重割り込みが必要になるのは、そもそもシステム設計の時点で問題があります。
BSD小僧さんも調べられていました。
uart割り込みとかは複雑なので、割り込み処理はタイマーで確認するのが分かりやすいです。
タイマー割り込みがちゃんと動くと、ブーとログの時間の表示が更新されるようになります。適当な値でタイマーを動かして確認し、値は後から直すのがいいです。
もちろんプライオリティを無視してハンドラを実装する事もできます。ターゲットによってはその方が良いケースもありそうです。 動く事もあるのですが、おかしな動きをすることもあるようなので、他と合わせたほうが無難なようです。
多重割り込みはプライオリティを元に処理しているようです。以下のようなことをやってると思われます。
他の割り込みがなければそのままハンドラは処理をして終了
割り込み中に割り込みが発生したが、プライオリティの低い割り込みの場合 -> 割り込みは処理をせず抜けて、元の処理を続行して完了後、多重割り込みした割り込み処理を行う。
複数段多重割り込みが発生した場合は、プライオリティで整理。
割り込み中にプライオリティの高い割り込みが発生 -> そのまま処理して終ったら元の割り込みにもどる。
この仕組みがいけてないと思うのは以下のようなことです。
- 割り込みはキャッシュを破壊するので、キャッシュを元にパフォーマンスを向上させてる近代のCPUに不向き
- ハードウエアでもプライオリティ処理があることがおおいので、わざわざソフトでやることはない
- 割り込みのようなクリティカルな処理は複雑にすべきではない
- 参照実装がないし、かなりばらばらに実装されてちゃんと動くのか疑問
RT1310のポートをしていて、多重割り込み禁止で実装したところsoftintrとuartの受信割り込みまわりで、暴走してしまい正常に動かなかったので、標準の実装方法にしました。