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


Raspberry Pi PicoをWSL(Windows Subsystem for Linux)で使うの続きです。WSL上でRaspberry Pi PicoのJTAGデバッグをやってみました。

接続の概要

Raspberry Pi Picoが2枚ある場合は、1枚をPicoprobeというアプリでJTAG(SWD)デバッグプローブにすることで、もう1枚のRaspberry Pi Pico上のアプリのデバッグに使うことができます。

具体的なやり方は Getting started with Raspberry Pi PicoAppendix 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://github.com/yunkya2/openocd-win64) にMSYS2上でOpenOCDをビルドしてWSL環境用のtarballを作るためのMakefileを用意しました。
また、私の環境でビルドしたバイナリをこちら に置いてありますので、ダウンロードして使っていただくこともできます(動作の保証は致しかねますので自己責任で)。

ビルドもしくはダウンロードした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バイナリが用意されていますので、ここ からダウンロードした 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) からlibusb-win32をインストールしてもOpenOCDがクラッシュしてしまいます。いろいろ試したところ、ここでWinUSBをインストールしたら動作するようになりました(このあたりのドライバの違いが良く分からん…)。

デバッグの実行

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から接続できるようにするためには、「パブリック ネットワーク」の方にもチェックを入れてアクセスを許可する必要があります。

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

更に、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起動スクリプト

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

#!/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に接続するところまでやってくれます。