From 3272987d9281590b0326f0ce6b577ad6f24d06bb Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Tue, 28 Sep 2021 15:59:11 -0700 Subject: [PATCH 01/11] Expand channel trait --- embassy-stm32/src/dma/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 60f6a3020..0ecd6d864 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -9,6 +9,7 @@ mod dmamux; pub use dmamux::*; use core::future::Future; +use core::task::Waker; use embassy::util::Unborrow; #[cfg(any(bdma_v2, dma_v2, dmamux))] @@ -50,6 +51,12 @@ pub trait Channel: sealed::Channel { num: usize, dst: *mut u8, ) -> Self::WriteFuture<'a>; + + fn stop<'a>(&'a mut self); + + fn is_stopped<'a>(&self) -> bool; + fn remaining_transfers<'a>(&'a mut self) -> usize; + fn set_waker(&mut self, waker: &Waker); } pub struct NoDma; From 2d2c6d0e01bc21b2207bb629030809be35ed4235 Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Tue, 28 Sep 2021 17:37:39 -0700 Subject: [PATCH 02/11] Implement extended Channel trait to dma.rs --- embassy-stm32/src/dma/dma.rs | 64 +++++++++++++++++++++++++++++++----- embassy-stm32/src/dma/mod.rs | 2 +- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index ec5ac98a0..754685c24 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -1,6 +1,6 @@ use core::future::Future; use core::sync::atomic::{fence, Ordering}; -use core::task::Poll; +use core::task::{Poll, Waker}; use embassy::interrupt::{Interrupt, InterruptExt}; use embassy::waitqueue::AtomicWaker; @@ -61,14 +61,7 @@ pub(crate) unsafe fn do_transfer( let ch = dma.st(channel_number as _); let on_drop = OnDrop::new(move || unsafe { - // 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); + _stop(&dma, channel_number); }); #[cfg(dmamux)] @@ -125,6 +118,47 @@ pub(crate) unsafe fn do_transfer( } } +/// 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 @@ -251,6 +285,18 @@ pac::dma_channels! { ) } } + 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 )} + } } }; } diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 0ecd6d864..aa2da1fa9 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -55,7 +55,7 @@ pub trait Channel: sealed::Channel { fn stop<'a>(&'a mut self); fn is_stopped<'a>(&self) -> bool; - fn remaining_transfers<'a>(&'a mut self) -> usize; + fn remaining_transfers<'a>(&'a mut self) -> u16; fn set_waker(&mut self, waker: &Waker); } From 3411039cb9ff832b7db5a750854167831fd8f120 Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Tue, 28 Sep 2021 19:33:40 -0700 Subject: [PATCH 03/11] Implement extended Channel trait to bdma.rs --- embassy-stm32/src/dma/bdma.rs | 57 +++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 35c0b3ee7..d6941f335 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -2,7 +2,7 @@ use core::future::Future; use core::sync::atomic::{fence, Ordering}; -use core::task::Poll; +use core::task::{Poll, Waker}; use embassy::interrupt::{Interrupt, InterruptExt}; use embassy::waitqueue::AtomicWaker; @@ -58,14 +58,7 @@ pub(crate) unsafe fn do_transfer( }); let on_drop = OnDrop::new(move || unsafe { - // 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); + _stop(dma , channel_number); }); #[cfg(dmamux)] @@ -118,6 +111,40 @@ pub(crate) unsafe fn do_transfer( } } +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 @@ -245,6 +272,18 @@ pac::dma_channels! { ) } } + 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 )} + } } }; } From 93e047ede28c387d7eb539dbc06b8a660ac527db Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Tue, 28 Sep 2021 19:35:07 -0700 Subject: [PATCH 04/11] cargo fmt --- embassy-stm32/src/dma/bdma.rs | 11 +++++------ embassy-stm32/src/dma/dma.rs | 8 +++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index d6941f335..c86999bea 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -58,7 +58,7 @@ pub(crate) unsafe fn do_transfer( }); let on_drop = OnDrop::new(move || unsafe { - _stop(dma , channel_number); + _stop(dma, channel_number); }); #[cfg(dmamux)] @@ -111,7 +111,7 @@ pub(crate) unsafe fn do_transfer( } } -unsafe fn _stop(dma:pac::bdma::Dma, ch: u8) { +unsafe fn _stop(dma: pac::bdma::Dma, ch: u8) { let ch = dma.ch(ch as _); // Disable the channel and interrupts with the default value. @@ -124,14 +124,14 @@ unsafe fn _stop(dma:pac::bdma::Dma, ch: u8) { fence(Ordering::Acquire); } -unsafe fn _is_stopped(dma:pac::bdma::Dma, ch: u8) -> bool { +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{ +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. @@ -139,10 +139,9 @@ unsafe fn _get_remaining_transfers(dma: pac::bdma::Dma, ch: u8) -> u16{ } /// Sets the waker for the specified DMA channel -unsafe fn _set_waker(dma: pac::bdma::Dma, state_number: u8, waker: &Waker){ +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 { diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 754685c24..672e632f8 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -134,7 +134,7 @@ unsafe fn _stop(dma: &pac::dma::Dma, ch: u8) { } /// Gets the running status of the channel -unsafe fn _is_stopped(dma: &pac::dma::Dma, ch: u8) -> bool{ +unsafe fn _is_stopped(dma: &pac::dma::Dma, ch: u8) -> bool { // get a handle on the channel itself let ch = dma.st(ch as _); @@ -144,7 +144,7 @@ unsafe fn _is_stopped(dma: &pac::dma::Dma, ch: u8) -> bool{ /// 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{ +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. @@ -152,13 +152,11 @@ unsafe fn _get_remaining_transfers(dma: &pac::dma::Dma, ch: u8) -> u16{ } /// Sets the waker for the specified DMA channel -unsafe fn _set_waker(dma: &pac::dma::Dma, state_number: u8, waker: &Waker){ +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 From e2719aba104b7ae623c141aa035f1a84e7d0dc42 Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Wed, 29 Sep 2021 18:19:01 -0700 Subject: [PATCH 05/11] Further extend the dma channel trait --- embassy-stm32/src/dma/bdma.rs | 2 +- embassy-stm32/src/dma/dma.rs | 118 ++++++++++++++++++++++++---------- embassy-stm32/src/dma/mod.rs | 7 +- 3 files changed, 92 insertions(+), 35 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index c86999bea..528e49f42 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -139,7 +139,7 @@ unsafe fn _get_remaining_transfers(dma: pac::bdma::Dma, ch: u8) -> u16 { } /// Sets the waker for the specified DMA channel -unsafe fn _set_waker(dma: pac::bdma::Dma, state_number: u8, waker: &Waker) { +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); } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 672e632f8..257f21a73 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -53,10 +53,7 @@ pub(crate) unsafe fn do_transfer( // Reset status let isrn = channel_number as usize / 4; let isrbit = channel_number as usize % 4; - dma.ifcr(isrn).write(|w| { - w.set_tcif(isrbit, true); - w.set_teif(isrbit, true); - }); + _reset_status(&dma, isrn, isrbit); let ch = dma.st(channel_number as _); @@ -64,37 +61,23 @@ pub(crate) unsafe fn do_transfer( _stop(&dma, channel_number); }); - #[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); - unsafe { - 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); - }); - } + // 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| { @@ -118,6 +101,55 @@ pub(crate) unsafe fn do_transfer( } } +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 @@ -152,7 +184,7 @@ unsafe fn _get_remaining_transfers(dma: &pac::dma::Dma, ch: u8) -> u16 { } /// Sets the waker for the specified DMA channel -unsafe fn _set_waker(dma: &pac::dma::Dma, state_number: u8, waker: &Waker) { +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); } @@ -295,6 +327,26 @@ pac::dma_channels! { 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 { + 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, + ) + } + } } }; } diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index aa2da1fa9..48b229c2e 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -52,11 +52,16 @@ pub trait Channel: sealed::Channel { 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 struct NoDma; From b2910558d36929dfde056f609a8af23d925e67f6 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Fri, 19 Nov 2021 19:15:55 +0100 Subject: [PATCH 06/11] 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(()) } From fd2fe62b5f6f5c9b8dceef33ffe3fd8c58eb2bec Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Dec 2021 01:51:39 +0100 Subject: [PATCH 07/11] stm32/dma: rename is_stopped to is_running. Note that this does NOT invert the result of `en()` because it was wrong before. --- embassy-stm32/src/dma/bdma.rs | 6 +++--- embassy-stm32/src/dma/dma.rs | 6 +++--- embassy-stm32/src/dma/mod.rs | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 1dcd0c228..a33543f76 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -155,8 +155,8 @@ pac::dma_channels! { 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 is_running(&self) -> bool { + unsafe {low_level_api::is_running(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)} @@ -240,7 +240,7 @@ mod low_level_api { fence(Ordering::SeqCst); } - pub unsafe fn is_stopped(dma: pac::bdma::Dma, ch: u8) -> bool { + pub unsafe fn is_running(dma: pac::bdma::Dma, ch: u8) -> bool { let ch = dma.ch(ch as _); ch.cr().read().en() } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index c99ff27ba..a5a959df4 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -149,8 +149,8 @@ pac::dma_channels! { 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 is_running(&self) -> bool { + unsafe {low_level_api::is_running(&crate::pac::$dma_peri, $channel_num)} } fn remaining_transfers(&mut self) -> u16 { @@ -240,7 +240,7 @@ mod low_level_api { } /// Gets the running status of the channel - pub unsafe fn is_stopped(dma: &pac::dma::Dma, ch: u8) -> bool { + pub unsafe fn is_running(dma: &pac::dma::Dma, ch: u8) -> bool { // get a handle on the channel itself let ch = dma.st(ch as _); diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 3928bbb92..f27a55f61 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -49,7 +49,7 @@ pub(crate) mod sealed { fn request_stop(&mut self); /// Returns whether this channel is active or stopped. - fn is_stopped(&self) -> bool; + fn is_running(&self) -> bool; /// Returns the total number of remaining transfers. fn remaining_transfers(&mut self) -> u16; @@ -173,10 +173,10 @@ mod transfers { // TODO in the future, error checking could be added so that this function returns an error - if channel.is_stopped() { - Poll::Ready(()) - } else { + if channel.is_running() { Poll::Pending + } else { + Poll::Ready(()) } }) .await From 4e349d0f5d79500d7918f62182c577a3c8797267 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Dec 2021 01:54:31 +0100 Subject: [PATCH 08/11] stm32/dma: use the right waker slot number for DMA2 (must add 8) --- embassy-stm32/src/dma/bdma.rs | 7 +++---- embassy-stm32/src/dma/dma.rs | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index a33543f76..3d06b6351 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -163,7 +163,7 @@ pac::dma_channels! { } fn set_waker(&mut self, waker: &Waker) { - unsafe {low_level_api::set_waker(crate::pac::$dma_peri, $channel_num, waker )} + unsafe {low_level_api::set_waker(dma_num!($dma_peri) * 8 + $channel_num, waker )} } } @@ -255,9 +255,8 @@ mod low_level_api { } /// 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 set_waker(state_number: usize, waker: &Waker) { + STATE.ch_wakers[state_number].register(waker); } pub unsafe fn reset_status(dma: pac::bdma::Dma, channel_number: u8) { diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index a5a959df4..baed24603 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -158,7 +158,7 @@ pac::dma_channels! { } fn set_waker(&mut self, waker: &Waker) { - unsafe {low_level_api::set_waker(&crate::pac::$dma_peri, $channel_num, waker )} + unsafe {low_level_api::set_waker(dma_num!($dma_peri) * 8 + $channel_num, waker )} } } @@ -258,9 +258,8 @@ mod low_level_api { } /// 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 set_waker(state_number: usize, waker: &Waker) { + STATE.ch_wakers[state_number].register(waker); } pub unsafe fn reset_status(dma: &crate::pac::dma::Dma, isrn: usize, isrbit: usize) { From 6179da6b9c3c6f20f0b0c1da8350cacb1ebb25d2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Dec 2021 03:04:39 +0100 Subject: [PATCH 09/11] stm32/dma: eagerly start transfers when calling the functions. `async fn`s do nothing until polled, but we want the DMA transfer to immediately start in this case. Drivers rely on it. Some require special orders, such as "start DMA, start SPI, then wait for DMA" which is awkward to do without eager start. Also use a manually-impl'd future, this allows getting rid of the "double" Unborrow channel clone. --- embassy-stm32/src/dma/mod.rs | 107 ++++++++++++++++------------------- 1 file changed, 49 insertions(+), 58 deletions(-) diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index f27a55f61..5e4b5d3de 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -8,7 +8,13 @@ mod dmamux; #[cfg(dmamux)] pub use dmamux::*; +use core::future::Future; +use core::marker::PhantomData; +use core::pin::Pin; +use core::task::Waker; +use core::task::{Context, Poll}; use embassy::util::Unborrow; +use embassy_hal_common::unborrow; #[cfg(feature = "unstable-pac")] pub use transfers::*; @@ -23,7 +29,7 @@ pub type Request = (); pub(crate) mod sealed { 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); @@ -86,104 +92,89 @@ impl Word for u32 { } 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, + pub fn read<'a, W: Word>( + channel: impl Unborrow + 'a, request: Request, reg_addr: *mut u32, buf: &'a mut [W], - ) { + ) -> impl Future + 'a { 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) + + Transfer { + channel, + _phantom: PhantomData, + } } #[allow(unused)] - pub async fn write<'a, W: Word>( - channel: &mut impl Unborrow, + pub fn write<'a, W: Word>( + channel: impl Unborrow + 'a, request: Request, buf: &'a [W], reg_addr: *mut u32, - ) { + ) -> impl Future + 'a { 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) + + Transfer { + channel, + _phantom: PhantomData, + } } #[allow(unused)] - pub async fn write_repeated( - channel: &mut impl Unborrow, + pub fn write_repeated<'a, W: Word>( + channel: impl Unborrow + 'a, request: Request, repeated: W, count: usize, reg_addr: *mut u32, - ) { - let drop_clone = unsafe { channel.unborrow() }; + ) -> impl Future + 'a { 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) + + Transfer { + channel, + _phantom: PhantomData, + } } - async fn wait_for_stopped(channel: &mut impl Unborrow) { - unborrow!(channel); - poll_fn(move |cx| { - channel.set_waker(cx.waker()); + struct Transfer<'a, C: Channel> { + channel: C, + _phantom: PhantomData<&'a mut C>, + } - // TODO in the future, error checking could be added so that this function returns an error + impl<'a, C: Channel> Drop for Transfer<'a, C> { + fn drop(&mut self) { + self.channel.request_stop(); + while self.channel.is_running() {} + } + } - if channel.is_running() { + impl<'a, C: Channel> Unpin for Transfer<'a, C> {} + impl<'a, C: Channel> Future for Transfer<'a, C> { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.channel.set_waker(cx.waker()); + if self.channel.is_running() { Poll::Pending } else { Poll::Ready(()) } - }) - .await + } } } -pub trait Channel: sealed::Channel + Unborrow {} +pub trait Channel: sealed::Channel + Unborrow + 'static {} pub struct NoDma; From b316d2620c73f7e2a5cd3c46dbc20c24b7a1c57f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Dec 2021 03:18:15 +0100 Subject: [PATCH 10/11] stm32/dma: improve trait docs, seal Word. --- embassy-stm32/src/dma/bdma.rs | 6 ++--- embassy-stm32/src/dma/dma.rs | 6 ++--- embassy-stm32/src/dma/mod.rs | 50 ++++++++++++++++++++++++++--------- embassy-stm32/src/i2c/v2.rs | 4 +-- embassy-stm32/src/usart/v1.rs | 4 +-- embassy-stm32/src/usart/v2.rs | 4 +-- 6 files changed, 50 insertions(+), 24 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 3d06b6351..29c9fd592 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -89,7 +89,7 @@ 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 { - unsafe fn start_write(&mut self, request: Request, buf: &[W], reg_addr: *mut u32) { + unsafe fn start_write(&mut self, request: Request, buf: &[W], reg_addr: *mut W) { low_level_api::reset_status(crate::pac::$dma_peri, $channel_num); low_level_api::start_transfer( crate::pac::$dma_peri, @@ -110,7 +110,7 @@ pac::dma_channels! { } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut u32) { + unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut W) { let buf = [repeated]; low_level_api::reset_status(crate::pac::$dma_peri, $channel_num); low_level_api::start_transfer( @@ -131,7 +131,7 @@ pac::dma_channels! { ) } - unsafe fn start_read(&mut self, request: Request, reg_addr: *mut u32, buf: &mut [W]) { + unsafe fn start_read(&mut self, request: Request, reg_addr: *mut W, buf: &mut [W]) { low_level_api::reset_status(crate::pac::$dma_peri, $channel_num); low_level_api::start_transfer( crate::pac::$dma_peri, diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index baed24603..05d62ef8f 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -84,7 +84,7 @@ 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 { - unsafe fn start_write(&mut self, request: Request, buf: &[W], reg_addr: *mut u32) { + unsafe fn start_write(&mut self, request: Request, buf: &[W], reg_addr: *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); @@ -104,7 +104,7 @@ pac::dma_channels! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut u32) { + unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut W) { let buf = [repeated]; let isrn = $channel_num as usize / 4; let isrbit = $channel_num as usize % 4; @@ -125,7 +125,7 @@ pac::dma_channels! { ) } - unsafe fn start_read(&mut self, request: Request, reg_addr: *mut u32, buf: &mut [W]) { + unsafe fn start_read(&mut self, request: Request, reg_addr: *mut W, 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); diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 5e4b5d3de..accc55653 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -30,37 +30,59 @@ pub type Request = (); pub(crate) mod sealed { use super::*; + pub trait Word {} + 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); + /// + /// Safety: + /// - `buf` must be alive for the entire duration of the DMA transfer. + /// - `reg_addr` must be a valid peripheral register address to write to. + unsafe fn start_write( + &mut self, + request: Request, + buf: &[W], + reg_addr: *mut W, + ); /// Starts this channel for writing a word repeatedly. - unsafe fn start_write_repeated( + /// + /// Safety: + /// - `reg_addr` must be a valid peripheral register address to write to. + unsafe fn start_write_repeated( &mut self, request: Request, repeated: W, count: usize, - reg_addr: *mut u32, + reg_addr: *mut W, ); /// Starts this channel for reading a stream of words. - unsafe fn start_read( + /// + /// Safety: + /// - `buf` must be alive for the entire duration of the DMA transfer. + /// - `reg_addr` must be a valid peripheral register address to write to. + unsafe fn start_read( &mut self, request: Request, - reg_addr: *mut u32, + reg_addr: *mut W, buf: &mut [W], ); - /// Stops this channel. + /// Requests the channel to stop. + /// NOTE: The channel does not immediately stop, you have to wait + /// for `is_running() = false`. fn request_stop(&mut self); - /// Returns whether this channel is active or stopped. + /// Returns whether this channel is running or stopped. + /// + /// The channel stops running when it either completes or is manually stopped. fn is_running(&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. + /// Sets the waker that is called when this channel stops (either completed or manually stopped) fn set_waker(&mut self, waker: &Waker); } } @@ -70,21 +92,25 @@ pub enum WordSize { TwoBytes, FourBytes, } -pub trait Word { +pub trait Word: sealed::Word { fn bits() -> WordSize; } +impl sealed::Word for u8 {} impl Word for u8 { fn bits() -> WordSize { WordSize::OneByte } } +impl sealed::Word for u16 {} impl Word for u16 { fn bits() -> WordSize { WordSize::TwoBytes } } + +impl sealed::Word for u32 {} impl Word for u32 { fn bits() -> WordSize { WordSize::FourBytes @@ -98,7 +124,7 @@ mod transfers { pub fn read<'a, W: Word>( channel: impl Unborrow + 'a, request: Request, - reg_addr: *mut u32, + reg_addr: *mut W, buf: &'a mut [W], ) -> impl Future + 'a { assert!(buf.len() <= 0xFFFF); @@ -117,7 +143,7 @@ mod transfers { channel: impl Unborrow + 'a, request: Request, buf: &'a [W], - reg_addr: *mut u32, + reg_addr: *mut W, ) -> impl Future + 'a { assert!(buf.len() <= 0xFFFF); unborrow!(channel); @@ -136,7 +162,7 @@ mod transfers { request: Request, repeated: W, count: usize, - reg_addr: *mut u32, + reg_addr: *mut W, ) -> impl Future + 'a { unborrow!(channel); diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index af3ac74bc..94c4d9a7c 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -415,7 +415,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_tcie(true); } }); - let dst = regs.txdr().ptr() as *mut u32; + let dst = regs.txdr().ptr() as *mut u8; let ch = &mut self.tx_dma; let request = ch.request(); @@ -508,7 +508,7 @@ 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 u32; + let src = regs.rxdr().ptr() as *mut u8; let ch = &mut self.rx_dma; let request = ch.request(); diff --git a/embassy-stm32/src/usart/v1.rs b/embassy-stm32/src/usart/v1.rs index 3a8c1d340..10f87f2dd 100644 --- a/embassy-stm32/src/usart/v1.rs +++ b/embassy-stm32/src/usart/v1.rs @@ -77,7 +77,7 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { }); } let r = self.inner.regs(); - let dst = r.dr().ptr() as *mut u32; + let dst = r.dr().ptr() as *mut u8; crate::dma::write(ch, request, buffer, dst).await; Ok(()) } @@ -94,7 +94,7 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { }); } let r = self.inner.regs(); - let src = r.dr().ptr() as *mut u32; + let src = r.dr().ptr() as *mut u8; 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 cd70afec7..697adf459 100644 --- a/embassy-stm32/src/usart/v2.rs +++ b/embassy-stm32/src/usart/v2.rs @@ -87,7 +87,7 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { }); } let r = self.inner.regs(); - let dst = r.tdr().ptr() as *mut u32; + let dst = r.tdr().ptr() as *mut u8; crate::dma::write(ch, request, buffer, dst).await; Ok(()) } @@ -104,7 +104,7 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { }); } let r = self.inner.regs(); - let src = r.rdr().ptr() as *mut u32; + let src = r.rdr().ptr() as *mut u8; crate::dma::read(ch, request, src, buffer).await; Ok(()) From 022c4cb7397e690080edc06e2b7d4dc8e19ccb0b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Dec 2021 03:30:07 +0100 Subject: [PATCH 11/11] stm32/dma: simplify impls a bit. --- embassy-stm32/src/dma/bdma.rs | 19 ++++++------ embassy-stm32/src/dma/dma.rs | 56 +++++++++++++++++------------------ 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 29c9fd592..b4c77757e 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -59,7 +59,7 @@ unsafe fn on_irq() { let isr = pac::$dma.isr().read(); let dman = dma_num!($dma); - for chn in 0..crate::pac::dma_channels_count!($dma) { + for chn in 0..pac::dma_channels_count!($dma) { let cr = pac::$dma.ch(chn).cr(); if isr.tcif(chn) && cr.read().tcie() { cr.write(|_| ()); // Disable channel interrupts with the default value. @@ -90,9 +90,8 @@ pac::dma_channels! { impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { unsafe fn start_write(&mut self, request: Request, buf: &[W], reg_addr: *mut W) { - low_level_api::reset_status(crate::pac::$dma_peri, $channel_num); low_level_api::start_transfer( - crate::pac::$dma_peri, + pac::$dma_peri, $channel_num, #[cfg(any(bdma_v2, dmamux))] request, @@ -112,9 +111,8 @@ pac::dma_channels! { unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut W) { let buf = [repeated]; - low_level_api::reset_status(crate::pac::$dma_peri, $channel_num); low_level_api::start_transfer( - crate::pac::$dma_peri, + pac::$dma_peri, $channel_num, #[cfg(any(bdma_v2, dmamux))] request, @@ -132,9 +130,8 @@ pac::dma_channels! { } unsafe fn start_read(&mut self, request: Request, reg_addr: *mut W, buf: &mut [W]) { - low_level_api::reset_status(crate::pac::$dma_peri, $channel_num); low_level_api::start_transfer( - crate::pac::$dma_peri, + pac::$dma_peri, $channel_num, #[cfg(any(bdma_v2, dmamux))] request, @@ -152,14 +149,14 @@ pac::dma_channels! { } fn request_stop(&mut self){ - unsafe {low_level_api::request_stop(crate::pac::$dma_peri, $channel_num);} + unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);} } fn is_running(&self) -> bool { - unsafe {low_level_api::is_running(crate::pac::$dma_peri, $channel_num)} + unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)} } fn remaining_transfers(&mut self) -> u16 { - unsafe {low_level_api::get_remaining_transfers(crate::pac::$dma_peri, $channel_num)} + unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)} } fn set_waker(&mut self, waker: &Waker) { @@ -198,6 +195,8 @@ mod low_level_api { ) { let ch = dma.ch(channel_number as _); + reset_status(dma, channel_number); + #[cfg(dmamux)] super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 05d62ef8f..efe4d1d9d 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -85,17 +85,15 @@ 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 { unsafe fn start_write(&mut self, request: Request, buf: &[W], reg_addr: *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( + pac::$dma_peri, + $channel_num, 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, @@ -106,17 +104,15 @@ pac::dma_channels! { unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut W) { 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( + pac::$dma_peri, + $channel_num, 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, @@ -126,17 +122,15 @@ pac::dma_channels! { } unsafe fn start_read(&mut self, request: Request, reg_addr: *mut W, 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( + pac::$dma_peri, + $channel_num, 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, @@ -146,15 +140,15 @@ pac::dma_channels! { } fn request_stop(&mut self) { - unsafe {low_level_api::request_stop(&crate::pac::$dma_peri, $channel_num);} + unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);} } fn is_running(&self) -> bool { - unsafe {low_level_api::is_running(&crate::pac::$dma_peri, $channel_num)} + unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)} } fn remaining_transfers(&mut self) -> u16 { - unsafe {low_level_api::get_remaining_transfers(&crate::pac::$dma_peri, $channel_num)} + unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)} } fn set_waker(&mut self, waker: &Waker) { @@ -179,13 +173,14 @@ mod low_level_api { use super::*; pub unsafe fn start_transfer( + dma: pac::dma::Dma, + channel_number: u8, 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, @@ -196,6 +191,9 @@ mod low_level_api { // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::SeqCst); + reset_status(dma, channel_number); + + let ch = dma.st(channel_number as _); 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 _)); @@ -223,34 +221,31 @@ mod low_level_api { } /// 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); - + pub unsafe fn request_stop(dma: pac::dma::Dma, channel_number: u8) { // 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(|_| ()); + // Disable the channel. Keep the IEs enabled so the irqs still fire. + ch.cr().write(|w| { + w.set_teie(true); + w.set_tcie(true); + }); // "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_running(dma: &pac::dma::Dma, ch: u8) -> bool { + pub unsafe fn is_running(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. + // Get whether it's enabled (running) 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 { + 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. @@ -262,7 +257,10 @@ mod low_level_api { STATE.ch_wakers[state_number].register(waker); } - pub unsafe fn reset_status(dma: &crate::pac::dma::Dma, isrn: usize, isrbit: usize) { + pub unsafe fn reset_status(dma: pac::dma::Dma, channel_number: u8) { + let isrn = channel_number as usize / 4; + let isrbit = channel_number as usize % 4; + dma.ifcr(isrn).write(|w| { w.set_tcif(isrbit, true); w.set_teif(isrbit, true);