Raspberry Pi PicoをWSL+OpenOCDでJTAG(SWD)デバッグする

[Raspberry Pi PicoをWSL(Windows Subsystem for Linux)で使う]
(https://qiita.com/yunkya2/items/e67996301a2d8d2a5609)の続きです。WSL上でRaspberry Pi PicoのJTAGデバッグをやってみました。


# 接続の概要

Raspberry Pi Picoが2枚ある場合は、1枚を**[Picoprobe](https://github.com/raspberrypi/picoprobe)**というアプリでJTAG(SWD)デバッグプローブにすることで、もう1枚のRaspberry Pi Pico上のアプリのデバッグに使うことができます。

![rppico-debug-connection.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642667/b2c1312e-7ce6-8c49-64e8-25c9227404f0.png)

具体的なやり方は **[Getting started with Raspberry Pi Pico](https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf)** の **Appendix A: Using Picoprobe** に載っているのですが、WSLを利用する場合、WSLからはPicoprobeのつながったUSBが見えないため、以下のような接続になります。

- OpenOCDはWindowsアプリとして動作させ、USBの先のPicoprobeに接続する
- gdbはLinuxアプリとしてWSL上で動作させ、Windows側のOpenOCDにTCP/IPで接続する

# OpenOCDのビルド

まず、PicoprobeとつながるWindows側のOpenOCDをビルドします。**A.1.2. Windows** のやり方に従って、WSLでなく[MSYS2](https://www.msys2.org/)を使用します。
[こちら](https://github.com/yunkya2/openocd-win64) (https://github.com/yunkya2/openocd-win64) にMSYS2上でOpenOCDをビルドしてWSL環境用のtarballを作るためのMakefileを用意しました。
また、私の環境でビルドしたバイナリを[こちら](https://github.com/yunkya2/openocd-win64/raw/master/release/openocd-w64-prebuilt-20210130.tar.gz) に置いてありますので、ダウンロードして使っていただくこともできます(動作の保証は致しかねますので自己責任で)。

ビルドもしくはダウンロードしたtarballは、WSL環境側で展開して`openocd-w64/bin`にパスを通しておきます。bashから`openocd.exe`が実行できることを確認します。

```
$ openocd.exe
Open On-Chip Debugger 0.11.0-rc2+dev-gabbf03b09 (2021-01-30-21:57)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
embedded:startup.tcl:26: Error: Can't find openocd.cfg
in procedure 'script'
at file "embedded:startup.tcl", line 26
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Error: Debug Adapter has to be specified, see "adapter driver" command
embedded:startup.tcl:26: Error:
in procedure 'script'
at file "embedded:startup.tcl", line 26
$
```

# Raspberry Pi Pico (デバッグプローブ用)の環境準備

デバッグプローブとなる Raspbberry Pi Pico にpicoprobeをインストールします。
方法は **A.2. Build and flash picoprobe** にありますが、Raspberry Pi公式でビルド済みのpicoprobeバイナリが用意されていますので、[ここ](https://www.raspberrypi.org/documentation/pico/getting-started/static/d6bbb56350627ecd9cf080cbee142b28/picoprobe.uf2) からダウンロードした picoprobe.uf2 を BOOTSELボタンを押しながら起動したRaspbberry Pi Picoにドラッグ&ドロップするだけでOKです。

# 結線

**A.3. Picoprobe Wiring** の通りです。
picoprobe自体にUSBシリアル変換の機能がありますので、Raspberry PiのUART同士をつないでやることで、picoprobeを接続したWindows PC側からデバッグターゲットのUARTを見ることができます。

# Windows側のドライバインストール

**A.4. Install Picoprobe driver (only needed on Windows)** の通り…かと思ったのですが、ドキュメント通りに[Zadig](http://zadig.akeo.ie) (http://zadig.akeo.ie) からlibusb-win32をインストールしてもOpenOCDがクラッシュしてしまいます。いろいろ試したところ、ここで**WinUSB**をインストールしたら動作するようになりました(このあたりのドライバの違いが良く分からん…)。

![zadig.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642667/fc5390e8-2d22-95b2-5247-59357f2ee4a6.png)

# デバッグの実行

WSL側には事前にgdb-multiarchをインストールしておきます。

```
$ sudo apt install gdb-multiarch
```

また、デバッグ対象のアプリのビルド時には、**5.1. Build "Hello World" debug version** にあるように、cmakeに`-DCMAKE_BUILD_TYPE=Debug`を指定してデバッグ情報が入るようにします。

```
$ cd ~/pico/pico-examples/
$ rm -rf build
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Debug ..
$ cd hello_world
$ make -j4
```



この後、WindowsアプリであるOpenOCDを起動して、WSL上で動かすgdbからこれに接続する必要があるのですが、この手順はWSL1とWSL2とで大きく異なります。WSL2はVMのLinuxカーネル上で動作しているため、WSL環境とWindows環境が異なるIPアドレスとして扱われるためです。

## WSL1の場合

**5.4. Use GDB and OpenOCD to debug Hello World** と同じ手順でできます。WSL上から以下のコマンドラインでOpenOCDを実行します。

```
$ openocd.exe -f interface/picoprobe.cfg -f target/rp2040.cfg
Open On-Chip Debugger 0.11.0-rc2+dev-00014-gabbf03b09-dirty (2021-01-28-13:19)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'swd'
Warn : Transport "swd" was already selected
adapter speed: 5000 kHz

Info : Hardware thread awareness created
Info : Hardware thread awareness created
Info : RP2040 Flash Bank Command
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 5000 kHz
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x00000001
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x10000001
Info : rp2040.core0: hardware has 4 breakpoints, 2 watchpoints
Info : rp2040.core1: hardware has 4 breakpoints, 2 watchpoints
Info : starting gdb server for rp2040.core0 on 3333
Info : Listening on port 3333 for gdb connections
```

OpenOCDを実行したのとは別のターミナルを開いて、デバッグ対象のELFファイルを指定してgdb-multiarchを実行し、`target remote`コマンドでOpenOCDに接続します。

```
$ gdb-multiarch hello_world.elf

:

(gdb) target remote localhost:3333
```

`load`コマンドでフラッシュにELFファイルをロードし、`monitor reset init`コマンド実行後、`continue`コマンドで実行を開始します。

```
(gdb) load
Loading section .boot2, size 0x100 lma 0x10000000
Loading section .text, size 0x49c8 lma 0x10000100
Loading section .rodata, size 0xd88 lma 0x10004ac8
Loading section .binary_info, size 0x24 lma 0x10005850
Loading section .data, size 0x9ec lma 0x10005874
Start address 0x10000104, load size 25184
Transfer rate: 6 KB/sec, 4197 bytes/write.
(gdb) monitor reset init
target halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
target halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
(gdb) continue
Continuing.
```

## WSL2の場合

以下のコマンドラインでOpenOCDを実行します。`bindto`コマンドの実行を追加することで、OpenOCDを実行しているホスト以外からの接続を受け入れるようにします。

```
$ openocd.exe -f interface/picoprobe.cfg -f target/rp2040.cfg -c 'bindto 0.0.0.0'
```

初回の接続の場合のみ、以下のようなファイアウォールの警告が出ます。WSL2から接続できるようにするためには、「パブリック ネットワーク」の方にもチェックを入れてアクセスを許可する必要があります。

![rppico-debug-fwall-first.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642667/df80204f-41de-6b66-5c59-379f68d5ad4a.png)

もしチェックを入れ忘れてダイアログを閉じてしまった場合は、コントロールパネルの「Windows Defenderファイアウォール」→「Windows Defenderファイアウォールを介したアプリまたは機能の許可」を選択し、openocd.exe に対して「プライベート」「パブリック」の両方にチェックが入っている状態にします。

![rppico-debug-fwall-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642667/7d5e97ed-bec6-5db5-5c71-c66e4eff20ca.png)

更に、OpenOCDの接続先となる、WSLから見たWindows側のIPアドレスを知る必要があります。/etc/resolv.confでnameserverが指定されているアドレスがそれになるので、事前に調べておきます。

```
$ cat /etc/resolv.conf
# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 172.xx.xx.xx         <=== このアドレスを覚えておく
```

gdbのリモート接続時にはこのアドレスを指定します。

```
$ gdb-multiarch hello_world.elf

:

(gdb) target remote 172.xx.xx.xx:3333   <=== 先ほど調べたアドレスを指定する
```

この後の実行手順は同じです。

### (追記)WSL2用gdb起動スクリプト

以下のようなスクリプトを用意しておくと、いちいちアドレスを調べる手間が省けて便利です。

```bash
#!/bin/sh
gdb-multiarch -ex "target remote `tail -1 /etc/resolv.conf|awk '{print $2}'`:3333" $*
```

上のスクリプトを`gdb-pico`という名前で保存しておけば、実行したいELFファイルを指定して

```
$ gdb-pico hello_world.elf
```

これだけで、OpenOCDに接続するところまでやってくれます。