316 lines
7.4 KiB
Rust
316 lines
7.4 KiB
Rust
//! Direct Memory Access (DMA)
|
|
use core::future::Future;
|
|
use core::pin::Pin;
|
|
use core::sync::atomic::{compiler_fence, Ordering};
|
|
use core::task::{Context, Poll};
|
|
|
|
use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
|
|
use embassy_sync::waitqueue::AtomicWaker;
|
|
use pac::dma::vals::DataSize;
|
|
|
|
use crate::interrupt::InterruptExt;
|
|
use crate::pac::dma::vals;
|
|
use crate::{interrupt, pac, peripherals};
|
|
|
|
#[cfg(feature = "rt")]
|
|
#[interrupt]
|
|
fn DMA_IRQ_0() {
|
|
let ints0 = pac::DMA.ints(0).read();
|
|
for channel in 0..CHANNEL_COUNT {
|
|
let ctrl_trig = pac::DMA.ch(channel).ctrl_trig().read();
|
|
if ctrl_trig.ahb_error() {
|
|
panic!("DMA: error on DMA_0 channel {}", channel);
|
|
}
|
|
|
|
if ints0 & (1 << channel) == (1 << channel) {
|
|
CHANNEL_WAKERS[channel].wake();
|
|
}
|
|
}
|
|
pac::DMA.ints(0).write_value(ints0);
|
|
}
|
|
|
|
pub(crate) unsafe fn init() {
|
|
interrupt::DMA_IRQ_0.disable();
|
|
interrupt::DMA_IRQ_0.set_priority(interrupt::Priority::P3);
|
|
|
|
pac::DMA.inte(0).write_value(0xFFFF);
|
|
|
|
interrupt::DMA_IRQ_0.enable();
|
|
}
|
|
|
|
/// DMA read.
|
|
///
|
|
/// SAFETY: Slice must point to a valid location reachable by DMA.
|
|
pub unsafe fn read<'a, C: Channel, W: Word>(
|
|
ch: impl Peripheral<P = C> + 'a,
|
|
from: *const W,
|
|
to: *mut [W],
|
|
dreq: vals::TreqSel,
|
|
) -> Transfer<'a, C> {
|
|
copy_inner(
|
|
ch,
|
|
from as *const u32,
|
|
to as *mut W as *mut u32,
|
|
to.len(),
|
|
W::size(),
|
|
false,
|
|
true,
|
|
dreq,
|
|
)
|
|
}
|
|
|
|
/// DMA write.
|
|
///
|
|
/// SAFETY: Slice must point to a valid location reachable by DMA.
|
|
pub unsafe fn write<'a, C: Channel, W: Word>(
|
|
ch: impl Peripheral<P = C> + 'a,
|
|
from: *const [W],
|
|
to: *mut W,
|
|
dreq: vals::TreqSel,
|
|
) -> Transfer<'a, C> {
|
|
copy_inner(
|
|
ch,
|
|
from as *const W as *const u32,
|
|
to as *mut u32,
|
|
from.len(),
|
|
W::size(),
|
|
true,
|
|
false,
|
|
dreq,
|
|
)
|
|
}
|
|
|
|
// static mut so that this is allocated in RAM.
|
|
static mut DUMMY: u32 = 0;
|
|
|
|
/// DMA repeated write.
|
|
///
|
|
/// SAFETY: Slice must point to a valid location reachable by DMA.
|
|
pub unsafe fn write_repeated<'a, C: Channel, W: Word>(
|
|
ch: impl Peripheral<P = C> + 'a,
|
|
to: *mut W,
|
|
len: usize,
|
|
dreq: vals::TreqSel,
|
|
) -> Transfer<'a, C> {
|
|
copy_inner(
|
|
ch,
|
|
core::ptr::addr_of_mut!(DUMMY) as *const u32,
|
|
to as *mut u32,
|
|
len,
|
|
W::size(),
|
|
false,
|
|
false,
|
|
dreq,
|
|
)
|
|
}
|
|
|
|
/// DMA copy between slices.
|
|
///
|
|
/// SAFETY: Slices must point to locations reachable by DMA.
|
|
pub unsafe fn copy<'a, C: Channel, W: Word>(
|
|
ch: impl Peripheral<P = C> + 'a,
|
|
from: &[W],
|
|
to: &mut [W],
|
|
) -> Transfer<'a, C> {
|
|
let from_len = from.len();
|
|
let to_len = to.len();
|
|
assert_eq!(from_len, to_len);
|
|
copy_inner(
|
|
ch,
|
|
from.as_ptr() as *const u32,
|
|
to.as_mut_ptr() as *mut u32,
|
|
from_len,
|
|
W::size(),
|
|
true,
|
|
true,
|
|
vals::TreqSel::PERMANENT,
|
|
)
|
|
}
|
|
|
|
fn copy_inner<'a, C: Channel>(
|
|
ch: impl Peripheral<P = C> + 'a,
|
|
from: *const u32,
|
|
to: *mut u32,
|
|
len: usize,
|
|
data_size: DataSize,
|
|
incr_read: bool,
|
|
incr_write: bool,
|
|
dreq: vals::TreqSel,
|
|
) -> Transfer<'a, C> {
|
|
into_ref!(ch);
|
|
|
|
let p = ch.regs();
|
|
|
|
p.read_addr().write_value(from as u32);
|
|
p.write_addr().write_value(to as u32);
|
|
#[cfg(feature = "rp2040")]
|
|
p.trans_count().write(|w| {
|
|
*w = len as u32;
|
|
});
|
|
#[cfg(feature = "_rp235x")]
|
|
p.trans_count().write(|w| {
|
|
w.set_mode(0.into());
|
|
w.set_count(len as u32);
|
|
});
|
|
|
|
compiler_fence(Ordering::SeqCst);
|
|
|
|
p.ctrl_trig().write(|w| {
|
|
w.set_treq_sel(dreq);
|
|
w.set_data_size(data_size);
|
|
w.set_incr_read(incr_read);
|
|
w.set_incr_write(incr_write);
|
|
w.set_chain_to(ch.number());
|
|
w.set_en(true);
|
|
});
|
|
|
|
compiler_fence(Ordering::SeqCst);
|
|
Transfer::new(ch)
|
|
}
|
|
|
|
/// DMA transfer driver.
|
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
pub struct Transfer<'a, C: Channel> {
|
|
channel: PeripheralRef<'a, C>,
|
|
}
|
|
|
|
impl<'a, C: Channel> Transfer<'a, C> {
|
|
pub(crate) fn new(channel: impl Peripheral<P = C> + 'a) -> Self {
|
|
into_ref!(channel);
|
|
|
|
Self { channel }
|
|
}
|
|
}
|
|
|
|
impl<'a, C: Channel> Drop for Transfer<'a, C> {
|
|
fn drop(&mut self) {
|
|
let p = self.channel.regs();
|
|
pac::DMA
|
|
.chan_abort()
|
|
.modify(|m| m.set_chan_abort(1 << self.channel.number()));
|
|
while p.ctrl_trig().read().busy() {}
|
|
}
|
|
}
|
|
|
|
impl<'a, C: Channel> Unpin for Transfer<'a, C> {}
|
|
impl<'a, C: Channel> Future for Transfer<'a, C> {
|
|
type Output = ();
|
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
// We need to register/re-register the waker for each poll because any
|
|
// calls to wake will deregister the waker.
|
|
CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker());
|
|
|
|
if self.channel.regs().ctrl_trig().read().busy() {
|
|
Poll::Pending
|
|
} else {
|
|
Poll::Ready(())
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "rp2040")]
|
|
pub(crate) const CHANNEL_COUNT: usize = 12;
|
|
#[cfg(feature = "_rp235x")]
|
|
pub(crate) const CHANNEL_COUNT: usize = 16;
|
|
static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];
|
|
|
|
trait SealedChannel {}
|
|
trait SealedWord {}
|
|
|
|
/// DMA channel interface.
|
|
#[allow(private_bounds)]
|
|
pub trait Channel: Peripheral<P = Self> + SealedChannel + Into<AnyChannel> + Sized + 'static {
|
|
/// Channel number.
|
|
fn number(&self) -> u8;
|
|
|
|
/// Channel registry block.
|
|
fn regs(&self) -> pac::dma::Channel {
|
|
pac::DMA.ch(self.number() as _)
|
|
}
|
|
|
|
/// Convert into type-erased [AnyChannel].
|
|
fn degrade(self) -> AnyChannel {
|
|
AnyChannel { number: self.number() }
|
|
}
|
|
}
|
|
|
|
/// DMA word.
|
|
#[allow(private_bounds)]
|
|
pub trait Word: SealedWord {
|
|
/// Word size.
|
|
fn size() -> vals::DataSize;
|
|
}
|
|
|
|
impl SealedWord for u8 {}
|
|
impl Word for u8 {
|
|
fn size() -> vals::DataSize {
|
|
vals::DataSize::SIZE_BYTE
|
|
}
|
|
}
|
|
|
|
impl SealedWord for u16 {}
|
|
impl Word for u16 {
|
|
fn size() -> vals::DataSize {
|
|
vals::DataSize::SIZE_HALFWORD
|
|
}
|
|
}
|
|
|
|
impl SealedWord for u32 {}
|
|
impl Word for u32 {
|
|
fn size() -> vals::DataSize {
|
|
vals::DataSize::SIZE_WORD
|
|
}
|
|
}
|
|
|
|
/// Type erased DMA channel.
|
|
pub struct AnyChannel {
|
|
number: u8,
|
|
}
|
|
|
|
impl_peripheral!(AnyChannel);
|
|
|
|
impl SealedChannel for AnyChannel {}
|
|
impl Channel for AnyChannel {
|
|
fn number(&self) -> u8 {
|
|
self.number
|
|
}
|
|
}
|
|
|
|
macro_rules! channel {
|
|
($name:ident, $num:expr) => {
|
|
impl SealedChannel for peripherals::$name {}
|
|
impl Channel for peripherals::$name {
|
|
fn number(&self) -> u8 {
|
|
$num
|
|
}
|
|
}
|
|
|
|
impl From<peripherals::$name> for crate::dma::AnyChannel {
|
|
fn from(val: peripherals::$name) -> Self {
|
|
crate::dma::Channel::degrade(val)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
channel!(DMA_CH0, 0);
|
|
channel!(DMA_CH1, 1);
|
|
channel!(DMA_CH2, 2);
|
|
channel!(DMA_CH3, 3);
|
|
channel!(DMA_CH4, 4);
|
|
channel!(DMA_CH5, 5);
|
|
channel!(DMA_CH6, 6);
|
|
channel!(DMA_CH7, 7);
|
|
channel!(DMA_CH8, 8);
|
|
channel!(DMA_CH9, 9);
|
|
channel!(DMA_CH10, 10);
|
|
channel!(DMA_CH11, 11);
|
|
#[cfg(feature = "_rp235x")]
|
|
channel!(DMA_CH12, 12);
|
|
#[cfg(feature = "_rp235x")]
|
|
channel!(DMA_CH13, 13);
|
|
#[cfg(feature = "_rp235x")]
|
|
channel!(DMA_CH14, 14);
|
|
#[cfg(feature = "_rp235x")]
|
|
channel!(DMA_CH15, 15);
|