PICO rp2.PIO(1),remove_program() makes all instruction memory available for python PIO and therefor allows overwrite of CYW43 PIO program
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
rp2/rp2_pio: Track use of PIO resources and free them on soft reset.
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.