Skip to main content

atsamd_hal/dmac/
transfer.rs

1//! # DMA transfer abstractions
2//!
3//! # Transfer types
4//!
5//! Four basic transfer types are supported:
6//!
7//! * Incrementing-source to incrementing-destination (normally used for
8//!   memory-to-memory transfers)
9//!
10//! * Incrementing-source to fixed-destination (normally used for
11//!   memory-to-peripheral transfers)
12//!
13//! * Fixed-source to incrementing-destination (normally used for
14//!   peripheral-to-memory transfers)
15//!
16//! * Fixed-source to fixed-destination (normally used for
17//!   peripheral-to-peripheral transfers)
18//!
19//! # Beat sizes
20//!
21//! A beat is an atomic, uninterruptible transfer size.Three beat sizes are
22//! supported:
23//!
24//! * Byte-per-byte (8 bit beats);
25//!
26//! * Halfword-per-halfword (16 bit beats);
27//!
28//! * Word-per-word (32 bit beats);
29//!
30//! The correct beat size will automatically be selected in function of the type
31//! of the source and destination buffers.
32//!
33//! # One-shot vs circular transfers
34//!
35//! If the transfer is setup as one-shot (`circular == false`), the
36//! transfer will run once and stop. Otherwise, if `circular == true`, then the
37//! transfer will be set as circular, meaning it will restart a new, identical
38//! block transfer when the current block transfer is complete. This is
39//! particularly useful when combined with a TC/TCC trigger source, for instance
40//! to periodically retreive a sample from an ADC and send it to a circular
41//! buffer, or send a sample to a DAC.
42//!
43//! # Starting a transfer
44//!
45//! A transfer is started by calling [`Transfer::begin`]. You will be
46//! required to supply a trigger source and a trigger action.
47//!
48//! # Waiting for a transfer to complete
49//!
50//! A transfer can waited upon by calling [`wait`](Transfer::wait). This is a
51//! _blocking_ method, meaning it will busy-wait until the transfer is
52//! completed. When it returns, it will release the source and destination
53//! buffers, as well as the DMA channel.
54//!
55//! # Interrupting (stopping) a transfer
56//!
57//! A transfer can be stopped (regardless of whether it has completed or not) by
58//! calling [`stop`](Transfer::stop). This is _not_ a blocking method,
59//! meaning it will stop the transfer and immediately return. When it returns,
60//! it will release the source and destination buffers, as well as the DMA
61//! channel.
62//!
63//! # Trigger sources
64//!
65//! Most peripherals can issue triggers to a DMA channel. A software trigger is
66//! also available (see [`trigger`](Transfer::software_trigger)). See
67//! ATSAMD21 datasheet, table 19-8 for all available trigger sources.
68//!
69//! # Trigger actions
70//!
71//! Three trigger actions are available:
72//!
73//! * BLOCK: One trigger required for each block transfer. In the context of
74//!   this driver, one Transfer is equivalent to one Block transfer.
75//!
76//! * BEAT: One trigger required for each beat transfer. In the context of this
77//!   driver, the beat size will depend on the type of buffer used (8, 16 or 32
78//!   bits).
79//!
80//! * TRANSACTION: One trigger required for a full DMA transaction. this is
81//!   useful for circular transfers in the context of this driver. One trigger
82//!   will set off the transaction, that will now run uninterrupted until it is
83//!   stopped.
84
85use super::{
86    Error, ReadyChannel, Result,
87    channel::{AnyChannel, Busy, Channel, ChannelId, InterruptFlags, Ready},
88    dma_controller::{TriggerAction, TriggerSource},
89};
90use crate::typelevel::{Is, Sealed};
91use modular_bitfield::prelude::*;
92
93//==============================================================================
94// Beat
95//==============================================================================
96
97/// Useable beat sizes for DMA transfers
98#[derive(Clone, Copy, BitfieldSpecifier)]
99#[bits = 2]
100pub enum BeatSize {
101    /// Byte = [`u8`](core::u8)
102    Byte = 0x00,
103    /// Half word = [`u16`](core::u16)
104    HalfWord = 0x01,
105    /// Word = [`u32`](core::u32)
106    Word = 0x02,
107}
108
109/// Convert 8, 16 and 32 bit types
110/// into [`BeatSize`]
111///
112/// # Safety
113///
114/// This trait should not be implemented outside of the crate-provided
115/// implementations
116pub unsafe trait Beat: Sealed {
117    /// Convert to BeatSize enum
118    const BEATSIZE: BeatSize;
119}
120
121macro_rules! impl_beat {
122    ( $( ($Type:ty, $Size:ident) ),+ ) => {
123        $(
124            unsafe impl Beat for $Type {
125                const BEATSIZE: BeatSize = BeatSize::$Size;
126            }
127        )+
128    };
129}
130
131impl_beat!(
132    (u8, Byte),
133    (i8, Byte),
134    (u16, HalfWord),
135    (i16, HalfWord),
136    (u32, Word),
137    (i32, Word),
138    (f32, Word)
139);
140
141//==============================================================================
142// Buffer
143//==============================================================================
144
145/// Buffer useable by the DMAC.
146///
147/// # Safety
148///
149/// This trait should only be implemented for valid DMAC sources/sinks. That is,
150/// you need to make sure that:
151/// * `dma_ptr` points to a valid memory location useable by the DMAC
152/// * `incrementing` is correct for the source/sink. For example, an `&[u8]` of
153///   size one is not incrementing.
154/// * `buffer_len` is correct for the source/sink.
155pub unsafe trait Buffer {
156    /// DMAC beat size
157    type Beat: Beat;
158    /// Pointer to the buffer. If the buffer is incrementing, the address should
159    /// point to one past the last beat transfer in the block.
160    fn dma_ptr(&mut self) -> *mut Self::Beat;
161    /// Return whether the buffer pointer should be incrementing or not
162    fn incrementing(&self) -> bool;
163    /// Buffer length in beats
164    fn buffer_len(&self) -> usize;
165}
166
167unsafe impl<T: Beat, const N: usize> Buffer for &mut [T; N] {
168    type Beat = T;
169    #[inline]
170    fn dma_ptr(&mut self) -> *mut Self::Beat {
171        let ptrs = self.as_mut_ptr_range();
172        if self.incrementing() {
173            ptrs.end
174        } else {
175            ptrs.start
176        }
177    }
178
179    #[inline]
180    fn incrementing(&self) -> bool {
181        N > 1
182    }
183
184    #[inline]
185    fn buffer_len(&self) -> usize {
186        N
187    }
188}
189
190unsafe impl<T: Beat> Buffer for &mut [T] {
191    type Beat = T;
192    #[inline]
193    fn dma_ptr(&mut self) -> *mut Self::Beat {
194        let ptrs = self.as_mut_ptr_range();
195        if self.incrementing() {
196            ptrs.end
197        } else {
198            ptrs.start
199        }
200    }
201
202    #[inline]
203    fn incrementing(&self) -> bool {
204        self.len() > 1
205    }
206
207    #[inline]
208    fn buffer_len(&self) -> usize {
209        self.len()
210    }
211}
212
213unsafe impl<T: Beat> Buffer for &mut T {
214    type Beat = T;
215    #[inline]
216    fn dma_ptr(&mut self) -> *mut Self::Beat {
217        *self as *mut T
218    }
219
220    #[inline]
221    fn incrementing(&self) -> bool {
222        false
223    }
224
225    #[inline]
226    fn buffer_len(&self) -> usize {
227        1
228    }
229}
230
231//==============================================================================
232// BufferPair
233//==============================================================================
234
235/// Struct holding the source and destination buffers of a
236/// [`Transfer`].
237pub struct BufferPair<S, D = S>
238where
239    S: Buffer,
240    D: Buffer<Beat = S::Beat>,
241{
242    /// Source buffer
243    pub source: S,
244    /// Destination buffer
245    pub destination: D,
246}
247
248//==============================================================================
249// AnyBufferPair
250//==============================================================================
251
252pub trait AnyBufferPair: Sealed + Is<Type = SpecificBufferPair<Self>> {
253    type Src: Buffer;
254    type Dst: Buffer<Beat = BufferPairBeat<Self>>;
255}
256
257pub type SpecificBufferPair<C> = BufferPair<<C as AnyBufferPair>::Src, <C as AnyBufferPair>::Dst>;
258
259pub type BufferPairSrc<B> = <B as AnyBufferPair>::Src;
260pub type BufferPairDst<B> = <B as AnyBufferPair>::Dst;
261pub type BufferPairBeat<B> = <BufferPairSrc<B> as Buffer>::Beat;
262
263impl<S, D> Sealed for BufferPair<S, D>
264where
265    S: Buffer,
266    D: Buffer<Beat = S::Beat>,
267{
268}
269
270impl<S, D> AnyBufferPair for BufferPair<S, D>
271where
272    S: Buffer,
273    D: Buffer<Beat = S::Beat>,
274{
275    type Src = S;
276    type Dst = D;
277}
278
279impl<S, D> AsRef<Self> for BufferPair<S, D>
280where
281    S: Buffer,
282    D: Buffer<Beat = S::Beat>,
283{
284    #[inline]
285    fn as_ref(&self) -> &Self {
286        self
287    }
288}
289
290impl<S, D> AsMut<Self> for BufferPair<S, D>
291where
292    S: Buffer,
293    D: Buffer<Beat = S::Beat>,
294{
295    #[inline]
296    fn as_mut(&mut self) -> &mut Self {
297        self
298    }
299}
300
301// TODO change source and dest types to Pin? (see https://docs.rust-embedded.org/embedonomicon/dma.html#immovable-buffers)
302/// DMA transfer, owning the resources until the transfer is done and
303/// [`Transfer::wait`] is called.
304pub struct Transfer<Chan, Buf>
305where
306    Buf: AnyBufferPair,
307    Chan: AnyChannel,
308{
309    chan: Chan,
310    buffers: Buf,
311    complete: bool,
312}
313
314impl<C, S, D, R> Transfer<C, BufferPair<S, D>>
315where
316    S: Buffer + 'static,
317    D: Buffer<Beat = S::Beat> + 'static,
318    C: AnyChannel<Status = R>,
319    R: ReadyChannel,
320{
321    /// Safely construct a new `Transfer`. To guarantee memory safety, both
322    /// buffers are required to be `'static`.
323    /// Refer [here](https://docs.rust-embedded.org/embedonomicon/dma.html#memforget) or
324    /// [here](https://blog.japaric.io/safe-dma/) for more information.
325    ///
326    /// If two array references can be used as source and destination buffers
327    /// (as opposed to slices), then it is recommended to use the
328    /// [`Transfer::new_from_arrays`] method instead.
329    ///
330    /// # Errors
331    ///
332    /// * Returns [`Error::LengthMismatch`] if both buffers have a length > 1
333    ///   and are not of equal length.
334    /// * Returns [`Error::TooManyBeats`] if the number of beats are greater
335    ///   than `u16::MAX``.
336    #[allow(clippy::new_ret_no_self)]
337    #[inline]
338    pub fn new(
339        chan: C,
340        source: S,
341        destination: D,
342        circular: bool,
343    ) -> Result<Transfer<C, BufferPair<S, D>>> {
344        Self::check_buffer_pair(&source, &destination)?;
345
346        // SAFETY: The safety checks are done by the function signature and the buffer
347        // length verification
348        Ok(unsafe { Self::new_unchecked(chan, source, destination, circular) })
349    }
350}
351
352impl<S, D, C> Transfer<C, BufferPair<S, D>>
353where
354    S: Buffer,
355    D: Buffer<Beat = S::Beat>,
356    C: AnyChannel,
357{
358    #[inline]
359    pub(super) fn check_buffer_pair(source: &S, destination: &D) -> Result<()> {
360        let src_len = source.buffer_len();
361        let dst_len = destination.buffer_len();
362
363        if src_len > 1 && dst_len > 1 && src_len != dst_len {
364            Err(Error::LengthMismatch)
365        } else if src_len.max(dst_len) > u16::MAX.into() {
366            Err(Error::TooManyBeats)
367        } else {
368            Ok(())
369        }
370    }
371}
372
373impl<C, S, D, R> Transfer<C, BufferPair<S, D>>
374where
375    S: Buffer,
376    D: Buffer<Beat = S::Beat>,
377    C: AnyChannel<Status = R>,
378    R: ReadyChannel,
379{
380    /// Construct a new `Transfer` without checking for memory safety.
381    ///
382    /// # Safety
383    ///
384    /// To guarantee the safety of creating a `Transfer` using this method, you
385    /// must uphold some invariants:
386    ///
387    /// * A `Transfer` holding a `Channel<Id, Running>` must *never* be dropped.
388    ///   It should *always* be explicitly be `wait`ed upon or `stop`ped.
389    ///
390    /// * The size in bytes or the source and destination buffers should be
391    ///   exacly the same, unless one or both buffers are of length 1. The
392    ///   transfer length will be set to the longest of both buffers if they are
393    ///   not of equal size.
394    ///
395    /// * The source and destination buffers should have a length smaller or
396    ///   equal to `u16::MAX`.
397    #[inline]
398    pub unsafe fn new_unchecked(
399        mut chan: C,
400        mut source: S,
401        mut destination: D,
402        circular: bool,
403    ) -> Transfer<C, BufferPair<S, D>> {
404        unsafe {
405            chan.as_mut()
406                .fill_descriptor(&mut source, &mut destination, circular);
407        }
408
409        let buffers = BufferPair {
410            source,
411            destination,
412        };
413
414        Transfer {
415            buffers,
416            chan,
417            complete: false,
418        }
419    }
420}
421
422impl<C, S, D> Transfer<C, BufferPair<S, D>>
423where
424    S: Buffer,
425    D: Buffer<Beat = S::Beat>,
426    C: AnyChannel<Status = Ready>,
427{
428    /// Begin DMA transfer in blocking mode. If [`TriggerSource::Disable`] is
429    /// used, a software trigger will be issued to the DMA channel to launch
430    /// the transfer. Is is therefore not necessary, in most cases, to manually
431    /// issue a software trigger to the channel.
432    #[inline]
433    pub fn begin(
434        mut self,
435        trig_src: TriggerSource,
436        trig_act: TriggerAction,
437    ) -> Transfer<Channel<ChannelId<C>, Busy>, BufferPair<S, D>> {
438        // Reset the complete flag before triggering the transfer.
439        // This way an interrupt handler could set complete to true
440        // before this function returns.
441        self.complete = false;
442
443        let chan = self.chan.into().start(trig_src, trig_act);
444
445        Transfer {
446            buffers: self.buffers,
447            chan,
448            complete: self.complete,
449        }
450    }
451
452    /// Free the [`Transfer`] and return the resources it holds.
453    ///
454    /// Similar to [`stop`](Transfer::stop), but it acts on a [`Transfer`]
455    /// holding a [`Ready`] channel, so there is no need to explicitly stop the
456    /// transfer.
457    pub fn free(self) -> (Channel<ChannelId<C>, Ready>, S, D) {
458        (
459            self.chan.into(),
460            self.buffers.source,
461            self.buffers.destination,
462        )
463    }
464}
465
466impl<B, C, R, const N: usize> Transfer<C, BufferPair<&'static mut [B; N]>>
467where
468    B: 'static + Beat,
469    C: AnyChannel<Status = R>,
470    R: ReadyChannel,
471{
472    /// Create a new `Transfer` from static array references of the same type
473    /// and length. When two array references are available (instead of slice
474    /// references), it is recommended to use this function over
475    /// [`Transfer::new`](Transfer::new), because it provides compile-time
476    /// checking that the array lengths match. It therefore does not panic, and
477    /// saves some runtime checking of the array lengths.
478    #[inline]
479    pub fn new_from_arrays(
480        chan: C,
481        source: &'static mut [B; N],
482        destination: &'static mut [B; N],
483        circular: bool,
484    ) -> Self {
485        unsafe { Self::new_unchecked(chan, source, destination, circular) }
486    }
487}
488
489impl<S, D, C> Transfer<C, BufferPair<S, D>>
490where
491    S: Buffer,
492    D: Buffer<Beat = S::Beat>,
493    C: AnyChannel<Status = Busy>,
494{
495    /// Issue a software trigger request to the corresponding channel.
496    /// Note that is not guaranteed that the trigger request will register,
497    /// if a trigger request is already pending for the channel.
498    #[inline]
499    pub fn software_trigger(&mut self) {
500        self.chan.as_mut().software_trigger();
501    }
502
503    /// Unsafely and mutably borrow the source buffer
504    ///
505    /// # Safety
506    ///
507    /// The source buffer should never be borrowed when a transfer is in
508    /// progress, as it is getting mutated or read in another context (ie,
509    /// the DMAC hardware "thread").
510    #[expect(dead_code)]
511    #[inline]
512    pub(crate) unsafe fn borrow_source(&mut self) -> &mut S {
513        &mut self.buffers.source
514    }
515
516    /// Unsafely and mutably borrow the destination buffer.
517    ///
518    /// # Safety
519    ///
520    /// The destination buffer should never be borrowed when a transfer is in
521    /// progress, as it is getting mutated or read in another context (ie,
522    /// the DMAC hardware "thread").
523    #[expect(dead_code)]
524    #[inline]
525    pub(crate) unsafe fn borrow_destination(&mut self) -> &mut D {
526        &mut self.buffers.destination
527    }
528
529    /// Wait for the DMA transfer to complete and release all owned
530    /// resources
531    ///
532    /// # Blocking: This method may block
533    #[inline]
534    pub fn wait(mut self) -> (Channel<ChannelId<C>, Ready>, S, D) {
535        // Wait for transfer to complete
536        while !self.complete() {}
537        self.stop()
538    }
539
540    /// Check if the transfer has completed
541    #[inline]
542    pub fn complete(&mut self) -> bool {
543        if !self.complete {
544            let chan = self.chan.as_mut();
545            let complete = chan.xfer_complete();
546            self.complete = complete;
547        }
548        self.complete
549    }
550
551    /// Checks and clears the block transfer complete interrupt flag
552    #[inline]
553    pub fn block_transfer_interrupt(&mut self) -> bool {
554        self.chan
555            .as_mut()
556            .check_and_clear_interrupts(InterruptFlags::new().with_tcmpl(true))
557            .tcmpl()
558    }
559
560    /// Modify a completed transfer with new `source` and `destination`, then
561    /// restart.
562    ///
563    /// Returns a Result containing the source and destination from the
564    /// completed transfer. Returns `Err(_)` if the buffer lengths are
565    /// mismatched or if the previous transfer has not yet completed.
566    #[inline]
567    pub fn recycle(&mut self, mut source: S, mut destination: D) -> Result<(S, D)> {
568        Self::check_buffer_pair(&source, &destination)?;
569
570        if !self.complete() {
571            return Err(Error::InvalidState);
572        }
573
574        // Circular transfers won't ever complete, so never re-fill as one
575        unsafe {
576            self.chan
577                .as_mut()
578                .fill_descriptor(&mut source, &mut destination, false);
579        }
580
581        let new_buffers = BufferPair {
582            source,
583            destination,
584        };
585
586        let old_buffers = core::mem::replace(&mut self.buffers, new_buffers);
587        self.chan.as_mut().restart();
588        Ok((old_buffers.source, old_buffers.destination))
589    }
590
591    /// Modify a completed transfer with a new `destination`, then restart.
592    ///
593    /// Returns a Result containing the destination from the
594    /// completed transfer. Returns `Err(_)` if the buffer lengths are
595    /// mismatched or if the previous transfer has not yet completed.
596    #[inline]
597    pub fn recycle_source(&mut self, mut destination: D) -> Result<D> {
598        Self::check_buffer_pair(&self.buffers.source, &destination)?;
599
600        if !self.complete() {
601            return Err(Error::InvalidState);
602        }
603
604        // Circular transfers won't ever complete, so never re-fill as one
605        unsafe {
606            self.chan
607                .as_mut()
608                .fill_descriptor(&mut self.buffers.source, &mut destination, false);
609        }
610
611        let old_destination = core::mem::replace(&mut self.buffers.destination, destination);
612        self.chan.as_mut().restart();
613        Ok(old_destination)
614    }
615
616    /// Modify a completed transfer with a new `source`, then restart.
617    ///
618    /// Returns a Result containing the source from the
619    /// completed transfer. Returns `Err(_)` if the buffer lengths are
620    /// mismatched or if the previous transfer has not yet completed.
621    #[inline]
622    pub fn recycle_destination(&mut self, mut source: S) -> Result<S> {
623        Self::check_buffer_pair(&source, &self.buffers.destination)?;
624
625        if !self.complete() {
626            return Err(Error::InvalidState);
627        }
628
629        // Circular transfers won't ever complete, so never re-fill as one
630        unsafe {
631            self.chan
632                .as_mut()
633                .fill_descriptor(&mut source, &mut self.buffers.destination, false);
634        }
635
636        let old_source = core::mem::replace(&mut self.buffers.source, source);
637        self.chan.as_mut().restart();
638        Ok(old_source)
639    }
640
641    /// Non-blocking; Immediately stop the DMA transfer and release all owned
642    /// resources
643    #[inline]
644    pub fn stop(self) -> (Channel<ChannelId<C>, Ready>, S, D) {
645        // `free()` stops the transfer, waits for the burst to finish, and emits a
646        // compiler fence.
647        let chan = self.chan.into().free();
648        (chan, self.buffers.source, self.buffers.destination)
649    }
650}