/home/tnishinaga/TechMEMO

日々行ったこと、面白かったことを書き留めます。

Macでmbedをデバッグする方法についてのまとめ

mbed Advent Calendar 2014の11日目です。

まえおき

mbedとの関わり

f:id:tnishinaga:20141211171140j:plain

現在私はmbedのプログラムを アセンブリ言語でコードをゴリゴリ実装して、バグを作っては治すを繰り返す日々をおくっています。

デバッガは必須

アセンブリ言語はCに比べて低級な言語なので、コーディング中に混乱して、バグを作ることが多いです(主観的発言)。 よくあるバグとしては、メモリ破壊、スタック破壊、リターンアドレスのpop忘れ、etc……。 簡単なデバッグ方法としてはprintfを用いたデバッグがありますが、今回はアセンブリで実装しているため、printfを挟むのは少々コストが大きいです。

そこで活躍するのがデバッガです。 このデバッガがあれば、プログラムを1命令ごと、レジスタの値を監視しながらデバッグすることができます。 これによりprintfでは調査しづらい細かな処理を追うことが可能となり、デバッグの効率を格段に上げることができます。 よって、デバッガは必須なのです。

mbedのデバッグ機構

f:id:tnishinaga:20141211171149j:plain

mbedの一部の製品には、メインのマイコンの他に、mbed interfaceと呼ばれる機能を持ったマイコンがついています。 このmbed interfaceはCMSIS−DAPデバッガとしての機能を持っているため、このmbed interfaceを持った一部の製品は、それ単体でデバッグを行うことができます。

f:id:tnishinaga:20141211172046p:plain

※ CMSIS-DAPの説明は特殊電子回路株式会社さまの「CMSIS-DAPって何?」を参照してください。

Macでmbedデバッグ

前置きが長くなりましたが、本題です。 Macでmbedのデバッグを行いましょう。

プログラムのエクスポート

mbedはプログラムをオンラインでコンパイルするという、画期的なシステムを採用しています。 しかし、このオンラインのシステムはコンパイルは出来ますが、デバッグを行うことができません。 デバッグのためにはオンラインのコードをローカルに保存し、オフラインコンパイル環境を用いる必要があります。 この方法については以下のページを参照し、「Exporting to GCC ARM Embedded」でGCC用のコードをエクスポートしてください。

デバッグ環境の整備

私は普段はMac OS Xを用いています。Windows機も所持していますが、もっぱらゲーム専用機としてしか使っていません。

残念なことに、ARM社謹製の統合開発環境 MDK-ARMは、現状Windows専用ソフトウェアです。 そのためMac環境ではライセンス料が云々の前に、使うこと自体ができません。 それでもなんとかMacで開発を行うため、GNUのフリーな環境を用いて開発環境・デバッグ環境を整えます。 その方法は以前ブログ記事にまとめたので、こちらを参照してください。

Tips: GNU開発環境でのアセンブリ利用時の注意

GNUな開発環境とMDK-ARMでは、アセンブリコードの書き方が異なるため、アセンブリコードの互換性がありません。 そのため、アセンブリで開発する際は、デバッガの使えないオンライン上で完結させるか、WindowsでMDK-ARMを用いて開発を行うか、もしくはオンラインへ戻ることを諦めてGNUな書き方を行うかを判断する必要があります。

私は青mbedのプログラムフラッシュが豊富であり、MDK-ARMを用いずともメモリが足りているので、GNUツールで開発することにしました。 オンラインへの復帰は、開発が完了した後に手動でアセンブリコードを書きなおして行う予定です。

GDBの操作方法(簡易説明)

GDBを用いてデバッグする方法を、簡単に説明します。

GDBサーバーへの接続

(gdb) target remote localhost:3333

プログラムのmake

(gdb) make

プログラムのロード

(gdb) load

なお、以前ロードしたプログラムと新しくロードするプログラムに差異がある場合は、後述のdisplayの設定が削除されます。 breakpointの設定は残ります。

状態の表示

displayコマンドを用いると、レジスタやメモリの値を逐次表示することができます。 自分のおすすめは以下。

(gdb) display/i $pc
(gdb) display/x $r0
(gdb) display/x $r1
(gdb) display/x $r2
(gdb) display/x $r3
(gdb) display/x $r4
(gdb) display/x $r5
(gdb) display/x $r6
(gdb) display/x $r7
(gdb) display/x $r8
(gdb) display/x $r9
(gdb) display/x $r10

これでプログラムカウンタの指すアセンブリコードを表示しつつ、レジスタr0~r10までの値を16進数で確認できます。

再実行

(gdb) continue

プログラムカウンタの指す場所から処理を再開します。 後述のbreakpointに引っかかるまで止まらないので注意してください。 強制的に止めたい場合はCtrl+Cを入力します。

breakpoint

(gdb) breakpoint *0x0000a410

特定のアドレスにbreakpointを仕込んで、その位置で処理を止めることができます。 上記のコマンドでは、プログラムカウンタがメモリアドレス0x0000a410になった時、処理を止めることができます。

なお、本来condition等を用いれば変数の値が変化した際に止める……ということができますが、pyOCDの環境で使ったらpyOCDがハングしました。 再現性の確認等ができていないので、バグ報告は行えていません。

1命令実行

(gdb) stepi
(gdb) nexti

アセンブリでは1命令ごと実行する必要があるため、 C言語のstep命令はstepi、next命令はnextiとなります。

おわりに

以上、Macでmbedをデバッグする方法についての簡易デバッグ説明でした。 デバッグ時にgdbサーバーを建てるため使用したpyOCDをもう少し活用すると、より柔軟なデバッグも可能ですが……今回はここまで。 要望があれば、PyOCDを使ってHard Fault原因を探る方法を、再度advendカレンダーに書こうかとおもいます。

緩募

  • ARMマイコン発売から現在までのシェア情報の書かれた資料
  • mbed発売から現在までのシェア情報の書かれた資料

次の人

次はjksoftさんです。よろしくおねがいします。