{"record":{"id":"4a161aa9ee71c8cb","repo":"embassy-rs/embassy","slug":"mdma-max-block-count-hit","errorCode":null,"errorMessage":"MDMA: max block count hit","messagePattern":"MDMA: max block count hit","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"embassy-stm32/src/dma/dma_bdma.rs","lineNumber":713,"sourceCode":"                assert!(mem_len_bytes > 0 && mem_len_bytes <= MDMA_MAX_BLOCK * MDMA_MAX_BLOCK_COUNT);\n\n                // Find the best block size/count. This is essentially a factorisation problem\n                // So it's best to avoid large prime number transfer sizes.\n                let mut block_count = mem_len_bytes.div_ceil(MDMA_MAX_BLOCK);\n                let mut block_size = mem_len_bytes.div_ceil(block_count);\n\n                loop {\n                    // Everything matches up so we're good to go\n                    if block_count * block_size == mem_len_bytes {\n                        break;\n                    }\n\n                    // Try a higher block count, lower block size\n                    block_count += 1;\n                    block_size = mem_len_bytes.div_ceil(block_count);\n\n                    if block_count > MDMA_MAX_BLOCK_COUNT {\n                        panic!(\"MDMA: max block count hit\");\n                    }\n                }\n\n                // MDMA requires BNDT (block_size) to be a multiple of TLEN+1 (buffer_size).\n                // Auto-decrease buffer_size until it divides cleanly into block_size.\n                let mut buffer_size = options.buffer_size as usize;\n                while block_size % buffer_size != 0 && buffer_size > 1 {\n                    buffer_size -= 1;\n                }\n                // Update the options so the TCR write uses the correct value\n\n                let (sinc, dinc) = match (incr_mem, dir) {\n                    (Increment::None, _) => (Incmode::Fixed, Incmode::Fixed),\n                    (Increment::Both, _) => (Incmode::Increment, Incmode::Increment),\n                    (Increment::Memory, Dir::MemoryToMemory) => (Incmode::Increment, Incmode::Fixed),\n                    (_, Dir::MemoryToMemory) => (Incmode::Increment, Incmode::Increment),\n                    (Increment::Peripheral, Dir::PeripheralToMemory) => (Incmode::Increment, Incmode::Fixed),\n                    (Increment::Peripheral, Dir::MemoryToPeripheral) => (Incmode::Fixed, Incmode::Increment),","sourceCodeStart":695,"sourceCodeEnd":731,"githubUrl":"https://github.com/embassy-rs/embassy/blob/463a07b963419a1bfe61d5d597c44acb810afb8b/embassy-stm32/src/dma/dma_bdma.rs#L695-L731","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet mut buf = [0u8; 65407]; // prime-ish size, poor factorization\nchannel.read(&mut buf).await;\n// after\nlet mut buf = [0u8; 65536]; // power of two: factors cleanly, one block\nchannel.read(&mut buf).await;","handlingStrategy":"validation","validationCode":"const MDMA_MAX_BLOCK: usize = 16384; // per chip family; check HAL constants\nconst MDMA_MAX_BLOCK_COUNT: usize = 128;\nfn mdma_len_ok(byte_len: usize) -> bool {\n    byte_len > 0 && byte_len <= MDMA_MAX_BLOCK * MDMA_MAX_BLOCK_COUNT\n        && (byte_len % 128 == 0 || byte_len <= MDMA_MAX_BLOCK)\n}","typeGuard":"fn is_factorable_len(byte_len: usize) -> bool {\n    // sizes <= MDMA_MAX_BLOCK or with a small divisor never exceed max block count\n    byte_len <= 16384 || (byte_len % 128 == 0 && byte_len <= 16384 * 128)\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["dma","mdma","panic","embedded","buffer-size","factorization"],"backgroundTag":"value-out-of-range","analyzedSha":"463a07b963419a1bfe61d5d597c44acb810afb8b","analyzedAt":"2026-09-10T13:38:26.660Z","contentChangedAt":"2026-09-10T13:38:26.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}