← index #9316Issue #8866
Related · high · value 0.587
QUERY · ISSUE

pico - zombie pio state machines after soft reset

openby ccanepaopened 2022-09-14updated 2026-03-19
bugport-rp2

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

CANDIDATE · ISSUE

rp2.StateMachine: state machine fails when I2S protocol is used

openby miketeachmanopened 2022-07-05updated 2022-07-08
bugport-rp2

A rp2 state machine instance can be made to fail when the I2S protocol is used.

Minimal test to reproduce
The code below uses a PIO state machine to flash the onboard LED on a Raspberry Pi Pico development board.

  • Working case: Onboard LED will flash for STATEMACHINE_INSTANCE = 1...7
  • Failure case: Onboard LED will NOT flash for STATEMACHINE_INSTANCE = 0 (shows the Issue)
import time
from machine import Pin, I2S
import rp2

STATEMACHINE_INSTANCE = 0

@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def prog():
    pass

sm = rp2.StateMachine(STATEMACHINE_INSTANCE, prog, set_base=Pin(25))
i2s = I2S(0, sck=Pin(16), ws=Pin(17), sd=Pin(18), mode=I2S.TX, bits=16, format=I2S.STEREO, rate=22_050, ibuf=10000)

while True:
	sm.exec("set(pins, 1)")
	time.sleep(0.5)
	sm.exec("set(pins, 0)")
	time.sleep(0.5)

In the failure case (STATEMACHINE_INSTANCE = 0) , both the rp2.StateMachine instance and the machine.I2S instance try to use state machine 0. Because I2S instantiation follows the rp2.StateMachine instantiation, the state machine configuration used by rp2.StateMachine is overwritten by I2S initialization. This causes a failure in the rp2.StateMachine instance (LED does not flash).

The root cause of this Issue seems to emerge from the rp2.StateMachine implementation. rp2.StateMachine does not make use of the functions for cooperative claiming of hardware that are provided by the Pico SDK. e.g. pio_sm_is_claimed(), pio_sm_unclaim(), pio_claim_unused_sm(). This does not allow any other MicroPython entities (such as I2S) to detect state machine configuration conflicts.

Suggestions for a fix to rp2.StateMachine implementation:
Enhance the rp2.StateMachine implementation to use the Pico SDK functions for cooperative claiming of PIO hardware. The I2S class implementation shows one way to use these functions, in the functions pio_configure() and pio_deinit().

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