Debug Probe を使ってデバッグする(2)

前回の記事では、Debug Probe のハードウェアの準備について説明した。今回はソフトウェア側を準備して、実際にプログラムをデバッグ実行してみる。

デバッグ用ツールのインストール

デバッグにはいくつかのツールが必要らしい。

  • OpenOCD(0.11.0 か 0.12.0 のバージョンが必要)
  • gdb-multiarch
  • UART0の確認のため適宜:miniterm

ubuntu 22.04の場合

ubuntu 22.04の場合は以下のようにパッケージをインストールする。

$ sudo apt install openocd gdb-multiarch

ubuntu 20.04の場合

ubuntu 20.04は公式パッケージにある OpenOCD のバージョンが 0.10.0だったので、ビルドしなければならない。

まず必要なパッケージをインストールする。

$ sudo apt install automake autoconf build-essential texinfo libtool libftdi-dev libusb-1.0-0-dev

次に OpenOCD のgithubレポジトリをクローンする。

$ git clone https://github.com/raspberrypi/openocd.git --branch rp2040 --depth=1 --no-single-branch

セットアップし、ビルドする

$ cd openocd
$ ./bootstrap
$ ./configure
$ make -j4

これまでのプロセスで問題が生じなければインストールする。

$ sudo make install

最後にgdbをインストールする。

$ sudo apt install gdb-multiarch

デバッグ用のプログラムの用意

Debug Probe のテストのため、CQ出版インターフェース誌7月号特集「ラズパイPicoで1500行 ゼロから作るOS」の第2部第4章のプログラムを使ってデバッグしてみる。

前回の反省から、ビルドに関するファイルを build フォルダにまとめることにした。

$ cd part_2_sect_4
$ tree .
.
├── application
│   └── main.c
├── boot
│   ├── boot2.c
│   ├── reset_hdr.c
│   └── vector_tbl.c
├── build
│   └── build.sh
├── include
│   ├── knldef.h
│   ├── sysdef.h
│   ├── syslib.h
│   ├── trykernel.h
│   └── typedef.h
├── kernel
│   └── syslib.c
├── lib -> ../part_2_sect_3/lib/
└── linker
    └── pico_memmap.ld

7 directories, 12 files

libディレクトリは第2部第3章のものを使うようにシンボリックリンクを作った。kernelディレクトリは第4章で新たに導入されたものである。

buildディレクトリには、ビルドのためのスクリプト(build.sh)を用意した。スクリプトの内容は以下の通り。(以前に説明したコマンドに比べて、デバッグ情報を生成するコンパイルオプションを加えている)

/usr/bin/arm-none-eabi-gcc -ggdb -g3 -c -I ../include -mthumb -mcpu=cortex-m0plus ../application/*.c ../boot/*.c ../kernel/*.c ../lib/*.c
/usr/bin/arm-none-eabi-gcc -nostartfiles -Wl,--script=../linker/pico_memmap.ld -Wl,-z,max-page-size=4096 -Wl,--gc-sections *.o -o out.elf

ビルドスクリプトを実行すると、out.elf が生成される。

$ cd build
$ sh build.sh

デバッグしてみる

上でビルドしたプログラムを Raspberry Pi pico に転送して、デバッグしてみる。

まずは pico に接続した Debug Probe のUSBケーブルをPCに接続し、pico のUSBにも電源を供給する。

続いて OpenOCD を使ってプログラムを転送・実行してみる。

$ sudo openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program out.elf verify reset exit"
Open On-Chip Debugger 0.11.0-g8e3c38f-dirty (2023-06-07-12:36)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
Info : Hardware thread awareness created
Info : Hardware thread awareness created
Info : RP2040 Flash Bank Command
adapter speed: 5000 kHz

Info : Using CMSIS-DAPv2 interface with VID:PID=0x2e8a:0x000c, serial=E6616407E31CA62C
Info : CMSIS-DAP: SWD  Supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 0 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 0
Info : CMSIS-DAP: Interface ready
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
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
** Programming Started **
Info : RP2040 B0 Flash Probe: 2097152 bytes @10000000, in 512 sectors

target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
Info : Writing 4096 bytes starting at 0x0
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
** Programming Finished **
** Verify Started **
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00
** Verified OK **
** Resetting Target **
shutdown command invoked

転送するファイルは out.uf2 ではなく、out.elf であることに注意する。

Debug Probe を使わずUSBドライブとして認識された pico にプログラムを転送する場合、いったんUSBケーブルを抜いてから、BOOTSEL を押した状態でUSBケーブルを接続し、UF2形式のファイルを転送しなければならない。

一方、Debug Probe を使う場合は pico のケーブル類を接続した状態のままで、PCからプログラムの転送や pico のリセットなどが行えるようになっている。

OpenOCD のサーバ動作

さて、プログラムをデバッグする際には、OpenOCD をサーバとして動作させながら gdb でデバッグ操作を行うため、シェルが2つ必要となる。

一方のシェルでは以下のように OpenOCD を実行させておく。

$ sudo openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000"
Open On-Chip Debugger 0.11.0-g8e3c38f-dirty (2023-06-07-12:36)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
Info : Hardware thread awareness created
Info : Hardware thread awareness created
Info : RP2040 Flash Bank Command
adapter speed: 5000 kHz

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : Using CMSIS-DAPv2 interface with VID:PID=0x2e8a:0x000c, serial=E6616407E31CA62C
Info : CMSIS-DAP: SWD  Supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 0 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 0
Info : CMSIS-DAP: Interface ready
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

gdb-multiarch によるデバッグ実行

この状態でもう一つのシェルを開き、build ディレクトリに移動してから gdb-multiarch を実行する。

$ cd ~/(..中略..)/part_2_sect_4/build
$ gdb-multiarch out.elf
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.                                                                                                                                            
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from out.elf...
(gdb) 

OpenOCD のサーバに接続する。

(gdb) target remote localhost:3333
Remote debugging using localhost:3333
0x00000000 in ?? ()
(gdb) 

pico にリセットをかけたところでストップ。

(gdb) monitor reset init
target halted due to debug-request, current mode: Thread 
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
(gdb) 

この状態では pico 上のLEDは消灯したままなので、プログラムを実行してみる。

(gdb) continue
Continuing.

すると pico のLEDが点滅を開始する。実行は Ctrl-C で止めることができる。

^Ctarget halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x00000178 msp: 0x20041f00

Thread 1 received signal SIGINT, Interrupt.
delay_ms (ms=1000) at ../application/main.c:9
9               if((in_w(SYST_CSR) & SYST_CSR_COUNTFLAG)!=0) {  /* TIMER_PERIOD経過するとフラグがセット */
(gdb) 

停止したところのコードが表示されているのがわかる。

UART0の出力確認

さらに別のシェルを開いて、UART0の出力を確認することもできる。(自分のマシンでは /dev/ttyACM1 としてシリアルポートが認識されていた)

第2部第4章のサンプルでは、シリアルポートにデバッグ出力するようになっている。

以下のように別シェルでコマンドを入力し、gdb のシェルで monitor reset init、continue を入力すれば「hello,world」と表示されることがわかる。

$ miniterm /dev/ttyACM1 115200
--- Miniterm on /dev/ttyACM1  115200,8,N,1 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
hello,world

関数にブレークポイントを設定したり step したりと、gdb のコマンドを使ったデバッグが行えることがわかった。

予想したよりはるかに簡単だった…