From f32caaeaafd77b15005612b6d8d039b101b36975 Mon Sep 17 00:00:00 2001 From: Thales Fragoso Date: Sat, 3 Jul 2021 17:58:59 -0300 Subject: [PATCH] STM: Start working on bdma-v1 --- embassy-stm32/src/bdma/mod.rs | 69 ++------- embassy-stm32/src/bdma/v1.rs | 275 ++++++++++++++++++++++++++++++++++ embassy-stm32/src/bdma/v2.rs | 1 + embassy-stm32/src/lib.rs | 4 + 4 files changed, 295 insertions(+), 54 deletions(-) create mode 100644 embassy-stm32/src/bdma/v1.rs create mode 100644 embassy-stm32/src/bdma/v2.rs diff --git a/embassy-stm32/src/bdma/mod.rs b/embassy-stm32/src/bdma/mod.rs index e85ade5d..1a49f4e1 100644 --- a/embassy-stm32/src/bdma/mod.rs +++ b/embassy-stm32/src/bdma/mod.rs @@ -7,63 +7,24 @@ mod _version; #[allow(unused)] pub use _version::*; -use crate::pac; -use crate::peripherals; +use core::future::Future; -pub(crate) mod sealed { - use super::*; +pub trait WriteDma { + type WriteDmaFuture<'a>: Future + 'a + where + Self: 'a; - pub trait Channel { - fn num(&self) -> u8; - - fn dma_num(&self) -> u8 { - self.num() / 8 - } - fn ch_num(&self) -> u8 { - self.num() % 8 - } - fn regs(&self) -> pac::dma::Dma { - pac::DMA(self.num() as _) - } - } + fn transfer<'a>(&'a mut self, buf: &'a [u8], dst: *mut u8) -> Self::WriteDmaFuture<'a> + where + T: 'a; } -pub trait Channel: sealed::Channel + Sized {} +pub trait ReadDma { + type ReadDmaFuture<'a>: Future + 'a + where + Self: 'a; -macro_rules! impl_dma_channel { - ($channel_peri:ident, $dma_num:expr, $ch_num:expr) => { - impl Channel for peripherals::$channel_peri {} - impl sealed::Channel for peripherals::$channel_peri { - #[inline] - fn num(&self) -> u8 { - $dma_num * 8 + $ch_num - } - } - }; + fn transfer<'a>(&'a mut self, src: *const u8, buf: &'a mut [u8]) -> Self::ReadDmaFuture<'a> + where + T: 'a; } - -/* -crate::pac::peripherals!( - (dma,DMA1) => { - impl_dma_channel!(DMA1_CH0, 0, 0); - impl_dma_channel!(DMA1_CH1, 0, 1); - impl_dma_channel!(DMA1_CH2, 0, 2); - impl_dma_channel!(DMA1_CH3, 0, 3); - impl_dma_channel!(DMA1_CH4, 0, 4); - impl_dma_channel!(DMA1_CH5, 0, 5); - impl_dma_channel!(DMA1_CH6, 0, 6); - impl_dma_channel!(DMA1_CH7, 0, 7); - }; - - (dma,DMA2) => { - impl_dma_channel!(DMA2_CH0, 1, 0); - impl_dma_channel!(DMA2_CH1, 1, 1); - impl_dma_channel!(DMA2_CH2, 1, 2); - impl_dma_channel!(DMA2_CH3, 1, 3); - impl_dma_channel!(DMA2_CH4, 1, 4); - impl_dma_channel!(DMA2_CH5, 1, 5); - impl_dma_channel!(DMA2_CH6, 1, 6); - impl_dma_channel!(DMA2_CH7, 1, 7); - }; -); - */ diff --git a/embassy-stm32/src/bdma/v1.rs b/embassy-stm32/src/bdma/v1.rs new file mode 100644 index 00000000..584d531f --- /dev/null +++ b/embassy-stm32/src/bdma/v1.rs @@ -0,0 +1,275 @@ +use core::future::Future; +use core::task::Poll; + +use atomic_polyfill::{AtomicU8, Ordering}; +use embassy::interrupt::{Interrupt, InterruptExt}; +use embassy::util::{AtomicWaker, OnDrop}; +use futures::future::poll_fn; + +use super::{ReadDma, WriteDma}; +use crate::interrupt; +use crate::pac; +use crate::pac::bdma::vals; + +const CH_COUNT: usize = pac::peripheral_count!(DMA) * 8; +const CH_STATUS_NONE: u8 = 0; +const CH_STATUS_COMPLETED: u8 = 1; +const CH_STATUS_ERROR: u8 = 2; + +struct State { + ch_wakers: [AtomicWaker; CH_COUNT], + ch_status: [AtomicU8; CH_COUNT], +} + +impl State { + const fn new() -> Self { + const AW: AtomicWaker = AtomicWaker::new(); + const AU: AtomicU8 = AtomicU8::new(CH_STATUS_NONE); + Self { + ch_wakers: [AW; CH_COUNT], + ch_status: [AU; CH_COUNT], + } + } +} + +static STATE: State = State::new(); + +#[allow(unused)] +pub(crate) async unsafe fn transfer_p2m(ch: &mut impl Channel, src: *const u8, dst: &mut [u8]) { + // ndtr is max 16 bits. + assert!(dst.len() <= 0xFFFF); + + let regs: pac::bdma::Ch = ch.regs(); + let state_number = ch.state_num(); + + // Reset status + // Generate a DMB here to flush the store buffer (M7) before enabling the DMA + STATE.ch_status[state_number].store(CH_STATUS_NONE, Ordering::Release); + + let on_drop = OnDrop::new(|| unsafe { + regs.cr().modify(|w| { + w.set_tcie(false); + w.set_teie(false); + w.set_en(false); + }); + while regs.cr().read().en() {} + }); + + regs.par().write_value(src as u32); + regs.mar().write_value(dst.as_mut_ptr() as u32); + regs.ndtr().write(|w| w.set_ndt(dst.len() as u16)); + regs.cr().write(|w| { + w.set_psize(vals::Size::BITS8); + w.set_msize(vals::Size::BITS8); + w.set_minc(vals::Inc::ENABLED); + w.set_teie(true); + w.set_tcie(true); + w.set_en(true); + }); + + let res = poll_fn(|cx| { + STATE.ch_wakers[state_number].register(cx.waker()); + match STATE.ch_status[state_number].load(Ordering::Acquire) { + CH_STATUS_NONE => Poll::Pending, + x => Poll::Ready(x), + } + }) + .await; + + on_drop.defuse(); + // TODO handle error + assert!(res == CH_STATUS_COMPLETED); +} + +#[allow(unused)] +pub(crate) async unsafe fn transfer_m2p(ch: &mut impl Channel, src: &[u8], dst: *mut u8) { + // ndtr is max 16 bits. + assert!(src.len() <= 0xFFFF); + + let regs: pac::bdma::Ch = ch.regs(); + let state_number = ch.state_num(); + + // Reset status + // Generate a DMB here to flush the store buffer (M7) before enabling the DMA + STATE.ch_status[state_number].store(CH_STATUS_NONE, Ordering::Release); + + let on_drop = OnDrop::new(|| unsafe { + regs.cr().modify(|w| { + w.set_tcie(false); + w.set_teie(false); + w.set_en(false); + }); + while regs.cr().read().en() {} + }); + + regs.par().write_value(dst as u32); + regs.mar().write_value(src.as_ptr() as u32); + regs.ndtr().write(|w| w.set_ndt(src.len() as u16)); + regs.cr().write(|w| { + w.set_psize(vals::Size::BITS8); + w.set_msize(vals::Size::BITS8); + w.set_minc(vals::Inc::ENABLED); + w.set_dir(vals::Dir::FROMMEMORY); + w.set_teie(true); + w.set_tcie(true); + w.set_en(true); + }); + + let res = poll_fn(|cx| { + STATE.ch_wakers[state_number].register(cx.waker()); + match STATE.ch_status[state_number].load(Ordering::Acquire) { + CH_STATUS_NONE => Poll::Pending, + x => Poll::Ready(x), + } + }) + .await; + + on_drop.defuse(); + // TODO handle error + assert!(res == CH_STATUS_COMPLETED); +} + +unsafe fn on_irq() { + pac::peripherals! { + (bdma, $dma:ident) => { + let isr = pac::$dma.isr().read(); + pac::$dma.ifcr().write_value(isr); + let dman = ::num() as usize; + + for chn in 0..7 { + let n = dman * 8 + chn; + if isr.teif(chn) { + STATE.ch_status[n].store(CH_STATUS_ERROR, Ordering::Relaxed); + STATE.ch_wakers[n].wake(); + } else if isr.tcif(chn) { + STATE.ch_status[n].store(CH_STATUS_COMPLETED, Ordering::Relaxed); + STATE.ch_wakers[n].wake(); + } + } + + }; + } +} + +/// safety: must be called only once +pub(crate) unsafe fn init() { + pac::interrupts! { + (DMA, $irq:ident) => { + crate::interrupt::$irq::steal().enable(); + }; + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Dma { + fn num() -> u8; + } + + pub trait Channel { + fn dma_regs() -> &'static pac::bdma::Dma; + + fn state_num(&self) -> usize; + + fn ch_num(&self) -> u8; + + fn regs(&self) -> pac::bdma::Ch { + Self::dma_regs().ch(self.ch_num() as usize) + } + } +} + +pub trait Dma: sealed::Dma + Sized {} +pub trait Channel: sealed::Channel + Sized {} + +macro_rules! impl_dma { + ($peri:ident, $num:expr) => { + impl Dma for crate::peripherals::$peri {} + impl sealed::Dma for crate::peripherals::$peri { + fn num() -> u8 { + $num + } + } + }; +} + +macro_rules! impl_dma_channel { + ($channel_peri:ident, $dma_peri:ident, $dma_num:expr, $ch_num:expr) => { + impl Channel for crate::peripherals::$channel_peri {} + impl sealed::Channel for crate::peripherals::$channel_peri { + #[inline] + fn dma_regs() -> &'static pac::bdma::Dma { + &crate::pac::$dma_peri + } + + fn state_num(&self) -> usize { + ($dma_num * 8) + $ch_num + } + + fn ch_num(&self) -> u8 { + $ch_num + } + } + + impl WriteDma for crate::peripherals::$channel_peri + where + T: 'static, + { + type WriteDmaFuture<'a> = impl Future; + + fn transfer<'a>(&'a mut self, buf: &'a [u8], dst: *mut u8) -> Self::WriteDmaFuture<'a> + where + T: 'a, + { + unsafe { transfer_m2p(self, buf, dst) } + } + } + + impl ReadDma for crate::peripherals::$channel_peri + where + T: 'static, + { + type ReadDmaFuture<'a> = impl Future; + + fn transfer<'a>( + &'a mut self, + src: *const u8, + buf: &'a mut [u8], + ) -> Self::ReadDmaFuture<'a> + where + T: 'a, + { + unsafe { transfer_p2m(self, src, buf) } + } + } + }; +} + +pac::peripherals! { + (bdma, DMA1) => { + impl_dma!(DMA1, 0); + pac::dma_channels! { + ($channel_peri:ident, DMA1, $channel_num:expr) => { + impl_dma_channel!($channel_peri, DMA1, 0, $channel_num); + }; + } + }; + (bdma, DMA2) => { + impl_dma!(DMA2, 1); + pac::dma_channels! { + ($channel_peri:ident, DMA2, $channel_num:expr) => { + impl_dma_channel!($channel_peri, DMA2, 1, $channel_num); + }; + } + }; +} + +pac::interrupts! { + (DMA, $irq:ident) => { + #[crate::interrupt] + unsafe fn $irq () { + on_irq() + } + }; +} diff --git a/embassy-stm32/src/bdma/v2.rs b/embassy-stm32/src/bdma/v2.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/embassy-stm32/src/bdma/v2.rs @@ -0,0 +1 @@ + diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 76a6ecd8..35a8d3ba 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -22,6 +22,8 @@ pub mod rcc; // Sometimes-present hardware #[cfg(adc)] pub mod adc; +#[cfg(bdma)] +pub mod bdma; #[cfg(timer)] pub mod clock; #[cfg(dac)] @@ -86,6 +88,8 @@ pub fn init(config: Config) -> Peripherals { unsafe { #[cfg(dma)] dma::init(); + #[cfg(bdma)] + bdma::init(); #[cfg(exti)] exti::init(); rcc::init(config.rcc);