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
333 /// buffers have a length > 1 and are not of equal length.
334 #[allow(clippy::new_ret_no_self)]
335 #[inline]
336 pub fn new(
337 chan: C,
338 source: S,
339 destination: D,
340 circular: bool,
341 ) -> Result<Transfer<C, BufferPair<S, D>>> {
342 Self::check_buffer_pair(&source, &destination)?;
343
344 // SAFETY: The safety checks are done by the function signature and the buffer
345 // length verification
346 Ok(unsafe { Self::new_unchecked(chan, source, destination, circular) })
347 }
348}
349
350impl<S, D, C> Transfer<C, BufferPair<S, D>>
351where
352 S: Buffer,
353 D: Buffer<Beat = S::Beat>,
354 C: AnyChannel,
355{
356 #[inline]
357 pub(super) fn check_buffer_pair(source: &S, destination: &D) -> Result<()> {
358 let src_len = source.buffer_len();
359 let dst_len = destination.buffer_len();
360
361 if src_len > 1 && dst_len > 1 && src_len != dst_len {
362 Err(Error::LengthMismatch)
363 } else {
364 Ok(())
365 }
366 }
367}
368
369impl<C, S, D, R> Transfer<C, BufferPair<S, D>>
370where
371 S: Buffer,
372 D: Buffer<Beat = S::Beat>,
373 C: AnyChannel<Status = R>,
374 R: ReadyChannel,
375{
376 /// Construct a new `Transfer` without checking for memory safety.
377 ///
378 /// # Safety
379 ///
380 /// To guarantee the safety of creating a `Transfer` using this method, you
381 /// must uphold some invariants:
382 ///
383 /// * A `Transfer` holding a `Channel<Id, Running>` must *never* be dropped.
384 /// It should *always* be explicitly be `wait`ed upon or `stop`ped.
385 ///
386 /// * The size in bytes or the source and destination buffers should be
387 /// exacly the same, unless one or both buffers are of length 1. The
388 /// transfer length will be set to the longest of both buffers if they are
389 /// not of equal size.
390 #[inline]
391 pub unsafe fn new_unchecked(
392 mut chan: C,
393 mut source: S,
394 mut destination: D,
395 circular: bool,
396 ) -> Transfer<C, BufferPair<S, D>> {
397 unsafe {
398 chan.as_mut()
399 .fill_descriptor(&mut source, &mut destination, circular);
400 }
401
402 let buffers = BufferPair {
403 source,
404 destination,
405 };
406
407 Transfer {
408 buffers,
409 chan,
410 complete: false,
411 }
412 }
413}
414
415impl<C, S, D> Transfer<C, BufferPair<S, D>>
416where
417 S: Buffer,
418 D: Buffer<Beat = S::Beat>,
419 C: AnyChannel<Status = Ready>,
420{
421 /// Begin DMA transfer in blocking mode. If [`TriggerSource::Disable`] is
422 /// used, a software trigger will be issued to the DMA channel to launch
423 /// the transfer. Is is therefore not necessary, in most cases, to manually
424 /// issue a software trigger to the channel.
425 #[inline]
426 pub fn begin(
427 mut self,
428 trig_src: TriggerSource,
429 trig_act: TriggerAction,
430 ) -> Transfer<Channel<ChannelId<C>, Busy>, BufferPair<S, D>> {
431 // Reset the complete flag before triggering the transfer.
432 // This way an interrupt handler could set complete to true
433 // before this function returns.
434 self.complete = false;
435
436 let chan = self.chan.into().start(trig_src, trig_act);
437
438 Transfer {
439 buffers: self.buffers,
440 chan,
441 complete: self.complete,
442 }
443 }
444
445 /// Free the [`Transfer`] and return the resources it holds.
446 ///
447 /// Similar to [`stop`](Transfer::stop), but it acts on a [`Transfer`]
448 /// holding a [`Ready`] channel, so there is no need to explicitly stop the
449 /// transfer.
450 pub fn free(self) -> (Channel<ChannelId<C>, Ready>, S, D) {
451 (
452 self.chan.into(),
453 self.buffers.source,
454 self.buffers.destination,
455 )
456 }
457}
458
459impl<B, C, R, const N: usize> Transfer<C, BufferPair<&'static mut [B; N]>>
460where
461 B: 'static + Beat,
462 C: AnyChannel<Status = R>,
463 R: ReadyChannel,
464{
465 /// Create a new `Transfer` from static array references of the same type
466 /// and length. When two array references are available (instead of slice
467 /// references), it is recommended to use this function over
468 /// [`Transfer::new`](Transfer::new), because it provides compile-time
469 /// checking that the array lengths match. It therefore does not panic, and
470 /// saves some runtime checking of the array lengths.
471 #[inline]
472 pub fn new_from_arrays(
473 chan: C,
474 source: &'static mut [B; N],
475 destination: &'static mut [B; N],
476 circular: bool,
477 ) -> Self {
478 unsafe { Self::new_unchecked(chan, source, destination, circular) }
479 }
480}
481
482impl<S, D, C> Transfer<C, BufferPair<S, D>>
483where
484 S: Buffer,
485 D: Buffer<Beat = S::Beat>,
486 C: AnyChannel<Status = Busy>,
487{
488 /// Issue a software trigger request to the corresponding channel.
489 /// Note that is not guaranteed that the trigger request will register,
490 /// if a trigger request is already pending for the channel.
491 #[inline]
492 pub fn software_trigger(&mut self) {
493 self.chan.as_mut().software_trigger();
494 }
495
496 /// Unsafely and mutably borrow the source buffer
497 ///
498 /// # Safety
499 ///
500 /// The source buffer should never be borrowed when a transfer is in
501 /// progress, as it is getting mutated or read in another context (ie,
502 /// the DMAC hardware "thread").
503 #[expect(dead_code)]
504 #[inline]
505 pub(crate) unsafe fn borrow_source(&mut self) -> &mut S {
506 &mut self.buffers.source
507 }
508
509 /// Unsafely and mutably borrow the destination buffer.
510 ///
511 /// # Safety
512 ///
513 /// The destination buffer should never be borrowed when a transfer is in
514 /// progress, as it is getting mutated or read in another context (ie,
515 /// the DMAC hardware "thread").
516 #[expect(dead_code)]
517 #[inline]
518 pub(crate) unsafe fn borrow_destination(&mut self) -> &mut D {
519 &mut self.buffers.destination
520 }
521
522 /// Wait for the DMA transfer to complete and release all owned
523 /// resources
524 ///
525 /// # Blocking: This method may block
526 #[inline]
527 pub fn wait(mut self) -> (Channel<ChannelId<C>, Ready>, S, D) {
528 // Wait for transfer to complete
529 while !self.complete() {}
530 self.stop()
531 }
532
533 /// Check if the transfer has completed
534 #[inline]
535 pub fn complete(&mut self) -> bool {
536 if !self.complete {
537 let chan = self.chan.as_mut();
538 let complete = chan.xfer_complete();
539 self.complete = complete;
540 }
541 self.complete
542 }
543
544 /// Checks and clears the block transfer complete interrupt flag
545 #[inline]
546 pub fn block_transfer_interrupt(&mut self) -> bool {
547 self.chan
548 .as_mut()
549 .check_and_clear_interrupts(InterruptFlags::new().with_tcmpl(true))
550 .tcmpl()
551 }
552
553 /// Modify a completed transfer with new `source` and `destination`, then
554 /// restart.
555 ///
556 /// Returns a Result containing the source and destination from the
557 /// completed transfer. Returns `Err(_)` if the buffer lengths are
558 /// mismatched or if the previous transfer has not yet completed.
559 #[inline]
560 pub fn recycle(&mut self, mut source: S, mut destination: D) -> Result<(S, D)> {
561 Self::check_buffer_pair(&source, &destination)?;
562
563 if !self.complete() {
564 return Err(Error::InvalidState);
565 }
566
567 // Circular transfers won't ever complete, so never re-fill as one
568 unsafe {
569 self.chan
570 .as_mut()
571 .fill_descriptor(&mut source, &mut destination, false);
572 }
573
574 let new_buffers = BufferPair {
575 source,
576 destination,
577 };
578
579 let old_buffers = core::mem::replace(&mut self.buffers, new_buffers);
580 self.chan.as_mut().restart();
581 Ok((old_buffers.source, old_buffers.destination))
582 }
583
584 /// Modify a completed transfer with a new `destination`, then restart.
585 ///
586 /// Returns a Result containing the destination from the
587 /// completed transfer. Returns `Err(_)` if the buffer lengths are
588 /// mismatched or if the previous transfer has not yet completed.
589 #[inline]
590 pub fn recycle_source(&mut self, mut destination: D) -> Result<D> {
591 Self::check_buffer_pair(&self.buffers.source, &destination)?;
592
593 if !self.complete() {
594 return Err(Error::InvalidState);
595 }
596
597 // Circular transfers won't ever complete, so never re-fill as one
598 unsafe {
599 self.chan
600 .as_mut()
601 .fill_descriptor(&mut self.buffers.source, &mut destination, false);
602 }
603
604 let old_destination = core::mem::replace(&mut self.buffers.destination, destination);
605 self.chan.as_mut().restart();
606 Ok(old_destination)
607 }
608
609 /// Modify a completed transfer with a new `source`, then restart.
610 ///
611 /// Returns a Result containing the source from the
612 /// completed transfer. Returns `Err(_)` if the buffer lengths are
613 /// mismatched or if the previous transfer has not yet completed.
614 #[inline]
615 pub fn recycle_destination(&mut self, mut source: S) -> Result<S> {
616 Self::check_buffer_pair(&source, &self.buffers.destination)?;
617
618 if !self.complete() {
619 return Err(Error::InvalidState);
620 }
621
622 // Circular transfers won't ever complete, so never re-fill as one
623 unsafe {
624 self.chan
625 .as_mut()
626 .fill_descriptor(&mut source, &mut self.buffers.destination, false);
627 }
628
629 let old_source = core::mem::replace(&mut self.buffers.source, source);
630 self.chan.as_mut().restart();
631 Ok(old_source)
632 }
633
634 /// Non-blocking; Immediately stop the DMA transfer and release all owned
635 /// resources
636 #[inline]
637 pub fn stop(self) -> (Channel<ChannelId<C>, Ready>, S, D) {
638 // `free()` stops the transfer, waits for the burst to finish, and emits a
639 // compiler fence.
640 let chan = self.chan.into().free();
641 (chan, self.buffers.source, self.buffers.destination)
642 }
643}