← index #18646PR #18976
Duplicate · high · value 3.703
QUERY · ISSUE

RP2 - PIO 'remove_programs()' does not correctly remove/de-reference StateMachine code

openby mungewellopened 2026-01-06updated 2026-03-16
bugport-rp2

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

CANDIDATE · PULL REQUEST

rp2: Fix stale program offset cache after remove_program().

openby bikeNomadopened 2026-03-21updated 2026-03-22
port-rp2

PIO.remove_program() without arguments clears SDK instruction-memory tracking but did not reset cached offsets in program objects. A subsequent StateMachine init using such a program would see the non-negative offset, assume the program was still loaded, and skip re-writing the instructions to PIO instruction memory.

Fix by validating the cached offset against the internal usage mask before trusting it. If the bit is clear the program is not present, so invalidate the stale offset and reload.

Summary

I discovered this issue when writing the PIO test suite in #18974; it caused a hang in test_restart.

Testing

I tested this on both RPI_PICO and RPI_PICO2 boards using the test suite from #18974 (and new PIO assembler support from #18975).

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.

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied