(H8移植編その2第12回)ethernetドライバのバグ修正

2011/01/13

あなたは 人目のお客様です.

先日のもくもく会 でethernetの送信割り込み対応をやっていたのだが,その続きを家でやっていて, バグをひとつ解決できたのでちょっとそれについて説明しよう.

まず,前回までのソースコードにはethernetドライバに バグがあって,以下の現象が発生する.これは展示会とかでよく発生していて 困っていた.

具体的には,以下の状態で固まる.
Transfer complete

XMODEM receive succeeded.
kzload> run
starting from entry point: 400100
kozos boot succeed!
co
ちなみに以下が正常に起動できた場合.
Transfer complete

XMODEM receive succeeded.
kzload> run
starting from entry point: 400100
kozos boot succeed!
command> MAC: 0002cb031908
なんかたまにうまく起動できないことがあるなーくらいに思っていてまあそのうち なんとかしようくらいだったのだけど,もくもく会で頻発してしまいさすがに ちょっとうざいというか,本来の開発に支障をきたすくらいになっていた.

で,だんだん気がついてきたのだけど,そういえばリセットでなく電源OFF/ONすると 問題なく復旧する.展示会のときはこれでなんとか乗り切っていた (もくもく会のときは,このことはすっかり忘れていた).

さらに,電源ONでなくリセットで発生するようだ.電源ONの初回では発生しない.

さらにさらに,一度発生すると,電源OFF/ONしないかぎりは,何度リセットしても 発生するようだ.

で,リセットで発生しているので,データ領域やBSSの初期化をもう一度見直して みたのだが問題はなさそう.ということでソフトウェア処理にバグがあるのでなく, I/Oまわりの操作にバグがあるのでは?とまず疑った.というのは,リセットボタンを 押すとCPUがリセットされ,さらに起動処理でデータ (データ領域とBSS領域とスタックポインタ)が初期化されるが, I/Oの状態はそのまま残っていて,再度初期化処理が行われることになるからだ.

ここで怪しいのはDRAMとethernetコントローラだ.どちらもリセット時には 状態はそのまま残っていて,再起動により初期化処理が再度行われることになる.

で,まずDRAMを疑った.で,ブートローダーのDRAM初期化処理(dram_init())を 見てみたのだが,たいしたことはやっていなくて問題ありそうに思えない. そもそもDRAMがおかしかったら,はじめからほとんど動作しないように思える. しかしブートメッセージが出力されていて,不完全ながらもプロンプトも出力 されているので,DRAMがおかしいということは考えにくそうだ.

次に疑うのはethernetドライバだ.実際に固まっている位置を考えてみると, 「kozos boot succeed!」というブートメッセージ(main()の中で,OSの起動前に 出力している)は出力されていて,さらに「co」というプロンプトの一部が出力 されている.プロンプトの出力は command タスクが consdev タスクに依頼して, consdev がシリアル送信割り込みを見ながら出力している.なのでシリアル割り込みは 一応動作していて,シリアル送信割り込み待ちの隙間で他のタスクが動作して 固まっているように思える.ここでetherドライバの初期化が走って,その先で 固まってしまっているのでは?と思える.(この意味でもDRAMの初期化がらみで固まって いることは考えにくい.dram_init()はブートローダー側で呼び出しているからだ)

で,ether関連の処理をところどころ #if 0 でくくって蓋をしてみると, etherの初期化を呼び出さないようにするとどうやら固まらなくなるようだ. 先述したように一度現象が発生すると,何度リセットしても繰り返し発生するので, OSを書き換えてロードさせて起動して,を繰り返すことで,どの部分を蓋すれば 発生しなくなるのか,しぼり込んでいくことができる.

で,いろいろしぼり込んだ結果,rtl8019.cの以下の箇所を蓋すると固まらなく なることが判明.

static int port_init()
{
  ...
  *H8_3069F_IER  = 0x20; /* IRQ5割り込み有効化 */

  return 0;
}
このへんでだんだん原因が予測ついてくるのだけど,どうやらethernetコントローラに 割り込みが残っている状態(つまり,割り込み線がアサートされている状態)で リセットがかかると,その後のOSが行うethernetコントローラの初期化で上記 port_init()が呼ばれた際に,割り込みベクタなどの準備ができてない状態で IRQ5の割り込みが開いてしまい,しかもethernetコントローラの割り込みレジスタ (IMR)は有効のまま(上記IERはH8が持つレジスタなのでリセットによって初期化されて いるが,IMRはethernetコントローラが持っているレジスタなので初期化されず, 有効のまま残っている)なので,割り込みが発生してしまい,なんかおかしなことに なってしまっているようだ.たぶん割り込みハンドラを登録していないから割り込みの クリアが行えず,割り込みが上がりっぱなしの無限ループになってしまっているの だろう.

