プロセッサがデバッグを行う仕組みをおさらい

西永俊文氏:それでは「JTAGでArmプロセッサをデバッグする方法のつづき」を発表します。tnishinagaです。組み込みが好きです。キャラクターボイスはついなちゃんです。以前、JTAGでArmプロセッサをデバッグする発表(1記事目2記事目)をしました。

本日の目次です。

Armデバッグのおさらい。最初にプロセッサがデバッグを行う仕組みをおさらいします。デバッグは、主にgdbクライアント経由で行います。このgdbクライアントは、OpenOCDなどのツールが立てたgdbサーバーに接続しています。gdbサーバーを立てているOpenOCDなどのツールは、gdbクライアントから来たコマンドに応じて、ターゲットからの情報取得や制御などを行います。

ターゲットとの通信は、JTAGやSWDなどのデバッグ信号線を用いて行います。この信号線の制御は、主にUSB接続のデバッグアダプタやデバッグインターフェイスと呼ばれる装置を用いて行います。

Armプロセッサデバッグの例として、OpenOCDなどのツールがプロセッサのレジスタの値を取得するまでをもう少し詳しく見ておきます。OpenOCDなどのツールは、インターフェイスを制御し、JTAGなどを用いてターゲットのDAPにアクセスします。そして、そのDAPを用いてデバッグ用のメモリ空間にアクセスし、Memory-mapped I/Oでデバッグユニットなどを制御します。

デバッグユニットには、プロセッサに命令を実行させるDBGITRレジスタと、デバッガとプロセッサの両方からアクセスできるデータ交換用のDBGDTRレジスタがあります。そのため、例えばプロセッサのレジスタの値を読みたい時は、レジスタの値をDBGDTRシステムレジスタに書き込む命令をDBGITRレジスタに書き込んで、命令を実行させたあと、DBGTRレジスタを読むとプロセッサのレジスタの値が得られます。以上でおさらいを終わります。

Armプロセッサをデバッグする方法の続き resume

Armプロセッサをデバッグする方法の続きです。ここからは新規の内容となります。今回新たにご紹介する内容は、resume、step実行、メモリアクセス、breakpointのやり方です。

resumeは、breakpointなどで止まった処理を再開するものです。gdbでは、continueコマンド実行時にやる処理になります。コアのresumeはCTIを使って、コアにシグナルを送って実現します。細かい手順は参考資料をご確認ください。

Armプロセッサをデバッグする方法の続き step実行

step実行は、1命令だけを実行する機能です。gdbではstepコマンド実行時にやる処理になります。デバッグユニットのEDSCRレジスタのSSフラグをセットすると、resumeして1命令実行後に自動でDebugStateに戻ってくるようになります。

Armプロセッサをデバッグする方法の続き メモリアクセス

次は、メモリアクセス方法についてです。メモリアクセスの方法は、基本版と高速版の2つがあります。

基本的なメモリアクセス方法は、CPUレジスタ経由で行うものになります。仕組みが簡単で柔軟に扱えますが、アクセスが遅いという欠点があります。そのため、少ないデータを読み書きする際に使うといいでしょう。

高速版は、専用の手順を用いるものになります。先ほど紹介した基本版に比べると高速ですが、小回りが利きにくいという欠点があります。そのため、多くのデータを読み書きする際に使うと良いでしょう。

高速メモリ読み込み手順の例を紹介します。最初に、デバッガからX0レジスタに読み込みたいメモリアドレスをセットします。次に、X0をDBGDTRレジスタに書き込む命令のopcodeをEDITRにセットします。さらにEDSCRのメモリアクセスモードフラグを1にセットすると準備完了です。あとは繰り返しDBGDTRからメモリの値を読み込んで、読み終わったらEDSCRのメモリアクセスモードフラグを0にして終了します。これで高速にメモリ読み込みが行えます。

Armプロセッサをデバッグする方法の続き breakpoint

breakpointは、特定アドレスの命令実行前に処理を中断させる機能です。software breakpointとhardware breakpointの2種類があります。それぞれのbreakpointを詳しく見ていきましょう。

software breakpointは、ハードの機能を使わないbreakpointです。gdbでbreakpointをセットすると通常はこちらが使われます。hardware breakpointは、CPUの機能を用いるbreakpointです。gdbではhbreakコマンドで利用できます。

各breakpointには、それぞれメリット・デメリットがあります。software breakpointは、breakpointが無限に作れるというメリットがありますが、デバッガの実装が少し大変だったり、デバッガが異常終了するとメモリ上のバイナリが壊れる可能性があります。

hardware breakpointは、デバッガの実装が比較的簡単ですが、作れるbreakpointの数に制限があります。

