isig_tim()がE_CTXを返す

timer.cの中でisig_tim()がE_CTXを返すというバグが見つかりました。

調べてみたところ、このエラーはisig_tim冒頭のCEHCK_INTCTX_UNLマクロで検出されていました。このマクロは

  • 非タスクコンテキスト
  • CPUアンロック

の条件が同時に満たされない場合にエラーを報告します。さて、問題のisig_tim()関数を呼び出しているのはtimer.cのtimer_hander()関数であり、この関数はタイマー割り込み処理用の割り込みハンドラです。その性質上、

  • 必ず割込み状態で呼ばれ
  • CPUロックが外れているはず

です。ところが、E_CTXが報告されたときにブレークを掛けてみると、IMASKが割込み禁止状態になっていました。CPUロック状態です。なぜCPUロック状態なのに割り込みが発生しているのでしょうか。トレースしていって理由がわかりました。
問題が発生したのはcpu_config.hのt_lock_cpu()。まさにCPUをロックしている場所です。

Inline void
t_lock_cpu()
{
#ifdef UNMANAGED_INT
    unsigned int imask;
    Asm( "cli %0;" :"=d"(imask) );
    Asm( "sti %0;" : :"d"(0xC01f | (imask & UNMANAGED_INT) ) );
#else
    Asm( "sti %0;" : :"d"(0xC01F) );
#endif
}

こんかい、管理外割り込みを使っていないため、else節側が使われています。このelse節では、sti命令を使ってIMASKを書き換えて割込みを禁止しています。問題の割り込みはsti命令の直後に起きていました。割り込みが起きる流れは次のようなものです。

  1. sti命令が実行される
  2. 割込みが認識され、実行番地が変わる
  3. パイプライン中に残っているsti命令の実行結果が反映され、IMASKが割込み禁止状態になる

割り込みを禁止にするためのcli命令は、以上のようなパイプラインによる問題が起きないような工夫がされています。しかしながら、stiは割り込みを許可するための命令ですので以上のような問題が起きたのでした。暫定的に上のコードにダミーのcliを入れたところ問題が出なくなりました

Inline void
t_lock_cpu()
{
#ifdef UNMANAGED_INT
    unsigned int imask;
    Asm( "cli %0;" :"=d"(imask) );
    Asm( "sti %0;" : :"d"(0xC01f | (imask & UNMANAGED_INT) ) );
#else
    Asm( "cli r0;" : : : "r0" );
    Asm( "sti %0;" : :"d"(0xC01F) );
#endif
}

実験はVisualDSP++環境で行っていますが、gnu環境で行っても同じ結果になると考えています。sti命令はCPU依存部とシステム依存部で数箇所使われています。修正が必要な場所は多くないと考えていますが、きちんとした検討は、まだこれからです。