embassy-rs/embassy · critical

DMA: error on DMA@ channel

Error message

DMA: error on DMA@{:08x} channel {}

What it means

The DMA (direct memory access) interrupt handler for classic STM32 DMA controllers panics when the transfer-error flag (TEIF) is set for the channel being serviced. A transfer error means the hardware could not complete the memory/peripheral access (bad address, bus fault, unsupported alignment/size), so the driver stops with a panic naming the DMA instance base address and channel number.

Solutions

  1. Check the buffer address/alignment: DMA targets must be in DMA-accessible RAM (not flash for writes, not CCMRAM on many chips) and aligned to the access size.
  2. Ensure the source/destination pointer and data width configuration match (e.g. u16 slice with 16-bit transfer).
  3. Verify the peripheral is enabled/clocked and the request multiplexer (DMAMUX) routes the correct request to the channel.
  4. Keep the buffer alive for the whole transfer (e.g. await the future or use a 'static/owned buffer).

Example fix

// before
let buf = [0u8; 16]; // stack, may be invalid after return
uart.write_dma(&buf).await;

// after
static BUF: [u8; 16] = [0u8; 16]; // DMA-accessible, alive during transfer
uart.write_dma(&BUF).await;
Defensive patterns

Strategy: validation

Validate before calling

// Before starting a DMA transfer:
let addr = buf.as_ptr() as u32;
assert!(addr % core::mem::size_of::<T>() == 0, "DMA buffer misaligned");
// and ensure buf lives in DMA-accessible RAM (not flash/CCM for writes).

Type guard

fn dma_accessible<T>(buf: &[T]) -> bool {
    let addr = buf.as_ptr() as u32;
    // SRAM range example; adjust per chip
    (0x2000_0000..0x2005_0000).contains(&addr)
}

Try / catch

// This is a hard panic in an IRQ handler; it cannot be caught.
// Guard instead: validate address and lifetime before write_dma/read_dma.
assert!(dma_accessible(&buf), "buffer not DMA accessible");

Prevention

When it happens

Trigger: A DMA transfer completes with isr.teif(info.num % 4) set when on_irq runs — e.g. invalid source/destination address, misaligned buffer, or peripheral FIFO error during an active DMA channel transfer.

Common situations: Passing a slice in flash/CCM RAM not accessible by DMA; buffer not aligned to the transfer size; writing to a peripheral register with wrong word size; stack-allocated buffer moved/freed while transfer in flight; wrong DMA channel/mux routing configuration.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at embassy-stm32/src/dma/dma_bdma.rs:26

use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer};
use super::word::{Word, WordSize};
use super::{Channel, Dir, Increment, Request, STATE, info};
use crate::_generated::DmaChannel;
use crate::interrupt::typelevel::Interrupt;
use crate::rcc::WakeGuard;
use crate::{interrupt, pac};

pub(crate) unsafe fn on_irq(channel: DmaChannel) {
    let info = info(channel);
    let state = &STATE[channel as usize];
    match info.dma {
        #[cfg(dma)]
        DmaInfo::Dma(r) => {
            let cr = r.st(info.num).cr();
            let isr = r.isr(info.num / 4).read();

            if isr.teif(info.num % 4) {
                panic!("DMA: error on DMA@{:08x} channel {}", r.as_ptr() as u32, info.num);
            }

            let mut activity = false;
            if isr.htif(info.num % 4) && cr.read().htie() {
                // Acknowledge half transfer complete interrupt
                r.ifcr(info.num / 4).write(|w| w.set_htif(info.num % 4, true));
                activity = true;
            }
            if isr.tcif(info.num % 4) && cr.read().tcie() {
                // Acknowledge transfer complete interrupt
                r.ifcr(info.num / 4).write(|w| w.set_tcif(info.num % 4, true));
                state.complete_count.fetch_add(1, Ordering::Release);
                activity = true;
            }
            if !activity {
                return;
            }
            state.waker.wake();

View on GitHub (pinned to 463a07b963)