Skip to content

Commit 2581fe8

Browse files
committed
Use linked transfers for async+DMA sercom
1 parent c8ed513 commit 2581fe8

File tree

10 files changed

+1260
-918
lines changed

10 files changed

+1260
-918
lines changed

hal/src/dmac/channel/mod.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ impl<Id: ChId> Channel<Id, Ready> {
512512
Ok(())
513513
}
514514

515-
/// Begin a [`Transfer`], without changing the channel's type to [`Busy`].
515+
/// Begin a transfer, without changing the channel's type to [`Busy`].
516516
///
517517
/// Also provides support for linked transfers via an optional `&mut
518518
/// DmacDescriptor`.
@@ -636,14 +636,57 @@ impl<Id: ChId> Channel<Id, ReadyFuture> {
636636
trig_src: TriggerSource,
637637
trig_act: TriggerAction,
638638
) -> Result<(), super::Error>
639+
where
640+
S: super::Buffer,
641+
D: super::Buffer<Beat = S::Beat>,
642+
{
643+
unsafe {
644+
self.transfer_future_linked(&mut source, &mut dest, trig_src, trig_act, None)
645+
.await
646+
}
647+
}
648+
649+
/// Begin an async transfer, without changing the channel's type to
650+
/// [`Busy`].
651+
///
652+
/// Also provides support for linked transfers via an optional `&mut
653+
/// DmacDescriptor`.
654+
///
655+
/// # Safety
656+
///
657+
/// * This method does not check that the two provided buffers have
658+
/// compatible lengths. You must guarantee that:
659+
/// - Either `source` or `dest` has a buffer length of 1, or
660+
/// - Both buffers have the same length.
661+
/// * You must ensure that the transfer is completed or stopped before
662+
/// returning the [`Channel`]. Doing otherwise breaks type safety, because
663+
/// a [`ReadyFuture`] channel would still be in the middle of a transfer.
664+
/// * If the provided `linked_descriptor` is `Some` it must not be dropped
665+
/// until the transfer is completed or stopped.
666+
/// * Additionnally, this function doesn't take `'static` buffers. Again,
667+
/// you must guarantee that the returned transfer has completed or has
668+
/// been stopped before giving up control of the underlying [`Channel`].
669+
pub(crate) async unsafe fn transfer_future_linked<S, D>(
670+
&mut self,
671+
source: &mut S,
672+
dest: &mut D,
673+
trig_src: TriggerSource,
674+
trig_act: TriggerAction,
675+
linked_descriptor: Option<&mut DmacDescriptor>,
676+
) -> Result<(), super::Error>
639677
where
640678
S: super::Buffer,
641679
D: super::Buffer<Beat = S::Beat>,
642680
{
643681
super::Transfer::<Self, super::transfer::BufferPair<S, D>>::check_buffer_pair(
644-
&source, &dest,
682+
source, dest,
645683
)?;
646-
unsafe { self.fill_descriptor(&mut source, &mut dest, false) };
684+
unsafe {
685+
self.fill_descriptor(source, dest, false);
686+
if let Some(next) = linked_descriptor {
687+
self.link_next(next as *mut _);
688+
}
689+
}
647690

648691
self.disable_interrupts(
649692
InterruptFlags::new()

hal/src/sercom/dma.rs

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -651,20 +651,35 @@ pub(crate) mod async_dma {
651651
use super::*;
652652

653653
/// Perform a SERCOM DMA read with a provided `&mut [T]`
654-
pub(in super::super) async fn read_dma<T: Beat, S: Sercom>(
654+
#[inline]
655+
pub(in super::super) async fn read_dma<T, B, S>(
655656
channel: &mut impl AnyChannel<Status = ReadyFuture>,
656657
sercom_ptr: SercomPtr<T>,
657-
words: &mut [T],
658-
) -> Result<(), Error> {
659-
read_dma_buffer::<_, _, S>(channel, sercom_ptr, words).await
658+
buf: &mut B,
659+
) -> Result<(), Error>
660+
where
661+
B: Buffer<Beat = T>,
662+
T: Beat,
663+
S: Sercom,
664+
{
665+
unsafe { read_dma_linked::<_, _, S>(channel, sercom_ptr, buf, None).await }
660666
}
661667

662-
/// Perform a SERCOM DMA read with a provided [`Buffer`]
668+
/// Perform a SERCOM DMA read with a provided [`Buffer`], and add an
669+
/// optional link to a next [`DmacDescriptor`] to support linked
670+
/// transfers.
671+
///
672+
/// # Safety
673+
///
674+
/// You **must** guarantee that the DMA transfer is either stopped or
675+
/// completed before giving back control of `channel` AND `buf`.
676+
#[inline]
663677
#[hal_macro_helper]
664-
pub(in super::super) async fn read_dma_buffer<T, B, S>(
678+
pub(in super::super) async unsafe fn read_dma_linked<T, B, S>(
665679
channel: &mut impl AnyChannel<Status = ReadyFuture>,
666-
sercom_ptr: SercomPtr<T>,
667-
buf: B,
680+
mut sercom_ptr: SercomPtr<T>,
681+
buf: &mut B,
682+
next: Option<&mut DmacDescriptor>,
668683
) -> Result<(), Error>
669684
where
670685
T: Beat,
@@ -677,35 +692,54 @@ pub(crate) mod async_dma {
677692
#[hal_cfg(any("dmac-d11", "dmac-d21"))]
678693
let trigger_action = TriggerAction::Beat;
679694

695+
// Safety: It is safe to bypass the buffer length check because `SercomPtr`
696+
// always has a buffer length of 1.
680697
channel
681698
.as_mut()
682-
.transfer_future(sercom_ptr, buf, S::DMA_RX_TRIGGER, trigger_action)
699+
.transfer_future_linked(
700+
&mut sercom_ptr,
701+
buf,
702+
S::DMA_RX_TRIGGER,
703+
trigger_action,
704+
next,
705+
)
683706
.await
684707
}
685708

686709
/// Perform a SERCOM DMA write with a provided `&[T]`
687-
pub(in super::super) async fn write_dma<T: Beat, S: Sercom>(
710+
#[inline]
711+
pub(in super::super) async fn write_dma<T, B, S>(
688712
channel: &mut impl AnyChannel<Status = ReadyFuture>,
689713
sercom_ptr: SercomPtr<T>,
690-
words: &[T],
691-
) -> Result<(), Error> {
692-
// SAFETY: Using ImmutableSlice is safe because we hold on
693-
// to words as long as the transfer hasn't completed.
694-
let words = SharedSliceBuffer::from_slice(words);
695-
696-
write_dma_buffer::<_, _, S>(channel, sercom_ptr, words).await
714+
buf: &mut B,
715+
) -> Result<(), Error>
716+
where
717+
B: Buffer<Beat = T>,
718+
T: Beat,
719+
S: Sercom,
720+
{
721+
unsafe { write_dma_linked::<_, _, S>(channel, sercom_ptr, buf, None).await }
697722
}
698723

699-
/// Perform a SERCOM DMA write with a provided [`Buffer`]
724+
/// Perform a SERCOM DMA write with a provided [`Buffer`], and add an
725+
/// optional link to a next [`DmacDescriptor`] to support linked
726+
/// transfers.
727+
///
728+
/// # Safety
729+
///
730+
/// You **must** guarantee that the DMA transfer is either stopped or
731+
/// completed before giving back control of `channel` AND `buf`.
732+
#[inline]
700733
#[hal_macro_helper]
701-
pub(in super::super) async fn write_dma_buffer<T, B, S>(
734+
pub(in super::super) async unsafe fn write_dma_linked<T, B, S>(
702735
channel: &mut impl AnyChannel<Status = ReadyFuture>,
703-
sercom_ptr: SercomPtr<T>,
704-
buf: B,
736+
mut sercom_ptr: SercomPtr<T>,
737+
buf: &mut B,
738+
next: Option<&mut DmacDescriptor>,
705739
) -> Result<(), Error>
706740
where
707-
T: Beat,
708741
B: Buffer<Beat = T>,
742+
T: Beat,
709743
S: Sercom,
710744
{
711745
#[hal_cfg("dmac-d5x")]
@@ -714,9 +748,17 @@ pub(crate) mod async_dma {
714748
#[hal_cfg(any("dmac-d11", "dmac-d21"))]
715749
let trigger_action = TriggerAction::Beat;
716750

751+
// Safety: It is safe to bypass the buffer length check because `SercomPtr`
752+
// always has a buffer length of 1.
717753
channel
718754
.as_mut()
719-
.transfer_future(buf, sercom_ptr, S::DMA_TX_TRIGGER, trigger_action)
755+
.transfer_future_linked(
756+
buf,
757+
&mut sercom_ptr,
758+
S::DMA_TX_TRIGGER,
759+
trigger_action,
760+
next,
761+
)
720762
.await
721763
}
722764
}

0 commit comments

Comments
 (0)