Raspberry Pi Pico2 の RISC-V を OpenOCD でデバッグする

Raspberry Pi Pico2 では ARM Cortex-M と RISC-V の2種類のCPUを使うことができる。
デバッガを使って開発できれば便利なので、OpenOCDからRISC-Vをデバッグできる環境を作ろうと試してみた。
想定上にはまるところが多かったものの、何とか動かすことができた。
ここでははまったポイントをまとめておく。

前提

  • CPU
    • Apple M2
  • Debugger
    • Raspberry Pi Debug probe
  • homebrew
1% brew --version
2Homebrew 5.0.7

OpenOCDでlinux/parport.hが見つからない

状況

MacでビルドしているのでLinuxの一部のヘッダが見つからないと言われる。
Linuxのヘッダが見つからないこと自体は特に不思議はないものの、
使用するOpenOCDのリポジトリによってエラーが出たり・出なかったりする。
今回はriscv-openocdの場合に出た。

1-I/opt/homebrew/Cellar/hidapi/0.15.0/include/hidapi -Wall -Wstrict-prototypes -Wformat-security -Wshadow -Wextra -Wno-unused-parameter -Wbad-function-cast -Wcast-align -Wredundant-decls -Wpointer-arith -Wundef -Werror -g -O2 -MT src/jtag/drivers/libocdjtagdrivers_la-amt_jtagaccel.lo -MD -MP -MF src/jtag/drivers/.deps/libocdjtagdrivers_la-amt_jtagaccel.Tpo -c src/jtag/drivers/amt_jtagaccel.c -o src/jtag/drivers/libocdjtagdrivers_la-amt_jtagaccel.o
2src/jtag/drivers/amt_jtagaccel.c:16:10: fatal error: 'linux/parport.h' file not found
3   16 | #include <linux/parport.h>
4      |          ^~~~~~~~~~~~~~~~~
51 error generated.
6make[2]: *** [src/jtag/drivers/libocdjtagdrivers_la-amt_jtagaccel.lo] Error 1
7make[1]: *** [all-recursive] Error 1
8make: *** [all] Error 2

対処方法

ソースコードを直すのも手かもしれないが、
そもそもこのアダプタを使用することがなければ、
アダプタ自体を無効にしてしまうのが手っ取り早い。
configureで以下のように指定する。

1% ./configure --disable-amtjtagaccel

なお、リポジトリによりエラーの状況が変わる理由は、
デフォルトで上記のJTAGアダプタを有効にするかどうかの違いだった。

Rasbperry Piから提供されているOpenOCDを使用する必要がある

状況

本家のOpenOCDやriscv-openocdでは、
Raspberry PiのDebug Probe経由でのデバッグに対応していなかった。
ざっと見たところでは以下の問題があるように見えた。

  • cfg関連の解釈できない部分があった(以下のエラー)
  • RISC-VターゲットがSWDに対応していなかった
 1% ./src/openocd -d4 -c "set USE_CORE rv0" -s ./tcl -f tcl/interface/cmsis-dap.cfg -f tcl/target/rp2350.cfg
 2
 3(中略)
 4
 5Debug: 33 7 command.c:86 script_debug(): command - ocd_find target/swj-dp.tcl
 6Debug: 34 7 configuration.c:88 find_file(): found ./tcl/target/swj-dp.tcl
 7Debug: 35 7 command.c:86 script_debug(): command - transport select
 8Debug: 36 7 command.c:86 script_debug(): command - transport select
 9Debug: 37 7 command.c:86 script_debug(): command - transport select
10Debug: 38 7 command.c:86 script_debug(): command - swd newdap rp2350 cpu -expected-id 0x00040927
11Debug: 39 7 tcl.c:405 handle_jtag_newtap_args(): Creating New Tap, Chip: rp2350, Tap: cpu, Dotted: rp2350.cpu, 2 params
12Debug: 40 7 core.c:1491 jtag_tap_init(): Created Tap: rp2350.cpu @ abs position 0, irlen 0, capture: 0x1 mask: 0x3
13Debug: 41 7 command.c:86 script_debug(): command - dap create rp2350.dap -adiv6 -chain-position rp2350.cpu
14Debug: 42 7 command.c:86 script_debug(): command - target create rp2350.rv0 riscv -dap rp2350.dap -ap-num 0xa000 -coreid 0
15Debug: 43 8 command.c:462 exec_command(): Command 'target create' failed with error code -603
16User : 44 8 command.c:534 command_run_line(): Unknown param: -dap, try one of: -type, -event, -work-area-virt, -work-area-phys, -work-area-size, -work-area-backup, -endian, -coreid, -chain-position, -dbgbase, -rtos, -defer-examine, -gdb-port, or -gdb-max-connections

