← index #15163PR #10652
Related · high · value 2.135
QUERY · ISSUE

PICO rp2.PIO(1),remove_program() makes all instruction memory available for python PIO and therefor allows overwrite of CYW43 PIO program

openby wodropened 2024-05-29updated 2026-03-24
bugport-rp2

Checks

  • I agree to follow the MicroPython Code of Conduct to ensure a safe and respectful space for everyone.

  • I've searched for existing issues matching this bug, and didn't find any.

Port, board and/or hardware

PICO W

MicroPython version

MicroPython v1.22.2 on 2024-02-22; Raspberry Pi Pico W with RP2040

Problem

rp2.PIO(1).remove_program() removes all programs even the cyw43 program.

If after this statement a program is added to PIO(1) it is written from the TOP address 1F.
This cause the PICO to hang until unplugged.

CAUSE

If no program is given in rp2_pio_remove_program() then a program of lenth 32 and offset 0 is removed:

see rp2_pio_remove_program.c(314):

    uint8_t length = 32;
    uint offset = 0;
...
    struct pio_program pio_program = { NULL, length, -1 };
    rp2_pio_remove_managed_program(self->pio, &pio_program, offset);

This will also free the cyw43 pio program in the pico sdk, which keeps track of used memory.

SOLUTION

If no program is given in rp2.PIO(1).remove_program() the function should use

   rp2_pio_remove_all_managed_programs()

like it is used in rp2_pio_deinit().

This will only free instruction memory, that was allocated by python PIO. At least I hope. Didn't check this.

Steps to reproduce

# just some nop() instructions
program = [array('H', [41026, 41026, 41026, 41026, 41026]), -1, -1, 16384, 0, None, None, None]
print(program) 
rp2.StateMachine(7,program)
print(program)   # program is at address 21, because pio_add_program knows about used instruction memory
rp2.PIO(1).remove_program()
program[2] = -1 # not allocated yet, you could also use a new program
rp2.StateMachine(7,program)
print(program)   # now has offet 27 and overwrite cyw43 

this output is:

[array('H', [41026, 41026, 41026, 41026, 41026]), -1, -1, 16384, 0, None, None, None]
[array('H', [41026, 41026, 41026, 41026, 41026]), -1, 21, 16384, 0, None, None, None]
[array('H', [41026, 41026, 41026, 41026, 41026]), -1, 27, 16384, 0, None, None, None]

The program is, when added the second time, loaded at offset 27and overwrites the cyw43 program.

Workaround

instead of using remove_program() of PIO(1) use this code to free all memory except the cyw43 memory:
This assumes that cyw43 is loaded at address 1A to 1F (as it is in V1.22.2)

    nops = [array('H', [40993] * 26), -1, 0, 16384, 0, None, None, None] # mov(x,x)
    rp2.PIO(1).remove_program(nops)
    nops[2] = 0

Debug

To list the current instruction in pio memory, you can do this:

def Dump():
    from machine import mem32
    _SIZE_SM = const(0xe0 - 0xC8)
    smId = 2    
    end = 32
    start = 0 
    pio  = 1
    sm = rp2.PIO(1).state_machine(smId)        
    pioBase = 0x50200000+ pio * 0x100000
    
    smId = smId & 0x3
    for i in range(start,end,1):
        
        sm.exec(f"set(x,{i})")
        sm.exec(f"mov(pc,x)")
        addr = mem32[pioBase+smId*_SIZE_SM+ 0x0d4]
        instr = mem32[pioBase+smId*_SIZE_SM+0xD8]

        print(f"  {i:02X} {addr:04x}: {instr:04X} ")

this prints:

  00 0000: B598 
  01 0001: BBFB 
  02 0002: 304B 
  03 0003: AF57 
  04 0004: FA06 
  05 0005: 2FFD 
  06 0006: 3AF0 
  07 0007: CF3A 
  08 0008: CA27 
  09 0009: 54D9 
  0A 000a: ADA1 
  0B 000b: 8BA7 
  0C 000c: CEBE 
  0D 000d: FD26 
  0E 000e: EC7E 
  0F 000f: CBDF 
  10 0010: B147 
  11 0011: BA79 
  12 0012: 8ABF 
  13 0013: EF59 
  14 0014: 5D9A 
  15 0015: D7FA 
  16 0016: 6F9B 
  17 0017: EE48 
  18 0018: FF2A 
  19 0019: D58B 
  1A 001a: 6001
  1B 001b: 105A 
  1C 001c: E080 
  1D 001d: B042 
  1E 001e: 4001 
  1F 001f: 109E 

Address 1A to 1F is the CYW43 program

Cheers,
Dirk

Reproduction

just some nop() instructions
program = [array('H', [41026, 41026, 41026, 41026, 41026]), -1, -1, 16384, 0, None, None, None]
print(program)
rp2.StateMachine(7,program)
print(program) # program is at address 21, because pio_add_program knows about used instruction memory
rp2.PIO(1).remove_program()
program[2] = -1 # not allocated yet, you could also use a new program
rp2.StateMachine(7,program)
print(program) # now has offet 27 and overwrites cyw43

Expected behaviour

Don't overwrite cyw43 instructions.

Observed behaviour

cyw43 instructions are overwritten

CANDIDATE · PULL REQUEST

rp2/rp2_pio: Track use of PIO resources and free them on soft reset.

mergedby dpgeorgeopened 2023-02-03updated 2023-02-07
port-rp2

Prior to this commit, on Pico W (where the CYW43 driver is enabled) the PIO instruction memory was not released on soft reset, so using PIO after a soft reset would eventually (after a few soft resets) lead to ENOMEM when allocating a PIO program.

This commit fixes that by tracking the use of PIO memory by this module and freeing it on soft reset.

Similarly, use of the state machines themselves are tracked and released on soft reset.

Fixes issue #9003.

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