こんにちは。はやく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で書いています。部品は秋月とチップワンストップで買えるものにしています。
上記回路図のpdf版も置いときますね。
FT2232_JTAG_ADAPTER_20130127.pdf
いつものFusion PCBと、佐々木さんに教えていただいたDFRobotの2社にお試しで発注してみました。(安いのでお試し可能だったもので...)基板が必要以上の枚数(23枚)になって処分に困ってます。笑
あ、ねむいさんの作例同様、EEPROMを搭載し、Amontec JTAGkey相当のデバイスとして認識されるようにしています。DFRobotとFusionとでの比較(というほどのもんではありませんが)はまた別記事にまとめます。
・・・あ、そういえばこの基板、フリスクサイズに入れる予定だったの、忘れてた。
DFRobotから届いた基板はこちら。
実装した後のボード。
またこの話しはおいおい記事にさせていただくとして、Arduino Dueのデバッグについて書いていきます!
●Arduino Dueのデバッグポートについて
Arduino Dueのデバッグポートは以下のようになっています。

初めから、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です。
なお、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の内部動作に至るまでステップ実行できるのでこまかく追いかけたい場合有効かなと思います。
ところで、Arduinoはスレッド実行環境じゃないのにgdbがスレッド環境と誤判別したのか、
RMT ERROR : failed to get thread list.ウォーニングを吐きます。
まぁ動作に影響なさそうなので放っているのですが・・・設定とかできちんと回避できるんでしょうか。ご存知のかた、是非お教え下さい!
ではー!これから普通にDueを使います!笑
Joseph Yiu
CQ出版
売り上げランキング: 126,976
桑野 雅彦 岡田 好一 共著
CQ出版
売り上げランキング: 151,899
千葉工業大学 林原 靖男/神奈川工科大学 兵頭 和人
CQ出版
売り上げランキング: 255,565