対応方法

Rasbperry Piから提供されているOpenOCDを使えば解決する。

https://github.com/raspberrypi/openocd

SWDは元々ARMの仕様なのでRISC-Vターゲットに存在しないのは理解するものの、
ターゲットの実装が伝送路(SWD)に依存しているのは少し意外だった。

OpenOCDのビルドに失敗する

状況

以下の通りWcast-alignの警告に引っかかってしまう。

 1depbase=`echo src/target/riscv/batch.lo | sed 's|[^/]*$|.deps/&|;s|\.lo$||'`;\
 2        /bin/sh ./libtool  --tag=CC   --mode=compile gcc -DHAVE_CONFIG_H -I.   -I./src -I./src -DPKGDATADIR=\"/usr/local/share/openocd\" -DBINDIR=\"/usr/local/bin\"  -I/opt/homebrew/Cellar/jimtcl/0.83/include  -Wall -Wstrict-prototypes -Wformat-security -Wshadow -Wextra -Wno-unused-parameter -Wbad-function-cast -Wcast-align -Wredundant-decls -Wpointer-arith -Wundef -Werror  -g -O2 -MT src/target/riscv/batch.lo -MD -MP -MF $depbase.Tpo -c -o src/target/riscv/batch.lo src/target/riscv/batch.c &&\
 3        mv -f $depbase.Tpo $depbase.Plo
 4libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I./src -I./src -DPKGDATADIR=\"/usr/local/share/openocd\" -DBINDIR=\"/usr/local/bin\" -I/opt/homebrew/Cellar/jimtcl/0.83/include -Wall -Wstrict-prototypes -Wformat-security -Wshadow -Wextra -Wno-unused-parameter -Wbad-function-cast -Wcast-align -Wredundant-decls -Wpointer-arith -Wundef -Werror -g -O2 -MT src/target/riscv/batch.lo -MD -MP -MF src/target/riscv/.deps/batch.Tpo -c src/target/riscv/batch.c -o src/target/riscv/batch.o
 5src/target/riscv/batch.c:194:24: error: cast from 'uint8_t *' (aka 'unsigned char *') to 'uint32_t *' (aka 'unsigned int *') increases required alignment from 1 to 4 [-Werror,-Wcast-align]
 6  194 |                         uint32_t *data_in = (uint32_t*)batch->data_in;
 7      |                                             ^~~~~~~~~~~~~~~~~~~~~~~~~
 8src/target/riscv/batch.c:243:13: error: incompatible pointer types initializing 'uint32_t *' (aka 'unsigned int *') with an expression of type 'uint8_t *const' (aka 'unsigned char *const') [-Werror,-Wincompatible-pointer-types]
 9  243 |                 uint32_t *data_in = batch->data_in;
10      |                           ^         ~~~~~~~~~~~~~~
112 errors generated.
12make[2]: *** [src/target/riscv/batch.lo] Error 1
13make[1]: *** [all-recursive] Error 1
14make: *** [all] Error 2

対応方法

本当は良くはないものの、Wcast-alignを外すぐらいしか方法がない。
以下のようにソースコードレベルで等価な実装にするのが良いものの、
関数内で引数のポインタの値を持ってしまっているせいか、
呼び出し側で回避する方法が見当たらなかった。

 1                        uint32_t data_in;
 2                        uint8_t  *dst = batch->data_in + (batch->used_scans * sizeof(uint32_t));
 3                        if (batch->ap) {
 4                                printf("batch->ap true.\n");
 5                                uint32_t dmi_addr = 4 * riscv_get_dmi_address(batch->target, address);
 6                                batch->queued_retval = mem_ap_read_u32(batch->ap, dmi_addr, &data_in);
 7                        } else {
 8                                printf("batch->ap false.\n");
 9                                uint32_t dmi_addr = riscv_get_dmi_address(batch->target, address);
10                                batch->queued_retval = riscv_dmi_read(batch->target,
11                                                                                   &data_in, dmi_addr);
12                        }
13                       memcpy(dst, &data_in, sizeof(uint32_t));

riscv-gnu-toolchainのgdbでエラーが出る

状況

riscv-gnu-toolchainをhomebrewでビルドできるようにみえるものの、
実は以下のエラーが出ていてgdbはビルドできていない。

riscv-gnu-toolchainのmakeとしては正常にビルドできているように見えていて、
エラーが検出されずにログだけ流れていくのでわかりづらい。
インストール先のディレクトリにgdbがないことから辿っていくと気づくことができる。

