From b2910558d36929dfde056f609a8af23d925e67f6 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Fri, 19 Nov 2021 19:15:55 +0100 Subject: [PATCH] Refactor DMA traits. --- embassy-stm32/src/dma/bdma.rs | 372 +++++++++++++-------------- embassy-stm32/src/dma/dma.rs | 457 ++++++++++++++-------------------- embassy-stm32/src/dma/mod.rs | 209 ++++++++++++---- embassy-stm32/src/i2c/v2.rs | 10 +- embassy-stm32/src/spi/v1.rs | 27 +- embassy-stm32/src/spi/v2.rs | 27 +- embassy-stm32/src/spi/v3.rs | 27 +- embassy-stm32/src/usart/v1.rs | 10 +- embassy-stm32/src/usart/v2.rs | 11 +- 9 files changed, 591 insertions(+), 559 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 528e49f42..1dcd0c228 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -1,20 +1,29 @@ #![macro_use] -use core::future::Future; use core::sync::atomic::{fence, Ordering}; -use core::task::{Poll, Waker}; +use core::task::Waker; use embassy::interrupt::{Interrupt, InterruptExt}; use embassy::waitqueue::AtomicWaker; -use embassy_hal_common::drop::OnDrop; -use futures::future::poll_fn; -use crate::dma::{Channel, Request}; +use crate::dma::Request; use crate::interrupt; use crate::pac; use crate::pac::bdma::vals; use crate::rcc::sealed::RccPeripheral; +use super::{Word, WordSize}; + +impl From for vals::Size { + fn from(raw: WordSize) -> Self { + match raw { + WordSize::OneByte => Self::BITS8, + WordSize::TwoBytes => Self::BITS16, + WordSize::FourBytes => Self::BITS32, + } + } +} + const CH_COUNT: usize = pac::peripheral_count!(bdma) * 8; struct State { @@ -32,118 +41,6 @@ impl State { static STATE: State = State::new(); -#[allow(unused)] -pub(crate) unsafe fn do_transfer( - dma: pac::bdma::Dma, - channel_number: u8, - state_number: u8, - request: Request, - dir: vals::Dir, - peri_addr: *const u8, - mem_addr: *mut u8, - mem_len: usize, - incr_mem: bool, - #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, - #[cfg(dmamux)] dmamux_ch_num: u8, -) -> impl Future { - // ndtr is max 16 bits. - assert!(mem_len <= 0xFFFF); - - let ch = dma.ch(channel_number as _); - - // Reset status - dma.ifcr().write(|w| { - w.set_tcif(channel_number as _, true); - w.set_teif(channel_number as _, true); - }); - - let on_drop = OnDrop::new(move || unsafe { - _stop(dma, channel_number); - }); - - #[cfg(dmamux)] - super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); - - #[cfg(bdma_v2)] - critical_section::with(|_| { - dma.cselr() - .modify(|w| w.set_cs(channel_number as _, request)) - }); - - // "Preceding reads and writes cannot be moved past subsequent writes." - fence(Ordering::Release); - - ch.par().write_value(peri_addr as u32); - ch.mar().write_value(mem_addr as u32); - ch.ndtr().write(|w| w.set_ndt(mem_len as u16)); - ch.cr().write(|w| { - w.set_psize(vals::Size::BITS8); - w.set_msize(vals::Size::BITS8); - if incr_mem { - w.set_minc(vals::Inc::ENABLED); - } else { - w.set_minc(vals::Inc::DISABLED); - } - w.set_dir(dir); - w.set_teie(true); - w.set_tcie(true); - w.set_en(true); - }); - - async move { - let res = poll_fn(|cx| { - STATE.ch_wakers[state_number as usize].register(cx.waker()); - - let isr = dma.isr().read(); - - // TODO handle error - assert!(!isr.teif(channel_number as _)); - - if isr.tcif(channel_number as _) { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - drop(on_drop) - } -} - -unsafe fn _stop(dma: pac::bdma::Dma, ch: u8) { - let ch = dma.ch(ch as _); - - // Disable the channel and interrupts with the default value. - ch.cr().write(|_| ()); - - // Wait for the transfer to complete when it was ongoing. - while ch.cr().read().en() {} - - // "Subsequent reads and writes cannot be moved ahead of preceding reads." - fence(Ordering::Acquire); -} - -unsafe fn _is_stopped(dma: pac::bdma::Dma, ch: u8) -> bool { - let ch = dma.ch(ch as _); - ch.cr().read().en() -} - -/// Gets the total remaining transfers for the channel -/// Note: this will be zero for transfers that completed without cancellation. -unsafe fn _get_remaining_transfers(dma: pac::bdma::Dma, ch: u8) -> u16 { - // get a handle on the channel itself - let ch = dma.ch(ch as _); - // read the remaining transfer count. If this is zero, the transfer completed fully. - ch.ndtr().read().ndt() -} - -/// Sets the waker for the specified DMA channel -unsafe fn _set_waker(_dma: pac::bdma::Dma, state_number: u8, waker: &Waker) { - let n = state_number as usize; - STATE.ch_wakers[n].register(waker); -} - macro_rules! dma_num { (DMA1) => { 0 @@ -190,100 +87,87 @@ pub(crate) unsafe fn init() { pac::dma_channels! { ($channel_peri:ident, $dma_peri:ident, bdma, $channel_num:expr, $dmamux:tt) => { - impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri {} - impl Channel for crate::peripherals::$channel_peri - { - type ReadFuture<'a> = impl Future + 'a; - type WriteFuture<'a> = impl Future + 'a; + impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { - fn read<'a>( - &'a mut self, - request: Request, - src: *mut u8, - buf: &'a mut [u8], - ) -> Self::ReadFuture<'a> { - unsafe { - do_transfer( - crate::pac::$dma_peri, - $channel_num, - (dma_num!($dma_peri) * 8) + $channel_num, - request, - vals::Dir::FROMPERIPHERAL, - src, - buf.as_mut_ptr(), - buf.len(), - true, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) - } + unsafe fn start_write(&mut self, request: Request, buf: &[W], reg_addr: *mut u32) { + low_level_api::reset_status(crate::pac::$dma_peri, $channel_num); + low_level_api::start_transfer( + crate::pac::$dma_peri, + $channel_num, + #[cfg(any(bdma_v2, dmamux))] + request, + vals::Dir::FROMMEMORY, + reg_addr as *const u32, + buf.as_ptr() as *mut u32, + buf.len(), + true, + vals::Size::from(W::bits()), + #[cfg(dmamux)] + ::DMAMUX_REGS, + #[cfg(dmamux)] + ::DMAMUX_CH_NUM, + ); } - fn write<'a>( - &'a mut self, - request: Request, - buf: &'a [u8], - dst: *mut u8, - ) -> Self::WriteFuture<'a> { - unsafe { - do_transfer( - crate::pac::$dma_peri, - $channel_num, - (dma_num!($dma_peri) * 8) + $channel_num, - request, - vals::Dir::FROMMEMORY, - dst, - buf.as_ptr() as *mut u8, - buf.len(), - true, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) - } + + unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut u32) { + let buf = [repeated]; + low_level_api::reset_status(crate::pac::$dma_peri, $channel_num); + low_level_api::start_transfer( + crate::pac::$dma_peri, + $channel_num, + #[cfg(any(bdma_v2, dmamux))] + request, + vals::Dir::FROMMEMORY, + reg_addr as *const u32, + buf.as_ptr() as *mut u32, + count, + false, + vals::Size::from(W::bits()), + #[cfg(dmamux)] + ::DMAMUX_REGS, + #[cfg(dmamux)] + ::DMAMUX_CH_NUM, + ) } - fn write_x<'a>( - &'a mut self, - request: Request, - word: &u8, - count: usize, - dst: *mut u8, - ) -> Self::WriteFuture<'a> { - unsafe { - do_transfer( - crate::pac::$dma_peri, - $channel_num, - (dma_num!($dma_peri) * 8) + $channel_num, - request, - vals::Dir::FROMMEMORY, - dst, - word as *const u8 as *mut u8, - count, - false, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) - } + unsafe fn start_read(&mut self, request: Request, reg_addr: *mut u32, buf: &mut [W]) { + low_level_api::reset_status(crate::pac::$dma_peri, $channel_num); + low_level_api::start_transfer( + crate::pac::$dma_peri, + $channel_num, + #[cfg(any(bdma_v2, dmamux))] + request, + vals::Dir::FROMPERIPHERAL, + reg_addr as *const u32, + buf.as_ptr() as *mut u32, + buf.len(), + true, + vals::Size::from(W::bits()), + #[cfg(dmamux)] + ::DMAMUX_REGS, + #[cfg(dmamux)] + ::DMAMUX_CH_NUM, + ); } - fn stop <'a>(&'a mut self){ - unsafe {_stop(crate::pac::$dma_peri, $channel_num);} + + fn request_stop(&mut self){ + unsafe {low_level_api::request_stop(crate::pac::$dma_peri, $channel_num);} } - fn is_stopped<'a>(&'a self) -> bool { - unsafe {_is_stopped(crate::pac::$dma_peri, $channel_num)} + + fn is_stopped(&self) -> bool { + unsafe {low_level_api::is_stopped(crate::pac::$dma_peri, $channel_num)} } - fn remaining_transfers<'a>(&'a mut self) -> u16 { - unsafe {_get_remaining_transfers(crate::pac::$dma_peri, $channel_num)} + fn remaining_transfers(&mut self) -> u16 { + unsafe {low_level_api::get_remaining_transfers(crate::pac::$dma_peri, $channel_num)} } - fn set_waker<'a>(&'a mut self, waker: &'a Waker) { - unsafe {_set_waker(crate::pac::$dma_peri, $channel_num, waker )} + + fn set_waker(&mut self, waker: &Waker) { + unsafe {low_level_api::set_waker(crate::pac::$dma_peri, $channel_num, waker )} } } + + impl crate::dma::Channel for crate::peripherals::$channel_peri {} }; } @@ -295,3 +179,91 @@ pac::interrupts! { } }; } + +mod low_level_api { + use super::*; + + pub unsafe fn start_transfer( + dma: pac::bdma::Dma, + channel_number: u8, + #[cfg(any(bdma_v2, dmamux))] request: Request, + dir: vals::Dir, + peri_addr: *const u32, + mem_addr: *mut u32, + mem_len: usize, + incr_mem: bool, + data_size: vals::Size, + #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, + #[cfg(dmamux)] dmamux_ch_num: u8, + ) { + let ch = dma.ch(channel_number as _); + + #[cfg(dmamux)] + super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); + + #[cfg(bdma_v2)] + critical_section::with(|_| { + dma.cselr() + .modify(|w| w.set_cs(channel_number as _, request)) + }); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + ch.par().write_value(peri_addr as u32); + ch.mar().write_value(mem_addr as u32); + ch.ndtr().write(|w| w.set_ndt(mem_len as u16)); + ch.cr().write(|w| { + w.set_psize(data_size); + w.set_msize(data_size); + if incr_mem { + w.set_minc(vals::Inc::ENABLED); + } else { + w.set_minc(vals::Inc::DISABLED); + } + w.set_dir(dir); + w.set_teie(true); + w.set_tcie(true); + w.set_en(true); + }); + } + + pub unsafe fn request_stop(dma: pac::bdma::Dma, channel_number: u8) { + reset_status(dma, channel_number); + + let ch = dma.ch(channel_number as _); + + // Disable the channel and interrupts with the default value. + ch.cr().write(|_| ()); + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } + + pub unsafe fn is_stopped(dma: pac::bdma::Dma, ch: u8) -> bool { + let ch = dma.ch(ch as _); + ch.cr().read().en() + } + + /// Gets the total remaining transfers for the channel + /// Note: this will be zero for transfers that completed without cancellation. + pub unsafe fn get_remaining_transfers(dma: pac::bdma::Dma, ch: u8) -> u16 { + // get a handle on the channel itself + let ch = dma.ch(ch as _); + // read the remaining transfer count. If this is zero, the transfer completed fully. + ch.ndtr().read().ndt() + } + + /// Sets the waker for the specified DMA channel + pub unsafe fn set_waker(_dma: pac::bdma::Dma, state_number: u8, waker: &Waker) { + let n = state_number as usize; + STATE.ch_wakers[n].register(waker); + } + + pub unsafe fn reset_status(dma: pac::bdma::Dma, channel_number: u8) { + dma.ifcr().write(|w| { + w.set_tcif(channel_number as _, true); + w.set_teif(channel_number as _, true); + }); + } +} diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 257f21a73..c99ff27ba 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -1,21 +1,28 @@ -use core::future::Future; use core::sync::atomic::{fence, Ordering}; -use core::task::{Poll, Waker}; +use core::task::Waker; use embassy::interrupt::{Interrupt, InterruptExt}; use embassy::waitqueue::AtomicWaker; -use embassy_hal_common::drop::OnDrop; -use futures::future::poll_fn; use crate::interrupt; use crate::pac; use crate::pac::dma::{regs, vals}; use crate::rcc::sealed::RccPeripheral; -use super::{Channel, Request}; +use super::{Request, Word, WordSize}; const CH_COUNT: usize = pac::peripheral_count!(DMA) * 8; +impl From for vals::Size { + fn from(raw: WordSize) -> Self { + match raw { + WordSize::OneByte => Self::BITS8, + WordSize::TwoBytes => Self::BITS16, + WordSize::FourBytes => Self::BITS32, + } + } +} + struct State { ch_wakers: [AtomicWaker; CH_COUNT], } @@ -31,164 +38,6 @@ impl State { static STATE: State = State::new(); -//async unsafe fn do_transfer(ch: &mut impl Channel, ch_func: u8, src: *const u8, dst: &mut [u8]) { - -#[allow(unused)] -pub(crate) unsafe fn do_transfer( - dma: pac::dma::Dma, - channel_number: u8, - state_number: u8, - request: Request, - dir: vals::Dir, - peri_addr: *const u8, - mem_addr: *mut u8, - mem_len: usize, - incr_mem: bool, - #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, - #[cfg(dmamux)] dmamux_ch_num: u8, -) -> impl Future { - // ndtr is max 16 bits. - assert!(mem_len <= 0xFFFF); - - // Reset status - let isrn = channel_number as usize / 4; - let isrbit = channel_number as usize % 4; - _reset_status(&dma, isrn, isrbit); - - let ch = dma.st(channel_number as _); - - let on_drop = OnDrop::new(move || unsafe { - _stop(&dma, channel_number); - }); - - // "Preceding reads and writes cannot be moved past subsequent writes." - fence(Ordering::Release); - - // Actually start the transaction - _start_transfer( - request, - dir, - peri_addr, - mem_addr, - mem_len, - incr_mem, - ch, - #[cfg(dmamux)] - dmamux_regs, - #[cfg(dmamux)] - dmamux_ch_num, - ); - - async move { - let res = poll_fn(|cx| { - let n = state_number as usize; - STATE.ch_wakers[n].register(cx.waker()); - - let isr = dma.isr(isrn).read(); - - // TODO handle error - assert!(!isr.teif(isrbit)); - - if isr.tcif(isrbit) { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - drop(on_drop) - } -} - -unsafe fn _reset_status(dma: &crate::pac::dma::Dma, isrn: usize, isrbit: usize) { - dma.ifcr(isrn).write(|w| { - w.set_tcif(isrbit, true); - w.set_teif(isrbit, true); - }); -} - -unsafe fn _start_transfer( - request: Request, - dir: vals::Dir, - peri_addr: *const u8, - mem_addr: *mut u8, - mem_len: usize, - incr_mem: bool, - ch: crate::pac::dma::St, - #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, - #[cfg(dmamux)] dmamux_ch_num: u8, -) { - #[cfg(dmamux)] - super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); - - // "Preceding reads and writes cannot be moved past subsequent writes." - fence(Ordering::Release); - - ch.par().write_value(peri_addr as u32); - ch.m0ar().write_value(mem_addr as u32); - ch.ndtr().write_value(regs::Ndtr(mem_len as _)); - ch.cr().write(|w| { - w.set_dir(dir); - w.set_msize(vals::Size::BITS8); - w.set_psize(vals::Size::BITS8); - if incr_mem { - w.set_minc(vals::Inc::INCREMENTED); - } else { - w.set_minc(vals::Inc::FIXED); - } - w.set_pinc(vals::Inc::FIXED); - w.set_teie(true); - w.set_tcie(true); - #[cfg(dma_v1)] - w.set_trbuff(true); - - #[cfg(dma_v2)] - w.set_chsel(request); - - w.set_en(true); - }); -} - -/// Stops the DMA channel. -unsafe fn _stop(dma: &pac::dma::Dma, ch: u8) { - // get a handle on the channel itself - let ch = dma.st(ch as _); - - // Disable the channel and interrupts with the default value. - ch.cr().write(|_| ()); - - // Wait for the transfer to complete when it was ongoing. - while ch.cr().read().en() {} - - // "Subsequent reads and writes cannot be moved ahead of preceding reads." - fence(Ordering::Acquire); -} - -/// Gets the running status of the channel -unsafe fn _is_stopped(dma: &pac::dma::Dma, ch: u8) -> bool { - // get a handle on the channel itself - let ch = dma.st(ch as _); - - // Wait for the transfer to complete when it was ongoing. - ch.cr().read().en() -} - -/// Gets the total remaining transfers for the channel -/// Note: this will be zero for transfers that completed without cancellation. -unsafe fn _get_remaining_transfers(dma: &pac::dma::Dma, ch: u8) -> u16 { - // get a handle on the channel itself - let ch = dma.st(ch as _); - // read the remaining transfer count. If this is zero, the transfer completed fully. - ch.ndtr().read().ndt() -} - -/// Sets the waker for the specified DMA channel -unsafe fn _set_waker(_dma: &pac::dma::Dma, state_number: u8, waker: &Waker) { - let n = state_number as usize; - STATE.ch_wakers[n].register(waker); -} - macro_rules! dma_num { (DMA1) => { 0 @@ -234,120 +83,86 @@ pub(crate) unsafe fn init() { pac::dma_channels! { ($channel_peri:ident, $dma_peri:ident, dma, $channel_num:expr, $dmamux:tt) => { - impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri {} - impl Channel for crate::peripherals::$channel_peri - { - type ReadFuture<'a> = impl Future + 'a; - type WriteFuture<'a> = impl Future + 'a; - - fn read<'a>( - &'a mut self, - request: Request, - src: *mut u8, - buf: &'a mut [u8], - ) -> Self::ReadFuture<'a> { - unsafe { - do_transfer( - crate::pac::$dma_peri, - $channel_num, - (dma_num!($dma_peri) * 8) + $channel_num, - request, - vals::Dir::PERIPHERALTOMEMORY, - src, - buf.as_mut_ptr(), - buf.len(), - true, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) - } - } - - fn write<'a>( - &'a mut self, - request: Request, - buf: &'a [u8], - dst: *mut u8, - ) -> Self::WriteFuture<'a> { - unsafe { - do_transfer( - crate::pac::$dma_peri, - $channel_num, - (dma_num!($dma_peri) * 8) + $channel_num, - request, - vals::Dir::MEMORYTOPERIPHERAL, - dst, - buf.as_ptr() as *mut u8, - buf.len(), - true, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) - } - } - - fn write_x<'a>( - &'a mut self, - request: Request, - word: &u8, - num: usize, - dst: *mut u8, - ) -> Self::WriteFuture<'a> { - unsafe { - do_transfer( - crate::pac::$dma_peri, - $channel_num, - (dma_num!($dma_peri) * 8) + $channel_num, - request, - vals::Dir::MEMORYTOPERIPHERAL, - dst, - word as *const u8 as *mut u8, - num, - false, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) - } - } - fn stop <'a>(&'a mut self){ - unsafe {_stop(&crate::pac::$dma_peri, $channel_num);} - } - fn is_stopped<'a>(&'a self) -> bool { - unsafe {_is_stopped(&crate::pac::$dma_peri, $channel_num)} - } - fn remaining_transfers<'a>(&'a mut self) -> u16 { - unsafe {_get_remaining_transfers(&crate::pac::$dma_peri, $channel_num)} - } - fn set_waker<'a>(&'a mut self, waker: &'a Waker) { - unsafe {_set_waker(&crate::pac::$dma_peri, $channel_num, waker )} - } - fn start<'a>(&'a mut self, request: Request, buf: &'a [u8], dst: *mut u8){ - unsafe { + impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { + unsafe fn start_write(&mut self, request: Request, buf: &[W], reg_addr: *mut u32) { let isrn = $channel_num as usize / 4; let isrbit = $channel_num as usize % 4; - _reset_status(&crate::pac::$dma_peri, isrn, isrbit); - _start_transfer( - request, - vals::Dir::MEMORYTOPERIPHERAL, - dst, - buf.as_ptr() as *mut u8, - buf.len(), - true, - crate::pac::$dma_peri.st($channel_num as _), - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) - } + low_level_api::reset_status(&crate::pac::$dma_peri, isrn, isrbit); + low_level_api::start_transfer( + request, + vals::Dir::MEMORYTOPERIPHERAL, + reg_addr as *const u32, + buf.as_ptr() as *mut u32, + buf.len(), + true, + crate::pac::$dma_peri.st($channel_num as _), + vals::Size::from(W::bits()), + #[cfg(dmamux)] + ::DMAMUX_REGS, + #[cfg(dmamux)] + ::DMAMUX_CH_NUM, + ) + } + + unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut u32) { + let buf = [repeated]; + let isrn = $channel_num as usize / 4; + let isrbit = $channel_num as usize % 4; + low_level_api::reset_status(&crate::pac::$dma_peri, isrn, isrbit); + low_level_api::start_transfer( + request, + vals::Dir::MEMORYTOPERIPHERAL, + reg_addr as *const u32, + buf.as_ptr() as *mut u32, + count, + false, + crate::pac::$dma_peri.st($channel_num as _), + vals::Size::from(W::bits()), + #[cfg(dmamux)] + ::DMAMUX_REGS, + #[cfg(dmamux)] + ::DMAMUX_CH_NUM, + ) + } + + unsafe fn start_read(&mut self, request: Request, reg_addr: *mut u32, buf: &mut [W]) { + let isrn = $channel_num as usize / 4; + let isrbit = $channel_num as usize % 4; + low_level_api::reset_status(&crate::pac::$dma_peri, isrn, isrbit); + low_level_api::start_transfer( + request, + vals::Dir::PERIPHERALTOMEMORY, + reg_addr as *const u32, + buf.as_ptr() as *mut u32, + buf.len(), + true, + crate::pac::$dma_peri.st($channel_num as _), + vals::Size::from(W::bits()), + #[cfg(dmamux)] + ::DMAMUX_REGS, + #[cfg(dmamux)] + ::DMAMUX_CH_NUM, + ); + } + + fn request_stop(&mut self) { + unsafe {low_level_api::request_stop(&crate::pac::$dma_peri, $channel_num);} + } + + fn is_stopped(&self) -> bool { + unsafe {low_level_api::is_stopped(&crate::pac::$dma_peri, $channel_num)} + } + + fn remaining_transfers(&mut self) -> u16 { + unsafe {low_level_api::get_remaining_transfers(&crate::pac::$dma_peri, $channel_num)} + } + + fn set_waker(&mut self, waker: &Waker) { + unsafe {low_level_api::set_waker(&crate::pac::$dma_peri, $channel_num, waker )} } } + + impl crate::dma::Channel for crate::peripherals::$channel_peri { } }; } @@ -359,3 +174,99 @@ pac::interrupts! { } }; } + +mod low_level_api { + use super::*; + + pub unsafe fn start_transfer( + request: Request, + dir: vals::Dir, + peri_addr: *const u32, + mem_addr: *mut u32, + mem_len: usize, + incr_mem: bool, + ch: crate::pac::dma::St, + data_size: vals::Size, + #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, + #[cfg(dmamux)] dmamux_ch_num: u8, + ) { + #[cfg(dmamux)] + super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + ch.par().write_value(peri_addr as u32); + ch.m0ar().write_value(mem_addr as u32); + ch.ndtr().write_value(regs::Ndtr(mem_len as _)); + ch.cr().write(|w| { + w.set_dir(dir); + w.set_msize(data_size); + w.set_psize(data_size); + w.set_pl(vals::Pl::VERYHIGH); + if incr_mem { + w.set_minc(vals::Inc::INCREMENTED); + } else { + w.set_minc(vals::Inc::FIXED); + } + w.set_pinc(vals::Inc::FIXED); + w.set_teie(true); + w.set_tcie(true); + #[cfg(dma_v1)] + w.set_trbuff(true); + + #[cfg(dma_v2)] + w.set_chsel(request); + + w.set_en(true); + }); + } + + /// Stops the DMA channel. + pub unsafe fn request_stop(dma: &pac::dma::Dma, channel_number: u8) { + // Reset status + let isrn = channel_number as usize / 4; + let isrbit = channel_number as usize % 4; + reset_status(dma, isrn, isrbit); + + // get a handle on the channel itself + let ch = dma.st(channel_number as _); + + // Disable the channel and interrupts with the default value. + ch.cr().write(|_| ()); + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } + + /// Gets the running status of the channel + pub unsafe fn is_stopped(dma: &pac::dma::Dma, ch: u8) -> bool { + // get a handle on the channel itself + let ch = dma.st(ch as _); + + // Wait for the transfer to complete when it was ongoing. + ch.cr().read().en() + } + + /// Gets the total remaining transfers for the channel + /// Note: this will be zero for transfers that completed without cancellation. + pub unsafe fn get_remaining_transfers(dma: &pac::dma::Dma, ch: u8) -> u16 { + // get a handle on the channel itself + let ch = dma.st(ch as _); + // read the remaining transfer count. If this is zero, the transfer completed fully. + ch.ndtr().read().ndt() + } + + /// Sets the waker for the specified DMA channel + pub unsafe fn set_waker(_dma: &pac::dma::Dma, state_number: u8, waker: &Waker) { + let n = state_number as usize; + STATE.ch_wakers[n].register(waker); + } + + pub unsafe fn reset_status(dma: &crate::pac::dma::Dma, isrn: usize, isrbit: usize) { + dma.ifcr(isrn).write(|w| { + w.set_tcif(isrbit, true); + w.set_teif(isrbit, true); + }); + } +} diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 48b229c2e..3928bbb92 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -8,61 +8,182 @@ mod dmamux; #[cfg(dmamux)] pub use dmamux::*; -use core::future::Future; -use core::task::Waker; use embassy::util::Unborrow; +#[cfg(feature = "unstable-pac")] +pub use transfers::*; + +#[cfg(not(feature = "unstable-pac"))] +pub(crate) use transfers::*; + #[cfg(any(bdma_v2, dma_v2, dmamux))] pub type Request = u8; #[cfg(not(any(bdma_v2, dma_v2, dmamux)))] pub type Request = (); pub(crate) mod sealed { - pub trait Channel {} + use super::*; + use core::task::Waker; + pub trait Channel { + /// Starts this channel for writing a stream of words. + unsafe fn start_write(&mut self, request: Request, buf: &[W], reg_addr: *mut u32); + + /// Starts this channel for writing a word repeatedly. + unsafe fn start_write_repeated( + &mut self, + request: Request, + repeated: W, + count: usize, + reg_addr: *mut u32, + ); + + /// Starts this channel for reading a stream of words. + unsafe fn start_read( + &mut self, + request: Request, + reg_addr: *mut u32, + buf: &mut [W], + ); + + /// Stops this channel. + fn request_stop(&mut self); + + /// Returns whether this channel is active or stopped. + fn is_stopped(&self) -> bool; + + /// Returns the total number of remaining transfers. + fn remaining_transfers(&mut self) -> u16; + + /// Sets the waker that is called when this channel completes. + fn set_waker(&mut self, waker: &Waker); + } } -pub trait Channel: sealed::Channel { - type ReadFuture<'a>: Future + 'a - where - Self: 'a; - - type WriteFuture<'a>: Future + 'a - where - Self: 'a; - - fn read<'a>( - &'a mut self, - request: Request, - src: *mut u8, - buf: &'a mut [u8], - ) -> Self::ReadFuture<'a>; - - fn write<'a>( - &'a mut self, - request: Request, - buf: &'a [u8], - dst: *mut u8, - ) -> Self::WriteFuture<'a>; - - fn write_x<'a>( - &'a mut self, - request: Request, - word: &u8, - num: usize, - dst: *mut u8, - ) -> Self::WriteFuture<'a>; - - /// Stops this channel. - fn stop<'a>(&'a mut self); - /// Returns whether this channel is active or stopped. - fn is_stopped<'a>(&self) -> bool; - /// Returns the total number of remaining transfers . - fn remaining_transfers<'a>(&'a mut self) -> u16; - /// Sets the waker that is called when this channel completes/ - fn set_waker(&mut self, waker: &Waker); - /// Starts this channel. - fn start<'a>(&'a mut self, request: Request, buf: &'a [u8], dst: *mut u8); +pub enum WordSize { + OneByte, + TwoBytes, + FourBytes, } +pub trait Word { + fn bits() -> WordSize; +} + +impl Word for u8 { + fn bits() -> WordSize { + WordSize::OneByte + } +} + +impl Word for u16 { + fn bits() -> WordSize { + WordSize::TwoBytes + } +} +impl Word for u32 { + fn bits() -> WordSize { + WordSize::FourBytes + } +} + +mod transfers { + use core::task::Poll; + + use super::Channel; + use embassy_hal_common::{drop::OnDrop, unborrow}; + use futures::future::poll_fn; + + use super::*; + + #[allow(unused)] + pub async fn read<'a, W: Word>( + channel: &mut impl Unborrow, + request: Request, + reg_addr: *mut u32, + buf: &'a mut [W], + ) { + assert!(buf.len() <= 0xFFFF); + let drop_clone = unsafe { channel.unborrow() }; + unborrow!(channel); + + channel.request_stop(); + let on_drop = OnDrop::new({ + let mut channel = drop_clone; + move || { + channel.request_stop(); + } + }); + + unsafe { channel.start_read::(request, reg_addr, buf) }; + wait_for_stopped(&mut channel).await; + drop(on_drop) + } + + #[allow(unused)] + pub async fn write<'a, W: Word>( + channel: &mut impl Unborrow, + request: Request, + buf: &'a [W], + reg_addr: *mut u32, + ) { + assert!(buf.len() <= 0xFFFF); + let drop_clone = unsafe { channel.unborrow() }; + unborrow!(channel); + + channel.request_stop(); + let on_drop = OnDrop::new({ + let mut channel = drop_clone; + move || { + channel.request_stop(); + } + }); + + unsafe { channel.start_write::(request, buf, reg_addr) }; + wait_for_stopped(&mut channel).await; + drop(on_drop) + } + + #[allow(unused)] + pub async fn write_repeated( + channel: &mut impl Unborrow, + request: Request, + repeated: W, + count: usize, + reg_addr: *mut u32, + ) { + let drop_clone = unsafe { channel.unborrow() }; + unborrow!(channel); + + channel.request_stop(); + let on_drop = OnDrop::new({ + let mut channel = drop_clone; + move || { + channel.request_stop(); + } + }); + + unsafe { channel.start_write_repeated::(request, repeated, count, reg_addr) }; + wait_for_stopped(&mut channel).await; + drop(on_drop) + } + + async fn wait_for_stopped(channel: &mut impl Unborrow) { + unborrow!(channel); + poll_fn(move |cx| { + channel.set_waker(cx.waker()); + + // TODO in the future, error checking could be added so that this function returns an error + + if channel.is_stopped() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await + } +} + +pub trait Channel: sealed::Channel + Unborrow {} pub struct NoDma; diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 11c8ab975..af3ac74bc 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -415,10 +415,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_tcie(true); } }); - let dst = regs.txdr().ptr() as *mut u8; + let dst = regs.txdr().ptr() as *mut u32; let ch = &mut self.tx_dma; - ch.write(ch.request(), bytes, dst) + let request = ch.request(); + crate::dma::write(ch, request, bytes, dst) }; let state_number = T::state_number(); @@ -507,10 +508,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_rxdmaen(true); w.set_tcie(true); }); - let src = regs.rxdr().ptr() as *mut u8; + let src = regs.rxdr().ptr() as *mut u32; let ch = &mut self.rx_dma; - ch.read(ch.request(), src, buffer) + let request = ch.request(); + crate::dma::read(ch, request, src, buffer) }; let state_number = T::state_number(); diff --git a/embassy-stm32/src/spi/v1.rs b/embassy-stm32/src/spi/v1.rs index 92449ea80..98f05770b 100644 --- a/embassy-stm32/src/spi/v1.rs +++ b/embassy-stm32/src/spi/v1.rs @@ -20,7 +20,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let request = self.txdma.request(); let dst = T::regs().tx_ptr(); - let f = self.txdma.write(request, write, dst); + let f = crate::dma::write(&mut self.txdma, request, write, dst); unsafe { T::regs().cr2().modify(|reg| { @@ -54,14 +54,18 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let rx_request = self.rxdma.request(); let rx_src = T::regs().rx_ptr(); - let rx_f = self.rxdma.read(rx_request, rx_src, read); + let rx_f = crate::dma::read(&mut self.rxdma, rx_request, rx_src, read); let tx_request = self.txdma.request(); let tx_dst = T::regs().tx_ptr(); - let clock_byte = 0x00; - let tx_f = self - .txdma - .write_x(tx_request, &clock_byte, clock_byte_count, tx_dst); + let clock_byte = 0x00u8; + let tx_f = crate::dma::write_repeated( + &mut self.txdma, + tx_request, + clock_byte, + clock_byte_count, + tx_dst, + ); unsafe { T::regs().cr2().modify(|reg| { @@ -110,13 +114,16 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let rx_request = self.rxdma.request(); let rx_src = T::regs().rx_ptr(); - let rx_f = self - .rxdma - .read(rx_request, rx_src, &mut read[0..write.len()]); + let rx_f = crate::dma::read( + &mut self.rxdma, + rx_request, + rx_src, + &mut read[0..write.len()], + ); let tx_request = self.txdma.request(); let tx_dst = T::regs().tx_ptr(); - let tx_f = self.txdma.write(tx_request, write, tx_dst); + let tx_f = crate::dma::write(&mut self.txdma, tx_request, write, tx_dst); unsafe { T::regs().cr2().modify(|reg| { diff --git a/embassy-stm32/src/spi/v2.rs b/embassy-stm32/src/spi/v2.rs index e24710dfe..286976329 100644 --- a/embassy-stm32/src/spi/v2.rs +++ b/embassy-stm32/src/spi/v2.rs @@ -24,7 +24,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let request = self.txdma.request(); let dst = T::regs().tx_ptr(); - let f = self.txdma.write(request, write, dst); + let f = crate::dma::write(&mut self.txdma, request, write, dst); unsafe { T::regs().cr2().modify(|reg| { @@ -67,14 +67,18 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let rx_request = self.rxdma.request(); let rx_src = T::regs().rx_ptr(); - let rx_f = self.rxdma.read(rx_request, rx_src, read); + let rx_f = crate::dma::read(&mut self.rxdma, rx_request, rx_src, read); let tx_request = self.txdma.request(); let tx_dst = T::regs().tx_ptr(); - let clock_byte = 0x00; - let tx_f = self - .txdma - .write_x(tx_request, &clock_byte, clock_byte_count, tx_dst); + let clock_byte = 0x00u8; + let tx_f = crate::dma::write_repeated( + &mut self.txdma, + tx_request, + clock_byte, + clock_byte_count, + tx_dst, + ); unsafe { T::regs().cr2().modify(|reg| { @@ -128,13 +132,16 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let rx_request = self.rxdma.request(); let rx_src = T::regs().rx_ptr(); - let rx_f = self - .rxdma - .read(rx_request, rx_src, &mut read[0..write.len()]); + let rx_f = crate::dma::read( + &mut self.rxdma, + rx_request, + rx_src, + &mut read[0..write.len()], + ); let tx_request = self.txdma.request(); let tx_dst = T::regs().tx_ptr(); - let tx_f = self.txdma.write(tx_request, write, tx_dst); + let tx_f = crate::dma::write(&mut self.txdma, tx_request, write, tx_dst); unsafe { T::regs().cr2().modify(|reg| { diff --git a/embassy-stm32/src/spi/v3.rs b/embassy-stm32/src/spi/v3.rs index 6d75de035..c31415a2f 100644 --- a/embassy-stm32/src/spi/v3.rs +++ b/embassy-stm32/src/spi/v3.rs @@ -24,7 +24,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let request = self.txdma.request(); let dst = T::regs().tx_ptr(); - let f = self.txdma.write(request, write, dst); + let f = crate::dma::write(&mut self.txdma, request, write, dst); unsafe { T::regs().cfg1().modify(|reg| { @@ -70,14 +70,18 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let rx_request = self.rxdma.request(); let rx_src = T::regs().rx_ptr(); - let rx_f = self.rxdma.read(rx_request, rx_src, read); + let rx_f = crate::dma::read(&mut self.rxdma, rx_request, rx_src, read); let tx_request = self.txdma.request(); let tx_dst = T::regs().tx_ptr(); - let clock_byte = 0x00; - let tx_f = self - .txdma - .write_x(tx_request, &clock_byte, clock_byte_count, tx_dst); + let clock_byte = 0x00u8; + let tx_f = crate::dma::write_repeated( + &mut self.txdma, + tx_request, + clock_byte, + clock_byte_count, + tx_dst, + ); unsafe { T::regs().cfg1().modify(|reg| { @@ -132,13 +136,16 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let rx_request = self.rxdma.request(); let rx_src = T::regs().rx_ptr(); - let rx_f = self - .rxdma - .read(rx_request, rx_src, &mut read[0..write.len()]); + let rx_f = crate::dma::read( + &mut self.rxdma, + rx_request, + rx_src, + &mut read[0..write.len()], + ); let tx_request = self.txdma.request(); let tx_dst = T::regs().tx_ptr(); - let tx_f = self.txdma.write(tx_request, write, tx_dst); + let tx_f = crate::dma::write(&mut self.txdma, tx_request, write, tx_dst); unsafe { T::regs().cfg1().modify(|reg| { diff --git a/embassy-stm32/src/usart/v1.rs b/embassy-stm32/src/usart/v1.rs index 8cd392d18..3a8c1d340 100644 --- a/embassy-stm32/src/usart/v1.rs +++ b/embassy-stm32/src/usart/v1.rs @@ -70,14 +70,15 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { TxDma: crate::usart::TxDma, { let ch = &mut self.tx_dma; + let request = ch.request(); unsafe { self.inner.regs().cr3().modify(|reg| { reg.set_dmat(true); }); } let r = self.inner.regs(); - let dst = r.dr().ptr() as *mut u8; - ch.write(ch.request(), buffer, dst).await; + let dst = r.dr().ptr() as *mut u32; + crate::dma::write(ch, request, buffer, dst).await; Ok(()) } @@ -86,14 +87,15 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { RxDma: crate::usart::RxDma, { let ch = &mut self.rx_dma; + let request = ch.request(); unsafe { self.inner.regs().cr3().modify(|reg| { reg.set_dmar(true); }); } let r = self.inner.regs(); - let src = r.dr().ptr() as *mut u8; - ch.read(ch.request(), src, buffer).await; + let src = r.dr().ptr() as *mut u32; + crate::dma::read(ch, request, src, buffer).await; Ok(()) } diff --git a/embassy-stm32/src/usart/v2.rs b/embassy-stm32/src/usart/v2.rs index e6e5f69db..cd70afec7 100644 --- a/embassy-stm32/src/usart/v2.rs +++ b/embassy-stm32/src/usart/v2.rs @@ -80,14 +80,15 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { TxDma: crate::usart::TxDma, { let ch = &mut self.tx_dma; + let request = ch.request(); unsafe { self.inner.regs().cr3().modify(|reg| { reg.set_dmat(true); }); } let r = self.inner.regs(); - let dst = r.tdr().ptr() as *mut u8; - ch.write(ch.request(), buffer, dst).await; + let dst = r.tdr().ptr() as *mut u32; + crate::dma::write(ch, request, buffer, dst).await; Ok(()) } @@ -96,14 +97,16 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { RxDma: crate::usart::RxDma, { let ch = &mut self.rx_dma; + let request = ch.request(); unsafe { self.inner.regs().cr3().modify(|reg| { reg.set_dmar(true); }); } let r = self.inner.regs(); - let src = r.rdr().ptr() as *mut u8; - ch.read(ch.request(), src, buffer).await; + let src = r.rdr().ptr() as *mut u32; + + crate::dma::read(ch, request, src, buffer).await; Ok(()) }