pico - zombie pio state machines after soft reset
version:
>>> import sys
>>> sys.implementation
(name='micropython', version=(1, 19, 1), _machine='Raspberry Pi Pico with RP2040', _mpy=4102)
Problem description: pio sm instances can be unexpectedly resucitated after a soft reset
More detailed description:
- instantiate and activate many pio state machines with the same pio program
- call machine.soft_reset()
- instantiate and activate only one sm, with the same pio program
- Expected: only one sm activelly driven his output
- Observed: all sm with same pio program will be activelly driven their outputs
The demo script expects to have led + resistor in gpios 10..13
It will instantiate and activate four sm, each blinling a led, do a soft reset, instantiate and activate only one sm, do a hard reset and repeat.
Expected 5 seconds of four led blinking, followed by 5 seconds of only one led blinking, repeat
Observed always four leds blinking
See docstring for instuctions to run
demo script: pio_and_machine_reset.py
""" demoing zombie state machines after soft reset
Using a pico lean and mean, not W or other variants
HW: led + resistor on gpios 10..13
Shoul do: blink the four leds 5 seconds, then blink only the led in gpio 13
Observed: four leds blinking always
Operation:
- if not first run, ensure the file 'soft_reset_flag.txt' does not
exist in the pico
- Close Thonny or other runner / debugger to discard other sw interference
- Unplug and plug the pico to ensure a hard reset
- Use minicom or Bootterm ( bt ) to get the serial output from the pico
"""
from machine import Pin
from rp2 import PIO, StateMachine, asm_pio
import time
def report_state_machines():
for p in (0, 1):
for sm_c in range(4):
fmt = "rp2.PIO(%d).state_machine(%d).active(): %s"
print( fmt % (p, sm_c, rp2.PIO(p).state_machine(sm_c).active()))
class FSFlag:
def __init__(self, name):
self.name = name
self.fname = name + "_flag.txt"
def is_flag_set(self):
present = False
try:
f = open(self.fname, "r")
present = True
f.close()
except Exception:
present = False
return present
def set_(self):
f = open(self.fname, "w")
f.write("flag file")
f.close()
def clear(self):
import os
if self.is_flag_set():
os.remove(self.fname)
def value(self, val=None):
if val is None:
return self.is_flag_set()
elif val:
self.set_()
else:
self.clear()
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink():
set(pins, 1) [31]
nop() [31]
nop() [31]
nop() [31]
nop() [31]
set(pins, 0) [31]
nop() [31]
nop() [31]
nop() [31]
nop() [31]
def blink_1_led(dt):
print("only led on gpio 13 should blink")
print("only SM 3 should be active")
sm = StateMachine(3, blink, freq=2000, set_base=Pin(13, Pin.OUT, value=0))
sm.active(1)
report_state_machines()
time.sleep(dt)
def blink_4_leds(dt):
print("leds in gpios (10, 11, 12, 13) should blink")
print("SMs 0..4 should be active")
gpios = (10, 11, 12, 13)
py_sm_ids = (0, 1, 2, 3)
pairs = [(id_, Pin(gp, Pin.OUT, value=0)) for id_, gp in zip(py_sm_ids, gpios)]
sms = {id_: StateMachine(id_, blink, freq=2000, set_base=pin) for id_, pin in pairs}
for sm in sms.values():
sm.active(1)
report_state_machines()
time.sleep(dt)
print("Comming Alive")
report_state_machines()
soft_reset = FSFlag("soft_reset")
if soft_reset.value():
print("after soft reset")
blink_1_led(5)
soft_reset.value(0)
machine.reset()
else:
print("after hard reset")
blink_4_leds(5)
soft_reset.value(1)
machine.soft_reset()
Additional info, not demoed but can easily show with variants on the demo code:
- always after soft reset all the sm active before the reset are shown as active by
rp2.PIO(p).state_machine(sm_c).active() - if no sm is instantiated after the soft reset, no pin activity shows in the pins controlled by the old sm's
- if a pio sm with a different pio program is instantiated after the soft reset, no pin activity shows in the pins controlled by the old sm's
- if a pio sm with same pio program is activated after the soft reset, the old sm's activelly drive their pins
This issue is probably related to
https://forums.raspberrypi.com/viewtopic.php?t=340099
https://github.com/thonny/thonny/issues/2455
RP2350: PIO2 State Machines do not work
Port, board and/or hardware
ports-rp2 Pico 2
MicroPython version
MicroPython v1.26.0-preview.251.g5ade8b705 on 2025-06-16; Raspberry Pi Pico2 with RP2350
Reproduction
pio_1hz.py from examples does not blink the LED or trigger the interrupt when switched to StateMachine IDs 8, 9, 10, or 11.
Expected behaviour
No response
Observed behaviour
What I've found so far:
PIO2_BASE + SM0_EXECCTRL is set incorrectly
PIO2 instruction memory is not loaded
Instruction start address is not set
Additional Information
This code mostly works.
The IRQ handler only triggers once, not repeatedly as expected.
import time
from machine import Pin
import rp2
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink_1hz():
# fmt: off
# Cycles: 1 + 1 + 6 + 32 * (30 + 1) = 1000
irq(rel(0))
set(pins, 1)
set(x, 31) [5]
label("delay_high")
nop() [29]
jmp(x_dec, "delay_high")
# Cycles: 1 + 1 + 6 + 32 * (30 + 1) = 1000
nop()
set(pins, 0)
set(x, 31) [5]
label("delay_low")
nop() [29]
jmp(x_dec, "delay_low")
# fmt: on
# Create the StateMachine with the blink_1hz program, outputting on Pin(25).
sm = rp2.StateMachine(8, blink_1hz, freq=2300, set_base=Pin(25))
# Set the IRQ handler to print the millisecond timestamp.
sm.irq(lambda p: print(time.ticks_ms()))
# -------- MANUAL REGISTER SETTING START -----------
import machine
PIO2_BASE = 0x50400000
SM0_EXECCTRL = 0xCC
INSTR_MEM0 = 0x48
SM0_INSTR = 0xD8
machine.mem32[PIO2_BASE + SM0_EXECCTRL] = 129920
instrs = [49168, 57345, 58687, 48450, 89, 41026, 57344, 58687, 48450, 94]
instr_start = 0x48 + (4 * (32-len(instrs)))
for idx,instr in enumerate(instrs):
machine.mem32[PIO2_BASE + instr_start + (idx*4)] = instr
machine.mem32[PIO2_BASE + SM0_INSTR]=32-len(instrs)
# -------- MANUAL REGISTER SETTING END -----------
# Start the StateMachine.
sm.active(1)
while(True):
pass
Code of Conduct
Yes, I agree