1checking for the correct version of gmp.h... no
2configure: error: Building GDB requires GMP 4.2+, and MPFR 3.1.0+.
3Try the --with-gmp and/or --with-mpfr options to specify
4their locations.  If you obtained GMP and/or MPFR from a vendor
5distribution package, make sure that you have installed both the libraries
6and the header files.  They may be located in separate packages.
7make: *** [Makefile:631: stamps/build-gdb-newlib] Error 1

対応方法

エラーの通り、gmp, mpfrが認識していない。
riscv-gnu-toolchainのビルド手順ではgmp, mpfrをbrewから
入れているので大丈夫、とは思うものの実はパスが通っていない。

対処方法としては、stamps/build-gdb-newlib ターゲットに
明示的にwith-gmp, with-mpfrを追加すれば良い。

 1stamps/build-gdb-newlib: $(GDB_SRCDIR) $(GDB_SRC_GIT) $(PREPARATION_STAMP)
 2        rm -rf $@ $(notdir $@)
 3        mkdir $(notdir $@)
 4# CC_FOR_TARGET is required for the ld testsuite.
 5        cd $(notdir $@) && CC_FOR_TARGET=$(NEWLIB_CC_FOR_TARGET) $</configure \
 6                --target=$(NEWLIB_TUPLE) \
 7                $(CONFIGURE_HOST) \
 8                --prefix=$(INSTALL_DIR) \
 9                 \
10                --disable-werror \
11                $(GDB_TARGET_FLAGS) \
12                --enable-gdb \
13                --disable-gas \
14                --disable-binutils \
15                --disable-ld \
16                --disable-gold \
17                --with-gmp=/opt/homebrew/ \
18                --with-mpfr=/opt/homebrew/ \
19                --disable-gprof
20        $(MAKE) -C $(notdir $@)
21        $(MAKE) -C $(notdir $@) $(INSTALL_TARGET)
22        mkdir -p $(dir $@) && touch $@

本来はautoconfのレベルでhomebrew環境かどうかを判定した上で
生成するMakefileを変えるのが良い。

また、README.mdを見ると以下のように指定する方法もありそう。
ただし、こちらは試していない。

1Example: ```GCC_EXTRA_CONFIGURE_FLAGS=--with-gmp=/opt/gmp make linux```

OpenOCDで接続するもRISC-Vがunavailableになる

状況

以下の通りunavailableになってしまう。

 1Debug: 210 43 target.c:726 target_examine_one(): [rp2350.rv1] Examination started
 2Debug: 211 43 target.c:1828 target_call_event_callbacks(): target event 19 (examine-start) for core rp2350.rv1
 3Debug: 212 43 riscv.c:1733 riscv_examine(): [rp2350.rv1] Starting examination
 4Debug: 213 43 riscv-013.c:2854 init_target(): [rp2350.rv1] Init.
 5Debug: 214 43 riscv-013.c:1990 examine(): [rp2350.rv1] dbgbase=0x0
 6Debug: 215 43 riscv-013.c:859 check_dbgbase_exists(): [rp2350.rv1] Searching for DM with DMI base address (dbgbase) = 0x0
 7Debug: 216 43 riscv-013.c:5505 riscv013_invalidate_cached_progbuf(): [rp2350.rv1] Invalidating progbuf cache
 8Debug: 217 44 riscv-013.c:2058 examine(): [rp2350.rv1] dmstatus:  0x004030a2
 9Debug: 218 44 riscv-013.c:2074 examine(): [rp2350.rv1] hartsellen=1
10Info : 219 45 riscv-013.c:2102 examine(): [rp2350.rv1] datacount=1 progbufsize=2
11Info : 220 46 riscv-013.c:2159 examine(): [rp2350.rv1] unavailable.
12Error: 221 46 target.c:732 target_examine_one(): [rp2350.rv1] Examination failed
13Debug: 222 46 target.c:733 target_examine_one(): [rp2350.rv1] examine() returned error code -4
14Debug: 223 46 target.c:1828 target_call_event_callbacks(): target event 20 (examine-fail) for core rp2350.rv1
15Warn : 224 46 target.c:781 target_examine(): target rp2350.rv1 examination failed

対応方法

Raspberry Pi Pico2(RP2350)の仕様書などを見ると、
RISC-Vを立ち上げる場合には以下のブートシーケンスになっている。

  • 最初はARMが立ち上がる
  • ARCHSELレジスタに使用するコアを指定する。今回の場合、RISC-Vを選択する
  • リセットをかける
  • RISC-Vが立ち上がる

