割り込み待ち
実行可能なタスクが無い場合、dispatch()のなかでタスクがスケジュールされるまで待つことになります。この場合、タスクのスケジュールの引き金を引くのは割り込みなので、結局dispatch()関数の中で割り込みを待つことになります。
タスク割り当て
昨日紹介したタスク割り当て部を再掲します。schedtskがヌルのとき、実行可能なタスクは無いため割り込み待ちを行います。dispatcher: p0.H = _schedtsk; p0.L = _schedtsk; p1.H = _runtsk; p1.L = _runtsk; r0 = [p0]; [p1] = r0; // schedtsk を runtskに cc = r0; if !cc jump dispatcher_1; // runtskが無ければ割り込み待ちに。 p0 = r0; // p0はruntsk sp = [p0+TCB_sp]; // タスクスタック復帰 p1 = [p0+TCB_pc]; // 実行再開番地復帰 jump (p1); // 実行再開番地へ飛ぶ
割り込み待ち
割り込み待ちはIVG14順位で行うため、この部分ではソフトウェア割り込みを行っています。割り込みから帰ってきたらreqflgを確認してディスパッチ要求が出ていないか調べます。要求が出ていれば、reqflgをクリアして再びタスク割り当てを実行します。要求が無ければ再び割り込みを待ちます。
dispatcher_1: csync; raise 14; // 割り込み待ち状態に移行 csync; p1.L = _reqflg; p1.H = _reqflg; r0 = [p1]; // reqflag取得 cc = r0; if !cc jump dispatcher_1; // reqflgが0なら割り込み待ち r0 = 0; [p1] = r0; // reqflgをクリア jump dispatcher; dispatcher.end:
割り込み待ち本体
割り込み待ちの本体はIVG14ハンドラの中にあります。この特殊なハンドラはdispatch()関数によって予約されており、ユーザーが使うことは出来ません。ユーザーがこのイベント用のハンドラを登録しても無視されます。わざわざタスク・コンテキスト*1からIVG14に遷移するのは、実際に発生する割り込みの中でdispatch()関数が再帰的に呼ばれることを阻止するためです。割り込みディスパッチャは割り込みのネスト状況を監視していますのでIVG14で発生した割り込みは決してdispatch()を呼びません。その反面、スタックをイベントスタックに設定するのはIVG14の役割になります。
さて、ここからBLACKfinの世界です。このハンドラは割り込み待ちのために用意されていますが、ハンドラ内部では割り込み待ちを行いません。BLACKfinは割り込みを安全に待つための仕掛けを持っておらず、代わりにウェイク・アップ信号を待つidle()命令を使います。idle()命令は割り込み信号も受け取るので、実行は割り込み禁止状態で行います。ウェイクアップ信号は割り込みを発生することのできるデバイスであれば、SIC_IWRレジスタで指定することによって信号を発することができます。したがって、どのデバイスがウェイクアップ信号を発生するかはユーザーが事前に指定しておかなければなりません。
いったんウェイクアップ信号が発生したらidle命令から抜け出し、次に割り込みを発生させます。割り込みから戻ってきたらハンドラを終了します。
この割り込みはdispatch()の中でしか発生しません。タスク・スタックは使用されていないため、割り込まれたときのスタックは破壊したまま復帰させません。
ivg14handler: sp.l = event_stack; // イベントスタックポインタを設定 sp.h = event_stack; csync; idle; // イベント待ち csync; [--sp] = reti; r1 = 0xffff(z); // CPU アンロック sti r1; csync; // 本当の割り込みはここで発生する。 r1 = 0xc01f(z); // CPU ロック sti r1; reti=[sp++]; ivg14handler.end:
*1:dispatch()は必ずタスク・コンテキストで呼ばれる