Raspberry Pi Pico2 では ARM Cortex-M と RISC-V の2種類のCPUを使うことができる。
デバッガを使って開発できれば便利なので、OpenOCDからRISC-Vをデバッグできる環境を作ろうと試してみた。
想定上にはまるところが多かったものの、何とか動かすことができた。
ここでははまったポイントをまとめておく。
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アダプタを有効にするかどうかの違いだった。
本家のOpenOCDやriscv-openocdでは、
Raspberry PiのDebug Probe経由でのデバッグに対応していなかった。
ざっと見たところでは以下の問題があるように見えた。
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)に依存しているのは少し意外だった。
以下の通り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を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```
以下の通り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を立ち上げる場合には以下のブートシーケンスになっている。
このあたりの処理をやっているのが以下のコード。
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だとリセット範囲が違うのかもしれない。
ここまできてようやく動かすことができた。
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 50… 51 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