電子工作でホビー用にBare Metal プログラム開発をしていると、ブートコードを書く必要があるので、JTAG ICEがほしくなります。JTAG ICEを使えばレジスタ状態などから、どこで止まっているか把握しやすくなります。もちろんブート後のプログラムでも、printデバッグコードを仕込むことなく変数の値を確認したりブレークをかけて見たいところで止めることができ便利です。
今回、購入したのは OlimexのARM-USB-TINY-Hというモデルです。
Olimex ARM-USB-TINY-H の特徴
JTAG ICEは本格的なものだと100万円以上します。当然、個人で買う余裕はありません。それに対して、このOlimexのARM-USB-TINY-Hは5000円程度で購入できます。今回はストロベリーリナックスから購入しました。ARM-USB-TINY-HはUSB2.0接続で、 ST MicroelectronicsのSTM32シリーズの他、Raspberry PiやPi 2でも実績があります。2009年ころにBeagleboardが流行ったころから使用例はあったようです。
OpenOCDというオープンソースのデバッグ環境を使います。BoardおよびJTAGの設定ファイル(cfg)を指定すればJTAG ICEとして動作します。
OpenOCDはGDBサーバーの形でターゲットのボードに接続します。TCPのポート3333を使います。OpenOCDがUSBからJTAGへの変換を行います。FT2232にはUSBシリアル変換アダプタはシリアル通信の他、JTAGと同期する機能も入っています。ターゲットボードにGDBサーバー機能を追加しなくてもOpenOCDがその肩代わりをしてくれます。
Raspberry Pi 2への接続
いよいよRaspberry Pi 2へ接続します。5つの端子を接続する必要があります。個々の端子をジャンパ線で直接つなぎました。20pinの接続コネクタを作れば、もっと簡単に脱着できますが、一度、ジャンパ線でつなげれば、しばらくはそのままにするので、今回はジャンパ線にしました。ただし、電源(5V)の接続が間違うとJTAG ICEが壊れる可能性があるので、接続時には確認を何度も行いました。ジャンパ線にも端子のどこにつなぐかを示すタグを一本一本つけて間違わないようにしました。
Eclipseとの連携
Ubuntu 16.04LTSをメインに使っていれば、OpenOCDはパッケージとしてインストールできます。標準のEclipseパッケージはまだ3.8 (3.7 Indigoのバグフィックス版) ですが、 4.4 (luna)以降を別途インストールしました。
インターフェース2017年2月号に詳しい設定が掲載
ARM-USB-TINY-HによるOpenOCDの解説がインターフェース2017年2月号に掲載されていました。Raspberry Pi, Pi 2へ接続する方法が記載されています。なんてタイムリーなんでしょう。IoTが流行っているのでBare Metalプログラミングする機会も多くなるからかもしれません。
ただ、Raspbeery Pi 3 の64bit環境へ接続する方法が2月号には記載されていませんでしたので、色々と試行錯誤してみました。
Raspberry Pi 3へのOpenOCD接続
色々とインターネットで情報を探りながら試行錯誤した結果、多少動作が不安定でエラーメッセージも出ますが、ARM-USB-TINY-Hを使ってRaspberry Pi 3の64bitモードでも接続できます。インターフェース2017年2月号の付録DVDのUbuntu環境に入っているOpenOCDだと64bitのaarch64での接続はサポートされていないので、以下の環境を別途取得しました。
daniel-k/rpi3-aarch64-jtag : Starting point for bare metal development and JTAG debugging on Raspberry Pi 3.
まず、OpenOCDのソースコードを取得し、ビルドします。
$ git clone https://github.com/daniel-k/openocd.git openocd-armv8 $ cd openocd-armv8 $ git checkout armv8 $ ./bootstrap $ ./configure $ make
ビルドした実行ファイルはopenocd-armv8/src/openocdに出力されます。環境ファイルはopenocd-armv8/tclフォルダの中にあります。以下のようにするとaarch64でOpenOCDに接続できます。
$ openocd-armv8/src/openocd -f openocd-armv8/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f openocd-armv8/tcl/target/rpi3.cfg
以下のようにエラーが色々と出ますが、この状態でgdbに接続することができます。
Open On-Chip Debugger 0.9.0-dev-gb796a58 (2017-01-19-23:17) Licensed under GNU GPL v2 For bug reports, read http://openocd.sourceforge.net/doc/doxygen/bugs.html trst_and_srst separate srst_gates_jtag trst_push_pull srst_open_drain connect_deassert_srst adapter speed: 1000 kHz jtag_ntrst_delay: 500 Info : clock speed 1000 kHz Info : JTAG tap: rpi3.dap tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x4) Info : rpi3.cpu: hardware has 6 breakpoints, 4 watchpoints Info : rpi3.cpu1: hardware has 6 breakpoints, 4 watchpoints Error: JTAG-DP STICKY ERROR Error: MEM_AP_CSW 0x80000042, MEM_AP_TAR 0x80012400 *** Info : rpi3.cpu2: hardware has 6 breakpoints, 4 watchpoints *** Info : rpi3.cpu3: hardware has 6 breakpoints, 4 watchpoints *** Error: JTAG-DP STICKY ERROR Error: MEM_AP_CSW 0x80000042, MEM_AP_TAR 0x80016830
Raspberry Pi 3でGDBを使ったデバッグ
Ubuntu 16.04LTSの標準パッケージのbinutils-aarch64-linux-gnu、gcc-aarch64-linux-gnuをインストールしただけでaarch64のビルドはできますが、gdbを使ったaarch64のデバッグができません。そこで以下を参考に64bitのクロス環境(aarch64-linux-gnu)を構築しました。QEMU AArch64(ARM64) で遊ぼう
インターフェース2017年2月号のRaspberry Pi 3の64bitコードを、aarch64-linux-gnuでそのままビルドするとlibcが動的リンクされたコードができてしまい正常動作しません。そこで、リンカ(ld)の引数に-staticをつけて、libcが静的リンクされるようにしました。
gdbに接続すると、以下のように止まります。EclipseからのOpenOCDの呼び出しとGDBの接続方法は、インターフェース2017年2月号の内容を参考にしました。
Raspberry Piの場合は、起動後にJTAGポートの設定が必要になるので、設定直後にwaitフラグでビジーループするようにしてからGDBで接続し、そこでwaitフラグを落としてプログラムを先に進めるのが良いようです。
インターフェース2017年2月号のサンプルコードを動かすときの注意点
記事では、以下からbootsect.binとstart.elfをダウンロードし、ビルドして生成されたkernel8.imgと一緒にmicroSDにコピーして起動することになっています。raspberrypi/firmware
しかし、このままだと起動した後に反応しませんでした。そこで、config.txtに以下の2行を加えて同じ階層にコピーして起動することで動作を確認しました。Raspberry Piの起動周りのバイナリファイル(bootsect.bin, start.elf)は時期によって動作が変わるらしく、記事の執筆時点とうまく合っていなかったのかもしれません。
kernel_old=1 disable_commandline_tags=1
kernel_oldが1になっていないと、全コアが0x0から始まらないようです。0だと、32bit環境(aarch32)なら0x8000からkernel7.imgを実行、64bit環境(aarch64)なら0x80000からkernel8.imgを実行するようになっているようです。
Using the serial port with kernel_old=1
また、disable_commandline_tagsが1になっていないと、Device Treeを特定の領域にロードしてしまうようです。
New device tree kernel is in testing
これらの起動周り(ブートローダー)のソースコードや仕様が現時点で公開されていないので、何がどう変わったかは把握しづらいのはRaspberry Piを使っているうえで気になる点ですね。
コメント一覧