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
- Split the transfer into chunks of at most 65535 bytes, chaining multiple linked-list items.
- Use a smaller data word size (e.g. u8 words instead of u32) if the data allows, keeping mem_len * word_bytes <= 65535.
- Use a 2D linked-list item or repeated blocks if the chip's GPDMA supports larger effective transfers.
- 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
- Enforce a 65535-byte cap on GPDMA linked-list buffers at the application boundary
- Remember the limit is in BYTES not elements (u32 buffers: max 16383 elements)
- Chunk large transfers into multiple linked-list items
- When porting from classic DMA, re-check length limits: GPDMA BNDT is 16-bit bytes
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
- MDMA: max block count hit
- memory-to-memory transfers are not valid for linked-list…
- DMA: link transfer error on DMA@
- DMA data error
- DMA: error on DMA_0 channel
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)