embassy-rs/embassy · error

memory-to-memory transfers not implemented for GPDMA

Error message

memory-to-memory transfers not implemented for GPDMA

What it means

The GPDMA configure routine was asked for a MemoryToMemory transfer, which this GPDMA driver does not implement (unlike the older DMA/BDMA drivers). The driver panics immediately in configure rather than silently misconfiguring the channel.

Solutions

  1. Perform the copy with the CPU (slice copy_from_slice) instead of DMA
  2. Use another transport (e.g. a timer-triggered peripheral loopback) if a hardware copy is required
  3. Check embassy-stm32 for updates; mem-to-mem GPDMA support may be added later
  4. Use a channel/driver on a non-GPDMA DMA instance if your chip has both

Example fix

// before
dma_copy(&src, &dst); // GPDMA mem-to-mem
// after
dst.copy_from_slice(&src); // CPU copy
Defensive patterns

Strategy: fallback

Validate before calling

let mem_to_mem = matches!(dir, Dir::MemoryToMemory);
assert!(!mem_to_mem, "use CPU copy for GPDMA");

Prevention

When it happens

Trigger: Calling a DMA-based API that passes Dir::MemoryToMemory to configure — e.g. manually constructing a Transfer or using an API that internally does memory-to-memory copy — on a chip using GPDMA (H503/H5/U5).

Common situations: Code ported from older STM32 DMA drivers that supported mem-to-mem (e.g. SPI DMA loops or buffer copies) onto H5/U5 boards; attempting a memory copy via DMA as an optimization.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

    /// This function also causes the channel to reset, which unsuspends (resumes) it.
    unsafe fn configure(
        &self,
        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() {}
        }

View on GitHub (pinned to 463a07b963)