exit_and_startの呼び出し状態

ものすごくトリッキーなバグが隠れていたのが見つかりました。実アプリケーションへの影響は非常に小さいです。

exit_and_startsはTOPPERS/JSP 1.4.2のconfig.txtによればCPUロック状態から呼び出さなければなりません。しかし、場合によってはアンロック状態で呼び出される事がわかりました。
kernel_start()は、システム起動時にタスクに先立って一度だけ実行される関数です。ここからexit_and_startが呼ばれると、ディスパッチャ内部でレディ状態のタスクが選び出されて最初のタスクの実行が始まります。このとき、タスクの実行開始に使われるactivate_rは、状態を強制的にCPUアンロック状態に変更します。その結果、最初のタスクの実行開始と共にこの問題は解消されます。「実アプリケーションへの影響は非常に小さい」とは、そういうことです*1
問題の根本原因は、IMASKのリセット状態を仮定していたことにあります。単純にVisualDSP++のICEからダウンロードする場合には問題になりませんが、gdbのリセット問題に対応するためのコードを経由すると、IMASKが初期化されない場合があるとわかりました。
IMASKが初期化されない問題については、別になんらかの調査が必要ですがとりあえずはリセット直後の初期化時にIMASKの値を強制することでこの問題を回避します。

もう少し詳細

さて、本来ならIMASKの値はstartup.Sの中で、割込みベクトルがすべて設定された後に正しく設定されているはずです。その部分のコードは以下のとおりです。

    LOADLABEL( r0, task_level)      // IVG15はタスクに遷移するためのエントリー
    [p0++p1] = r0;

    LOADLABEL( p2, user)            // ダミーの戻り番地を設定
    reti = p2;
    csync;
    raise    15;                    // IVG15を生起する。割り込みは遅延発生する。

    r0 = 0xC01F(z);                 // IVG15を受付可能に(CPUロック状態)
    sti r0;
    csync;
    rti;                            // ユーザーモードへ遷移する

下から3行目のsti命令でIMASKが設定されるはずです。その直前で割込みを生起していますが、この割り込みは2重の歯止めによって禁止されているため、sti命令の実行を阻害しません。2重の歯止めとは:

  1. リセット直後のIMASKの値が0x001Fであるため、IVG15が発生しない
  2. リセット直後のIPENDの値が0x0012であるため、IVG15が発生しない

ところが、今回ソフトウェアリセットによって、IMASKの値がクリアされず、IPENDの値が0x8000であったため、上の二つの歯止めが外れていました。後者は特に驚きで、調べたところBlackfin内部のBoot ROMの動作によってこの状態に遷移していました。
sti実行時のイベント状態はこのようにソフトウェアリセットによってIVG15状態になっています。IVG15状態においては、ペリフェラルからのIVG15イベントはペンディング状態になりますが、raise命令だけはセルフネストが有効なため、raise 15が直ちに実行されているのでした。

*1:ただし、カーネル管理外割り込みをUNMANAGED_INTマクロでを使って利用している場合は、管理外割り込みが許可の状態でシステムが走り始める