J-Link Remote Serverを使い、WSL2内のGDBからホストPCのUSBにつながるJ-Linkに接続します。

はじめに

WSL2(以降WSL)上でマイコンのプログラムのビルドとデバッグを行いたいです。 具体的には以下のようことをやりたいです。

  • STM32のソースをWSL上でビルドする
  • WSL上のgdb (arm-none-eabi-gdb) を使ってデバッグをする
  • STM32にはJ-Linkを使って接続する

WSL上でビルド自体はGNU Arm Embedded Toolchainを使えば特に困ることなくできます。 デバッグはWSLからUSB機器を簡単にはいじれないため何か工夫が必要でです。

WSLからホストのUSBにつなぐ一般的な方法

WSLのlinux kernelを設定してUSB/IP protocolを使い、ホスト側のUSB機器をWSL内からアクセスする方法が結構昔からありました。設定が大変そうです。

rpasek/usbip-WSL-instructions

最近WSLに関して以下のような情報が出ました。

Connecting USB devices to WSL

USB/IP protocolに関するいろいろをMS公式でサポートしてくれるというものです。 ほかの事情でWindows 11にupdateできないので、まだ試せていません。

将来的にはこの方法がUSB機器をWSL内からアクセスする一般的な方法になりそうです。

対象となる環境

私が使っていて、今回の記事の内容を検証した環境は以下の通りです。

  • ホスト: Windows 10 Pro (build 19044.1415)
  • WSL: WSL2
  • linux kernel: 5.10.16.3-microsoft-standard-WSL2
  • Linux Distribution: Ubuntu 20.04 LTS
  • J-Link: STM32F4Discoveryのst-linkをJ-Link OB化したもの

Windows Insider Programには参加していません。 WSL周りは頻繁に更新されているので、ここで紹介する方法はすぐに陳腐化するかもしれないです。

WSL内からホストのJ-Linkを使ってデバッグをする

結論として、以下のような構成によってWSL上のGDBからJ-Linkに接続してSTM32のデバッグを行うことができました。 面倒な設定が一切なので楽です。

WSLで動くlinuxとホストPCであるWindowsは、別のipアドレスを持つデバイスのように見えます。 WSLとホストの間をJ-Link Remote Serverでつなぐことで、WSL上のGDBからホストのUSBにつながるJ-Linkに接続することができます。

J-Link Remote Server

以降ではこの方法を紹介します。

ホストのWindowsでやること

準備として、J-LinkのツールをWindowsにインストールしておきます。

以下のリンク先にあるJ-Link Software and Documentation Pack にあるWindows用のインストーラを使います。

J-Link / J-Trace Downloads

デバッグのたびに以下の操作を行います。

1. マイコン、J-LinkをホストPCのUSBにつなぐ

いつも通りPCにJ-Linkをつなぎます。 マイコンにも電源を入れてJ-Linkとケーブルで接続しておきます。

以下の画像のように設定し、J-Link Remote Serverを起動します。

うまく起動できると以下のような接続待ち状態になります。

ちなみにJLinkRemoteServerCL.exeの方を使うとGUIなしでコマンドライン上でserverを起動できます。

3. WSL内から見たホストのipアドレスを調べる

ホストのWindowsでipconfigを実行して、以下の赤線部の表示されるipアドレスがWSLの内側からみたホストのアドレスです。 あとで使うのでメモしておきます。

よく調べていませんが、このアドレスは何かの拍子に変わるのかも知れないです(自分のところではまだ変わっていません)。

WSL上でやること

Windowsでの準備と同じく、J-LinkのツールをWSL上のLinuxにもインストールしておきます。 WSL上でUbuntuを使っているなら.debファイルを使うと楽です。

1. JLinkGDBServerを起動する

WSL上のJLinkGDBServerを起動します。 起動オプションで-select ip=172.28.192.1を設定すると、USBでつながるJ-Linkではなく、 172.28.192.1(=ホスト)で動くJ-Link Remote Serverにつながります。

$JLinkGDBServer -select ip=172.28.192.1 -if swd -device STM32F407VG

うまくつながるとWSL上では以下のように表示され、マイコンが認識されているのが分かります。

