embassy-rs/embassy · critical

DMA: error on DMA_0 channel

Error message

DMA: error on DMA_0 channel {}

What it means

The DMA interrupt handler checks each channel's `ctrl_trig.ahb_error` flag and panics if the DMA transfer hit an AHB bus error (bad address, inaccessible peripheral memory, or FIFO underrun/overrun at the bus level). This indicates the transfer descriptor pointed at invalid or unserviced memory.

Solutions

  1. Verify the buffer passed to the DMA transfer outlives the transfer (use `'static`/owned buffers, not stack locals)
  2. Check the read/write address and DREQ settings on channel `ch(channel)` configuration for the reported channel number
  3. Reproduce with the channel number from the panic and inspect its `read_addr`/`trans_count` registers in a debugger
  4. Ensure the peripheral is enabled/clocked before the DMA transfer starts
  5. Handle errors gracefully by replacing the panic-based IRQ handler with one that logs and aborts the channel

Example fix

// before
fn send(mut buf: [u8; 64]) { dma.write(buf.into(), ...).await; } // buf dies at fn end, DMA may fault
// after
async fn send(dma: &mut DmaChannel, buf: &'static mut [u8; 64]) { dma.write(buf.into(), ...).await; } // static/lifetime-safe buffer
Defensive patterns

Strategy: type-guard

Validate before calling

fn validate_dma_target(buf: &[u8]) -> Result<(), &'static str> {
    if buf.as_ptr() as u32 >= 0x2000_0000 && buf.as_ptr() as u32 < 0x2004_2000 { Ok(()) } else { Err("DMA buffer must be in SRAM") }
}
// Also ensure the buffer is 'static / owned for the transfer duration.

Type guard

fn is_sram_slice<T>(s: &[T]) -> bool {
    let a = s.as_ptr() as usize;
    (0x2000_0000..0x2004_2000).contains(&a)
}

Prevention

When it happens

Trigger: A DMA channel configured with a read/write address that is invalid or becomes invalid mid-transfer (unmapped RAM region, peripheral FIFO not ready), triggering `ahb_error` when the IRQ fires; chained transfers with wrong ring/carrier addresses.

Common situations: DMA into/from a buffer moved or dropped while transfer is in flight (stack buffer, dangling pointer); using DMA with flash/XIP regions incorrectly; wrong DREQ selection causing FIFO underrun on a peripheral; buffer size crossing an address boundary the bus forbids.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at embassy-rp/src/dma.rs:28

use embassy_sync::waitqueue::AtomicWaker;
use pac::dma::vals::DataSize;

use crate::interrupt::typelevel::Interrupt;
use crate::mode::{Async, Blocking, Mode};
use crate::pac::dma::vals;
use crate::{RegExt, interrupt, pac, peripherals};

/// DMA interrupt handler.
pub struct InterruptHandler<T: ChannelInstance> {
    _phantom: PhantomData<T>,
}

impl<T: ChannelInstance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
    unsafe fn on_interrupt() {
        let channel = T::number() as usize;
        let ctrl_trig = pac::DMA.ch(channel).ctrl_trig().read();
        if ctrl_trig.ahb_error() {
            panic!("DMA: error on DMA_0 channel {}", channel);
        }

        let ints0 = pac::DMA.ints(0).read();
        if ints0 & (1 << channel) != 0 {
            pac::DMA.ints(0).write_value(1 << channel);

            CHANNEL_WAKERS[channel].wake();
        }
    }
}

pub(crate) unsafe fn init() {
    interrupt::DMA_IRQ_0.set_priority(interrupt::Priority::P3);
}

/// DMA channel driver.
pub struct Channel<'d, M: Mode> {
    number: u8,

View on GitHub (pinned to 463a07b963)