Refactor DMA traits.

This commit is contained in:
Matous Hybl 2021-11-19 19:15:55 +01:00 committed by Dario Nieuwenhuis
parent e2719aba10
commit b2910558d3
9 changed files with 591 additions and 559 deletions

View File

@ -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<WordSize> 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<Output = ()> {
// 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<Output = ()> + 'a;
type WriteFuture<'a> = impl Future<Output = ()> + '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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
)
}
unsafe fn start_write<W: Word>(&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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
)
}
unsafe fn start_write_repeated<W: Word>(&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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
)
}
unsafe fn start_read<W: Word>(&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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::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);
});
}
}

View File

@ -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<WordSize> 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<Output = ()> {
// 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<Output = ()> + 'a;
type WriteFuture<'a> = impl Future<Output = ()> + '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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::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<W: Word>(&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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
)
}
unsafe fn start_write_repeated<W: Word>(&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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_CH_NUM,
)
}
unsafe fn start_read<W: Word>(&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)]
<Self as super::dmamux::sealed::MuxChannel>::DMAMUX_REGS,
#[cfg(dmamux)]
<Self as super::dmamux::sealed::MuxChannel>::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);
});
}
}

View File

@ -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<W: Word>(&mut self, request: Request, buf: &[W], reg_addr: *mut u32);
/// Starts this channel for writing a word repeatedly.
unsafe fn start_write_repeated<W: Word>(
&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<W: Word>(
&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<Output = ()> + 'a
where
Self: 'a;
type WriteFuture<'a>: Future<Output = ()> + '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<Target = impl Channel>,
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::<W>(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<Target = impl Channel>,
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::<W>(request, buf, reg_addr) };
wait_for_stopped(&mut channel).await;
drop(on_drop)
}
#[allow(unused)]
pub async fn write_repeated<W: Word>(
channel: &mut impl Unborrow<Target = impl Channel>,
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::<W>(request, repeated, count, reg_addr) };
wait_for_stopped(&mut channel).await;
drop(on_drop)
}
async fn wait_for_stopped(channel: &mut impl Unborrow<Target = impl Channel>) {
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<Target = Self> {}
pub struct NoDma;

View File

@ -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();

View File

@ -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| {

View File

@ -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| {

View File

@ -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| {

View File

@ -70,14 +70,15 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
TxDma: crate::usart::TxDma<T>,
{
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<T>,
{
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(())
}

View File

@ -80,14 +80,15 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
TxDma: crate::usart::TxDma<T>,
{
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<T>,
{
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(())
}