Skip to content

hardware_pio: fix pio_encode_mov(pio_pindirs, ...) on RP2350#2946

Closed
anoshyn wants to merge 1 commit into
raspberrypi:developfrom
anoshyn:fix-pio-mov-pindirs
Closed

hardware_pio: fix pio_encode_mov(pio_pindirs, ...) on RP2350#2946
anoshyn wants to merge 1 commit into
raspberrypi:developfrom
anoshyn:fix-pio-mov-pindirs

Conversation

@anoshyn
Copy link
Copy Markdown

@anoshyn anoshyn commented May 16, 2026

Summary

Fixes #2754. On RP2350, pio_encode_mov(pio_pindirs, pio_x) returned 0xA081 (which is MOV EXEC, X) instead of the correct 0xA061 (MOV PINDIRS, X).

The PIO MOV destination encoding for PINDIRS differs from the SET/IN/OUT encoding:

  • SET/IN/OUT encode PINDIRS as field 4
  • MOV (RP2350 only) encodes PINDIRS as field 3; field 4 is reserved for EXEC

The pio_src_dest enum collapses both pio_pindirs and pio_exec_mov to low-3-bits = 4 because the SET/IN/OUT encoder is the original consumer. The MOV encoder needs to remap PINDIRS to 3 — but only for PINDIRS, not for EXEC. In release builds (NDEBUG), pio_pindirs and pio_exec_mov are numerically identical (the _PIO_INVALID_* flag bits are all zero), so a naive "dest == 4 → 3" remap would also break pio_encode_mov(pio_exec_mov, ...).

This PR adds a non-NDEBUG-gated marker bit (_PIO_MARKER_PINDIRS = 0x100) carried only by pio_pindirs. The new _pio_mov_dest_bits() helper checks the marker on PICO_PIO_VERSION > 0 and remaps to 3. All existing encoders already mask with & 7u before emitting the destination field, so the marker bit is discarded everywhere it isn't being checked — no other encoder is affected. The debug-mode _PIO_INVALID_MOV_DEST assertion is also dropped from pio_pindirs on RP2350.

Test plan

Host-side verification of the constant-folded encoding emitted by each pio_encode_*:

test RP2350 (v1) RP2040 (v0)
mov pindirs, x 0xA061 (fixed) 0xA081 (unchanged; invalid on RP2040)
mov exec_mov, x 0xA081 ✓ 0xA081 ✓
mov pc, x 0xA0A1 ✓ 0xA0A1 ✓
mov y, y (NOP) 0xA042 ✓ 0xA042 ✓
set pindirs, 1 0xE081 ✓ 0xE081 ✓
out pindirs, 1 0x6081 ✓ 0x6081 ✓
in pins, 1 0x4001 ✓ 0x4001 ✓
mov_not pindirs, x 0xA069 ✓ 0xA089 ✓
mov_reverse pindirs, x 0xA071 ✓ 0xA091 ✓
  • Full SDK build clean for PICO_PLATFORM=rp2040 PICO_BOARD=pico.
  • Full SDK build clean for PICO_PLATFORM=rp2350 PICO_BOARD=pico2.
  • CI to confirm broader configurations (RISC-V, alternate boards).

On RP2350, MOV destination PINDIRS exists and encodes as field 3 in the
instruction word. SET/IN/OUT continue to encode PINDIRS as 4. The
pio_src_dest enum collapses these into a single value (pio_pindirs = 4)
because SET/IN/OUT see it that way, so the MOV encoder was emitting
field 4 - that's MOV EXEC, not MOV PINDIRS. pio_encode_mov(pio_pindirs,
pio_x) was returning 0xA081 instead of the correct 0xA061 (raspberrypi#2754).

The bug was hard to fix simply because pio_pindirs and pio_exec_mov are
numerically identical in release builds: both reduce to 4 once the
NDEBUG-gated _PIO_INVALID_* flags are zero. A naive remap of "dest == 4
to 3 in MOV" would also break pio_encode_mov(pio_exec_mov, ...), where
field 4 is correct.

Add a non-NDEBUG-gated marker bit (_PIO_MARKER_PINDIRS, 0x100) carried
on pio_pindirs only. _pio_mov_dest_bits() checks the marker and remaps
to 3 on PICO_PIO_VERSION > 0. The existing & 7u masks in every encoder
discard the high bits before they reach the emitted instruction word,
so no other encoder is affected. Also drop _PIO_INVALID_MOV_DEST from
pio_pindirs on RP2350 so the debug-mode assertion no longer fires for
the now-valid case.

Verified by host-side test harness, dumping the constant-folded
encoding emitted by each pio_encode_* on both PIO versions:

  test                  RP2350 (v1)        RP2040 (v0)
  mov pindirs,x         0xa061 (fixed)     0xa081 (unchanged)
  mov exec_mov,x        0xa081             0xa081       (no regression)
  mov pc,x              0xa0a1             0xa0a1
  mov y,y (NOP)         0xa042             0xa042
  set pindirs,1         0xe081             0xe081       (SET uses 4)
  out pindirs,1         0x6081             0x6081       (OUT uses 4)
  in  pins,1            0x4001             0x4001
  mov_not pindirs,x     0xa069             0xa089
  mov_reverse pindirs,x 0xa071             0xa091

Full SDK build clean for both PICO_PLATFORM=rp2040 and PICO_PLATFORM=rp2350.

Fixes raspberrypi#2754.
@anoshyn anoshyn force-pushed the fix-pio-mov-pindirs branch from a552d06 to 6a990bb Compare May 19, 2026 19:53
@anoshyn
Copy link
Copy Markdown
Author

anoshyn commented May 19, 2026

Force-pushed to drop the Co-Authored-By trailer. New HEAD: 6a990bb.

@kilograham
Copy link
Copy Markdown
Contributor

closed in favor of #2958

@kilograham kilograham closed this May 19, 2026
@kilograham kilograham added this to the none milestone May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants