ラズパイ4: OpenOCD + Raspberry Pi4のJTAGデバッグ
1. 概要
Raspberry Pi4 などのボードでお手軽にJTAGデバッガを利用してデバッグするための情報をまとめています。
- JTAGアダプタ: C232HM-DDHSL-0
- デバッガ: OpenOCD
- OS環境: Linux (Ubuntu 22)
2. JTAGアダプタ C232HM-DDHSL-0
OpenOCDに対応したJTAGアダプタ(3.3v版)が必要です。該当する商品はいくつか存在するのですが、使い勝手が良さそうということで少しお値段は高めではるのですが、C232HM-DDHSL-0というJTAGアダプタを使うことにしました。
C232HM-DDHSL-0は他の通販サイトで買うよりも少し値段が安かったので、モノタロウで購入しました。モノタロウ自体は個人でも購入可能です。
ラズパイ4とJTAGアダプタの接続
以下の情報に従って、JTAGアダプタのピンとラズパイ4のピンヘッダを接続します。
JTAG | C232HM-DDHSL-0 | ラズパイ4 ピンヘッダ番号 |
---|---|---|
TDI | 黄色のケーブル | 37 |
TDO | 緑色のケーブル | 18 |
TCK | オレンジ色のケーブル | 22 |
TMS | 茶色のケーブル | 13 |
TRST | 灰色のケーブル | 15 |
SRST | - | Not connected |
RTCK | 青色のケーブル | 16 |
VCC | -(接続しない) | - |
GND | 黒色のケーブル | 39(他にもある) |
ラズパイ4側のピンアサイン:
画像はここからから抜粋
JTAGアダプタ側のピンアサイン:
JTAGアダプタのデータシート:
3. OpenOCD環境の準備
インストール
openocdをインストールします。
sudo apt install openocd
ラズパイ4のJTAGインタフェースの設定
ラズパイ4のJTAGインターフェースを有効化するために、SDカードのbootパーティション直下にあるconfig.txt
に以下を追加します。
enable_jtag_gpio=1
コンフィグファイルの用意
JTAGアダプタ用の設定ファイルと、Raspberry Pi4用の設定ファイルの2つを作成します。なお、ここに掲載しているファイルは以下にコミットしているので利用して下さい。
C232HM-DDHSL-0インタフェース用configファイル
JTAGアダプタのピンアサインなどをC232HM-DDHSL-0用に設定します。
adapter driver ftdi
ftdi_device_desc C232HM-DDHSL-0
ftdi_vid_pid 0x0403 0x6014
ftdi_layout_init 0x0018 0x007b
ftdi_layout_signal nTRST -data 0x0010
Raspberry Pi 4用configファイル
設定の文法含めて設定の意味はよく理解していないのですが、基本的に以下のサイトに記載してある情報そのままです。
set _CHIPNAME bcm2711
set _DAP_TAPID 0x4ba00477
adapter speed 1000
transport select jtag
reset_config trst_and_srst
telnet_port 4444
# create tap
jtag newtap auto0 tap -irlen 4 -expected-id $_DAP_TAPID
# create dap
dap create auto0.dap -chain-position auto0.tap
set CTIBASE {0x80420000 0x80520000 0x80620000 0x80720000}
set DBGBASE {0x80410000 0x80510000 0x80610000 0x80710000}
set _cores 4
set _TARGETNAME $_CHIPNAME.a72
set _CTINAME $_CHIPNAME.cti
set _smp_command ""
for {set _core 0} {$_core < $_cores} { incr _core} {
cti create $_CTINAME.$_core -dap auto0.dap -ap-num 0 -baseaddr [lindex $CTIBASE $_core]
set _command "target create ${_TARGETNAME}.$_core aarch64 \
-dap auto0.dap -dbgbase [lindex $DBGBASE $_core] \
-coreid $_core -cti $_CTINAME.$_core"
if {$_core != 0} {
set _smp_command "$_smp_command $_TARGETNAME.$_core"
} else {
set _smp_command "target smp $_TARGETNAME.$_core"
}
eval $_command
}
eval $_smp_command
targets $_TARGETNAME.0
OpenOCDを起動
OpenOCDを起動すると、gdbサーバー(ポート番号3333)とtelnet用(ポート番号4444)の2つのTCPサービスが起動します。gdbをアタッチしたり、telnetコマンドでログインして操作することが可能です。
$ sudo openocd -f c232hm-edhsl-0.cfg -f raspi4.cfg
Open On-Chip Debugger 0.11.0-g610f137 (2022-05-04-10:57)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 1000 kHz
Info : JTAG tap: auto0.tap tap/device found: 0x4ba00477 (mfg: 0x23b (ARM Ltd), part: 0xba00, ver: 0x4)
Info : bcm2711.a72.0: hardware has 6 breakpoints, 4 watchpoints
Info : bcm2711.a72.1: hardware has 6 breakpoints, 4 watchpoints
Info : bcm2711.a72.2: hardware has 6 breakpoints, 4 watchpoints
Info : bcm2711.a72.3: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for bcm2711.a72.0 on 3333
Info : Listening on port 3333 for gdb connections
4. gdbデバッガをアタッチ
ここからはgdb(コマンド)を利用したデバッグ方法について解説していきます。
gdbのインストール
リモートでaarch64ターゲットのデバッグをするために、gdb-multiarch
をインストールします。
$ sudo apt install gdb-multiarch
デバッガを接続する
$ gdb-multiarch
〜 (中略) 〜
(gdb) set architecture aarch64
The target architecture is set to "aarch64".
(gdb) target extended-remote localhost:3333
Remote debugging using localhost:3333
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x0000000000088c88 in ?? ()
(gdb) info registers
x0 0xfe201000 4263514112
x1 0x0 0
x2 0x1 1
x3 0x88294e13 2284408339
デバッグ対象のelfをロード
(gdb) file <elfファイルパス>
以降は通常のgdbを利用したデバッグと同じです。
5. telnetで操作してみる
telnetでログインしてから、専用コマンドを利用してデバッグすることも可能ですので、それについて簡単に解説していきます。
ログイン
telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
>
CPUコアの情報を確認
> targets
TargetName Type Endian TapName State
-- ------------------ ---------- ------ ------------------ ------------
0* bcm2711.a72.0 aarch64 little auto0.tap halted
1 bcm2711.a72.1 aarch64 little auto0.tap halted
2 bcm2711.a72.2 aarch64 little auto0.tap halted
3 bcm2711.a72.3 aarch64 little auto0.tap halted
CPUレジスタ情報を確認
> reg
===== Aarch64 registers
(0) x0 (/64): 0x00000000fe201000 (dirty)
(1) x1 (/64): 0x0000000000000000 (dirty)
(2) x2 (/64): 0x0000000000000001
(3) x3 (/64): 0x0000000088294e13
(4) x4 (/64): 0x0000000000000000
(5) x5 (/64): 0x0000000000000000
6. VSCodeの設定
VSCodeのUI操作でデバッグするために設定を行います。VSCode ExtentionsのNative Debugを利用したデバッガのアタッチは上手く動作させることが出来なかったため、代わりに標準?のcppdbg
を利用しました。
openocdの起動
sudo openocd -f c232hm-edhsl-0.cfg -f raspi4.cfg
launch.jsonファイルの作成
とりあえずプログラムをデバイスのSDカード等に書き込んでおいて、後からデバッガを起動してアタッチするという方法だと、ブート初期などの早い段階でデバッグが必要な場合において時間的に全然間に合いません。
そこで、load
コマンドを使うことで、ラズパイ4のメモリにelfファイルを転送してそのバイナリで動作できるようにしています。つまりローカルでビルドしたelfファイルを再度SDカード等へ手動で転送してデバッグする手間が不要になります。
{
"version": "0.2.0",
"configurations": [
{
"name": "OpenOCD + GDB Debug",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/<elfファイルへのパス>",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceRoot}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb-multiarch",
"setupCommands": [
{"text": "set architecture aarch64"},
{"text": "target extended-remote localhost:3333"},
{"text": "file ${workspaceRoot}/<elfファイルへのパス>"},
{"text": "interrupt"},
{"text": "monitor reset halt"},
{"text": "load"},
{"text": "break main"},
]
}
]
}
デバッグ
デバイスの電源を入れて、VSCodeからデバッグ(再生)ボタンを押せば、指定したelfファイルをデバイスにロードして実行してくれます。これでブレークポイントを張っている部分のデバッグ等が可能です。
7. 参考文献
Discussion