embassy-rs/embassy · error
MDMA: max block count hit
Error message
MDMA: max block count hit
What it means
This panic comes from MDMA channel configuration in embassy-stm32 when computing the block layout for a transfer. MDMA moves data in blocks, and the driver factorizes the total transfer length into block_count x block_size. If no valid factorization is found before block_count exceeds MDMA_MAX_BLOCK_COUNT (typically 128), the driver panics because the hardware cannot express the transfer.
Solutions
- Reduce the transfer length so the byte count (mem_len * word size) is comfortably below MDMA_MAX_BLOCK * MDMA_MAX_BLOCK_COUNT and has small factors (e.g. multiple of 128 or 1024).
- Pad or align the buffer length to a highly composite size so block_count * block_size == mem_len_bytes is found immediately (initial block_size = MDMA_MAX_BLOCK divides evenly).
- Split the transfer into several smaller DMA operations.
- Use a different DMA type (DMA/BDMA or GPDMA channel) that handles arbitrary lengths in 65535-byte blocks without factorization.
- Note: sizes with small factors never hit this loop break condition — check mem_len bytes for prime factors before choosing buffer size.
Example fix
// before let mut buf = [0u8; 65407]; // prime-ish size, poor factorization channel.read(&mut buf).await; // after let mut buf = [0u8; 65536]; // power of two: factors cleanly, one block channel.read(&mut buf).await;
Defensive patterns
Strategy: validation
Validate before calling
const MDMA_MAX_BLOCK: usize = 16384; // per chip family; check HAL constants
const MDMA_MAX_BLOCK_COUNT: usize = 128;
fn mdma_len_ok(byte_len: usize) -> bool {
byte_len > 0 && byte_len <= MDMA_MAX_BLOCK * MDMA_MAX_BLOCK_COUNT
&& (byte_len % 128 == 0 || byte_len <= MDMA_MAX_BLOCK)
} Type guard
fn is_factorable_len(byte_len: usize) -> bool {
// sizes <= MDMA_MAX_BLOCK or with a small divisor never exceed max block count
byte_len <= 16384 || (byte_len % 128 == 0 && byte_len <= 16384 * 128)
} Prevention
- Keep MDMA transfer byte sizes powers of two or multiples of the max block size
- Check mem_len_bytes <= MDMA_MAX_BLOCK * MDMA_MAX_BLOCK_COUNT before starting a transfer
- Avoid large prime buffer sizes; pad buffers to round sizes
- Split oversized transfers into chunks
When it happens
Trigger: Starting an MDMA transfer (e.g. read/write on an MDMA-backed channel) whose total byte length (mem_len * word size) cannot be decomposed into at most MDMA_MAX_BLOCK_COUNT blocks whose sizes exactly multiply back to the length — typically because the byte length is a large number with poor factors near the upper limit (max block size * max block count), such as a large prime or awkwardly sized buffer.
Common situations: Using large buffers with sizes that are large primes or poorly factorable values (e.g. 65407-byte buffers), transferring buffers whose byte length barely exceeds MDMA_MAX_BLOCK * MDMA_MAX_BLOCK_COUNT, or word sizes (u16/u32) pushing an element count over the factorizable range.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- DMA: error on MDMA@ channel
- DMA transfers may not be larger than 65535 bytes.
- DMA data error
- DMA: error on DMA_0 channel
- Output buffer length must match input length.
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/4a161aa9ee71c8cb.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32/src/dma/dma_bdma.rs:713
assert!(mem_len_bytes > 0 && mem_len_bytes <= MDMA_MAX_BLOCK * MDMA_MAX_BLOCK_COUNT);
// Find the best block size/count. This is essentially a factorisation problem
// So it's best to avoid large prime number transfer sizes.
let mut block_count = mem_len_bytes.div_ceil(MDMA_MAX_BLOCK);
let mut block_size = mem_len_bytes.div_ceil(block_count);
loop {
// Everything matches up so we're good to go
if block_count * block_size == mem_len_bytes {
break;
}
// Try a higher block count, lower block size
block_count += 1;
block_size = mem_len_bytes.div_ceil(block_count);
if block_count > MDMA_MAX_BLOCK_COUNT {
panic!("MDMA: max block count hit");
}
}
// MDMA requires BNDT (block_size) to be a multiple of TLEN+1 (buffer_size).
// Auto-decrease buffer_size until it divides cleanly into block_size.
let mut buffer_size = options.buffer_size as usize;
while block_size % buffer_size != 0 && buffer_size > 1 {
buffer_size -= 1;
}
// Update the options so the TCR write uses the correct value
let (sinc, dinc) = match (incr_mem, dir) {
(Increment::None, _) => (Incmode::Fixed, Incmode::Fixed),
(Increment::Both, _) => (Incmode::Increment, Incmode::Increment),
(Increment::Memory, Dir::MemoryToMemory) => (Incmode::Increment, Incmode::Fixed),
(_, Dir::MemoryToMemory) => (Incmode::Increment, Incmode::Increment),
(Increment::Peripheral, Dir::PeripheralToMemory) => (Incmode::Increment, Incmode::Fixed),
(Increment::Peripheral, Dir::MemoryToPeripheral) => (Incmode::Fixed, Incmode::Increment),View on GitHub (pinned to 463a07b963)