Write Arm and RISC-V, put it on the board and debug it step by step – without OpenOCD, without an SDK.
With .target pico or .target pico2 a program says which board it is for. Asmonite assembles it, checks it against the board, writes it into the RAM or the flash over a debug probe and stops at _start. Then you go through it instruction by instruction and see the registers, the stack and the memory. Asmonite drives the probe itself over USB; you need no OpenOCD, GDB or Pico SDK.
The examples are in the folders examples\pico and examples\pico2 of the ZIP. The full guide is in the source repository as docs/en/riscv-pico2.md.
The Pico has Arm cores, the Pico 2 two kinds of cores; the boot ROM chooses which runs from the program in the flash. Asmonite writes programs for all three and debugs core 0 of each.
| Pico | Pico 2, Arm | Pico 2, RISC-V | |
|---|---|---|---|
| Chip, core | RP2040, Cortex-M0+ | RP2350, Cortex-M33 | RP2350, Hazard3 |
| Directives | .target pico.arch thumb | .target pico2.arch thumb | .target pico2.arch riscv32 |
| Instructions | Thumb (ARMv6-M) | Thumb-2 (ARMv8-M) | RV32IMA, Zicsr, Zifencei, Zba, Zbb, Zbs, Zbc, Zbkb |
| RAM, flash | 264 KB, 2 MB | 520 KB from 0x20000000, 4 MB from 0x10000000 | |
| Start from the flash | boot2 block (.boot2) + vector table | vector table + IMAGE_DEF (0x1021) | IMAGE_DEF block (0x1101) |
| Hardware breakpoints | 4 in the flash, any number in the RAM | 8 | 4 |
| Registers in the debugger | R0…R12, SP, LR, PC, XPSR, flags N Z C V | x0…x31, pc | |
The Cortex-M0+ knows the instructions of the ARMv6-M, and a flash program starts with a boot2 block. File: examples\pico\blink_flash.asm.
.target pico .arch thumb .imagebase 0x10000000 ; flash .boot2 ; 256 bytes that set up the flash chip vectors: ; at 0x10000100 dd 0x20042000 ; stack pointer dd _start + 1 ; start address (+1: Thumb code) _start: ldr r0, resets_clr ; a 32-bit constant from a word behind the code ...
With .target pico the assembler allows the instructions of the ARMv6-M only: 16-bit Thumb, of the 32-bit ones only bl, msr, mrs and the barriers. There is no movw/movt, no cbz and no it; any other instruction is an error with its line. .boot2 puts in the boot2 block of the Pico SDK (BSD-3-Clause, in the ZIP and built into Asmonite), with its CRC-32 that the boot ROM checks. For a board with another flash chip, .boot2 file.bin takes a block of your own.
The LED at GPIO 25 blinks: switch on the output, then toggle it in a loop and wait. Files: examples\pico2\blink_arm_ram.asm and blink_ram.asm (shortened).
.target pico2 .arch thumb .imagebase 0x20000000 ; RAM _start: ... ; pads and GPIO movw r0, #(SIO_BASE & 0xffff) movt r0, #(SIO_BASE >> 16) movw r1, #(LED_MASK & 0xffff) movt r1, #(LED_MASK >> 16) blink_loop: str r1, [r0, #SIO_GPIO_OUT_XOR] movw r2, #(DELAY_COUNT & 0xffff) movt r2, #(DELAY_COUNT >> 16) delay: subs r2, r2, #1 bne delay b blink_loop
.target pico2 .arch riscv32 .imagebase 0x20000000 ; RAM _start: li sp, SRAM_END ... ; pads and GPIO li t0, SIO_BASE li t1, LED_MASK blink_loop: sw t1, SIO_GPIO_OUT_XOR(t0) li t2, DELAY_COUNT delay: addi t2, t2, -1 bnez t2, delay j blink_loop
RISC-V has no flags: bnez compares and branches in one instruction, and li builds a 32-bit constant from lui and addi. Arm loads it in two halves with movw and movt, subs sets the flags and bne branches on them. The peripherals are the same on both cores: registers at fixed addresses, each with aliases that flip, set or clear bits.
debugprobe firmware (CMSIS-DAP). Three wires to the debug pins: SWCLK, GND, SWDIO. The board gets its power over its own USB cable. Windows needs no driver._start. F7 and F8 take a step, F9 runs, F4 runs to the cursor, F5 sets a breakpoint, Debug > Pause stops.If the program does not fit the board at the probe (a Pico program at a Pico 2, or the other way round), Asmonite refuses it with the name of the board and writes nothing.
.imagebase 0x20000000 | The program goes into the RAM. Quick to try out; it is gone after a power cycle. |
.imagebase 0x10000000 | Asmonite writes the program into the flash through the functions of the boot ROM, only the 4 KB sectors that differ, and reads them back. It is debugged there and starts by itself after power-up. |
| Project > Save as UF2 file | A file for the BOOTSEL drive, with the family of the chip (RP2040, Arm or RISC-V). Without a debug probe: hold BOOTSEL, plug in, drag the file onto the drive. |
Switching between RISC-V and Arm. The RP2350 runs one kind of cores at a time. If you start a flash program for the other kind, Asmonite writes it into the flash with the cores that run, resets the chip, and the boot ROM starts the cores the program is for. This takes about two seconds. A RAM program cannot switch; Asmonite then says which flash program to start first.
Without OpenOCD. Asmonite speaks CMSIS-DAP over USB and through it SWD with the chip: the debug module of the RISC-V cores, the debug registers of the Arm cores, the multidrop bus of the RP2040. If you still want OpenOCD, you can choose it as a backup for the RISC-V cores (host and port of its GDB server).
c.*); the disassembler reads them.Every instruction with its bit encoding and pseudo code, for the Arm and the RISC-V cores.