このあたりの処理をやっているのが以下のコード。

https://github.com/raspberrypi/pico-bootrom-rp2350/blob/master/src/main/arm/varm_apis.c#L169

上記を実現するのに一番簡単のは、
すでにRISC-V向けにビルドされたバイナリを持ってくること。
例えば、以下のバイナリを持ってくる。

https://micropython.org/download/RPI_PICO2/

ちなみに、一度ARM側から以下のようにARCHSELを書いてから
ターゲットをリセットする方法も試してみたもののこれだとうまくいかなかった。
monitor resetだとリセット範囲が違うのかもしれない。

1target remote localhost:3333
2set {int}0x40120158 = 0x3
3monitor reset

ようやく動く

ここまできてようやく動かすことができた。

 1% ./src/openocd  -c "adapter speed 5000" -s ./tcl -f tcl/interface/cmsis-dap.cfg -f tcl/target/rp2350-riscv.cfg
 2/Library/Developer/CommandLineTools/usr/bin/make  all-recursive
 3Making all in testing
 4Making all in tcl_commands
 5make[3]: Nothing to be done for `all'.
 6make[3]: Nothing to be done for `all-am'.
 7make[2]: Nothing to be done for `all-am'.
 8Open On-Chip Debugger 0.12.0+dev-00002-gcd4873400 (2025-12-27-22:18)
 9Licensed under GNU GPL v2
10For bug reports, read
11        http://openocd.org/doc/doxygen/bugs.html
12adapter speed: 5000 kHz
13Info : [rp2350.rv0] Hardware thread awareness created
14Info : [rp2350.rv1] Hardware thread awareness created
15ocd_process_reset_inner
16Info : Listening on port 6666 for tcl connections
17Info : Listening on port 4444 for telnet connections
18Info : Using CMSIS-DAPv2 interface with VID:PID=0x2e8a:0x000c, serial=E6647C74036C1832
19Warn : ***
20Warn : *** Old Raspberry Pi Debugprobe firmware detected (1.0.1)
21Warn : *** Using low-performance workaround
22Warn : *** Please update to the latest release at:
23Warn : *** https://github.com/raspberrypi/debugprobe/releases/latest
24Warn : ***
25Info : CMSIS-DAP: SWD supported
26Info : CMSIS-DAP: Atomic commands supported
27Info : CMSIS-DAP: Test domain timer supported
28Info : CMSIS-DAP: FW Version = 2.0.0
29Info : CMSIS-DAP: Interface Initialised (SWD)
30Info : SWCLK/TCK = 0 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 0
31Info : CMSIS-DAP: Interface ready
32Info : clock speed 5000 kHz
33Info : SWD DPIDR 0x4c013477 DPv3
34Info : [rp2350.rv0] datacount=1 progbufsize=2
35Info : [rp2350.rv0] Disabling abstract command reads from CSRs.
36Info : [rp2350.rv0] Disabling abstract command writes to CSRs.
37Info : [rp2350.rv0] Core 0 could not be made part of halt group 1.
38Info : [rp2350.rv0] Examined RISC-V core
39Info : [rp2350.rv0]  XLEN=32, misa=0x40901105
40Info : [rp2350.rv0] Examination succeed
41Info : [rp2350.rv1] datacount=1 progbufsize=2
42Info : [rp2350.rv1] Disabling abstract command reads from CSRs.
43Info : [rp2350.rv1] Disabling abstract command writes to CSRs.
44Info : [rp2350.rv1] Core 1 could not be made part of halt group 1.
45Info : [rp2350.rv1] Examined RISC-V core
46Info : [rp2350.rv1]  XLEN=32, misa=0x40901105
47Info : [rp2350.rv1] Examination succeed
48Info : [rp2350.rv0] starting gdb server on 3333
49Info : Listening on port 3333 for gdb connections
5051
52gdb側はこんな感じ。
53
54```sh
55(gdb) target remote localhost:3333
56A program is being debugged already.  Kill it? (y or n) y
57Remote connection closed
58(gdb) target remote localhost:3333
59Remote debugging using localhost:3333
60warning: No executable has been specified and target does not support
61determining executable automatically.  Try using the "file" command.
62
63Thread 1 "rp2350.rv0" received signal SIGTRAP, Trace/breakpoint trap.
640x100478bc in ?? ()
65(gdb) info all-registers
66zero           0x0  0
67ra             0x2001041c   0x2001041c
68sp             0x20010400   0x20010400
69gp             0x200071e0   0x200071e0
70tp             0x0  0x0
71t0             0x80000102   -2147483390
Posted at : 2025-12-31 13:47:20 / Category : none