各breakpointの実装を見ていきましょう。

software breakpointは、breakpointを設定したいメモリアドレスの命令をHalt命令に書き換え、その命令実行時にHaltさせてDebugStateに移行させることで実現しています。この時、デバッガ側で書き換え前の命令を覚えておき、resume時にその命令を実行できるようにしておく必要があります。

このような実装になっているので、OpenOCDなどが異常終了した場合にメモリ上のバイナリが壊れる可能性があると考えています。

hardware breakpointは、DBGBVRレジスタにbreakpointを設定したいメモリアドレスをセットし、DBGBCRレジスタでbreakpointを有効化すれば使えます。software breakpointに比べると簡単に利用できますが、実装されているレジスタ数がハードウェア依存なので、利用可能なbreakpoint数に制限があります。

gdbサーバー機能を持ったデバッグインターフェイスを開発中

「OdangoProbe」の紹介です。現在、Rustを使ってマイコン上で動くデバッグインターフェイス用ファームウェアの開発をしています。このファームウェアは、gdbサーバー機能を持っており、OpenOCDなどを介さず直接gdbクライアントからアクセスできるという特徴があります。制御のほとんどをマイコンにオフロードするので、RP2040のような通信速度が遅いマイコン上で動かす場合は、速度メリットが出てくるのではと考えています……というのは建て前で、作りたかったから作ったというのが本音です。

開発初期は、新規性のあるものと考えていましたが、あとで「BlackMagicProbe」という先行プロダクトがあると教えていただきました。今のところBlackMagicProbeと比較して優れている点はなさそうですが、趣味のプロダクトなのでひととおり動くところまでは作りたいと考えています。

OdangoProbeは、embassy-rsというライブラリを用いて、非同期とマルチコアを活用して実装しています。gdb stubのasync対応が不足していたため、1コアをgdb stubの専用にして、残りの1コアでusbとprobeのタスクを動かしています。

VSCodeのprobe-rsプラグインがマルチコア対応していないため、デバッグしにくかったり、コンパイル時にstack overflowを検出できず解決に時間がかかったりなど、開発やデバッグで苦労しています。

もし、このあたりの開発方法のベストプラクティスをご存じの方がいたら、教えていただけるとうれしいです。

本日はこちらのデモをお見せしたかったのですが、リファクタが間に合いませんでした。すみません。次回のKernel/VMまでには出せるようにがんばりたいと思います。最後に本日の参考資料のリンクです。以上で発表を終わります。ありがとうございました。

質疑応答

司会者:マイクをオンにしてもらって。

西永:この時間(発表動画を流している間)に動的にデモを生成しようとしたのですが、失敗しました。すみません。

(会場笑)

進捗としては、今JTAGは動いていて、(プロセッサの)中のレジスタが取れるようにはなっているのですが、gdbサーバーのタスクがぜんぜん動いてくれてない状況になっています。もうちょっとでできると思うので、ちょっと長い目で見ていただけるとという感じです。以上です。質問などございましたら。

質問者1:だいみょーじんと申します。発表ありがとうございました。breakpointを作成しているということでしたが、例えばwatchpointとか、そういう機能はあるんでしょうか?

西永:僕が見ているのはArmの特にv8(Armv8)のコアしかないのですが、watchpointもあります。ですが、まだ使ったことがないので、ちょっと説明できるレベルにはないという感じです。

質問者1:わかりました。これからそういうのを作る予定はあるんですか?

西永:まぁ少しずつ(笑)。範囲を広げられたらなと思っています。

質問者1:わかりました。ありがとうございます。

司会者:他にありますか?

質問者2:井田です。OpenOCD相当の機能をマイコンに実装しないといけないのかなと思っているんですけど、フラッシュ書き込みのstubはどうされるんですか?

西永:フラッシュ書き込み?

質問者2:フラッシュ書き込みがいる時って、フラッシュ書き込み用のドライバをターゲットに転送して書き込みをさせるじゃないですか。あれってけっこう大きいと思うんですけど、今の作り方の場合はどうされるんですか?

西永:今のところは考えておりません。

質問者2:なるほど。ありがとうございます。

西永:BlackMagicProbeの件を教えていただいて、本当にありがとうございました。

(会場笑)

司会者:ちなみにJTAGデバッガをこの3年以内に使ったことがある方がいたら手を挙げてください。

(会場挙手)

5、6人。まぁ、JTAGは(価格が)超高い印象がありますが、最近Arm系は規格化されて、ホビー向けの西永さんが開発しているようなものもあるので、Raspberry Pi Picoを持っていたら手を出してみるのもいいんじゃないでしょうか。では、終わりたいと思います。ありがとうございました。あらためて拍手をお願いします。

(会場拍手)