2013年01月27日

Arduino Due+自作JTAGアダプタでスケッチのソースレベルデバッグしてみた

こんにちは。はやくArduino Dueで遊びたいのですがなんだかんだと他にやりたい事やらやらなければならない事やらあり、あまり時間がさけてなくて、遅々として進まない状況が続いています。

●JTAGアダプタを製作してみた

ところで、ツイッターにたまに書いていたのですが、年末のお休みを利用して、
Kamiki氏の回路 http://www.koka-in.org/~kensyu/handicraft/diary/20080926.html
ねむいさんの回路 http://nemuisan.blog.bai.ne.jp/?eid=155027
を参考に自分なりにJTAGのデバッグ回路を作っていたので、まずはこれを使ってみることにしました。
メインチップはFT2232Dです。

作成してみた回路図はこちら。Eagleで書いています。部品は秋月とチップワンストップで買えるものにしています。
jtag_sch.png

上記回路図のpdf版も置いときますね。
FT2232_JTAG_ADAPTER_20130127.pdf
いつものFusion PCBと、佐々木さんに教えていただいたDFRobotの2社にお試しで発注してみました。(安いのでお試し可能だったもので...)基板が必要以上の枚数(23枚)になって処分に困ってます。笑
あ、ねむいさんの作例同様、EEPROMを搭載し、Amontec JTAGkey相当のデバイスとして認識されるようにしています。DFRobotとFusionとでの比較(というほどのもんではありませんが)はまた別記事にまとめます。

・・・あ、そういえばこの基板、フリスクサイズに入れる予定だったの、忘れてた。

DFRobotから届いた基板はこちら。
P1130029resized.jpg

実装した後のボード。
P1130027resized.jpg

またこの話しはおいおい記事にさせていただくとして、Arduino Dueのデバッグについて書いていきます!

●Arduino Dueのデバッグポートについて

Arduino Dueのデバッグポートは以下のようになっています。

arduino_due_debugport.jpg
初めから、SWD(Serial Wire Debug)もJTAGもピンヘッダが実装されています。SWDはJTAGよりも少ないピンでデバッグ機能を提供します(SWDには、JTAGのバウンダリスキャン相当の機能提供はありません。まぁデバッガ繋ぎたいだけなのでそのような機能、特に必要でもありませんが)。
本ボードでは、10ピンの1.27mmピッチピンヘッダにJTAGが、そして4ピンの2.54mmピッチピンヘッダにSWDが出ています。今回はこのJTAG側を使用しています。
(後ほど、SWDを使用したデバッグも上記のデバッガ(+SWDアダプタ)でお試ししてみたいと思っています)

さて、JTAGデバッグに使用する1.27mmピッチピンヘッダですが、ストロベリー・リナックスで販売している、OlimexのARM-JTAG-20-10(630円)が便利です。シンプルな構成のピッチ変換基板です。
http://strawberry-linux.com/catalog/items?code=15079



このARM-JTAG-20-10と、JTAGアダプタの3.3V、GND、TMS、TCK、TDI、TDOを適当な線材で接続すればOKです。
jtag_connection.jpg

なお、Arduino Dueのデバッグ端子(だけじゃないけど)については武蔵野電波さんのピンアウトダイアグラムが便利です。
http://www.musashinodenpa.com/arduino/lib/Duepinout.pdf

●開発環境の整備

今回も、過去記事のSTM32F4-Discoveryで取り上げましたOpenOCDを使用します。過去記事ではLinux環境(Ubuntu 10.04LTS 64bit)でOpenOCDを使用していたので、自前ビルドのものを使用しましたが、今回はWindows8+Cygwin環境で使用しているので、「ねむいさんのぶろぐ」においてあるバイナリファイルをそのまま使用させて頂きました。
もちろんLinux環境からも使用出来ると思います(試してないけど)。

OpenOCDは、各デバッグI/Fやターゲットボード別にスクリプト言語 Tclで記述された処理を使用してデバッグします。Arduino Dueは「ATSAM3X8E」を使用しています。以下のように指定して起動すれば、ATSAM3X8E向け環境になるかなと思います。

Cygwinでの実行例。DOSプロンプトならWindows流のパス指定にしてください。

# ./openocd.exe -s ./tcl -f ./tcl/interface/jtagkey.cfg -f ./tcl/target/at91sam3ax_8x.cfg

OpenOCDでは、デフォルトはGDBServerのポートは3333、telnetインターフェースが4444になっています。
なので、TeraTerm等で4444番ポートに接続するだけで、OpenOCDの持つ各種コマンドインターフェースを利用できます。

例えば、上記の通りのコマンドライン指定でOpenOCDを起動してみます。OpenOCDを起動した時のログは以下のようになりました。


