🐩

ラズパイ4: OpenOCD + Raspberry Pi4のJTAGデバッグ

2022/11/18に公開

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は他の通販サイトで買うよりも少し値段が安かったので、モノタロウで購入しました。モノタロウ自体は個人でも購入可能です。

https://www.monotaro.com/g/02087095/

ラズパイ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アダプタのデータシート:
http://www.ftdichip.com/Support/Documents/DataSheets/Cables/DS_C232HM_MPSSE_CABLE.pdf

3. OpenOCD環境の準備

インストール

openocdをインストールします。

sudo apt install openocd

ラズパイ4のJTAGインタフェースの設定

ラズパイ4のJTAGインターフェースを有効化するために、SDカードのbootパーティション直下にあるconfig.txtに以下を追加します。

enable_jtag_gpio=1

コンフィグファイルの用意

JTAGアダプタ用の設定ファイルと、Raspberry Pi4用の設定ファイルの2つを作成します。なお、ここに掲載しているファイルは以下にコミットしているので利用して下さい。

https://github.com/HidenoriMatsubayashi/openocd-configs

C232HM-DDHSL-0インタフェース用configファイル

JTAGアダプタのピンアサインなどをC232HM-DDHSL-0用に設定します。

c232hm-edhsl-0.cfg
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ファイル

設定の文法含めて設定の意味はよく理解していないのですが、基本的に以下のサイトに記載してある情報そのままです。

https://smartobject.gitlab.io/so3/so3_jtag_rpi4.html

raspi4.cfg
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カード等へ手動で転送してデバッグする手間が不要になります。

.vscode/launch.json
{
  "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. 参考文献

https://hikalium.hatenablog.jp/entry/2021/07/18/214013

https://smartobject.gitlab.io/so3/so3_jtag_rpi4.html

https://qiita.com/misodengaku/items/085692f1d2a2094d04f9

https://qiita.com/tnishinaga/items/89af35b32b6c4698cf9f

Discussion