FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

GDBリモートデバッグサーバを自作ISSに実装する(stepの実装とパケットの注意点)

前回の実装では、continue命令を実装したのだった。次は、step操作を実装していこう。

msyksphinz.hatenablog.com

step実行は、GDBの中ではどのように実装されるのだろうか?

Howto: GDB Remote Serial Protocol

In the RSP, the s packet indicates stepping of a single machine instruction, not a high level statement. In this way it maps to GDB's stepi command, not its step command (which confusingly can be abbreviated to just s).

なるほど。とりあえずsコマンドは1命令実行として表現されるんだな。さらに、この場合何回かsコマンドが発生され、1ステートメント分が実行されるという訳か。

ベーシックな1命令実行コマンド

sコマンドを入力すると、単純に1命令を実行するだけで良い。

    case 's' : {
        m_env->StepSimulation (1);
        PutPacket ("S05");
        break;
    }

step実行を行うことにより発行されるコマンド

main()にブレークポイントを仕掛け、その直後にstepコマンドを実行した場合のコマンドだ。

+Packet : mffffffff800025fc,4     // 止まっている場所(Main関数)の次の命令を取得
ffffffff800025fc, 4
PutPacket : $7800a567#d2
+Packet : Z0,ffffffff800025f8,4   // main関数の位置のブレークポイントを設定
PutPacket : $OK#9a
+Packet : Hc0                     // スレッドを選択
PutPacket : $OK#9a
+Packet : s                       // 1命令実行
count = 1
PutPacket : $S05#b8
+Packet : p25                     // PC取得
p = 25
PutPacket : $00260080ffffffff#c0
+Packet : p1d                     // SP取得
p = 1d
PutPacket : $383f007fffffffff#31
+Packet : s                       // 1命令実行
count = 1
PutPacket : $S05#b8
+Packet : p25                     // PC取得
p = 25
PutPacket : $04260080ffffffff#c4
+Packet : p1d                     // SP取得
p = 1d
PutPacket : $383f007fffffffff#31
+Packet : s                       // 1命令実行
count = 1
PutPacket : $S05#b8
+Packet : p25                     // PC取得
p = 25
PutPacket : $08260080ffffffff#c8
+Packet : p1d                     // SP取得
p = 1d
PutPacket : $383f007fffffffff#31
+Packet : z0,ffffffff800025f8,4   // main関数のブレークポイントを設定
PutPacket : $OK#9a

ぶっちゃけ、何でブレークポイントが解除されたり設定されているのかは良く分からないのだけれども、一応思い通りに動いている気がする。

命令をトレースしながらハマった点 (エスケープシーケンスに注意)

今回ステップ実行の実装にずいぶんと時間がかかったのだが、メモリにデータを正しくダウンロードできない問題があり、何だろうと調査をしていた。 どうやら、GDBのパケットにはエスケープシーケンスが定義されており、通常のスタートシンボルとストップシンボル($と#)をデータとして転送したい場合は、まずは0x7d(})が送信され、次の値を0x20で排他的論理和を取らなければならない。 これの実装を忘れていたため、メモリに命令が正しくダウンロードできず、ずいぶんと時間を消費してしまった。

        for (; idx < packet_str.length();) {
            Word_t mem_data = 0;
            for (int i = 0; i < 4 && idx < packet_str.length(); i++) {
                if (packet_char[idx] == '}') {
                    mem_data |= ((packet_char[++idx] & 0x0FF) ^ 0x20) << (i * 8);
                } else {
                    mem_data |= (packet_char[idx] & 0x0FF) << (i * 8);
                }
                idx++;
            }

            m_env->StoreMemoryDebug (mem_base, mem_data, Size_Word);
            mem_base += 4;
        }