ということは,高負荷などかけてバッファ不足を起こした後のリセットで発生する はずだ.また,リセット後に(この時点ではethernetコントローラがまだ動いている ので)パケット受信させてからOSをロードし起動しても現象発生するはずだ. で,ためしにやってみたらほんとに現象発生した.また,上記のIRQ5の割り込み 有効化をrtl8019_init()の最後(RTL8019のレジスタをひととおり初期化して, IMRで割り込みをdisableにした後)に持っていったら現象発生しなくなった.

ということで説明が長くなったが,修正したソースコードは以下.

修正点はいまさら説明するまでもないけど以下.IRQ5の割り込み有効化を ethernetコントローラの初期化の後に持っていっている.

 static int port_init()
 {
 #if 0 /* DRAMで設定しているので不要 */
   *H8_3069F_P1DDR = 0x1f; /* A0 - A4 */
   *H8_3069F_P3DDR = ...; /* モード5では D8-D15 の設定は不要 */
   *H8_3069F_P6DDR = ...; /* モード5では HWR,RD の設定は不要 */
 #endif
 
 #if 0
   *H8_3069F_P8DDR = 0xe8; /* CS1 */
 #else
   *H8_3069F_P8DDR = 0xec; /* CS1(ether) and CS2(DRAM) */
 #endif
 
 #if 0
   *H8_3069F_P9DDR = ...; /* IERの設定によるので設定不要 */
 #endif
 
   *H8_3069F_ISCR = 0x00; /* lowレベルで割り込み */
   *H8_3069F_IPRA = 0x00;
-  *H8_3069F_IER  = 0x20; /* IRQ5割り込み有効化 */
 
   return 0;
 }
 
 int rtl8019_init(int index, unsigned char macaddr[])
 {
   unsigned char t1;
   unsigned char t2[12];
   int i;
 
   port_init();
         
   t1 = *RTL8019_RP;
   *RTL8019_RP = t1;
 
   mdelay(10);
 
   *NE2000_CR     = NE2000_CR_P0 | NE2000_CR_RD_ABORT | NE2000_CR_STP;
   *NE2000_DCR    = NE2000_DCR_F1 | NE2000_DCR_LS | NE2000_DCR_BOS;
   *NE2000_RBCR0  = 0x00;
   *NE2000_RBCR1  = 0x00;
 #if 0
@@ -250,44 +249,46 @@
   *NE2000_MAR4 = 0x00;
   *NE2000_MAR5 = 0x00;
   *NE2000_MAR6 = 0x00;
   *NE2000_MAR7 = 0x00;
 
 #if 0
   *NE2000_CR   = NE2000_CR_P3 | NE2000_CR_RD_ABORT | NE2000_CR_STP;
   *RTL8019_9346CR  = 0xc0;
   *RTL8019_CONFIG1 = 0x80 | 0x40;
   *RTL8019_CONFIG2 = 0x00;
   *RTL8019_9346CR  = 0x00;
 #endif
 
   *NE2000_CR   = NE2000_CR_P0 | NE2000_CR_RD_ABORT | NE2000_CR_STP;
 #if 0 /* MONだと実機で受信できない */
   *NE2000_RCR  = NE2000_RCR_AM | NE2000_RCR_AB | NE2000_RCR_MON;
 #else
   *NE2000_RCR  = NE2000_RCR_AM | NE2000_RCR_AB | NE2000_RCR_PRO;
 #endif
   *NE2000_CR   = NE2000_CR_P0 | NE2000_CR_RD_ABORT | NE2000_CR_STA;
   *NE2000_TCR  = NE2000_TCR_NORMAL;
   *NE2000_IMR  = 0x00;
+
+  *H8_3069F_IER  = 0x20; /* IRQ5割り込み有効化 */
 
   return 0;
 }
しばらく動作が不安定で展示会とか開発中とかに困っていたのだが, これで安定動作するようになる.よかったよかった.
メールは kozos(アットマーク)kozos.jp まで