ishii_000@Win7tab /cygdrive/c/local/ocd
$ ./openocd.exe -s ./tcl -f ./tcl/interface/jtagkey.cfg -f ./tcl/target/at91sam3ax_8x.cfg
Open On-Chip Debugger 0.7.0-dev-00134-g48e01a4-dirty (2013-01-03-11:34)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.sourceforge.net/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'jtag'
adapter speed: 500 kHz
adapter_nsrst_delay: 100
jtag_ntrst_delay: 100
cortex_m3 reset_config sysresetreq
Info : clock speed 500 kHz
Info : JTAG tap: sam3.cpu tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x4)
Info : sam3.cpu: hardware has 6 breakpoints, 4 watchpoints

# 今気づいたけど僕のマシン名「WIN7TAB」だった。Win8タブレットなんたけど。あーあ。orz

次に、OpenOCDのAT91SAM3 INFOコマンドを実行してみます。(OpenOCDでは一部のターゲット向けにスペシャルコマンドがあるので)


> at91sam3 info
    CKGR_MOR: [0x400e0420] -> 0x00000000
            MOSCXTEN:     0 [0x0000] (main xtal enabled: NO)
            MOSCXTBY:     0 [0x0000] (main osc bypass: NO)
            MOSCRCEN:     0 [0x0000] (onchip RC-OSC enabled: NO)
             MOSCRCF:     0 [0x0000] (onchip RC-OSC freq: 4 MHz)
            MOSCXTST:     0 [0x0000] (startup clks, time= 0.000000 uSecs)
             MOSCSEL:     0 [0x0000] (mainosc source: internal RC)
               CFDEN:     0 [0x0000] (clock failure enabled: NO)
   CKGR_MCFR: [0x400e0424] -> 0x00000000
            MAINFRDY:     0 [0x0000] (main ready: NO)
               MAINF:     0 [0x0000] (0.000 Mhz (32.768khz slowclk)
  CKGR_PLLAR: [0x400e0428] -> 0x00000000
                DIVA:     0 [0x0000]
                MULA:     0 [0x0000]
        PLLA Freq: (Disabled,mula = 0)
   CKGR_UCKR: [0x400e041c] -> 0x00000000
    PMC_FSMR: [0x400e0470] -> 0x00000000
    PMC_FSPR: [0x400e0474] -> 0x00000000
     PMC_IMR: [0x400e046c] -> 0x00000000
    PMC_MCKR: [0x400e0430] -> 0x00000000
                 CSS:     0 [0x0000] slowclk (0.033 Mhz)
                PRES:     0 [0x0000] (selected clock)
                Result CPU Freq: 0.033
    PMC_PCK0: [0x400e0440] -> 0x000001ff
    PMC_PCK1: [0x400e0444] -> 0x000001ff
    PMC_PCK2: [0x400e0448] -> 0x000001ff
    PMC_PCSR: [0x400e0418] -> 0x00000000
    PMC_SCSR: [0x400e0408] -> 0x00000000
      PMC_SR: [0x400e0468] -> 0x00000000
 CHIPID_CIDR: [0x400e0740] -> 0x285e0a60
             Version:     0 [0x0000]
               EPROC:     3 [0x0003] cortex-m3
             NVPSIZE:    10 [0x000a] 512K bytes
            NVPSIZE2:     0 [0x0000] none
            SRAMSIZE:    14 [0x000e] 96K Bytes
                ARCH:   133 [0x0085] ATSAM3XxE Series (144-pin version)
              NVPTYP:     2 [0x0002] embedded flash memory
               EXTID:     0 [0x0000] (exists: NO)
CHIPID_CIDR2: [0x400e0940] -> 0x285e0a60
             Version:     0 [0x0000]
               EPROC:     3 [0x0003] cortex-m3
             NVPSIZE:    10 [0x000a] 512K bytes
            NVPSIZE2:     0 [0x0000] none
            SRAMSIZE:    14 [0x000e] 96K Bytes
                ARCH:   133 [0x0085] ATSAM3XxE Series (144-pin version)
              NVPTYP:     2 [0x0002] embedded flash memory
               EXTID:     0 [0x0000] (exists: NO)
 CHIPID_EXID: [0x400e0744] -> 0x00000000
CHIPID_EXID2: [0x400e0944] -> 0x00000000
   rc-osc: 0.000 MHz
  mainosc: 0.000 MHz
     plla: 0.000 MHz
 cpu-freq: 0.033 MHz
mclk-freq: 0.033 MHz
 UniqueId: 0x52323120 0x36303437 0x30303420 0x35313034

デバイスの諸元がいろいろ読み取れたみたいです。

●Arduinoスケッチを実機に書き込んで、ソースコードレベルデバッグを使用する

上述の通り、OpenOCDにはデフォルトポート番号3333でGDBサーバーとして振舞います。
ARM用のGDBクライアントが必要なのですが、実はArduino IDE内のARM用ツールチェイン(Sourcery g++ Liteのようです)にもgdbがちゃんと入ってます。だからこれを利用すればOKだと思います。
Windows版の場合のパスは、「arduino\hardware\tools\g++_arm_none_eabi\bin\arm-none-eabi-gdb.exe」です。

今回は、僕がたまたま自分のPCに別途インストールしてたSourcery g++ Lite(ARM EABI)があったんでそれ使いました(パス通してたんで。みたいな理由です^^;)。

gdbのフロントエンドには、InsightやEclipse+CDT、DDDなどいろいろありますが、定番の1つ、Emacsを使用してみました。その他環境でも別に差なく使用出来ると思います。。


1. ArduinoのスケッチをコンパイルしてArduino Dueマイコンボードに書き込む

Arduino Dueボード上についてる橙色のLED(デジタルピン13番)を500[ms]毎に明るい→暗い→明るい→暗いを繰り返す適当なサンプルコードを作りました。
loop関数が呼ばれる度、変数flagの値が0(false)と1(true)交互に変化します。

●BlinkPWM.ino
void setup()
{
}

boolean flag = false;

void loop()
{
  
  if(flag){
    analogWrite(13,40); // 暗い
  } else {
    analogWrite(13,255); // 明るい
  }
  
  flag = !flag;

  delay(500); // 500[ms]待つ
}

コンパイルすると、テンポラリディレクトリにバイナリを含むビルド関連生成物が吐き出されます。Arduino IDEの「ファイル」→「環境設定」の「より詳細な情報を表示する : コンパイル」のチェックボックスにはチェックを入れておきます。
(生成物のテンポラリ出力先のパスを確認する為)
スケッチビルドのログを見てみます。適当に最後のobjcopyしている行をコピーしたのが以下です。

C:\local\arduino/hardware/tools/g++_arm_none_eabi/bin/arm-none-eabi-objcopy -O binary c:\temp\build2226462045159247312.tmp/BlinkPWM.cpp.elf c:\temp\build2226462045159247312.tmp/BlinkPWM.cpp.bin 
上記では生成先は「c:\temp\build2226462045159247312.tmp」となったようです。

あ、、ところでデバッグする時はgccにデバッグオプション(-g)を付加しておく必要がありますが、Arduino IDEでは何故かデフォルト「-g」付きになっていますので、そのままでOKです(これは、AVRマイコン向けのビルド時も同じ)。
Arduino IDEで.ino(または.pde)のスケッチをビルドした後、ビルドエラーが出た時に該当行を抽出する時にデバッグ情報を使ってるのかなと思ってます。違ったらスミマセン。


んで、上記で調べた生成先のディレクトリ内のファイルはこんな感じでした。

C:\temp\build2226462045159247312.tmp>dir ドライブ C のボリューム ラベルがありません。 ボリューム シリアル番号は DACD-C207 です C:\temp\build2226462045159247312.tmp のディレクトリ 2013/01/27 17:40 <DIR> . 2013/01/27 17:40 <DIR> .. 2013/01/27 17:39 252 BlinkPWM.cpp 2013/01/27 17:40 27,064 BlinkPWM.cpp.bin 2013/01/27 17:40 233,014 BlinkPWM.cpp.elf 2013/01/27 17:40 366,790 BlinkPWM.cpp.map 2013/01/27 17:39 4,456 BlinkPWM.cpp.o 2013/01/27 17:40 26,664 CDC.cpp.o 2013/01/27 17:40 417,598 core.a 2013/01/27 17:39 5,716 cortex_handlers.c.o 2013/01/27 17:39 2,272 cxxabi-compat.cpp.o 2013/01/27 17:40 23,664 HID.cpp.o 2013/01/27 17:39 2,064 hooks.c.o 2013/01/27 17:39 910 iar_calls_sam3.c.o 2013/01/27 17:40 14,620 IPAddress.cpp.o 2013/01/27 17:39 6,100 itoa.c.o 2013/01/27 17:40 4,676 main.cpp.o 2013/01/27 17:40 32,940 Print.cpp.o 2013/01/27 17:40 6,208 Reset.cpp.o 2013/01/27 17:40 4,780 RingBuffer.cpp.o 2013/01/27 17:40 25,008 Stream.cpp.o 2013/01/27 17:39 9,460 syscalls_sam3.c.o 2013/01/27 17:40 24,076 UARTClass.cpp.o 2013/01/27 17:40 24,568 USARTClass.cpp.o 2013/01/27 17:40 34,048 USBCore.cpp.o 2013/01/27 17:40 21,328 variant.cpp.o 2013/01/27 17:39 15,624 WInterrupts.c.o 2013/01/27 17:39 6,516 wiring.c.o 2013/01/27 17:39 22,836 wiring_analog.c.o 2013/01/27 17:39 10,040 wiring_digital.c.o 2013/01/27 17:40 9,112 wiring_pulse.cpp.o 2013/01/27 17:39 5,620 wiring_shift.c.o 2013/01/27 17:40 5,964 WMath.cpp.o 2013/01/27 17:40 63,628 WString.cpp.o 32 個のファイル 1,457,616 バイト 2 個のディレクトリ 17,473,040,384 バイトの空き領域 C:\temp\build2226462045159247312.tmp>

objdumpでソースコード付きの逆アセンブル表示ができますので、試してみます。

> arm-none-eabi-objdump -Sdl C:\temp\build2226462045159247312.tmp\BlinkPWM.cpp.elf

結果の内、メインのスケッチあたりの一部抜粋をしてみます。


00080198 :
setup():
C:\local\arduino/BlinkPWM.ino:3
void setup()
{
}
   80198:	4770      	bx	lr
	...

0008019c :
loop():
C:\local\arduino/BlinkPWM.ino:8

boolean flag = false;

void loop()
{
   8019c:	b510      	push	{r4, lr}
C:\local\arduino/BlinkPWM.ino:10
  
  if(flag){
   8019e:	4b0b      	ldr	r3, [pc, #44]	; (801cc )
   801a0:	781b      	ldrb	r3, [r3, #0]
   801a2:	b113      	cbz	r3, 801aa 
C:\local\arduino/BlinkPWM.ino:11
    analogWrite(13,40);
   801a4:	200d      	movs	r0, #13
   801a6:	2128      	movs	r1, #40	; 0x28
   801a8:	e001      	b.n	801ae 
みてみると、スケッチのBlinkPWM.inoのパスがC:\local\arduinoになっています。
これは僕のPCのArduino IDEインストール先です。
とはいえ、BlinkPWM.inoを保存してるパスはそこじゃありません。今回は、
手っ取り早く本パスにスケッチをコピっちゃいます。笑
> copy (path-to-store-sketch)BlinkPWM.ino C:\local\arduino

2. OpenOCDを起動する
Cygwinから起動してみました。別に素のDOSプロンプトでもいけるかと思います。

# ./openocd.exe -s ./tcl -f ./tcl/interface/jtagkey.cfg -f ./tcl/target/at91sam3ax_8x.cfg

3. Emacsを起動する
Windowsマシンにまだエディタをインストールしてなかったので、gnupack(emacs+cygwin)というパッケージをインストールしてみました。
http://sourceforge.jp/projects/gnupack/releases/
取り敢えず起動するなら、展開してemacs.exeを実行するだけです。

4. gdbを開始
Emacsから、「M-x gdb」としてGDBのモードを起動します。プロンプトが出ますので以下のように入力します。

Run gdb (like this): arm-none-eabi-gdb -i=mi C:\temp\build2226462045159247312.tmp\BlinkPWM.cpp.elf

5. ブレーク貼ってステップ実行してみる
loop関数とdelay関数にブレーク貼るなら、「b loop」と「b delay」など。
「p flag」とすれば、変数の値を確認できます。
ここではloopでブレークかかる度、flagの値が0と1、交互に切り替わる様子が確認できます。
ARMのレジスタを見るなら「i reg」とすればひと通り出ます。

スクリーンショットを撮ってみました。
左側がgdbのコマンド、右側がデバッグ中のソースコード(BlinkPWM.ino)です。loop関数のif文のところについてる赤いマーカーは、loop関数にブレークを張ったよって意味です。
結構視覚的にわかりやすいです。左側は、loopで止めた状態でARMのレジスタを表示してます。
もちろん、ArduinoのAPIの内部動作に至るまでステップ実行できるのでこまかく追いかけたい場合有効かなと思います。
emacs_ss.jpg


ところで、Arduinoはスレッド実行環境じゃないのにgdbがスレッド環境と誤判別したのか、
RMT ERROR : failed to get thread list.ウォーニングを吐きます。
まぁ動作に影響なさそうなので放っているのですが・・・設定とかできちんと回避できるんでしょうか。ご存知のかた、是非お教え下さい!

ではー!これから普通にDueを使います!笑

ARMでOS超入門 (ARMマイコン)
桑野 雅彦 岡田 好一 共著
CQ出版
売り上げランキング: 151,899
posted by いしいっち at 23:34| Comment(0) | TrackBack(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

※ブログオーナーが承認したコメントのみ表示されます。

この記事へのトラックバック
×

この広告は90日以上新しい記事の投稿がないブログに表示されております。