embassy-rs/embassy · error
QSPI data is empty, so the data width should be NONE
Error message
QSPI data is empty, so the data width should be NONE
What it means
In setup_transaction, embassy-stm32's QSPI driver requires that when no data bytes are transferred (data_len is None), the transaction's data width (dwidth) must be QspiWidth::NONE. If you pass no data but a non-NONE width, the driver panics because the hardware config would be inconsistent. It is a fail-fast invariant check on the command/transaction configuration.
Solutions
- Set dwidth: QspiWidth::NONE on the command whenever no data bytes are transferred.
- If data should be sent, provide a non-empty buffer/length (Some(len), len >= 1) matching the dwidth.
- Validate at construction: ensure (data_len.is_some() == (dwidth != QspiWidth::NONE)) before calling the blocking APIs.
Example fix
// before let mut cmd = QspiCommand::new(instruction, QspiWidth::SING); cmd.data_width = QspiWidth::QUAD; // data_len is None -> panic qspi.blocking_command(cmd); // after let mut cmd = QspiCommand::new(instruction, QspiWidth::SING); cmd.data_width = QspiWidth::NONE; // no data phase qspi.blocking_command(cmd);
Defensive patterns
Strategy: validation
Validate before calling
assert_eq!(
cmd.args.data.is_some(),
cmd.data_width != QspiWidth::NONE,
"QSPI: data phase presence must match data width"
);
assert!(cmd.args.data.as_ref().map_or(true, |d| !d.is_empty()), "QSPI: data must be at least one byte"); Type guard
fn has_consistent_data_phase(cmd: &QspiCommand) -> bool {
match (cmd.args.data.as_ref().map(|d| d.len()), cmd.data_width) {
(Some(0), _) | (Some(_), QspiWidth::NONE) => false,
(None, QspiWidth::NONE) | (Some(_), _) => true,
(None, _) => false,
}
} Prevention
- Always set data_width to NONE when building commands without a data buffer.
- Never hardcode data_width independently of the data buffer; derive one from the other.
- Add a unit test that enumerates your command set and checks the (data_len, dwidth) consistency matrix.
When it happens
Trigger: Calling blocking_read, blocking_write, setup_auto_poll, or setup_command with a QspiCommand whose data phase is omitted (no buffer/length) while transaction.dwidth is set to something other than QspiWidth::NONE. Related panics in the same match: Some(0) length, or Some(len) with QspiWidth::NONE, also panic.
Common situations: Copying a command struct from an example that sent data but stripping out the buffer without resetting dwidth; building commands dynamically where the data field is Option and the width is hardcoded; mixing up the alternate/instruction width with the data width.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Can only take the executor once
- DMA data error
- MCLK frequency < 9.5 MHz is not compatible with the TRNG
- Unknown value
- Invalid value
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/9ab5d5eaa0cc73ad.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32/src/qspi/mod.rs:387
match (transaction.address, transaction.awidth) {
(Some(_), QspiWidth::NONE) => panic!("QSPI address can't be sent with an address width of NONE"),
(Some(address), _) => {
// u32::bit_width was only stabilized in 1.97
let address_bit_width = u32::BITS - address.leading_zeros();
if address_bit_width > transaction.address_size.bit_width() as u32 {
panic!("QSPI address too large to be represented with the given address size");
}
}
(None, QspiWidth::NONE) => {}
(None, _) => panic!("QSPI address is not set, so the address width should be NONE"),
}
match (data_len, transaction.dwidth) {
(Some(0), _) => panic!("QSPI data must be at least one byte"),
(Some(_), QspiWidth::NONE) => panic!("QSPI data can't be sent with a data width of NONE"),
(Some(_), _) => {}
(None, QspiWidth::NONE) => {}
(None, _) => panic!("QSPI data is empty, so the data width should be NONE"),
}
T::REGS.fcr().modify(|v| {
v.set_csmf(true);
v.set_ctcf(true);
v.set_ctef(true);
v.set_ctof(true);
});
while T::REGS.sr().read().busy() {}
if let Some(len) = data_len {
T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
}
T::REGS.ccr().write(|v| {
v.set_fmode(fmode.into());
v.set_imode(transaction.iwidth.into());View on GitHub (pinned to 463a07b963)