embassy-rs/embassy · error

DMA transfers may not be larger than 65535 bytes.

Error message

DMA transfers may not be larger than 65535 bytes.

What it means

The computed transfer size (length in elements times word size in bytes) does not fit into the 16-bit BNDT register, so it exceeds 65535 bytes. GPDMA transfers are capped at 64 KiB minus one byte per channel programming operation. The driver panics rather than truncating the transfer.

Solutions

  1. Split the transfer into chunks of at most 65535 bytes and loop
  2. Reduce the buffer size to fit under 64 KiB
  3. Use DMA linked-list mode with multiple items each ≤ 65535 bytes
  4. Use a non-GPDMA DMA instance with a larger (e.g. 17/32-bit) count register if available

Example fix

// before
let buf = [0u8; 100_000];
usart.read(&mut buf).await?;
// after
let mut buf = [0u8; 100_000];
for chunk in buf.chunks_mut(65_535) {
    usart.read(chunk).await?;
}
Defensive patterns

Strategy: validation

Validate before calling

fn gpdma_len_ok<T>(buf: &[T]) -> bool { buf.len() * core::mem::size_of::<T>() <= 65_535 }

Prevention

When it happens

Trigger: Calling read/write with a buffer whose byte length is > 65535 (e.g. a 128 KiB buffer, or 40000 u16 elements = 80000 bytes) on a GPDMA channel.

Common situations: Large ADC sample buffers, big UART/SPI frames, or audio buffers exceeding 64 KiB in a single DMA transfer.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


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

Appendix: source

Thrown at embassy-stm32/src/dma/gpdma/mod.rs:653

        request: Request,
        dir: Dir,
        peri_addr: *const u32,
        mem_addr: *mut u32,
        mem_len: usize,
        incr_mem: bool,
        data_size: WordSize,
        dst_size: WordSize,
        options: TransferOptions,
    ) {
        // BNDT is the number of source bytes. For a packing/unpacking transfer
        // the memory side dictates how much data the caller wants moved.
        let mem_size = match dir {
            Dir::MemoryToPeripheral => data_size,
            Dir::PeripheralToMemory => dst_size,
            Dir::MemoryToMemory => panic!("memory-to-memory transfers not implemented for GPDMA"),
        };
        let Ok(bndt) = (mem_len * mem_size.bytes()).try_into() else {
            panic!("DMA transfers may not be larger than 65535 bytes.");
        };

        let info = self.info();
        let ch = info.dma.ch(info.num);

        // "Preceding reads and writes cannot be moved past subsequent writes."
        fence(Ordering::SeqCst);

        // The reset is effective when the channel is in steady state, meaning one of the following:
        // - active channel in suspended state (GPDMA_CxSR.SUSPF = 1 and GPDMA_CxSR.IDLEF = GPDMA_CxCR.EN = 1)
        // - channel in disabled state (GPDMA_CxSR.IDLEF = 1 and GPDMA_CxCR.EN = 0).
        if ch.cr().read().en() {
            ch.cr().modify(|w| w.set_susp(true));
            while !ch.sr().read().suspf() {}
        }
        ch.cr().write(|w| w.set_reset(true));

        ch.fcr().write(|w| {

View on GitHub (pinned to 463a07b963)