embassy-rs/embassy · error

pin is outside the PIO's GPIOBASE window .. ; use…

Error message

pin {} is outside the PIO's GPIOBASE window {}..{}; use Config::set_input_sync_bypass or call this after StateMachine::set_config

What it means

`set_input_sync_bypass` on a PIO pin group panics when the requested pin cannot be expressed in the PIO block's current GPIOBASE-relative window. On RP235x, PIO2 can be routed to pins 16-47, so pins are addressed relative to the block's GPIOBASE (0 or 16); a pin outside the 32-pin window starting at that base cannot be referenced.

Solutions

  1. Move the pin into the PIO's current GPIOBASE window (offset..offset+32) as the panic message indicates
  2. Call set_input_sync_bypass after StateMachine::set_config so the GPIOBASE routing is latched
  3. Use the Config-level pin assignment (e.g. set the pin in the PIO config) instead of raw bypass manipulation
  4. On RP235x, choose pins that match the block's routing (low bank for base 0, pins 16-47 for base 16)

Example fix

// before
sm.set_input_sync_bypass(pin_10); // pin outside current window
// after
let sm = pio.sm0.set_config(&config); // latch routing first
sm.set_input_sync_bypass(pin_10);
Defensive patterns

Strategy: validation

Validate before calling

fn pin_in_window(pin: u8, offset: u8) -> bool { pin.checked_sub(offset).map_or(false, |r| r < 32) }
// check before calling: pin_in_window(pin, gpio_base_offset)

Type guard

fn in_pio_window(pin: u8, offset: u8) -> Option<u8> {
    pin.checked_sub(offset).filter(|&r| r < 32)
}

Prevention

When it happens

Trigger: Calling `set_input_sync_bypass` with a pin number below the block's GPIOBASE or at/above base+32 — typically configuring bypass for a pin >=16 (or <16) on RP235x while the PIO's gpiobase routing places it outside the window, or calling before `StateMachine::set_config` has latched the routing.

Common situations: RP235x projects moving a PIO block to the high pin bank (16-47) while bypass/pin code still references low pin numbers; code copied from RP2040 (where offset is always 0) that uses pins >=16.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10). Data as JSON: /api/errors/fec2675011859537. Report an issue: GitHub.

Appendix: source

Thrown at embassy-rp/src/pio/mod.rs:320

    }

    /// Set the pin's input sync bypass.
    ///
    /// Prefer [`Config::set_input_sync_bypass`]: `input_sync_bypass` is indexed
    /// relative to the PIO's `GPIOBASE`, which is only established by
    /// [`StateMachine::set_config`]. If calling this directly (e.g. to clear a
    /// bypass), do so after configuring the state machine.
    ///
    /// Panics if the pin is outside the PIO's current `GPIOBASE` window.
    pub fn set_input_sync_bypass(&mut self, bypass: bool) {
        #[cfg(feature = "rp2040")]
        let offset = 0u8;
        #[cfg(feature = "_rp235x")]
        let offset = if PIO::PIO.gpiobase().read().gpiobase() { 16 } else { 0 };

        let rel = match self.pin().checked_sub(offset) {
            Some(rel) if rel < 32 => rel,
            _ => panic!(
                "pin {} is outside the PIO's GPIOBASE window {}..{}; use Config::set_input_sync_bypass or call this after StateMachine::set_config",
                self.pin(),
                offset,
                offset + 32
            ),
        };
        let mask = 1u32 << rel;
        if bypass {
            PIO::PIO.input_sync_bypass().write_set(|w| *w = mask);
        } else {
            PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask);
        }
    }

    /// Set the pin's input sync bypass.
    pub fn set_input_inversion(&mut self, invert: bool) {
        self.pin.gpio().ctrl().modify(|w| {
            w.set_inover(if invert {

View on GitHub (pinned to 463a07b963)