この記事は自作OS Advent Calendar 2019 18日目の記事です。
はじまり
Raspberry Pi 4が日本で買えるようになったので、早速JTAGを使ったハードウェアデバッグできるかを試してみました。
JTAGを使ったハードウェアデバッグができるとOSなど低レイヤーの開発にデバッガが使えるようになるので、非常に効率が上がります。
用意するもの
- OpenOCDに対応したJTAGアダプタ(3.3v対応のあるもの)
- ジャンパワイヤ(オスメス)
- Raspberry Pi 4 Model B
- その他Raspberry Pi動かすのに必要なもの
JTAGアダプタの接続
JTAGデバッグを行うには、JTAGアダプタの6(+2)つの端子をターゲット(今回はRaspberry Pi 4)に接続する必要があります。
- TRST
- RTCK
- TDO
- TCK
- TDI
- TMS
- (VTref)
- (GND)
Raspberry PiのJTAG端子はGPIOのAlternate Functionとして出ています。
そして、どのGPIOポートがどの機能に対応しているかは、Raspberry Pi 4に乗っているSoCであるBCM2711のデータシートにかかれています。
なので、このデータシートから各JTAG端子に対応したGPIOポートを調べれば、JTAGアダプタとの接続が行えるようになります。
- BCM2711のデータシート
データシートpp.10にある「Table 5: Raspberry Pi 4 GPIO Alternate Functions」より、JTAG端子とGPIOポートは以下のように接続すれば良いとわかります。
JTAG | GPIO |
---|---|
TRST | GPIO 22 |
RTCK | GPIO 23 |
TDO | GPIO 24 |
TCK | GPIO 25 |
TDI | GPIO 26 |
TMS | GPIO 27 |
JTAG端子とGPIOポートの対応はわかりましたが、実際に接続を行うには どのJTAGアダプタのピン と どのRaspberry Pi 4のGPIOのピン を繋げばよいかの情報が必要になります。
例に上げたARM-USB-TINY-HはARM JTAG 20コネクタをもつJTAGアダプタです。
ARM JTAG 20コネクタの仕様はarm社のサイトで公開されているので、こちらを参照します。
- ARM の DSTREAM ターゲットのインタフェース接続 > ARM JTAG 20
Raspberry Pi 4側のGPIOとピン番号の対応は回路図のJ8コネクタにあるので、こちらを参照します。
- Raspberry Pi 4 Schematics
以上より、JTAGアダプタとRaspberry Pi 4のGPIOコネクタは以下のピンを接続すれば良いとわかります。
(VTrefとGNDは、それぞれどれか1本を繋げば良い)
JTAG | JTAGアダプタ | GPIOコネクタ |
---|---|---|
TRST | 3 | 15 |
RTCK | 11 | 16 |
TDO | 13 | 18 |
TCK | 9 | 22 |
TDI | 5 | 37 |
TMS | 7 | 13 |
VTref | 1 | 1,17 |
GND | 4,6,8,10,12,14,16,18,20 | 6,9,14,20,25,30,34,39 |
Raspberry Pi 4の設定
先程調べたように、Raspberry PiのJTAG端子はGPIOのAlternate Functionとして実装されているので、JTAG端子として利用するにはGPIOの設定が必要です。
この設定を行うため、昔は自分たちでプログラムを書く必要がありましたが、最近は以下のページを参考にconfig.txtに設定を書くだけでファームウェアが勝手に設定してくれます。
しかし、私が試したところこれだけではJTAG端子は有効になりませんでした。
- GPIO control in config.txt
調査の結果、以下のオプションをconfig.txtに記述するとGPIOの設定とJTAG端子の有効可が行われることを確認しました。
よって、config.txtに以下を追記してRaspberry Piを起動してください。
これでRaspberry PiがJTAGデバッグ可能な状態になります。
enable_jtag_gpio=1
- 参考
OpenOCDの起動
OpenOCDのインストール
JTAGアダプタの接続とRaspberry PiのJTAG有効可設定が完了したので、OpenOCDを利用してJTAGデバッグを行えるようにします。
現在OpenOCDの最新バージョンは0.10.0ですが、私の作った設定ファイルはgit headでないと動かないため、利用される方はgitのリポジトリから最新のOpenOCDをビルドしてインストールしてください。
- OpenOCD official mirror of this repository
なおMacをお使いの方はbrewで最新のOpenOCDをインストールできます。
brew install openocd --HEAD
設定ファイルの取得
OpenOCDでデバッグを行うには、JTAGアダプタの設定ファイルと接続先の情報が書かれた設定ファイルが必要になります。
JTAGアダプタの設定ファイルはOpenOCD側が持っていますが、Raspberry Pi 4のCPUに接続するための設定ファイルはありません。
この設定ファイルは自分で調べて作ることも可能ですが、今回は私が調べて書いたものを以下でダウンロードして使ってください。
- OpenOCD config for Raspberry Pi 4
※ APのROMからアドレス調べてやる方法は、以前書いたこちらの資料を参照してください
※ APのROMからアドレス調べた後に、6ヶ月前にこちらの記事のコメントにRaspberry Pi 4向けのアドレスが書かれているのを見つけました。海外の人ははやいです。
OpenOCDの起動
先程ダウンロードしたRaspberry Pi 4向けの設定ファイル(raspi4.cfg)のあるディレクトリで以下のコマンドを実行してください。
(別のJTAGアダプタを利用している方は/usr/local/share/openocd/scripts/interface
以下などから設定ファイルを探してコマンドを書き換えてください)
openocd -f interface/ftdi/olimex-arm-usb-tiny-h.cfg -f raspi4.cfg
うまく接続等ができていれば、以下のようなメッセージが出てきます。
~/p/s/s/s/s/openocd ❯❯❯ openocd -f interface/ftdi/olimex-arm-usb-tiny-h.cfg -f raspi4.cfg
Open On-Chip Debugger 0.10.0+dev-00865-g04ebb43f (2019-05-17-04:25)
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 : Listening on port 3333 for gdb connections
あとは以下のようにgdbでアクセスすればデバッグができるでしょう(筆者はdocker内に入れたgdb-multiarchで確認しました)(Raspberry Piは何もしないと32bit modeで起動するので、32bit armとしてつないでいます)。
~ ❯❯❯ docker run -it --rm debian:latest /bin/bash
root@5f39aa814899:/# apt -y install gdb-multiarch
root@5f39aa814899:/# gdb-multiarch
(gdb) set architecture armv8-a
(gdb) target remote host.docker.internal:3333
Remote debugging using host.docker.internal:3333
warning: Selected architecture armv8-a is not compatible with reported target architecture aarch64
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0xc021ce88 in ?? ()
(gdb) i r
r0 0x0 0
r1 0x240b4 147636
r2 0xeff1d3f0 4025603056
r3 0xc021ce80 3223441024
r4 0xc1004dbc 3238022588
r5 0xc1000000 3238002688
r6 0xc1004e04 3238022660
r7 0x1 1
r8 0xc109641a 3238618138
r9 0xc10a56c0 3238680256
r10 0xc0e67a38 3236330040
r11 0xc1001f34 3238010676
r12 0xc1001f38 3238010680
sp 0xc1001f28 0xc1001f28
lr 0xc0209a9c 0xc0209a9c
pc 0xc021ce88 0xc021ce88
cpsr 0x60000093 1610612883
fpscr 0x0 0
おまけで、telnetで確認した結果はこちら
~ ❯❯❯ telnet localhost 4444 ✘ 130
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> reg
===== Aarch64 registers
(0) x0 (/64): 0x0000000000000000 (dirty)
(1) x1 (/64): 0x00000000000240B4
(2) x2 (/64): 0x00000000EFF1D3F0
(3) x3 (/64): 0x00000000C021CE80
(4) x4 (/64): 0x00000000C1004DBC
(5) x5 (/64): 0x00000000C1000000
(6) x6 (/64): 0x00000000C1004E04
(7) x7 (/64): 0x0000000000000001
(8) x8 (/64): 0x00000000C109641A
(9) x9 (/64): 0x00000000C10A56C0
(10) x10 (/64): 0x00000000C0E67A38
(11) x11 (/64): 0x00000000C1001F34
(12) x12 (/64): 0x00000000C1001F38
(13) x13 (/64): 0x00000000C1001F28
(14) x14 (/64): 0x00000000C0209A9C
(15) x15 (/64)
(16) x16 (/64)
(17) x17 (/64)
(18) x18 (/64)
(19) x19 (/64)
(20) x20 (/64)
(21) x21 (/64)
(22) x22 (/64)
(23) x23 (/64)
(24) x24 (/64)
(25) x25 (/64)
(26) x26 (/64)
(27) x27 (/64)
(28) x28 (/64)
(29) x29 (/64)
(30) x30 (/64)
(31) sp (/64)
(32) pc (/64): 0x00000000C021CE88 (dirty)
(33) cpsr (/32): 0x60000093 (dirty)
(34) v0 (/128)
(35) v1 (/128)
(36) v2 (/128)
(37) v3 (/128)
(38) v4 (/128)
(39) v5 (/128)
(40) v6 (/128)
(41) v7 (/128)
(42) v8 (/128)
(43) v9 (/128)
(44) v10 (/128)
(45) v11 (/128)
(46) v12 (/128)
(47) v13 (/128)
(48) v14 (/128)
(49) v15 (/128)
(50) v16 (/128)
(51) v17 (/128)
(52) v18 (/128)
(53) v19 (/128)
(54) v20 (/128)
(55) v21 (/128)
(56) v22 (/128)
(57) v23 (/128)
(58) v24 (/128)
(59) v25 (/128)
(60) v26 (/128)
(61) v27 (/128)
(62) v28 (/128)
(63) v29 (/128)
(64) v30 (/128)
(65) v31 (/128)
(66) fpsr (/32)
(67) fpcr (/32)
(68) ELR_EL1 (/64): 0x00000000C0209A9C
(69) ESR_EL1 (/32): 0x00000206
(70) SPSR_EL1 (/32): 0x40000013
(71) ELR_EL2 (/64)
(72) ESR_EL2 (/32)
(73) SPSR_EL2 (/32)
(74) ELR_EL3 (/64)
(75) ESR_EL3 (/32)
(76) SPSR_EL3 (/32)
===== Aarch32 registers
(77) r0 (/32): 0x00000000
(78) r1 (/32): 0x000240B4
(79) r2 (/32): 0xEFF1D3F0
(80) r3 (/32): 0xC021CE80
(81) r4 (/32): 0xC1004DBC
(82) r5 (/32): 0xC1000000
(83) r6 (/32): 0xC1004E04
(84) r7 (/32): 0x00000001
(85) r8 (/32): 0xC109641A
(86) r9 (/32): 0xC10A56C0
(87) r10 (/32): 0xC0E67A38
(88) r11 (/32): 0xC1001F34
(89) r12 (/32): 0xC1001F38
(90) sp (/32): 0xC1001F28
(91) lr (/32): 0xC0209A9C
(92) pc (/32): 0xC021CE88
(93) cpsr (/32): 0x60000093
(94) d0 (/64)
(95) d1 (/64)
(96) d2 (/64)
(97) d3 (/64)
(98) d4 (/64)
(99) d5 (/64)
(100) d6 (/64)
(101) d7 (/64)
(102) d8 (/64)
(103) d9 (/64)
(104) d10 (/64)
(105) d11 (/64)
(106) d12 (/64)
(107) d13 (/64)
(108) d14 (/64)
(109) d15 (/64)
(110) d16 (/64)
(111) d17 (/64)
(112) d18 (/64)
(113) d19 (/64)
(114) d20 (/64)
(115) d21 (/64)
(116) d22 (/64)
(117) d23 (/64)
(118) d24 (/64)
(119) d25 (/64)
(120) d26 (/64)
(121) d27 (/64)
(122) d28 (/64)
(123) d29 (/64)
(124) d30 (/64)
(125) d31 (/64)
(126) fpscr (/32)
おしまい
Raspberry Pi 4で低レイヤー開発生活をお楽しみください。