GDBのリセット問題に対処

すこし時間がとれたので懸案問題にとりくんでみました。解決できたように思います。取り組んだのは「gdbproxyでターゲットにダウンロードしたときに正しく実行できない」問題です。これはVisualDSP++を使ったときには発生しません。原因はgdbproxyがターゲットをリセットしないことです。

背景

bfin-elf-gdbとgdbproxyによるデバッグは、ドキュメントによるとターゲットをリセットする機能がありません。

Note that the above examples assume, that the peripheral hardware has been properly initialized. GDB does not initialize anything on start, since it has no knowledge about the current state of the system. Before you load a program into the Blackfin, make sure you have done the correct initialization - read more hints below.

「gdbproxyには初期化機能がないので自分でやれ」と言うことですが、どないせっちゅーねんてなものです。

monitor reset

さて、ドキュメントには機能がないと書いてありますが、実際にはgdbproxyは

monitor reset

コマンドを受け付けます。そして、このコマンドをgdbコマンドラインから発行すると

  • SIC_ISRが0クリアされていることから、システムリセットがなされている
  • IPENDが0x0013であることから、コアリセットがなされている

と、うまくいくように感じられます。ところが、このあとにターゲットをロードしても正しくどうさしてくれません。なぜでしょうか。よくわかりません。おそらくgdbproxyがどのようにターゲットをリセットしているのか調べればわかるのでしょう。それはそれで興味深いのですが、いまはそこまで手を広げる余裕がありません。

TOPPERS/JSP側で対処

確実に動かすため、TOPPERS/JSPのごく初期段階でプロセッサをリセットするコードを追加しました。このコードは

のふたつの仕事をおこないます。前者は簡単ですが後者は少し頭をひねらなければなりません。なぜなら何も考えずにコアをリセットすると、ブートシーケンスが実行されてデバッガからダウンロードしたアプリケーションではなく、ROMに焼いたアプリケーションがロードされて実行されるからです。
そこで工夫を凝らして次のようなルーチンを、スタックの設定直後に呼んでいます。

void boot_for_gdb(void)
{
    unsigned int usp;

    asm volatile( "%0 = USP;" : "=d"(usp) );  /* USP の値を取り出す */
    if ( usp == SIGNATURE )           /* SIGNATURE が見付かったのなら処理継続 */
        asm volatile( "USP = %0;" : : "d"(0) );
    else{                             /* みつからないならリセット */
        *pSWRST |=  0x07;                /* 内蔵ペリフェラルのリセット */
        asm volatile( "ssync;" );
        *pSWRST &= ~0x07;                /* 内蔵ペリフェラルのリセット解除。不要な気がする */
        asm volatile( "ssync;" );
        *pSYSCR |= 0x10;                /* no boot on core reset */
        asm volatile( "ssync;" );
        asm volatile( "USP = %0;" : : "d"(SIGNATURE) ); /*印を付けておく*/
        asm volatile( "raise 1;" );    /* コアリセット */
        while( 1 )
            ;                          /*リセットが発生するまでループ*/
    }
}

きちんと動作させるために汚いトリックを使っています。USPはユーザーモードのスタックポインタですが、TOPPERS/JSPアプリでは使いません。そこで、ここをメッセージボックスとして使っています。
このコードが最初に実行されるとメッセージボックスの値は不定です。その場合は内蔵ペリフェラルとコアのリセットが必要であると判断することになります。内蔵ペリフェラルのリセットはSWRSTレジスタへの書き込みでおこなえます。そしてメッセージボックスシグナチャーを書き込んだ後、コアのリセットをraise命令でおこないます。
raise命令を不注意に行うとブートシーケンスが実行されます。そこで、ブートさせないように、SYSCRレジスタのbit4を1にしておきます。これでraise 1を実行すると、リセットがおき、プログラムの先頭番地に制御が移ります。
リセットが起きてアプリケーションが再実行されると、今度はメッセージボックスにシグナチャーがみつかります。そこでシグナチャーを消して先に進みます。

試してみました

実装が終わった後、強制的に割り込み処理が宙ぶらりんな状態をつくり出して実行する、monitor resetコマンドの後に実行するなど簡単な試験をおこないました。その結果、どのような場合でも安定してうごくようになりました。
gdbproxyが安定するまではこのコードを使ってプロセッサの初期化を行うことにします。