atsamd_hal/dmac/
dma_controller.rs

1//! # Abstractions to setup and use the DMA controller
2//!
3//! # Initializing
4//!
5//! The DMAC should be initialized using the
6//! [`DmaController::init`] method. It will consume the
7//! DMAC object generated by the PAC. By default, all four priority levels
8//! will be enabled, but can be selectively enabled/disabled through the
9//! [`DmaController::enable_levels`] ansd [`DmaController::disable_levels`]
10//! methods.
11//!
12//! # Splitting Channels
13//!
14//! Using the [`DmaController::split`] method will return
15//! a struct containing handles to individual channels.
16//!
17//! # Releasing the DMAC
18//!
19//! Using the [`free`](DmaController::free) method will
20//! deinitialize the DMAC and return the underlying PAC object.
21#![allow(unused_braces)]
22
23use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
24use core::marker::PhantomData;
25
26use modular_bitfield::prelude::*;
27use seq_macro::seq;
28
29#[hal_cfg(any("dmac-d11", "dmac-d21"))]
30pub use crate::pac::dmac::chctrlb::{
31    Lvlselect as PriorityLevel, Trigactselect as TriggerAction, Trigsrcselect as TriggerSource,
32};
33
34#[hal_cfg("dmac-d5x")]
35pub use crate::pac::dmac::channel::{
36    chctrla::{
37        Burstlenselect as BurstLength, Thresholdselect as FifoThreshold,
38        Trigactselect as TriggerAction, Trigsrcselect as TriggerSource,
39    },
40    chprilvl::Prilvlselect as PriorityLevel,
41};
42
43use super::{
44    channel::{Channel, Uninitialized},
45    sram,
46};
47use crate::{
48    pac::{Dmac, Pm},
49    typelevel::NoneT,
50};
51
52/// Trait representing a DMA channel ID
53pub trait ChId {
54    const U8: u8;
55    const USIZE: usize;
56}
57
58/// Mask representing which priority levels should be enabled/disabled
59#[bitfield]
60#[repr(u16)]
61pub struct PriorityLevelMask {
62    #[skip]
63    _reserved: B8,
64    /// Level 0
65    pub level0: bool,
66    /// Level 1
67    pub level1: bool,
68    /// Level 2
69    pub level2: bool,
70    /// Level 3
71    pub level3: bool,
72    #[skip]
73    _reserved: B4,
74}
75
76impl Default for PriorityLevelMask {
77    fn default() -> Self {
78        Self::new()
79    }
80}
81
82/// Mask representing which priority levels should be configured as round-robin
83#[bitfield]
84#[repr(u32)]
85pub struct RoundRobinMask {
86    #[skip]
87    _reserved: B7,
88    /// Level 0
89    pub level0: bool,
90    #[skip]
91    _reserved: B7,
92    /// Level 1
93    pub level1: bool,
94    #[skip]
95    _reserved: B7,
96    /// Level 2
97    pub level2: bool,
98    #[skip]
99    _reserved: B7,
100    /// Level 3
101    pub level3: bool,
102}
103
104impl Default for RoundRobinMask {
105    fn default() -> Self {
106        Self::new()
107    }
108}
109
110macro_rules! define_channels_struct {
111    ($num_channels:literal) => {
112        seq!(N in 0..$num_channels {
113            #(
114                /// Type alias for a channel number
115                pub enum Ch~N {}
116
117                impl ChId for Ch~N {
118                    const U8: u8 = N;
119                    const USIZE: usize = N;
120                }
121            )*
122
123            /// Struct generating individual handles to each DMA channel
124            pub struct Channels(
125                #(
126                    pub Channel<Ch~N, Uninitialized>,
127                )*
128            );
129        });
130    };
131}
132
133with_num_channels!(define_channels_struct);
134
135#[cfg(feature = "async")]
136macro_rules! define_channels_struct_future {
137    ($num_channels:literal) => {
138        seq!(N in 0..$num_channels {
139            /// Struct generating individual handles to each DMA channel for `async` operation
140            pub struct FutureChannels(
141                #(
142                    pub Channel<Ch~N, super::channel::UninitializedFuture>,
143                )*
144            );
145        });
146    };
147}
148
149#[cfg(feature = "async")]
150with_num_channels!(define_channels_struct_future);
151
152/// Initialized DMA Controller
153pub struct DmaController<I = NoneT> {
154    dmac: Dmac,
155    _irqs: PhantomData<I>,
156}
157
158impl DmaController {
159    /// Initialize the DMAC and return a DmaController object useable by
160    /// [`Transfer`](super::transfer::Transfer)'s. By default, all
161    /// priority levels are enabled unless subsequently disabled using the
162    /// `level_x_enabled` methods.
163    #[inline]
164    #[hal_macro_helper]
165    pub fn init(mut dmac: Dmac, _pm: &mut Pm) -> Self {
166        // ----- Initialize clocking ----- //
167        #[hal_cfg(any("dmac-d11", "dmac-d21"))]
168        {
169            // Enable clocking
170            _pm.ahbmask().modify(|_, w| w.dmac_().set_bit());
171            _pm.apbbmask().modify(|_, w| w.dmac_().set_bit());
172        }
173
174        Self::swreset(&mut dmac);
175
176        // SAFETY:
177        //
178        // This is safe because we write a whole u32 to 32-bit registers,
179        // and the descriptor array addesses will never change since they are static.
180        // We just need to ensure the writeback and descriptor_section addresses
181        // are valid.
182        unsafe {
183            dmac.baseaddr()
184                .write(|w| w.baseaddr().bits(sram::descriptor_section_addr() as u32));
185            dmac.wrbaddr()
186                .write(|w| w.wrbaddr().bits(sram::writeback_addr() as u32));
187        }
188
189        // ----- Select priority levels ----- //
190        dmac.ctrl().modify(|_, w| {
191            w.lvlen3().set_bit();
192            w.lvlen2().set_bit();
193            w.lvlen1().set_bit();
194            w.lvlen0().set_bit()
195        });
196
197        // Enable DMA controller
198        dmac.ctrl().modify(|_, w| w.dmaenable().set_bit());
199
200        Self {
201            dmac,
202            _irqs: PhantomData,
203        }
204    }
205
206    /// Release the DMAC and return the register block.
207    ///
208    /// **Note**: The [`Channels`] struct is consumed by this method. This means
209    /// that any [`Channel`] obtained by [`split`](DmaController::split) must be
210    /// moved back into the [`Channels`] struct before being able to pass it
211    /// into [`free`](DmaController::free).
212    #[inline]
213    #[hal_macro_helper]
214    pub fn free(mut self, _channels: Channels, _pm: &mut Pm) -> Dmac {
215        self.dmac.ctrl().modify(|_, w| w.dmaenable().clear_bit());
216
217        Self::swreset(&mut self.dmac);
218
219        #[hal_cfg(any("dmac-d11", "dmac-d21"))]
220        {
221            // Disable the DMAC clocking
222            _pm.apbbmask().modify(|_, w| w.dmac_().clear_bit());
223            _pm.ahbmask().modify(|_, w| w.dmac_().clear_bit());
224        }
225
226        // Release the DMAC
227        self.dmac
228    }
229}
230
231impl<T> DmaController<T> {
232    /// Enable multiple priority levels simultaneously
233    #[inline]
234    pub fn enable_levels(&mut self, mask: PriorityLevelMask) {
235        // SAFETY This is safe because the use of bitfields ensures that only the
236        // LVLENx bits are written to. The fact that we are given a mask means we need
237        // to do the bit-level setting ourselves.
238        let mask: u16 = mask.into();
239        unsafe {
240            self.dmac.ctrl().modify(|r, w| w.bits(r.bits() | mask));
241        }
242    }
243
244    /// Disable multiple priority levels simultaneously
245    #[inline]
246    pub fn disable_levels(&mut self, mask: PriorityLevelMask) {
247        // SAFETY This is safe because the use of bitfields ensures that only the
248        // LVLENx bits are written to. The fact that we are given a mask means we need
249        // to do the bit-level clearing ourselves.
250        let mask: u16 = mask.into();
251        unsafe {
252            self.dmac.ctrl().modify(|r, w| w.bits(r.bits() & !mask));
253        }
254    }
255
256    /// Enable round-robin arbitration for multiple priority levels
257    /// simultaneously
258    #[inline]
259    pub fn round_robin_arbitration(&mut self, mask: RoundRobinMask) {
260        // SAFETY This is safe because the use of bitfields ensures that only the
261        // RRLVLENx bits are written to. The fact that we are given a mask means we need
262        // to do the bit-level setting ourselves.
263        let mask: u32 = mask.into();
264        unsafe {
265            self.dmac.prictrl0().modify(|r, w| w.bits(r.bits() | mask));
266        }
267    }
268
269    /// Disable round-robin arbitration (ie, enable static priorities) for
270    /// multiple priority levels simultaneously
271    #[inline]
272    pub fn static_arbitration(&mut self, mask: RoundRobinMask) {
273        // SAFETY This is safe because the use of bitfields ensures that only the
274        // RRLVLENx bits are written to. The fact that we are given a mask means we need
275        // to do the bit-level clearing ourselves.
276        let mask: u32 = mask.into();
277        unsafe {
278            self.dmac.prictrl0().modify(|r, w| w.bits(r.bits() & !mask));
279        }
280    }
281
282    /// Use the [`DmaController`] in async mode. You are required to provide the
283    /// struct created by the
284    /// [`bind_interrupts`](crate::bind_interrupts) macro to prove
285    /// that the interrupt sources have been correctly configured. This function
286    /// will automatically enable the relevant NVIC interrupt sources. However,
287    /// you are required to configure the desired interrupt priorities prior to
288    /// calling this method. Consult [`crate::async_hal::interrupts`]
289    /// module-level documentation for more information.
290    /// [`bind_interrupts`](crate::bind_interrupts).
291    #[cfg(feature = "async")]
292    #[inline]
293    pub fn into_future<I>(self, _interrupts: I) -> DmaController<I>
294    where
295        I: crate::async_hal::interrupts::Binding<
296            crate::async_hal::interrupts::DMAC,
297            super::async_api::InterruptHandler,
298        >,
299    {
300        use crate::async_hal::interrupts::{InterruptSource, DMAC};
301
302        DMAC::unpend();
303        unsafe { DMAC::enable() };
304
305        DmaController {
306            dmac: self.dmac,
307            _irqs: PhantomData,
308        }
309    }
310
311    /// Issue a software reset to the DMAC and wait for reset to complete
312    #[inline]
313    fn swreset(dmac: &mut Dmac) {
314        dmac.ctrl().modify(|_, w| w.swrst().set_bit());
315        while dmac.ctrl().read().swrst().bit_is_set() {}
316    }
317}
318
319#[cfg(feature = "async")]
320impl<I> DmaController<I>
321where
322    I: crate::async_hal::interrupts::Binding<
323        crate::async_hal::interrupts::DMAC,
324        super::async_api::InterruptHandler,
325    >,
326{
327    /// Release the DMAC and return the register block.
328    ///
329    /// **Note**: The [`Channels`] struct is consumed by this method. This means
330    /// that any [`Channel`] obtained by [`split`](DmaController::split) must be
331    /// moved back into the [`Channels`] struct before being able to pass it
332    /// into [`free`](DmaController::free).
333    #[inline]
334    #[hal_macro_helper]
335    pub fn free(mut self, _channels: FutureChannels, _pm: &mut Pm) -> Dmac {
336        self.dmac.ctrl().modify(|_, w| w.dmaenable().clear_bit());
337
338        Self::swreset(&mut self.dmac);
339
340        #[hal_cfg(any("dmac-d11", "dmac-d21"))]
341        {
342            // Disable the DMAC clocking
343            _pm.apbbmask().modify(|_, w| w.dmac_().clear_bit());
344            _pm.ahbmask().modify(|_, w| w.dmac_().clear_bit());
345        }
346
347        // Release the DMAC
348        self.dmac
349    }
350}
351
352macro_rules! define_split {
353    ($num_channels:literal) => {
354        seq!(N in 0..$num_channels {
355            /// Split the DMAC into individual channels
356            #[inline]
357            pub fn split(&mut self) -> Channels {
358                Channels(
359                    #(
360                        crate::dmac::channel::new_chan(core::marker::PhantomData),
361                    )*
362                )
363            }
364        });
365    };
366}
367
368impl DmaController {
369    with_num_channels!(define_split);
370}
371
372#[cfg(feature = "async")]
373macro_rules! define_split_future {
374    ($num_channels:literal) => {
375        seq!(N in 0..$num_channels {
376            /// Split the DMAC into individual channels
377            #[inline]
378            pub fn split(&mut self) -> FutureChannels {
379                FutureChannels(
380                    #(
381                        crate::dmac::channel::new_chan_future(core::marker::PhantomData),
382                    )*
383                )
384            }
385        });
386    };
387}
388
389#[cfg(feature = "async")]
390impl<I> DmaController<I>
391where
392    I: crate::async_hal::interrupts::Binding<
393        crate::async_hal::interrupts::DMAC,
394        super::async_api::InterruptHandler,
395    >,
396{
397    with_num_channels!(define_split_future);
398}