(省略)
Connecting to J-Link...
J-Link is connected.
Firmware: J-Link STLink V2 compiled Aug 12 2019 10:28:03
Hardware: V1.00
S/N: 772585318
Checking target voltage...
Target voltage: 3.30 V
Listening on TCP/IP port 2331
Connecting to target...
Connected to target
Waiting for GDB connection...

ホストのJ-Link Remote Serverの表示は、クライアントと繋がったような表示に変わります。

JLinkGDBServer起動時のipアドレスとして名前は設定できず、アドレスを直接指定するしかないないです。 苦しまぎれですが、bashなどから実行するときは以下のようにhostとawkを使うと、shell上で名前をアドレスに変換してJLinkGDBServerに渡すことができます。

$JLinkGDBServer -select ip=`host $(hostname).local | awk 'NR==1 {print $4}'` -if swd -device STM32F407VG

$(hostname).localはWSL内から見たホストPCの名前を指します。

2. WSL上のGDBからJLinkGDBServerに接続する

WSL上のGDB(arm-none-eabi-gdb)を起動します。 引数にはデバッグをする実行ファイルを指定します。

$arm-none-eabi-gdb test.elf

GDB上で以下のようなコマンドを実行して、同じく動くJLinkGDBServerに接続します。

(gdb)target remote localhost:2331

短縮して以下のコマンドでも問題ありません。

(gdb)tar ext :2331

これでGDBからJ-Linkがつながり、マイコンのデバッグができるようになります。

WSLのVS codeのCortex Debugを使う方法

上の方法を応用して、GDBを直接操作せずにVS Codeを使ってデバッグできるようにしました。 Windowsで動くVS codeからWSLにRemote接続し、Cortex Debugを使ってWSL上のGDBを使ってデバッグします。

図にすると以下のような構成です。

ホストのWindowsのVS codeでは Remote - WSL のextensionを使います。 Remote接続したWSL環境のVScodeに Cortex Debug をインストールします。 Cortex DebugはWSL側に追加することに注意してください。

Cortex Debugを使ったデバッグ用にlaunch.jsonに以下の設定を追加します。 これは具体例なので、お使いの環境に併せて書き換えてください。

{
    "name": "Cortex Debug",
    "cwd": "${workspaceRoot}",
    "executable": "${workspaceRoot}/build/test.elf",
    "request": "launch",
    "type": "cortex-debug",
    "armToolchainPath": "/usr/share/gcc-arm-none-eabi-10.3-2021.10/bin",
    "servertype": "jlink",
    "serverpath": "/opt/SEGGER/JLink/JLinkGDBServer",
    "ipAddress": "172.28.192.1",
    "device": "STM32F407VG",
    "interface": "swd",
    "runToMain": true,
    "svdFile": "STM32F407.svd",
    "rtos": "FreeRTOS"
}

以下の設定がポイントです。

  • armToolchainPath: WSL内のLinuxにインストールしたtoolchainへのパス
  • ipAddress: WSL内から見たホストのアドレス
  • serverpath: WSL内のLinuxにインストールしたJLinkGDBServerへのパス

これらの設定により、VS code上のデバッグボタン一つでgdbとJLinkGDBServerを起動してデバッグできました。

VS codeでデバッグを開始終了するたびにgdbとJLinkGDBServerは起動終了を繰り返します。 それでもホスト側のJ-Link Remote Serverは起動したままなので、デバッグ毎にホストでなにかする必要はありません。

できそうで出来なかったこと

最初に以下のようなことを試しましたが、なぜかWSL上で動くgdbからJLinkGDBServerには接続できませんでした。

windows上でweb serverを動かしてWSL内からそのweb serverにアクセスすることはできたので、WSL内とホストとの通信自体はできているようです。 JlinkGDBServerがlocalhost以外からは通信を受け付けないようになっているのかもしれないです。

おわりに

J-Link Remote Serverの公式サイトの説明をみると、GDBなどのデバッガを動かすPCとJ-LinkがつながるPCが分かれていることを想定して作られているようです。 今回はそれをGDBの動くWSLとJ-LinkのつながるホストWindowsの橋渡しに利用しました。

この記事の方法と同じようにして、Dockerコンテナ内にマイコンの開発環境を作り、コンテナ内のGDBからコンテナ外のホストにつながるJ-Linkに接続してデバッグすることもできそうです(未検証です)。 この場合もVS Code Remote Coninerを使えばコーディングやデバッグはVS Codeから行えるはずです。