#![allow(unused_braces)]
use core::marker::PhantomData;
use core::sync::atomic;
use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
use super::{
Beat, Buffer, Error,
dma_controller::{ChId, PriorityLevel, TriggerAction, TriggerSource},
sram::{self, DmacDescriptor},
transfer::{BufferPair, Transfer},
};
use crate::typelevel::{Is, Sealed};
use modular_bitfield::prelude::*;
mod reg;
use reg::RegisterBlock;
#[hal_cfg("dmac-d5x")]
use super::dma_controller::{BurstLength, FifoThreshold};
pub trait Status: Sealed {
type Uninitialized: Status;
type Ready: Status;
}
pub enum Uninitialized {}
impl Sealed for Uninitialized {}
impl Status for Uninitialized {
type Uninitialized = Uninitialized;
type Ready = Ready;
}
pub enum Ready {}
impl Sealed for Ready {}
impl Status for Ready {
type Uninitialized = Uninitialized;
type Ready = Ready;
}
pub enum Busy {}
impl Sealed for Busy {}
impl Status for Busy {
type Uninitialized = Uninitialized;
type Ready = Ready;
}
#[cfg(feature = "async")]
pub enum UninitializedFuture {}
#[cfg(feature = "async")]
impl Sealed for UninitializedFuture {}
#[cfg(feature = "async")]
impl Status for UninitializedFuture {
type Uninitialized = UninitializedFuture;
type Ready = ReadyFuture;
}
#[cfg(feature = "async")]
pub enum ReadyFuture {}
#[cfg(feature = "async")]
impl Sealed for ReadyFuture {}
#[cfg(feature = "async")]
impl Status for ReadyFuture {
type Uninitialized = UninitializedFuture;
type Ready = ReadyFuture;
}
pub trait ReadyChannel: Status {}
impl ReadyChannel for Ready {}
#[cfg(feature = "async")]
impl ReadyChannel for ReadyFuture {}
pub trait AnyChannel: Sealed + Is<Type = SpecificChannel<Self>> {
type Status: Status;
type Id: ChId;
}
pub type SpecificChannel<C> = Channel<<C as AnyChannel>::Id, <C as AnyChannel>::Status>;
pub type ChannelStatus<C> = <C as AnyChannel>::Status;
pub type ChannelId<C> = <C as AnyChannel>::Id;
impl<Id, S> Sealed for Channel<Id, S>
where
Id: ChId,
S: Status,
{
}
impl<Id, S> AnyChannel for Channel<Id, S>
where
Id: ChId,
S: Status,
{
type Id = Id;
type Status = S;
}
impl<Id, S> AsRef<Self> for Channel<Id, S>
where
Id: ChId,
S: Status,
{
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
impl<Id, S> AsMut<Self> for Channel<Id, S>
where
Id: ChId,
S: Status,
{
#[inline]
fn as_mut(&mut self) -> &mut Self {
self
}
}
pub struct Channel<Id: ChId, S: Status> {
regs: RegisterBlock<Id>,
_status: PhantomData<S>,
}
#[inline]
pub(super) fn new_chan<Id: ChId>(_id: PhantomData<Id>) -> Channel<Id, Uninitialized> {
Channel {
regs: RegisterBlock::new(_id),
_status: PhantomData,
}
}
#[cfg(feature = "async")]
#[inline]
pub(super) fn new_chan_future<Id: ChId>(_id: PhantomData<Id>) -> Channel<Id, UninitializedFuture> {
Channel {
regs: RegisterBlock::new(_id),
_status: PhantomData,
}
}
impl<Id: ChId, S: Status> Channel<Id, S> {
#[inline]
#[hal_macro_helper]
pub fn init(mut self, lvl: PriorityLevel) -> Channel<Id, S::Ready> {
self._reset_private();
#[hal_cfg(any("dmac-d11", "dmac-d21"))]
self.regs.chctrlb.modify(|_, w| w.lvl().variant(lvl));
#[hal_cfg("dmac-d5x")]
self.regs.chprilvl.modify(|_, w| w.prilvl().variant(lvl));
self.change_status()
}
#[inline]
pub fn enable_interrupts(&mut self, flags: InterruptFlags) {
self.regs
.chintenset
.write(|w| unsafe { w.bits(flags.into()) });
}
#[inline]
pub fn disable_interrupts(&mut self, flags: InterruptFlags) {
self.regs
.chintenclr
.write(|w| unsafe { w.bits(flags.into()) });
}
#[inline]
pub fn check_and_clear_interrupts(&mut self, flags: InterruptFlags) -> InterruptFlags {
let mut cleared = 0;
self.regs.chintflag.modify(|r, w| {
cleared = r.bits() & flags.into_bytes()[0];
unsafe { w.bits(cleared) }
});
InterruptFlags::from_bytes([cleared])
}
#[inline]
pub(super) fn change_status<N: Status>(self) -> Channel<Id, N> {
Channel {
regs: self.regs,
_status: PhantomData,
}
}
#[inline]
fn _reset_private(&mut self) {
self.regs.chctrla.modify(|_, w| w.swrst().set_bit());
while self.regs.chctrla.read().swrst().bit_is_set() {}
}
#[inline]
fn _trigger_private(&mut self) {
self.regs.swtrigctrl.set_bit();
}
#[inline]
fn _enable_private(&mut self) {
atomic::fence(atomic::Ordering::Release); self.regs.chctrla.modify(|_, w| w.enable().set_bit());
}
#[inline]
pub(crate) fn stop(&mut self) {
self.regs.chctrla.modify(|_, w| w.enable().clear_bit());
while !self.xfer_complete() {
core::hint::spin_loop();
}
atomic::fence(atomic::Ordering::Acquire); }
#[inline]
pub(crate) fn xfer_complete(&mut self) -> bool {
self.regs.chctrla.read().enable().bit_is_clear()
}
#[allow(dead_code)]
#[inline]
pub(crate) fn xfer_success(&mut self) -> super::Result<()> {
let success = self.regs.chintflag.read().terr().bit_is_clear();
success.then_some(()).ok_or(Error::TransferError)
}
#[inline]
fn descriptor_mut(&mut self) -> &mut DmacDescriptor {
unsafe {
let id = ChannelId::<Self>::USIZE;
&mut *sram::get_descriptor(id)
}
}
#[inline]
pub(super) unsafe fn fill_descriptor<Src: Buffer, Dst: Buffer<Beat = Src::Beat>>(
&mut self,
source: &mut Src,
destination: &mut Dst,
circular: bool,
) {
let descriptor = self.descriptor_mut();
let descaddr = if circular {
descriptor as *mut _
} else {
core::ptr::null_mut()
};
unsafe {
write_descriptor(descriptor, source, destination, descaddr);
}
}
pub(super) unsafe fn link_next(&mut self, next: *mut DmacDescriptor) {
self.descriptor_mut().descaddr = next;
}
}
impl<Id, R> Channel<Id, R>
where
Id: ChId,
R: ReadyChannel,
{
#[inline]
pub fn reset(mut self) -> Channel<Id, R::Uninitialized> {
self._reset_private();
self.change_status()
}
#[hal_cfg("dmac-d5x")]
#[inline]
pub fn fifo_threshold(&mut self, threshold: FifoThreshold) {
self.regs
.chctrla
.modify(|_, w| w.threshold().variant(threshold));
}
#[cfg(doc)]
#[hal_cfg(not("dmac-d5x"))]
pub fn fifo_threshold(&mut self) {
unimplemented!()
}
#[hal_cfg("dmac-d5x")]
#[inline]
pub fn burst_length(&mut self, burst_length: BurstLength) {
self.regs
.chctrla
.modify(|_, w| w.burstlen().variant(burst_length));
}
#[cfg(doc)]
#[hal_cfg(not("dmac-d5x"))]
pub fn burst_length(&mut self) {
unimplemented!()
}
#[inline]
#[hal_macro_helper]
pub(super) unsafe fn _start_private(
&mut self,
trig_src: TriggerSource,
trig_act: TriggerAction,
) {
self.configure_trigger(trig_src, trig_act);
self._enable_private();
if trig_src == TriggerSource::Disable {
self._trigger_private();
}
}
#[inline]
#[hal_macro_helper]
pub(super) fn configure_trigger(&mut self, trig_src: TriggerSource, trig_act: TriggerAction) {
#[hal_cfg(any("dmac-d11", "dmac-d21"))]
self.regs.chctrlb.modify(|_, w| {
w.trigsrc().variant(trig_src);
w.trigact().variant(trig_act)
});
#[hal_cfg("dmac-d5x")]
self.regs.chctrla.modify(|_, w| {
w.trigsrc().variant(trig_src);
w.trigact().variant(trig_act)
});
}
}
impl<Id: ChId> Channel<Id, Ready> {
#[inline]
pub(crate) fn start(
mut self,
trig_src: TriggerSource,
trig_act: TriggerAction,
) -> Channel<Id, Busy> {
unsafe {
self._start_private(trig_src, trig_act);
}
self.change_status()
}
#[inline]
#[allow(dead_code)]
pub(crate) unsafe fn transfer<S, D>(
&mut self,
source: &mut S,
dest: &mut D,
trig_src: TriggerSource,
trig_act: TriggerAction,
linked_descriptor: Option<&mut DmacDescriptor>,
) -> Result<(), Error>
where
S: Buffer,
D: Buffer<Beat = S::Beat>,
{
Transfer::<Self, BufferPair<S, D>>::check_buffer_pair(source, dest)?;
unsafe {
self.transfer_unchecked(source, dest, trig_src, trig_act, linked_descriptor);
}
Ok(())
}
#[inline]
pub(crate) unsafe fn transfer_unchecked<S, D>(
&mut self,
source: &mut S,
dest: &mut D,
trig_src: TriggerSource,
trig_act: TriggerAction,
linked_descriptor: Option<&mut DmacDescriptor>,
) where
S: Buffer,
D: Buffer<Beat = S::Beat>,
{
unsafe {
self.fill_descriptor(source, dest, false);
if let Some(next) = linked_descriptor {
self.link_next(next as *mut _);
}
}
self.configure_trigger(trig_src, trig_act);
self._enable_private();
if trig_src == TriggerSource::Disable {
self._trigger_private();
}
}
}
impl<Id: ChId> Channel<Id, Busy> {
#[inline]
pub(crate) fn software_trigger(&mut self) {
self._trigger_private();
}
#[inline]
pub(crate) fn free(mut self) -> Channel<Id, Ready> {
self.stop();
self.change_status()
}
#[inline]
pub(crate) fn restart(&mut self) {
self._enable_private();
}
}
impl<Id: ChId> From<Channel<Id, Ready>> for Channel<Id, Uninitialized> {
fn from(mut item: Channel<Id, Ready>) -> Self {
item._reset_private();
item.change_status()
}
}
#[cfg(feature = "async")]
impl<Id: ChId> Channel<Id, ReadyFuture> {
#[inline]
pub async fn transfer_future<S, D>(
&mut self,
mut source: S,
mut dest: D,
trig_src: TriggerSource,
trig_act: TriggerAction,
) -> Result<(), super::Error>
where
S: super::Buffer,
D: super::Buffer<Beat = S::Beat>,
{
unsafe {
self.transfer_future_linked(&mut source, &mut dest, trig_src, trig_act, None)
.await
}
}
pub(crate) async unsafe fn transfer_future_linked<S, D>(
&mut self,
source: &mut S,
dest: &mut D,
trig_src: TriggerSource,
trig_act: TriggerAction,
linked_descriptor: Option<&mut DmacDescriptor>,
) -> Result<(), super::Error>
where
S: super::Buffer,
D: super::Buffer<Beat = S::Beat>,
{
super::Transfer::<Self, super::transfer::BufferPair<S, D>>::check_buffer_pair(
source, dest,
)?;
unsafe {
self.fill_descriptor(source, dest, false);
if let Some(next) = linked_descriptor {
self.link_next(next as *mut _);
}
}
self.disable_interrupts(
InterruptFlags::new()
.with_susp(true)
.with_tcmpl(true)
.with_terr(true),
);
self.configure_trigger(trig_src, trig_act);
transfer_future::TransferFuture::new(self, trig_src).await
}
}
#[cfg(feature = "async")]
mod transfer_future {
use super::*;
pub(super) struct TransferFuture<'a, Id: ChId> {
triggered: bool,
trig_src: TriggerSource,
chan: &'a mut Channel<Id, ReadyFuture>,
}
impl<'a, Id: ChId> TransferFuture<'a, Id> {
pub(super) fn new(chan: &'a mut Channel<Id, ReadyFuture>, trig_src: TriggerSource) -> Self {
Self {
triggered: false,
trig_src,
chan,
}
}
}
impl<Id: ChId> Drop for TransferFuture<'_, Id> {
fn drop(&mut self) {
self.chan.stop();
}
}
impl<Id: ChId> core::future::Future for TransferFuture<'_, Id> {
type Output = Result<(), super::Error>;
fn poll(
mut self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
use crate::dmac::waker::WAKERS;
use core::task::Poll;
let flags_to_check = InterruptFlags::new().with_tcmpl(true).with_terr(true);
let triggered_flags = self.chan.check_and_clear_interrupts(flags_to_check);
if triggered_flags.tcmpl() {
Poll::Ready(Ok(()))
} else if triggered_flags.terr() {
Poll::Ready(Err(super::Error::TransferError))
} else {
WAKERS[Id::USIZE].register(cx.waker());
self.chan.enable_interrupts(flags_to_check);
self.chan._enable_private();
if !self.triggered && self.trig_src == TriggerSource::Disable {
self.triggered = true;
self.chan._trigger_private();
}
Poll::Pending
}
}
}
}
#[bitfield]
#[repr(u8)]
#[derive(Clone, Copy)]
pub struct InterruptFlags {
pub terr: bool,
pub tcmpl: bool,
pub susp: bool,
#[skip]
_reserved: B5,
}
impl Default for InterruptFlags {
fn default() -> Self {
Self::new()
}
}
#[inline]
pub(crate) unsafe fn write_descriptor<Src: Buffer, Dst: Buffer<Beat = Src::Beat>>(
descriptor: &mut DmacDescriptor,
source: &mut Src,
destination: &mut Dst,
next: *mut DmacDescriptor,
) {
let src_ptr = source.dma_ptr();
let src_inc = source.incrementing();
let src_len = source.buffer_len();
let dst_ptr = destination.dma_ptr();
let dst_inc = destination.incrementing();
let dst_len = destination.buffer_len();
let length = core::cmp::max(src_len, dst_len);
let btctrl = sram::BlockTransferControl::new()
.with_srcinc(src_inc)
.with_dstinc(dst_inc)
.with_beatsize(Src::Beat::BEATSIZE)
.with_valid(true);
*descriptor = DmacDescriptor {
descaddr: next,
srcaddr: src_ptr as *mut _,
dstaddr: dst_ptr as *mut _,
btcnt: length as u16,
btctrl,
};
}