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

When constructing a GPDMA linked-list Item, the block length BNDT is computed as mem_len * data_size bytes and stored in a u16 register field. If the byte count exceeds 65535, the TryInto<u16> conversion fails and the driver panics, because GPDMA hardware encodes block transfer length in 16 bits.

Solutions

  1. Split the transfer into chunks of at most 65535 bytes, chaining multiple linked-list items.
  2. Use a smaller data word size (e.g. u8 words instead of u32) if the data allows, keeping mem_len * word_bytes <= 65535.
  3. Use a 2D linked-list item or repeated blocks if the chip's GPDMA supports larger effective transfers.
  4. Use a classic DMA/BDMA channel (16-bit NDTR in elements, up to 65535 elements) that fits the required length.

Example fix

// before
let mut buf = [0u32; 20000]; // 80000 bytes > 65535
let item = LinearItem::new_read(request, peri, &mut buf);
// after
let (mut buf1, mut buf2) = ([0u32; 16000], [0u32; 4000]); // 64000 + 16000 bytes
let item1 = LinearItem::new_read(request, peri, &mut buf1);
let item2 = LinearItem::new_read(request, peri, &mut buf2);
Defensive patterns

Strategy: validation

Validate before calling

fn gpdma_bndt_ok(mem_len: usize, word_bytes: usize) -> bool {
    mem_len.checked_mul(word_bytes).map_or(false, |b| b <= 65535)
}
// before building the item:
assert!(gpdma_bndt_ok(buf.len(), core::mem::size_of::<u32>()));

Type guard

fn fits_gpdma_block<T>(buf: &[T]) -> bool {
    core::mem::size_of_val(buf) <= 65535
}

Try / catch

// panics are not catchable in embedded Rust; validate before the call:
if core::mem::size_of_val(&buf) > 65535 {
    // chunk the transfer instead of constructing the item
    return Err(DmaError::TooLarge);
}
let item = LinearItem::new_read(request, peri, &mut buf);

Prevention

When it happens

Trigger: Calling new_read/new_write (or LinearItem/TwoDItem construction) on a GPDMA channel with a buffer whose total byte size (mem_len * data_size.bytes()) is greater than 65535, e.g. a 70000-byte buffer or a 40000-element u16 buffer.

Common situations: Passing large RAM buffers straight to a GPDMA linked-list transfer without chunking; assuming element count limits rather than byte limits (a u32 buffer of 20000 elements already exceeds 65535 bytes); moving code that worked on DMA/BDMA (32-bit NDTR) to GPDMA chips (e.g. H5/U5).

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/c22ebf3ad6e3b1c9. Report an issue: GitHub.

Appendix: source

Thrown at embassy-stm32/src/dma/gpdma/linked_list.rs:125

impl Item {
    /// Build a new item from raw transfer parameters.
    ///
    /// # Safety
    /// The caller must ensure `peri_addr` and `mem_addr` are valid for the transfer.
    #[allow(clippy::too_many_arguments)]
    pub(super) unsafe fn new(
        request: Request,
        dir: Dir,
        peri_addr: *const u32,
        mem_addr: *mut u32,
        mem_len: usize,
        incr_mem: bool,
        data_size: WordSize,
        dst_size: WordSize,
    ) -> Self {
        let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else {
            panic!("DMA transfers may not be larger than 65535 bytes.");
        };

        let mut br1 = regs::ChBr1(0);
        br1.set_bndt(bndt);

        let mut tr1 = regs::ChTr1(0);
        tr1.set_sdw(data_size.into());
        tr1.set_ddw(dst_size.into());
        tr1.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem);
        tr1.set_dinc(dir == Dir::PeripheralToMemory && incr_mem);

        #[cfg(gpdma)]
        {
            use stm32_metapac::gpdma::vals::Ap;
            tr1.set_sap(match dir {
                Dir::MemoryToPeripheral => Ap::Port0,
                Dir::PeripheralToMemory => Ap::Port1,
                Dir::MemoryToMemory => panic!("memory-to-memory transfers are not valid for linked-list items"),

View on GitHub (pinned to 463a07b963)