RP2 - PIO 'remove_programs()' does not correctly remove/de-reference StateMachine code
Port, board and/or hardware
rp2
MicroPython version
MicroPython v1.26.1 on 2025-09-11; Raspberry Pi Pico with RP2040
Reproduction
Minimal example showing issue:
pio_load_test.py.txt
Expected behaviour
When alternately loading PIO StateMachine code, microPython loads the wrong (previously loaded) code.
Example code alternates between 'mac-one' and 'mac-two'; on the 3rd iteration we expect 'mac-one' to be loaded, but instead 'mac-two' is placed (or left) in PIO address space... and that code loads '2' into the RX FIFO.
MicroPython v1.26.1 on 2025-09-11; Raspberry Pi Pico with RP2040
Type "help()" for more information.
>>> %Run -c $EDITOR_CONTENT
MPY: soft reboot
Loading primary: mac-one
0x502000d4 : 0x0000001c = 0x0000e021
0x502000ec : 0x0000000b = 0x00006026
0x50200104 : 0x00000006 = 0x00007e01
0x5020011c : 0x00000001 = 0x0000ae08
RX FIFO = 1
Loading alternate: mac-two
0x502000d4 : 0x0000001c = 0x0000e022
0x502000ec : 0x0000000b = 0x00006026
0x50200104 : 0x00000006 = 0x00007e01
0x5020011c : 0x00000001 = 0x0000ae08
RX FIFO = 2
Loading primary: mac-one
0x502000d4 : 0x0000001c = 0x0000e022
0x502000ec : 0x0000000b = 0x00006026
0x50200104 : 0x00000006 = 0x00007e01
0x5020011c : 0x00000001 = 0x0000ae08
RX FIFO = 2
The code also 'peeks' (mem32) the address space related to PIO0 and we can see the start address and the first instruction (as complied value).
>>> print("0x%8.8x" % rp2.asm_pio_encode("set(x,1)", 0))
0x0000e021
>>> print("0x%8.8x" % rp2.asm_pio_encode("set(x,2)", 0))
0x0000e022
Observed behaviour
Example code alternates between 'mac-one' and 'mac-two'; on the 3rd iteration we expect 'mac-one' to be loaded, but instead 'mac-two' is placed (or left) in PIO address space... and that code loads '2' into the RX FIFO.
Additional Information
As a workaround test script appears to correctly work if you implicitly specify the name of the StateMachine(s) to remove
# clean-up
rp2.PIO(0).remove_program(mac_one)
rp2.PIO(0).remove_program(mac_two)
rp2.PIO(0).remove_program()
Docs suggest this should not be necessary.
https://docs.micropython.org/en/latest/library/rp2.PIO.html#rp2.PIO.remove_program
Code of Conduct
Yes, I agree
rp2: Add PIO assembler and StateMachine test suite.
Tests cover asm_pio encoding (nop, wait, irq, mov, in_count, fifo_join including RP2350-specific modes), StateMachine hardware behavior (loopback, FIFO counts, exec, IRQ, restart, program memory reuse), and RP2350-specific features (3 PIO blocks, 12 SMs, PIO(2) loopback).
Summary
I wanted to update the RP2 PIO code to support the new RP2350 PIO changes.
To make sure that my changes were working, I created this test script to run from mpremote (similarly to rp2_dma.py).
Testing
I've run this on both a RPI_PICO2 board and a RPI_PICO board. I did discover an issue in the existing PIO code, which is fixed by #18976.
This issue causes test_restart to hang.
Some of the RP2350-specific tests depend on functionality that is not yet in mainline code; #18975 adds this functionality.
Generative AI
I used generative AI tools when creating this PR, but a human has checked the
code and is responsible for the code and the description above.