Skip to main content

ra_hal/
dmac.rs

1//! Direct Memory Access Controller (`DMAC`).
2//!
3//! `DMAC` is one of two DMA engines on the `RA4M1`.
4//!
5//! # Notes
6//! - There are four (4) channels, and as the API currently stands one interrupt is required to signal the end of a transfer.
7//! - Configuration is performed directly on `DMAC` registers and typically results in lower latency than with `DTC`.
8//! - Operation may be less predictable than `DTC`.
9
10use core::{marker::PhantomData, task::Poll};
11
12use embassy_hal_internal::{Peri, PeripheralType, interrupt::InterruptExt as _};
13use embassy_sync::waitqueue::AtomicWaker;
14
15use crate::{
16    event_link::{IcuInterrupt as _, InterruptEvent},
17    interrupt::typelevel::{Handler as InterruptHandler, Interrupt as InterruptType},
18    pac::{
19        self,
20        dmac::vals::{Dctg, Dm, Dts, Md, Sm, Sz},
21    },
22};
23
24/// Interrupt handler that handles `DMAC` interrupts.
25pub struct DmacInterruptHandler<C: Instance> {
26    phantom: PhantomData<C>,
27}
28
29/// `DMAC` instance.
30#[allow(private_bounds)]
31pub trait Instance: SealedInstance + Into<AnyChannel> {}
32
33pub(crate) trait SealedInstance: PeripheralType {
34    const INTERRUPT_EVENT: InterruptEvent;
35    const DMAC_INDEX: u8;
36    fn regs() -> pac::dmac::Dmac;
37}
38
39/// Trait that defines the size of a `DMAC` "word".
40///
41/// `DMAC` supports 8-, 16-, and 32- bit words.
42#[allow(private_bounds)]
43pub trait Word: SealedWord {}
44trait SealedWord: Copy {
45    const DMA_WIDTH: Sz;
46}
47
48/// Type erased `DMAC` channel.
49#[derive(Debug, Clone, Copy)]
50pub struct AnyChannel {
51    pub(crate) dmac_index: u8,
52    pub(crate) interrupt_event: InterruptEvent,
53}
54
55impl<C: Instance, Int: InterruptType> InterruptHandler<Int> for DmacInterruptHandler<C> {
56    unsafe fn on_interrupt() {
57        Int::IRQ.icu_unpend();
58        DMAC_WAKERS[usize::from(C::DMAC_INDEX)].wake();
59    }
60}
61
62impl PeripheralType for AnyChannel {}
63
64const DMAC_COUNT: usize = 4;
65static DMAC_WAKERS: [AtomicWaker; DMAC_COUNT] = [const { AtomicWaker::new() }; DMAC_COUNT];
66
67/// `DMAC` channel.
68pub struct Channel<'d> {
69    pub(crate) channel: Peri<'d, AnyChannel>,
70}
71
72impl AnyChannel {
73    #[inline(always)]
74    fn regs(&self) -> pac::dmac::Dmac {
75        let dmac_ptr = pac::DMAC0.as_ptr() as usize;
76        let offset = 0x40 * usize::from(self.dmac_index);
77        unsafe { crate::pac::dmac::Dmac::from_ptr(dmac_ptr.wrapping_add(offset) as _) }
78    }
79}
80
81/// Future representing a `DMAC` transfer.
82pub struct Transfer<'d> {
83    channel: Channel<'d>,
84}
85
86impl<'d> Channel<'d> {
87    /// Creates a new `DMAC` driver for a specific channel.
88    pub fn new<C: Instance, Int: InterruptType>(
89        channel: Peri<'d, C>,
90        irq: impl crate::interrupt::typelevel::Binding<Int, DmacInterruptHandler<C>> + 'd,
91    ) -> Self {
92        let _ = irq;
93
94        // Safety: Interrupt handlers are defined by the irqs argument and thus the interrupts are safe to enable.
95        unsafe {
96            Int::IRQ.enable();
97            Int::IRQ.icu_enable(C::INTERRUPT_EVENT);
98        }
99
100        trace!("DMAC{}, enable={}", C::DMAC_INDEX, C::INTERRUPT_EVENT);
101
102        let dmac = C::regs();
103        dmac.dmint().modify(|r| {
104            r.set_rptie(true);
105            r.set_dtie(true);
106        });
107
108        Self {
109            channel: channel.into(),
110        }
111    }
112
113    /// Reborrow the DMA Channel with a shorter lifetime.
114    #[inline]
115    pub fn reborrow(&mut self) -> Channel<'_> {
116        Channel {
117            channel: self.channel.reborrow(),
118        }
119    }
120
121    /// Configures the fixed DMA registers for a repeated peripheral-to-memory transfer.
122    ///
123    /// Sets up DMAMD, DMTMD, DELSR, and DMSAR once; only DMDAR and DMCRA need updating
124    /// on each re-arm (via [`rearm`](Self::rearm)).
125    pub(crate) fn configure_peripheral_read<W: Word>(
126        &mut self,
127        src: *const W,
128        event: InterruptEvent,
129    ) {
130        let dmac = self.channel.regs();
131        let ctrl_block = cfg_select! {
132            ra8m1 => pac::DMA,
133            _ => pac::ICU,
134        };
135
136        dmac.dmamd().write(|r| {
137            r.set_sm(Sm::Fixed);
138            r.set_dm(Dm::Increment);
139        });
140        dmac.dmtmd().write(|r| {
141            r.set_sz(W::DMA_WIDTH);
142            r.set_md(Md::Normal);
143            r.set_dts(Dts::RepeatDestination);
144            r.set_dctg(Dctg::Interrupts);
145        });
146
147        ctrl_block.delsr(self.channel.dmac_index as _).write(|r| {
148            r.set_dels(event as u16);
149        });
150
151        dmac.dmsar().write_value(src as u32);
152    }
153
154    /// Re-arms the DMA channel to transfer `len` words into `dest`, then enables it.
155    ///
156    /// Call after the previous transfer completes (DTIF) to start the next one without
157    /// re-programming the fixed registers set by [`configure_peripheral_read`](Self::configure_peripheral_read).
158    pub(crate) fn rearm<W: Word>(&mut self, dest: *mut W, len: usize) {
159        let dmac = self.channel.regs();
160        dmac.dmdar().write_value(dest as u32);
161        assert!(len <= usize::from(u16::MAX));
162        dmac.dmcra().write(|r| {
163            r.set_dmcrah(len as u16);
164            r.set_dmcral(len as u16);
165        });
166        dmac.dmcnt().write(|r| r.set_dte(true));
167    }
168
169    /// Disables the DMA channel (clears DTE).
170    pub(crate) fn disable_dte(&mut self) {
171        self.channel.regs().dmcnt().write(|r| r.set_dte(false));
172    }
173
174    /// Registers a waker to be woken on the next DTIF interrupt for this channel.
175    pub(crate) fn register_waker(&self, waker: &core::task::Waker) {
176        DMAC_WAKERS[usize::from(self.channel.dmac_index)].register(waker);
177    }
178
179    /// Returns `true` if DTIF is set and clears it. Returns `false` if not yet set.
180    pub(crate) fn take_dtif(&self) -> bool {
181        let dmac = self.channel.regs();
182        if dmac.dmsts().read().dtif() {
183            dmac.dmsts().modify(|r| r.set_dtif(false));
184            true
185        } else {
186            false
187        }
188    }
189
190    /// Configures a `DMAC` transfer that reads from an address incremented by the specified [`InterruptEvent`] and writes to a fixed address.
191    /// e.g. memory-to-peripheral.
192    ///
193    /// # Arguments
194    /// - `src`
195    /// - `dest`
196    /// - `event`
197    ///
198    /// # Returns
199    /// A future that will finish upon completion of the transfer.
200    pub fn write<W: Word>(
201        &mut self,
202        src: &[W],
203        dest: *mut W,
204        event: InterruptEvent,
205    ) -> Transfer<'_> {
206        let dmac = self.channel.regs();
207
208        let ctrl_block = cfg_select! {
209            ra8m1 => pac::DMA,
210            _ => pac::ICU,
211        };
212
213        dmac.dmamd().write(|r| {
214            r.set_sm(Sm::Increment);
215            r.set_dm(Dm::Fixed);
216        });
217        dmac.dmtmd().write(|r| {
218            r.set_sz(W::DMA_WIDTH);
219            r.set_md(Md::Normal);
220            r.set_dts(Dts::RepeatSource);
221            r.set_dctg(Dctg::Interrupts);
222        });
223
224        ctrl_block.delsr(self.channel.dmac_index as _).write(|r| {
225            r.set_dels(event as u16);
226        });
227
228        dmac.dmsar().write_value(src.as_ptr() as u32);
229        dmac.dmdar().write_value(dest as u32);
230        assert!(src.len() < usize::from(u16::MAX));
231        dmac.dmcra().write(|r| {
232            r.set_dmcrah(src.len() as u16);
233            r.set_dmcral(src.len() as u16);
234        });
235
236        dmac.dmcnt().modify(|r| r.set_dte(true));
237
238        Transfer {
239            channel: self.reborrow(),
240        }
241    }
242
243    /// Configures a `DMAC` transfer that reads from a fixed address and writes to an address incremented by the specified [`InterruptEvent`].
244    /// e.g. peripheral-to-memory.
245    ///
246    /// # Arguments
247    /// - `src`
248    /// - `dest`
249    /// - `event`
250    ///
251    /// # Returns
252    /// A future that will finish upon completion of the transfer.
253    pub fn read<W: Word>(
254        &mut self,
255        src: *const W,
256        dest: &mut [W],
257        event: InterruptEvent,
258    ) -> Transfer<'_> {
259        let dmac = self.channel.regs();
260        let ctrl_block = cfg_select! {
261            ra8m1 => pac::DMA,
262            _ => pac::ICU,
263        };
264
265        dmac.dmamd().write(|r| {
266            r.set_sm(Sm::Fixed);
267            r.set_dm(Dm::Increment);
268        });
269        dmac.dmtmd().write(|r| {
270            r.set_sz(W::DMA_WIDTH);
271            r.set_md(Md::Normal);
272            r.set_dts(Dts::RepeatDestination);
273            r.set_dctg(Dctg::Interrupts);
274        });
275
276        ctrl_block.delsr(self.channel.dmac_index as _).write(|r| {
277            r.set_dels(event as u16);
278        });
279
280        dmac.dmsar().write_value(src as u32);
281        dmac.dmdar().write_value(dest.as_mut_ptr() as u32);
282        assert!(dest.len() < usize::from(u16::MAX));
283        dmac.dmcra().write(|r| {
284            r.set_dmcrah(dest.len() as u16);
285            r.set_dmcral(dest.len() as u16);
286        });
287        dmac.dmcnt().write(|r| r.set_dte(true));
288
289        Transfer {
290            channel: self.reborrow(),
291        }
292    }
293}
294
295impl<'d> Future for Transfer<'d> {
296    type Output = ();
297
298    fn poll(
299        self: core::pin::Pin<&mut Self>,
300        ctx: &mut core::task::Context<'_>,
301    ) -> Poll<Self::Output> {
302        DMAC_WAKERS[usize::from(self.channel.channel.dmac_index)].register(ctx.waker());
303        let dmac = self.channel.channel.regs();
304        if dmac.dmsts().read().dtif() {
305            dmac.dmcnt().write(|r| r.set_dte(false));
306
307            #[cfg(feature = "cache")]
308            {
309                let fcache = pac::FCACHE;
310                fcache.fcacheiv().write(|r| r.set_fcacheiv(true));
311
312                while fcache.fcacheiv().read().fcacheiv() {}
313            }
314
315            Poll::Ready(())
316        } else {
317            Poll::Pending
318        }
319    }
320}
321
322impl<'d> Drop for Channel<'d> {
323    fn drop(&mut self) {
324        #[cfg(not(ra8m1))]
325        let block = pac::ICU;
326        #[cfg(ra8m1)]
327        let block = pac::DMA;
328
329        let index = self.channel.dmac_index;
330
331        block.delsr(index as _).write(|r| r.set_dels(0));
332    }
333}
334
335pub(crate) fn init() {
336    let dma = pac::DMA;
337    dma.dmast().write(|r| r.set_dmst(true));
338}
339
340macro_rules! dmac_chan {
341    ($index:literal) => {
342        paste::paste! {
343            impl crate::dmac::Instance for crate::peripherals::[< DMAC $index >] {}
344
345            impl crate::dmac::SealedInstance for crate::peripherals::[< DMAC $index >] {
346                const INTERRUPT_EVENT: InterruptEvent = crate::event_link::InterruptEvent::[< Dmac $index Int >];
347                const DMAC_INDEX: u8 = $index;
348
349                #[inline(always)]
350                fn regs() -> crate::pac::dmac::Dmac {
351                    crate::pac::[< DMAC $index >]
352                }
353            }
354
355            impl From<crate::peripherals::[< DMAC $index >]> for AnyChannel {
356                fn from(_: crate::peripherals::[< DMAC $index >]) -> Self {
357                    AnyChannel {
358                        dmac_index: $index,
359                        interrupt_event: crate::event_link::InterruptEvent::[< Dmac $index Int >],
360                    }
361                }
362            }
363
364        }
365    };
366}
367
368dmac_chan!(0);
369dmac_chan!(1);
370dmac_chan!(2);
371dmac_chan!(3);
372
373impl Word for u8 {}
374impl SealedWord for u8 {
375    const DMA_WIDTH: Sz = Sz::_8bits;
376}
377
378impl Word for u16 {}
379impl SealedWord for u16 {
380    const DMA_WIDTH: Sz = Sz::_16bits;
381}
382
383impl Word for u32 {}
384impl SealedWord for u32 {
385    const DMA_WIDTH: Sz = Sz::_32bits;
386}