embassy-rs/embassy · error

QSPI transfer width exceeds configured IO lanes

Error message

QSPI transfer width exceeds configured IO lanes

What it means

The QSPI controller only has as many IO lines wired up as configured (1, 2 or 4). assert_transfer_widths() checks the instruction, address, and data widths of every transaction against that maximum and panics if any of them requests more lanes than exist. This keeps bus configuration consistent with the hardware wiring.

Solutions

  1. Set transaction iwidth/awidth/dwidth to widths <= the configured max_transfer_width() (check your Qspi wiring/pins).
  2. Reconfigure the Qspi instance with four IO pins if quad transfers are genuinely required.
  3. For memory-mapped mode, ensure the memory configuration's widths match the wired lanes before calling enable_memory_map().

Example fix

// before (2-line QSPI)
let mut t = TransferConfig::new(cmd, QspiWidth::SING, None, QspiWidth::NONE, QspiWidth::FOUR);
qspi.blocking_command(t);
// after
let mut t = TransferConfig::new(cmd, QspiWidth::SING, None, QspiWidth::NONE, QspiWidth::DUAL);
qspi.blocking_command(t);
Defensive patterns

Strategy: validation

Validate before calling

fn widths_fit(transaction: &TransferConfig, max: QspiWidth) -> bool {
    transaction.iwidth <= max && transaction.awidth <= max && transaction.dwidth <= max
}

Prevention

When it happens

Trigger: Submitting a TransferConfig with iwidth, awidth, or dwidth of FOURBITS (or any width above the configured max) to a Qspi set up for one- or two-line mode, via blocking_command/blocking_read/blocking_write, setup_auto_poll, or entering memory-mapped mode (enable_memory_map).

Common situations: Copy-pasting a quad-mode flash driver config onto a board wired for dual/single SPI; flash chip datasheet commands (e.g. quad fast read) used without the quad IO lanes being configured; enabling memory-mapped mode with a quad transaction on a dual-lane setup.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at embassy-stm32/src/qspi/mod.rs:210

            self.bk1d0.as_ref(),
            self.bk1d1.as_ref(),
            self.bk1d2.as_ref(),
            self.bk1d3.as_ref(),
        );
        let bk2 = bank_max(
            self.bk2d0.as_ref(),
            self.bk2d1.as_ref(),
            self.bk2d2.as_ref(),
            self.bk2d3.as_ref(),
        );
        bk1.max(bk2)
    }

    /// Panic if any width in `transaction` exceeds the wired-up IO lanes.
    fn assert_transfer_widths(&self, transaction: &TransferConfig) {
        let max = self.max_transfer_width();
        if transaction.iwidth > max || transaction.awidth > max || transaction.dwidth > max {
            panic!("QSPI transfer width exceeds configured IO lanes");
        }
    }

    /// Do a QSPI command.
    pub fn blocking_command(&mut self, transaction: TransferConfig) {
        self.setup_command(transaction);

        while !T::REGS.sr().read().tcf() {}
        T::REGS.fcr().modify(|v| v.set_ctcf(true));
    }

    /// Blocking read data.
    pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
        #[cfg(not(stm32h7))]
        T::REGS.cr().modify(|v| v.set_dmaen(false));
        self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));

        let current_ar = T::REGS.ar().read().address();

View on GitHub (pinned to 463a07b963)