Skip to content

Commit ece2aff

Browse files
authored
fix(dmac): DMAC handler now waits for channel to report as disabled (#938)
1 parent a02e538 commit ece2aff

File tree

2 files changed

+46
-32
lines changed

2 files changed

+46
-32
lines changed

hal/src/dmac/async_api.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! APIs for async DMAC operations.
22
33
use atsamd_hal_macros::hal_cfg;
4+
use core::sync::atomic;
45

56
use crate::{
67
async_hal::interrupts::{DMAC, Handler},
@@ -49,6 +50,16 @@ impl Handler<DMAC> for InterruptHandler {
4950
dmac.chctrla().modify(|_, w| w.enable().clear_bit());
5051
dmac.chctrlb()
5152
.modify(|_, w| w.trigsrc().variant(TriggerSource::Disable));
53+
54+
while dmac.chctrla().read().enable().bit_is_set() {
55+
core::hint::spin_loop();
56+
}
57+
58+
// Prevent the compiler from re-ordering read/write
59+
// operations beyond this fence.
60+
// (see https://docs.rust-embedded.org/embedonomicon/dma.html#compiler-misoptimizations)
61+
atomic::fence(atomic::Ordering::Acquire); // ▼
62+
5263
WAKERS[pend_channel as usize].wake();
5364
}
5465
}
@@ -96,6 +107,16 @@ impl Handler<DMAC> for InterruptHandler {
96107
w.enable().clear_bit();
97108
w.trigsrc().variant(TriggerSource::Disable)
98109
});
110+
111+
while dmac.channel(channel).chctrla().read().enable().bit_is_set() {
112+
core::hint::spin_loop();
113+
}
114+
115+
// Prevent the compiler from re-ordering read/write
116+
// operations beyond this fence.
117+
// (see https://docs.rust-embedded.org/embedonomicon/dma.html#compiler-misoptimizations)
118+
atomic::fence(atomic::Ordering::Acquire); // ▼
119+
99120
WAKERS[channel].wake();
100121
}
101122
}

hal/src/dmac/channel/mod.rs

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -719,17 +719,14 @@ impl<Id: ChId> Channel<Id, ReadyFuture> {
719719

720720
self.configure_trigger(trig_src, trig_act);
721721

722-
transfer_future::TransferFuture::new(self, trig_src).await;
722+
transfer_future::TransferFuture::new(self, trig_src).await
723723

724-
// No need to defensively disable channel here; it's automatically stopped when
725-
// TransferFuture is dropped. Even though `stop()` is implicitly called
726-
// through TransferFuture::drop, it *absolutely* must be called before
727-
// this function is returned, because it emits the compiler fence which ensures
728-
// memory operations aren't reordered beyond the DMA transfer's bounds.
729-
730-
// TODO currently this will always return Ok(()) since we unconditionally clear
731-
// ERROR
732-
self.xfer_success()
724+
// No need to defensively disable channel here; it's automatically
725+
// stopped when TransferFuture is dropped. Even though `stop()`
726+
// is implicitly called through TransferFuture::drop, it
727+
// *absolutely* must be called before this function is returned,
728+
// because it emits the compiler fence which ensures memory operations
729+
// aren't reordered beyond the DMA transfer's bounds.
733730
}
734731
}
735732

@@ -769,7 +766,7 @@ mod transfer_future {
769766
}
770767

771768
impl<Id: ChId> core::future::Future for TransferFuture<'_, Id> {
772-
type Output = ();
769+
type Output = Result<(), super::Error>;
773770

774771
fn poll(
775772
mut self: core::pin::Pin<&mut Self>,
@@ -778,29 +775,25 @@ mod transfer_future {
778775
use crate::dmac::waker::WAKERS;
779776
use core::task::Poll;
780777

781-
self.chan._enable_private();
782-
783-
if !self.triggered && self.trig_src == TriggerSource::Disable {
784-
self.triggered = true;
785-
self.chan._trigger_private();
786-
}
787-
788778
let flags_to_check = InterruptFlags::new().with_tcmpl(true).with_terr(true);
789-
790-
if self.chan.check_and_clear_interrupts(flags_to_check).tcmpl() {
791-
return Poll::Ready(());
779+
let triggered_flags = self.chan.check_and_clear_interrupts(flags_to_check);
780+
781+
if triggered_flags.tcmpl() {
782+
Poll::Ready(Ok(()))
783+
} else if triggered_flags.terr() {
784+
Poll::Ready(Err(super::Error::TransferError))
785+
} else {
786+
WAKERS[Id::USIZE].register(cx.waker());
787+
self.chan.enable_interrupts(flags_to_check);
788+
self.chan._enable_private();
789+
790+
if !self.triggered && self.trig_src == TriggerSource::Disable {
791+
self.triggered = true;
792+
self.chan._trigger_private();
793+
}
794+
795+
Poll::Pending
792796
}
793-
794-
WAKERS[Id::USIZE].register(cx.waker());
795-
self.chan.enable_interrupts(flags_to_check);
796-
797-
if self.chan.check_and_clear_interrupts(flags_to_check).tcmpl() {
798-
self.chan.disable_interrupts(flags_to_check);
799-
800-
return Poll::Ready(());
801-
}
802-
803-
Poll::Pending
804797
}
805798
}
806799
}

0 commit comments

Comments
 (0)