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
- Verify the buffer passed to the DMA transfer outlives the transfer (use `'static`/owned buffers, not stack locals)
- Check the read/write address and DREQ settings on channel `ch(channel)` configuration for the reported channel number
- Reproduce with the channel number from the panic and inspect its `read_addr`/`trans_count` registers in a debugger
- Ensure the peripheral is enabled/clocked before the DMA transfer starts
- 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
- Only hand DMA slices that live in SRAM and outlive the transfer
- Use embassy-rp's typed buffer abstractions (owned/'static buffers) instead of raw pointers
- Never start DMA before the target peripheral is enabled and clocked
- Verify DREQ selection matches the peripheral to avoid FIFO faults
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
- DMA data error
- DMA: error on DMA_0 channel
- timer must be stopped before setting counter
- Failed to configure PLL_SYS
- Failed to configure PLL_USB
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)