diff --git a/esp-hal-common/src/dma/gdma.rs b/esp-hal-common/src/dma/gdma.rs index 6ef63d2a5dc..222980c37c9 100644 --- a/esp-hal-common/src/dma/gdma.rs +++ b/esp-hal-common/src/dma/gdma.rs @@ -1,426 +1,20 @@ //! Direct Memory Access -use core::{marker::PhantomData, sync::atomic::compiler_fence}; - -use crate::system::{Peripheral, PeripheralClockControl}; - -/// DMA Errors -#[derive(Debug, Clone, Copy)] -pub enum DmaError { - InvalidAlignment, - OutOfDescriptors, - InvalidDescriptorSize, - DescriptorError, -} - -/// DMA Priorities -pub enum DmaPriority { - Priority0 = 0, - Priority1 = 1, - Priority2 = 2, - Priority3 = 3, - Priority4 = 4, - Priority5 = 5, - Priority6 = 6, - Priority7 = 7, - Priority8 = 8, - Priority9 = 9, - Priority10 = 10, - Priority11 = 11, - Priority12 = 12, - Priority13 = 13, - Priority14 = 14, - Priority15 = 15, -} - -enum Owner { - Cpu = 0, - Dma = 1, -} - -impl From for Owner { - fn from(value: u32) -> Self { - match value { - 0 => Owner::Cpu, - _ => Owner::Dma, - } - } -} - -trait DmaLinkedListDw0 { - fn set_size(&mut self, len: u16); - fn get_size(&mut self) -> u16; - fn set_length(&mut self, len: u16); - fn get_length(&mut self) -> u16; - fn set_err_eof(&mut self, err_eof: bool); - fn get_err_eof(&mut self) -> bool; - fn set_suc_eof(&mut self, suc_eof: bool); - fn get_suc_eof(&mut self) -> bool; - fn set_owner(&mut self, owner: Owner); - fn get_owner(&mut self) -> Owner; -} - -impl DmaLinkedListDw0 for &mut u32 { - fn set_size(&mut self, len: u16) { - let mask = 0b111111111111; - let bit_s = 0; - **self = (**self & !(mask << bit_s)) | (len as u32) << bit_s; - } - - fn get_size(&mut self) -> u16 { - let mask = 0b111111111111; - let bit_s = 0; - ((**self & (mask << bit_s)) >> bit_s) as u16 - } - - fn set_length(&mut self, len: u16) { - let mask = 0b111111111111; - let bit_s = 12; - **self = (**self & !(mask << bit_s)) | (len as u32) << bit_s; - } - - fn get_length(&mut self) -> u16 { - let mask = 0b111111111111; - let bit_s = 12; - ((**self & (mask << bit_s)) >> bit_s) as u16 - } - - fn set_err_eof(&mut self, err_eof: bool) { - let mask = 0b1; - let bit_s = 28; - **self = (**self & !(mask << bit_s)) | (err_eof as u32) << bit_s; - } - - fn get_err_eof(&mut self) -> bool { - let mask = 0b1; - let bit_s = 28; - ((**self & (mask << bit_s)) >> bit_s) != 0 - } - - fn set_suc_eof(&mut self, suc_eof: bool) { - let mask = 0b1; - let bit_s = 30; - **self = (**self & !(mask << bit_s)) | (suc_eof as u32) << bit_s; - } - - fn get_suc_eof(&mut self) -> bool { - let mask = 0b1; - let bit_s = 30; - ((**self & (mask << bit_s)) >> bit_s) != 0 - } - - fn set_owner(&mut self, owner: Owner) { - let mask = 0b1; - let bit_s = 31; - **self = (**self & !(mask << bit_s)) | (owner as u32) << bit_s; - } - - fn get_owner(&mut self) -> Owner { - let mask = 0b1; - let bit_s = 31; - ((**self & (mask << bit_s)) >> bit_s).into() - } -} - -/// DMA capable peripherals -pub enum DmaPeripheral { - Spi2 = 0, - Uhci0 = 2, - I2s = 3, - Aes = 6, - Sha = 7, - Adc = 8, -} - -/// DMA Rx -/// -/// The functions here are not meant to be used outside the HAL and will be -/// hidden/inaccessible in future. -pub trait Rx { - fn init(&mut self, burst_mode: bool, priority: DmaPriority); - fn prepare_transfer( - &mut self, - peri: DmaPeripheral, - data: *mut u8, - len: usize, - ) -> Result<(), DmaError>; - fn is_done(&mut self) -> bool; -} - -pub(crate) trait RxChannel -where - R: RegisterAccess, -{ - fn init(&mut self, burst_mode: bool, priority: DmaPriority) { - R::set_in_burstmode(burst_mode); - R::set_in_priority(priority); - } - - fn prepare_transfer( - &mut self, - descriptors: &mut [u32], - peri: DmaPeripheral, - data: *mut u8, - len: usize, - ) -> Result<(), DmaError> { - for descr in descriptors.iter_mut() { - *descr = 0; - } - - compiler_fence(core::sync::atomic::Ordering::SeqCst); - - let mut processed = 0; - let mut descr = 0; - loop { - let chunk_size = usize::min(4092, len - processed); - let last = processed + chunk_size >= len; - - descriptors[descr + 1] = data as u32 + processed as u32; - - let mut dw0 = &mut descriptors[descr]; - - dw0.set_suc_eof(last); - dw0.set_owner(Owner::Dma); - dw0.set_size(chunk_size as u16); // align to 32 bits? - dw0.set_length(0); // actual size of the data!? - - if !last { - descriptors[descr + 2] = (&descriptors[descr + 3]) as *const _ as *const () as u32; - } else { - descriptors[descr + 2] = 0; - } - - processed += chunk_size; - descr += 3; - - if processed >= len { - break; - } - } - - R::clear_in_interrupts(); - R::reset_in(); - R::set_in_descriptors(descriptors.as_ptr() as u32); - R::set_in_peripheral(peri as u8); - R::start_in(); - - if R::has_in_descriptor_error() { - return Err(DmaError::DescriptorError); - } - - Ok(()) - } - - fn is_done(&mut self) -> bool { - R::is_in_done() - } -} - -pub(crate) struct ChannelRx<'a, T, R> -where - T: RxChannel, - R: RegisterAccess, -{ - descriptors: &'a mut [u32], - burst_mode: bool, - rx_impl: T, - _phantom: PhantomData, -} - -impl<'a, T, R> Rx for ChannelRx<'a, T, R> -where - T: RxChannel, - R: RegisterAccess, -{ - fn init(&mut self, burst_mode: bool, priority: DmaPriority) { - self.rx_impl.init(burst_mode, priority); - } - - fn prepare_transfer( - &mut self, - peri: DmaPeripheral, - data: *mut u8, - len: usize, - ) -> Result<(), DmaError> { - if self.descriptors.len() % 3 != 0 { - return Err(DmaError::InvalidDescriptorSize); - } - - if self.descriptors.len() / 3 < len / 4092 { - return Err(DmaError::OutOfDescriptors); - } - - if self.burst_mode && (len % 4 != 0 || data as u32 % 4 != 0) { - return Err(DmaError::InvalidAlignment); - } - - self.rx_impl - .prepare_transfer(self.descriptors, peri, data, len)?; - Ok(()) - } - - fn is_done(&mut self) -> bool { - self.rx_impl.is_done() - } -} - -/// DMA Tx -/// -/// The functions here are not meant to be used outside the HAL and will be -/// hidden/inaccessible in future. -pub trait Tx { - fn init(&mut self, burst_mode: bool, priority: DmaPriority); - fn prepare_transfer( - &mut self, - peri: DmaPeripheral, - data: *const u8, - len: usize, - ) -> Result<(), DmaError>; - fn is_done(&mut self) -> bool; -} - -pub(crate) trait TxChannel -where - R: RegisterAccess, -{ - fn init(&mut self, burst_mode: bool, priority: DmaPriority) { - R::set_out_burstmode(burst_mode); - R::set_out_priority(priority); - } - - fn prepare_transfer( - &mut self, - descriptors: &mut [u32], - peri: DmaPeripheral, - data: *const u8, - len: usize, - ) -> Result<(), DmaError> { - for descr in descriptors.iter_mut() { - *descr = 0; - } - - compiler_fence(core::sync::atomic::Ordering::SeqCst); - - let mut processed = 0; - let mut descr = 0; - loop { - let chunk_size = usize::min(4092, len - processed); - let last = processed + chunk_size >= len; - - descriptors[descr + 1] = data as u32 + processed as u32; - - let mut dw0 = &mut descriptors[descr]; - - dw0.set_suc_eof(last); - dw0.set_owner(Owner::Dma); - dw0.set_size(chunk_size as u16); // align to 32 bits? - dw0.set_length(chunk_size as u16); // actual size of the data!? - - if !last { - descriptors[descr + 2] = (&descriptors[descr + 3]) as *const _ as *const () as u32; - } else { - descriptors[descr + 2] = 0; - } - - processed += chunk_size; - descr += 3; - - if processed >= len { - break; - } - } - - R::clear_out_interrupts(); - R::reset_out(); - R::set_out_descriptors(descriptors.as_ptr() as u32); - R::set_out_peripheral(peri as u8); - R::start_out(); - - if R::has_out_descriptor_error() { - return Err(DmaError::DescriptorError); - } - - Ok(()) - } - - fn is_done(&mut self) -> bool { - R::is_out_done() - } -} - -pub(crate) struct ChannelTx<'a, T, R> -where - T: TxChannel, - R: RegisterAccess, -{ - descriptors: &'a mut [u32], - #[allow(unused)] - burst_mode: bool, - tx_impl: T, - _phantom: PhantomData, -} - -impl<'a, T, R> Tx for ChannelTx<'a, T, R> -where - T: TxChannel, - R: RegisterAccess, -{ - fn init(&mut self, burst_mode: bool, priority: DmaPriority) { - self.tx_impl.init(burst_mode, priority); - } - - fn prepare_transfer( - &mut self, - peri: DmaPeripheral, - data: *const u8, - len: usize, - ) -> Result<(), DmaError> { - if self.descriptors.len() % 3 != 0 { - return Err(DmaError::InvalidDescriptorSize); - } - - if self.descriptors.len() / 3 < len / 4092 { - return Err(DmaError::OutOfDescriptors); - } - - self.tx_impl - .prepare_transfer(self.descriptors, peri, data, len)?; - - Ok(()) - } - - fn is_done(&mut self) -> bool { - self.tx_impl.is_done() - } -} - -pub(crate) trait RegisterAccess { - fn set_out_burstmode(burst_mode: bool); - fn set_out_priority(priority: DmaPriority); - fn clear_out_interrupts(); - fn reset_out(); - fn set_out_descriptors(address: u32); - fn has_out_descriptor_error() -> bool; - fn set_out_peripheral(peripheral: u8); - fn start_out(); - fn is_out_done() -> bool; - fn set_in_burstmode(burst_mode: bool); - fn set_in_priority(priority: DmaPriority); - fn clear_in_interrupts(); - fn reset_in(); - fn set_in_descriptors(address: u32); - fn has_in_descriptor_error() -> bool; - fn set_in_peripheral(peripheral: u8); - fn start_in(); - fn is_in_done() -> bool; -} +use crate::{ + dma::gdma::private::*, + system::{Peripheral, PeripheralClockControl}, +}; macro_rules! ImplChannel { ($num: literal) => { paste::paste! { - pub(crate) struct [] {} + pub struct [] {} impl RegisterAccess for [] { + fn init_channel() { + // nothing special to be done here + } + fn set_out_burstmode(burst_mode: bool) { let dma = unsafe { &*crate::pac::DMA::PTR }; @@ -564,80 +158,80 @@ macro_rules! ImplChannel { } } - pub(crate) struct [] {} + pub struct [] {} impl<'a> TxChannel<[]> for [] {} - pub(crate) struct [] {} + pub struct [] {} impl<'a> RxChannel<[]> for [] {} - /// Create the Tx half of the DMA channel - pub struct [] { + pub struct [] { } - impl [] { - pub fn get<'a>( + impl [] { + pub fn configure<'a>( self, - descriptors: &'a mut [u32], burst_mode: bool, + tx_descriptors: &'a mut [u32], + rx_descriptors: &'a mut [u32], priority: DmaPriority, - ) -> impl Tx + 'a { + ) -> Channel], []>, ChannelRx<'a, [], []>, []> { let mut tx_impl = [] {}; tx_impl.init(burst_mode, priority); - ChannelTx { - descriptors, + let tx_channel = ChannelTx { + descriptors: tx_descriptors, burst_mode, tx_impl: tx_impl, _phantom: PhantomData::default(), - } - } - } - - /// Create the Rx half of the DMA channel - pub struct [] { - } + }; - impl [] { - pub fn get<'a>( - self, - descriptors: &'a mut [u32], - burst_mode: bool, - priority: DmaPriority, - ) -> impl Rx + 'a { let mut rx_impl = [] {}; rx_impl.init(burst_mode, priority); - ChannelRx { - descriptors, + let rx_channel = ChannelRx { + descriptors: rx_descriptors, burst_mode, rx_impl: rx_impl, _phantom: PhantomData::default(), + }; + + Channel { + tx: tx_channel, + rx: rx_channel, + _phantom: PhantomData::default(), } } } + + pub struct [] {} + impl PeripheralMarker for [] {} + + // with GDMA every channel can be used for any peripheral + impl SpiPeripheral for [] {} + impl Spi2Peripheral for [] {} } }; } -ImplChannel!(0); -ImplChannel!(1); -ImplChannel!(2); +/// Crate private implementatin details +pub(crate) mod private { + use crate::dma::{private::*, *}; -/// DMA Channel -pub struct Channel { - pub tx: TX, - pub rx: RX, + ImplChannel!(0); + ImplChannel!(1); + ImplChannel!(2); } /// GDMA Peripheral +/// /// This offers the available DMA channels. pub struct Gdma { _inner: crate::pac::DMA, - pub channel0: Channel, - pub channel1: Channel, - pub channel2: Channel, + pub channel0: ChannelCreator0, + pub channel1: ChannelCreator1, + pub channel2: ChannelCreator2, } impl Gdma { @@ -653,32 +247,9 @@ impl Gdma { Gdma { _inner: dma, - channel0: Channel { - rx: RxCreator0 {}, - tx: TxCreator0 {}, - }, - channel1: Channel { - rx: RxCreator1 {}, - tx: TxCreator1 {}, - }, - channel2: Channel { - rx: RxCreator2 {}, - tx: TxCreator2 {}, - }, + channel0: ChannelCreator0 {}, + channel1: ChannelCreator1 {}, + channel2: ChannelCreator2 {}, } } } - -/// Trait to be implemented for an in progress dma transfer. -#[allow(drop_bounds)] -pub trait DmaTransfer: Drop { - /// Wait for the transfer to finish. - fn wait(self) -> (B, T); -} - -/// Trait to be implemented for an in progress dma transfer. -#[allow(drop_bounds)] -pub trait DmaTransferRxTx: Drop { - /// Wait for the transfer to finish. - fn wait(self) -> (BR, BT, T); -} diff --git a/esp-hal-common/src/dma/mod.rs b/esp-hal-common/src/dma/mod.rs index 763cef6e36f..ebeaf8a674c 100644 --- a/esp-hal-common/src/dma/mod.rs +++ b/esp-hal-common/src/dma/mod.rs @@ -1,2 +1,512 @@ +//! Direct Memory Access Commons + +use core::{marker::PhantomData, sync::atomic::compiler_fence}; + +use private::*; + #[cfg(esp32c3)] pub mod gdma; + +#[cfg(esp32)] +pub mod pdma; + +/// DMA Errors +#[derive(Debug, Clone, Copy)] +pub enum DmaError { + InvalidAlignment, + OutOfDescriptors, + InvalidDescriptorSize, + DescriptorError, +} + +/// DMA Priorities +#[cfg(any(esp32c3, esp32s3))] +#[derive(Clone, Copy)] +pub enum DmaPriority { + Priority0 = 0, + Priority1 = 1, + Priority2 = 2, + Priority3 = 3, + Priority4 = 4, + Priority5 = 5, + Priority6 = 6, + Priority7 = 7, + Priority8 = 8, + Priority9 = 9, + Priority10 = 10, + Priority11 = 11, + Priority12 = 12, + Priority13 = 13, + Priority14 = 14, + Priority15 = 15, +} + +/// DMA Priorities +#[cfg(any(esp32, esp32s2))] +#[derive(Clone, Copy)] +pub enum DmaPriority { + Priority0 = 0, +} + +/// DMA capable peripherals +/// The values need to match the TRM +#[cfg(esp32c3)] +#[derive(Clone, Copy)] +pub enum DmaPeripheral { + Spi2 = 0, + Uhci0 = 2, + I2s = 3, + Aes = 6, + Sha = 7, + Adc = 8, +} + +/// DMA capable peripherals +#[cfg(esp32)] +#[derive(Clone, Copy)] +pub enum DmaPeripheral { + Spi2 = 0, + Spi3 = 1, +} + +enum Owner { + Cpu = 0, + Dma = 1, +} + +impl From for Owner { + fn from(value: u32) -> Self { + match value { + 0 => Owner::Cpu, + _ => Owner::Dma, + } + } +} + +trait DmaLinkedListDw0 { + fn set_size(&mut self, len: u16); + fn get_size(&mut self) -> u16; + fn set_length(&mut self, len: u16); + fn get_length(&mut self) -> u16; + fn set_err_eof(&mut self, err_eof: bool); + #[cfg(not(esp32))] + fn get_err_eof(&mut self) -> bool; + #[cfg(not(esp32))] + fn set_suc_eof(&mut self, suc_eof: bool); + fn get_suc_eof(&mut self) -> bool; + fn set_owner(&mut self, owner: Owner); + fn get_owner(&mut self) -> Owner; +} + +impl DmaLinkedListDw0 for &mut u32 { + fn set_size(&mut self, len: u16) { + let mask = 0b111111111111; + let bit_s = 0; + **self = (**self & !(mask << bit_s)) | (len as u32) << bit_s; + } + + fn get_size(&mut self) -> u16 { + let mask = 0b111111111111; + let bit_s = 0; + ((**self & (mask << bit_s)) >> bit_s) as u16 + } + + fn set_length(&mut self, len: u16) { + let mask = 0b111111111111; + let bit_s = 12; + **self = (**self & !(mask << bit_s)) | (len as u32) << bit_s; + } + + fn get_length(&mut self) -> u16 { + let mask = 0b111111111111; + let bit_s = 12; + ((**self & (mask << bit_s)) >> bit_s) as u16 + } + + fn set_err_eof(&mut self, err_eof: bool) { + let mask = 0b1; + let bit_s = 28; + **self = (**self & !(mask << bit_s)) | (err_eof as u32) << bit_s; + } + + #[cfg(not(esp32))] + fn get_err_eof(&mut self) -> bool { + let mask = 0b1; + let bit_s = 28; + ((**self & (mask << bit_s)) >> bit_s) != 0 + } + + #[cfg(not(esp32))] + fn set_suc_eof(&mut self, suc_eof: bool) { + let mask = 0b1; + let bit_s = 30; + **self = (**self & !(mask << bit_s)) | (suc_eof as u32) << bit_s; + } + + fn get_suc_eof(&mut self) -> bool { + let mask = 0b1; + let bit_s = 30; + ((**self & (mask << bit_s)) >> bit_s) != 0 + } + + fn set_owner(&mut self, owner: Owner) { + let mask = 0b1; + let bit_s = 31; + **self = (**self & !(mask << bit_s)) | (owner as u32) << bit_s; + } + + fn get_owner(&mut self) -> Owner { + let mask = 0b1; + let bit_s = 31; + ((**self & (mask << bit_s)) >> bit_s).into() + } +} + +/// Crate private implementatin details +pub(crate) mod private { + use super::*; + + pub trait PeripheralMarker {} + + /// Marks channels as useable for SPI + pub trait SpiPeripheral: PeripheralMarker {} + + /// Marks channels as useable for SPI2 + pub trait Spi2Peripheral: SpiPeripheral + PeripheralMarker {} + + /// Marks channels as useable for SPI3 + #[cfg(esp32)] + pub trait Spi3Peripheral: SpiPeripheral + PeripheralMarker {} + + /// DMA Rx + /// + /// The functions here are not meant to be used outside the HAL and will be + /// hidden/inaccessible in future. + pub trait Rx { + fn init(&mut self, burst_mode: bool, priority: DmaPriority); + + fn init_channel(&mut self); + + fn prepare_transfer( + &mut self, + peri: DmaPeripheral, + data: *mut u8, + len: usize, + ) -> Result<(), DmaError>; + + fn is_done(&mut self) -> bool; + } + + pub trait RxChannel + where + R: RegisterAccess, + { + fn init(&mut self, burst_mode: bool, priority: DmaPriority) { + R::set_in_burstmode(burst_mode); + R::set_in_priority(priority); + } + + fn prepare_transfer( + &mut self, + descriptors: &mut [u32], + peri: DmaPeripheral, + data: *mut u8, + len: usize, + ) -> Result<(), DmaError> { + for descr in descriptors.iter_mut() { + *descr = 0; + } + + compiler_fence(core::sync::atomic::Ordering::SeqCst); + + let mut processed = 0; + let mut descr = 0; + loop { + let chunk_size = usize::min(4092, len - processed); + let last = processed + chunk_size >= len; + + descriptors[descr + 1] = data as u32 + processed as u32; + + let mut dw0 = &mut descriptors[descr]; + + #[cfg(not(esp32))] + dw0.set_suc_eof(last); + + dw0.set_owner(Owner::Dma); + dw0.set_size(chunk_size as u16); // align to 32 bits? + dw0.set_length(0); // actual size of the data!? + + if !last { + descriptors[descr + 2] = + (&descriptors[descr + 3]) as *const _ as *const () as u32; + } else { + descriptors[descr + 2] = 0; + } + + processed += chunk_size; + descr += 3; + + if processed >= len { + break; + } + } + + R::clear_in_interrupts(); + R::reset_in(); + R::set_in_descriptors(descriptors.as_ptr() as u32); + R::set_in_peripheral(peri as u8); + R::start_in(); + + if R::has_in_descriptor_error() { + return Err(DmaError::DescriptorError); + } + + Ok(()) + } + + fn is_done(&mut self) -> bool { + R::is_in_done() + } + } + + pub struct ChannelRx<'a, T, R> + where + T: RxChannel, + R: RegisterAccess, + { + pub descriptors: &'a mut [u32], + pub burst_mode: bool, + pub rx_impl: T, + pub _phantom: PhantomData, + } + + impl<'a, T, R> Rx for ChannelRx<'a, T, R> + where + T: RxChannel, + R: RegisterAccess, + { + fn init(&mut self, burst_mode: bool, priority: DmaPriority) { + self.rx_impl.init(burst_mode, priority); + } + + fn prepare_transfer( + &mut self, + peri: DmaPeripheral, + data: *mut u8, + len: usize, + ) -> Result<(), DmaError> { + if self.descriptors.len() % 3 != 0 { + return Err(DmaError::InvalidDescriptorSize); + } + + if self.descriptors.len() / 3 < len / 4092 { + return Err(DmaError::OutOfDescriptors); + } + + if self.burst_mode && (len % 4 != 0 || data as u32 % 4 != 0) { + return Err(DmaError::InvalidAlignment); + } + + self.rx_impl + .prepare_transfer(self.descriptors, peri, data, len)?; + Ok(()) + } + + fn is_done(&mut self) -> bool { + self.rx_impl.is_done() + } + + fn init_channel(&mut self) { + R::init_channel(); + } + } + + /// DMA Tx + /// + /// The functions here are not meant to be used outside the HAL and will be + /// hidden/inaccessible in future. + pub trait Tx { + fn init(&mut self, burst_mode: bool, priority: DmaPriority); + + fn init_channel(&mut self); + + fn prepare_transfer( + &mut self, + peri: DmaPeripheral, + data: *const u8, + len: usize, + ) -> Result<(), DmaError>; + + fn is_done(&mut self) -> bool; + } + + pub trait TxChannel + where + R: RegisterAccess, + { + fn init(&mut self, burst_mode: bool, priority: DmaPriority) { + R::set_out_burstmode(burst_mode); + R::set_out_priority(priority); + } + + fn prepare_transfer( + &mut self, + descriptors: &mut [u32], + peri: DmaPeripheral, + data: *const u8, + len: usize, + ) -> Result<(), DmaError> { + for descr in descriptors.iter_mut() { + *descr = 0; + } + + compiler_fence(core::sync::atomic::Ordering::SeqCst); + + let mut processed = 0; + let mut descr = 0; + loop { + let chunk_size = usize::min(4092, len - processed); + let last = processed + chunk_size >= len; + + descriptors[descr + 1] = data as u32 + processed as u32; + + let mut dw0 = &mut descriptors[descr]; + + #[cfg(not(esp32))] + dw0.set_suc_eof(last); + + dw0.set_owner(Owner::Dma); + dw0.set_size(chunk_size as u16); // align to 32 bits? + dw0.set_length(chunk_size as u16); // actual size of the data!? + + if !last { + descriptors[descr + 2] = + (&descriptors[descr + 3]) as *const _ as *const () as u32; + } else { + descriptors[descr + 2] = 0; + } + + processed += chunk_size; + descr += 3; + + if processed >= len { + break; + } + } + + R::clear_out_interrupts(); + R::reset_out(); + R::set_out_descriptors(descriptors.as_ptr() as u32); + R::set_out_peripheral(peri as u8); + R::start_out(); + + if R::has_out_descriptor_error() { + return Err(DmaError::DescriptorError); + } + + Ok(()) + } + + fn is_done(&mut self) -> bool { + R::is_out_done() + } + } + + pub struct ChannelTx<'a, T, R> + where + T: TxChannel, + R: RegisterAccess, + { + pub descriptors: &'a mut [u32], + #[allow(unused)] + pub burst_mode: bool, + pub tx_impl: T, + pub _phantom: PhantomData, + } + + impl<'a, T, R> Tx for ChannelTx<'a, T, R> + where + T: TxChannel, + R: RegisterAccess, + { + fn init(&mut self, burst_mode: bool, priority: DmaPriority) { + self.tx_impl.init(burst_mode, priority); + } + + fn init_channel(&mut self) { + R::init_channel(); + } + + fn prepare_transfer( + &mut self, + peri: DmaPeripheral, + data: *const u8, + len: usize, + ) -> Result<(), DmaError> { + if self.descriptors.len() % 3 != 0 { + return Err(DmaError::InvalidDescriptorSize); + } + + if self.descriptors.len() / 3 < len / 4092 { + return Err(DmaError::OutOfDescriptors); + } + + self.tx_impl + .prepare_transfer(self.descriptors, peri, data, len)?; + + Ok(()) + } + + fn is_done(&mut self) -> bool { + self.tx_impl.is_done() + } + } + + pub trait RegisterAccess { + fn init_channel(); + fn set_out_burstmode(burst_mode: bool); + fn set_out_priority(priority: DmaPriority); + fn clear_out_interrupts(); + fn reset_out(); + fn set_out_descriptors(address: u32); + fn has_out_descriptor_error() -> bool; + fn set_out_peripheral(peripheral: u8); + fn start_out(); + fn is_out_done() -> bool; + fn set_in_burstmode(burst_mode: bool); + fn set_in_priority(priority: DmaPriority); + fn clear_in_interrupts(); + fn reset_in(); + fn set_in_descriptors(address: u32); + fn has_in_descriptor_error() -> bool; + fn set_in_peripheral(peripheral: u8); + fn start_in(); + fn is_in_done() -> bool; + } +} + +/// DMA Channel +pub struct Channel +where + TX: Tx, + RX: Rx, + P: PeripheralMarker, +{ + pub(crate) tx: TX, + pub(crate) rx: RX, + _phantom: PhantomData

, +} + +/// Trait to be implemented for an in progress dma transfer. +#[allow(drop_bounds)] +pub trait DmaTransfer: Drop { + /// Wait for the transfer to finish. + fn wait(self) -> (B, T); +} + +/// Trait to be implemented for an in progress dma transfer. +#[allow(drop_bounds)] +pub trait DmaTransferRxTx: Drop { + /// Wait for the transfer to finish. + fn wait(self) -> (BR, BT, T); +} diff --git a/esp-hal-common/src/dma/pdma.rs b/esp-hal-common/src/dma/pdma.rs new file mode 100644 index 00000000000..b367601e422 --- /dev/null +++ b/esp-hal-common/src/dma/pdma.rs @@ -0,0 +1,234 @@ +//! Direct Memory Access + +use crate::{ + dma::pdma::private::*, + system::{Peripheral, PeripheralClockControl}, +}; + +macro_rules! ImplSpiChannel { + ($num: literal) => { + paste::paste! { + pub struct [] {} + + impl RegisterAccess for [] { + fn init_channel() { + // (only) on ESP32 we need to configure DPORT for the SPI DMA channels + let dport = unsafe { &*crate::pac::DPORT::PTR }; + + match $num { + 2 => { + dport + .spi_dma_chan_sel + .modify(|_, w| w.spi2_dma_chan_sel().variant(1)); + }, + 3 => { + dport + .spi_dma_chan_sel + .modify(|_, w| w.spi3_dma_chan_sel().variant(2)); + }, + _ => panic!("Only SPI2 and SPI3 supported"), + } + } + + fn set_out_burstmode(burst_mode: bool) { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_conf + .modify(|_, w| w.outdscr_burst_en().bit(burst_mode)); + } + + fn set_out_priority(_priority: DmaPriority) {} + + fn clear_out_interrupts() { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_int_clr.write(|w| { + w.out_done_int_clr() + .set_bit() + .out_eof_int_clr() + .set_bit() + .out_total_eof_int_clr() + .set_bit() + .outlink_dscr_error_int_clr() + .set_bit() + }); + } + + fn reset_out() { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_conf.modify(|_, w| w.out_rst().set_bit()); + spi.dma_conf.modify(|_, w| w.out_rst().clear_bit()); + } + + fn set_out_descriptors(address: u32) { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_out_link + .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); + } + + fn has_out_descriptor_error() -> bool { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_int_raw.read().outlink_dscr_error_int_raw().bit() + } + + fn set_out_peripheral(_peripheral: u8) { + // no-op + } + + fn start_out() { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_out_link.modify(|_, w| w.outlink_start().set_bit()); + } + + fn is_out_done() -> bool { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_int_raw.read().out_done_int_raw().bit() + } + + fn set_in_burstmode(burst_mode: bool) { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_conf + .modify(|_, w| w.indscr_burst_en().bit(burst_mode)); + } + + fn set_in_priority(_priority: DmaPriority) {} + + fn clear_in_interrupts() { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_int_clr.write(|w| { + w.in_done_int_clr() + .set_bit() + .in_err_eof_int_clr() + .set_bit() + .in_suc_eof_int_clr() + .set_bit() + .inlink_dscr_error_int_clr() + .set_bit() + }); + } + + fn reset_in() { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_conf.modify(|_, w| w.in_rst().set_bit()); + spi.dma_conf.modify(|_, w| w.in_rst().clear_bit()); + } + + fn set_in_descriptors(address: u32) { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_in_link + .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); + } + + fn has_in_descriptor_error() -> bool { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_int_raw.read().inlink_dscr_error_int_raw().bit() + } + + fn set_in_peripheral(_peripheral: u8) { + // no-op + } + + fn start_in() { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_in_link.modify(|_, w| w.inlink_start().set_bit()); + } + + fn is_in_done() -> bool { + let spi = unsafe { &*crate::pac::[]::PTR }; + spi.dma_int_raw.read().in_done_int_raw().bit() + } + } + + pub struct [] {} + + impl<'a> TxChannel<[]> for [] {} + + pub struct [] {} + + impl<'a> RxChannel<[]> for [] {} + + pub struct [] {} + + impl [] { + pub fn configure<'a>( + self, + burst_mode: bool, + tx_descriptors: &'a mut [u32], + rx_descriptors: &'a mut [u32], + priority: DmaPriority, + ) -> Channel< + ChannelTx<[], []>, + ChannelRx<[], []>, + [], + > { + let mut tx_impl = [] {}; + tx_impl.init(burst_mode, priority); + + let tx_channel = ChannelTx { + descriptors: tx_descriptors, + burst_mode, + tx_impl: tx_impl, + _phantom: PhantomData::default(), + }; + + let mut rx_impl = [] {}; + rx_impl.init(burst_mode, priority); + + let rx_channel = ChannelRx { + descriptors: rx_descriptors, + burst_mode, + rx_impl: rx_impl, + _phantom: PhantomData::default(), + }; + + Channel { + tx: tx_channel, + rx: rx_channel, + _phantom: PhantomData::default(), + } + } + } + } + }; +} + +/// Crate private implementatin details +pub(crate) mod private { + use crate::dma::{private::*, *}; + + pub struct Spi2DmaSuitablePeripheral {} + impl PeripheralMarker for Spi2DmaSuitablePeripheral {} + impl SpiPeripheral for Spi2DmaSuitablePeripheral {} + impl Spi2Peripheral for Spi2DmaSuitablePeripheral {} + + pub struct Spi3DmaSuitablePeripheral {} + impl PeripheralMarker for Spi3DmaSuitablePeripheral {} + impl SpiPeripheral for Spi3DmaSuitablePeripheral {} + impl Spi3Peripheral for Spi3DmaSuitablePeripheral {} + + ImplSpiChannel!(2); + ImplSpiChannel!(3); +} + +/// DMA Peripheral +/// +/// This offers the available DMA channels. +pub struct Dma { + _inner: crate::system::Dma, + pub spi2channel: Spi2DmaChannelCreator, + pub spi3channel: Spi3DmaChannelCreator, +} + +impl Dma { + /// Create a DMA instance. + pub fn new( + dma: crate::system::Dma, + peripheral_clock_control: &mut PeripheralClockControl, + ) -> Dma { + peripheral_clock_control.enable(Peripheral::Dma); + + Dma { + _inner: dma, + spi2channel: Spi2DmaChannelCreator {}, + spi3channel: Spi3DmaChannelCreator {}, + } + } +} diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index b5be3d3811e..289608ab7bd 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -90,6 +90,7 @@ pub mod efuse; #[cfg_attr(xtensa, path = "interrupt/xtensa.rs")] pub mod interrupt; +#[cfg(any(esp32c3, esp32))] pub mod dma; /// Enumeration of CPU cores diff --git a/esp-hal-common/src/spi.rs b/esp-hal-common/src/spi.rs index 6e363e408fa..0e56b4d55b9 100644 --- a/esp-hal-common/src/spi.rs +++ b/esp-hal-common/src/spi.rs @@ -50,8 +50,10 @@ use fugit::HertzU32; -#[cfg(any(esp32c3))] -use crate::dma::gdma::{DmaError, Rx, Tx}; +#[cfg(any(esp32c3, esp32))] +use crate::dma::private::{Rx, Tx}; +#[cfg(any(esp32c3, esp32))] +use crate::dma::{DmaError, DmaPeripheral}; use crate::{ clock::Clocks, pac::spi2::RegisterBlock, @@ -74,13 +76,13 @@ const MAX_DMA_SIZE: usize = 32736; #[derive(Debug, Clone, Copy)] pub enum Error { - #[cfg(esp32c3)] + #[cfg(any(esp32c3, esp32))] DmaError(DmaError), MaxDmaTransferSizeExceeded, Unknown, } -#[cfg(esp32c3)] +#[cfg(any(esp32c3, esp32))] impl From for Error { fn from(value: DmaError) -> Self { Error::DmaError(value) @@ -260,57 +262,103 @@ where } } -#[cfg(any(esp32c3))] +#[cfg(any(esp32c3, esp32))] pub mod dma { use core::mem; use embedded_dma::{ReadBuffer, WriteBuffer}; - use super::{Instance, InstanceDma, Spi, MAX_DMA_SIZE}; - use crate::dma::gdma::{DmaTransfer, DmaTransferRxTx, Rx, Tx}; + #[cfg(esp32)] + use super::Spi3Instance; + use super::{Instance, InstanceDma, Spi, Spi2Instance, MAX_DMA_SIZE}; + #[cfg(esp32)] + use crate::dma::private::Spi3Peripheral; + use crate::dma::{ + private::{Rx, Spi2Peripheral, SpiPeripheral, Tx}, + Channel, + DmaTransfer, + DmaTransferRxTx, + }; - impl Spi + pub trait WithDmaSpi2 where - T: Instance, + T: Instance + Spi2Instance, + TX: Tx, + RX: Rx, + P: SpiPeripheral, { - /// Make this SPI instance into a DMA capable instance. - /// Pass the Rx and Tx half of the DMA channel. - pub fn with_tx_rx_dma(self, tx: TX, rx: RX) -> SpiDma - where - T: InstanceDma, - TX: Tx, - RX: Rx, - { + fn with_dma(self, channel: Channel) -> SpiDma; + } + + #[cfg(esp32)] + pub trait WithDmaSpi3 + where + T: Instance + Spi3Instance, + TX: Tx, + RX: Rx, + P: SpiPeripheral, + { + fn with_dma(self, channel: Channel) -> SpiDma; + } + + impl WithDmaSpi2 for Spi + where + T: Instance + Spi2Instance, + TX: Tx, + RX: Rx, + P: SpiPeripheral + Spi2Peripheral, + { + fn with_dma(self, mut channel: Channel) -> SpiDma { + channel.tx.init_channel(); // no need to call this for both, TX and RX + SpiDma { spi: self.spi, - tx, - rx, + channel, } } } + #[cfg(esp32)] + impl WithDmaSpi3 for Spi + where + T: Instance + Spi3Instance, + TX: Tx, + RX: Rx, + P: SpiPeripheral + Spi3Peripheral, + { + fn with_dma(self, mut channel: Channel) -> SpiDma { + channel.tx.init_channel(); // no need to call this for both, TX and RX + + SpiDma { + spi: self.spi, + channel, + } + } + } /// An in-progress DMA transfer - pub struct SpiDmaTransferRxTx + pub struct SpiDmaTransferRxTx where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { - spi_dma: SpiDma, + spi_dma: SpiDma, rbuffer: RBUFFER, tbuffer: TBUFFER, } - impl DmaTransferRxTx> - for SpiDmaTransferRxTx + impl DmaTransferRxTx> + for SpiDmaTransferRxTx where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { /// Wait for the DMA transfer to complete and return the buffers and the /// SPI instance. - fn wait(mut self) -> (RXBUF, TXBUF, SpiDma) { + fn wait(mut self) -> (RXBUF, TXBUF, SpiDma) { self.spi_dma.spi.flush().ok(); // waiting for the DMA transfer is not enough // `DmaTransfer` needs to have a `Drop` implementation, because we accept @@ -330,11 +378,12 @@ pub mod dma { } } - impl Drop for SpiDmaTransferRxTx + impl Drop for SpiDmaTransferRxTx where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { fn drop(&mut self) { self.spi_dma.spi.flush().ok(); @@ -342,25 +391,28 @@ pub mod dma { } /// An in-progress DMA transfer. - pub struct SpiDmaTransfer + pub struct SpiDmaTransfer where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { - spi_dma: SpiDma, + spi_dma: SpiDma, buffer: BUFFER, } - impl DmaTransfer> for SpiDmaTransfer + impl DmaTransfer> + for SpiDmaTransfer where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { /// Wait for the DMA transfer to complete and return the buffers and the /// SPI instance. - fn wait(mut self) -> (BUFFER, SpiDma) { + fn wait(mut self) -> (BUFFER, SpiDma) { self.spi_dma.spi.flush().ok(); // waiting for the DMA transfer is not enough // `DmaTransfer` needs to have a `Drop` implementation, because we accept @@ -379,11 +431,12 @@ pub mod dma { } } - impl Drop for SpiDmaTransfer + impl Drop for SpiDmaTransfer where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { fn drop(&mut self) { self.spi_dma.spi.flush().ok(); @@ -391,17 +444,22 @@ pub mod dma { } /// A DMA capable SPI instance. - pub struct SpiDma { - spi: T, - tx: TX, - rx: RX, + pub struct SpiDma + where + TX: Tx, + RX: Rx, + P: SpiPeripheral, + { + pub(crate) spi: T, + pub(crate) channel: Channel, } - impl SpiDma + impl SpiDma where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { /// Return the raw interface to the underlying peripheral instance pub fn free(self) -> T { @@ -416,7 +474,7 @@ pub mod dma { pub fn dma_write( mut self, words: TXBUF, - ) -> Result, super::Error> + ) -> Result, super::Error> where TXBUF: ReadBuffer, { @@ -426,7 +484,8 @@ pub mod dma { return Err(super::Error::MaxDmaTransferSizeExceeded); } - self.spi.start_write_bytes_dma(ptr, len, &mut self.tx)?; + self.spi + .start_write_bytes_dma(ptr, len, &mut self.channel.tx)?; Ok(SpiDmaTransfer { spi_dma: self, buffer: words, @@ -441,7 +500,7 @@ pub mod dma { pub fn dma_read( mut self, mut words: RXBUF, - ) -> Result, super::Error> + ) -> Result, super::Error> where RXBUF: WriteBuffer, { @@ -451,7 +510,8 @@ pub mod dma { return Err(super::Error::MaxDmaTransferSizeExceeded); } - self.spi.start_read_bytes_dma(ptr, len, &mut self.rx)?; + self.spi + .start_read_bytes_dma(ptr, len, &mut self.channel.rx)?; Ok(SpiDmaTransfer { spi_dma: self, buffer: words, @@ -467,7 +527,7 @@ pub mod dma { mut self, words: TXBUF, mut read_buffer: RXBUF, - ) -> Result, super::Error> + ) -> Result, super::Error> where TXBUF: ReadBuffer, RXBUF: WriteBuffer, @@ -484,8 +544,8 @@ pub mod dma { write_len, read_ptr, read_len, - &mut self.tx, - &mut self.rx, + &mut self.channel.tx, + &mut self.channel.rx, )?; Ok(SpiDmaTransferRxTx { spi_dma: self, @@ -495,30 +555,32 @@ pub mod dma { } } - impl embedded_hal::blocking::spi::Transfer for SpiDma + impl embedded_hal::blocking::spi::Transfer for SpiDma where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { type Error = super::Error; fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { self.spi - .transfer_in_place_dma(words, &mut self.tx, &mut self.rx) + .transfer_in_place_dma(words, &mut self.channel.tx, &mut self.channel.rx) } } - impl embedded_hal::blocking::spi::Write for SpiDma + impl embedded_hal::blocking::spi::Write for SpiDma where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { type Error = super::Error; fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - self.spi.write_bytes_dma(words, &mut self.tx)?; + self.spi.write_bytes_dma(words, &mut self.channel.tx)?; self.spi.flush()?; Ok(()) } @@ -528,49 +590,53 @@ pub mod dma { mod ehal1 { use embedded_hal_1::spi::{SpiBus, SpiBusFlush, SpiBusRead, SpiBusWrite}; - use super::{super::InstanceDma, SpiDma}; - use crate::dma::gdma::{Rx, Tx}; + use super::{super::InstanceDma, SpiDma, SpiPeripheral}; + use crate::dma::private::{Rx, Tx}; - impl embedded_hal_1::spi::ErrorType for SpiDma + impl embedded_hal_1::spi::ErrorType for SpiDma where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { type Error = super::super::Error; } - impl SpiBusWrite for SpiDma + impl SpiBusWrite for SpiDma where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { /// See also: [`write_bytes`]. fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - self.spi.write_bytes_dma(words, &mut self.tx)?; + self.spi.write_bytes_dma(words, &mut self.channel.tx)?; self.flush() } } - impl SpiBusRead for SpiDma + impl SpiBusRead for SpiDma where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.spi - .transfer_dma(&[], words, &mut self.tx, &mut self.rx)?; + .transfer_dma(&[], words, &mut self.channel.tx, &mut self.channel.rx)?; self.flush() } } - impl SpiBus for SpiDma + impl SpiBus for SpiDma where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { /// Write out data from `write`, read response into `read`. /// @@ -582,7 +648,7 @@ pub mod dma { /// simultaneously. fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.spi - .transfer_dma(write, read, &mut self.tx, &mut self.rx)?; + .transfer_dma(write, read, &mut self.channel.tx, &mut self.channel.rx)?; self.flush() } @@ -593,17 +659,21 @@ pub mod dma { /// [`write`](SpiBusWrite::write), [`flush`](SpiBusFlush::flush) and /// [`read`](SpiBusRead::read). fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { - self.spi - .transfer_in_place_dma(words, &mut self.tx, &mut self.rx)?; + self.spi.transfer_in_place_dma( + words, + &mut self.channel.tx, + &mut self.channel.rx, + )?; self.flush() } } - impl SpiBusFlush for SpiDma + impl SpiBusFlush for SpiDma where T: InstanceDma, TX: Tx, RX: Rx, + P: SpiPeripheral, { fn flush(&mut self) -> Result<(), Self::Error> { self.spi.flush() @@ -846,7 +916,7 @@ mod ehal1 { } } -#[cfg(any(esp32c3))] +#[cfg(any(esp32c3, esp32))] pub trait InstanceDma: Instance where TX: Tx, @@ -926,33 +996,13 @@ where tx.is_done(); rx.is_done(); - reg_block.dma_conf.modify(|_, w| w.dma_tx_ena().set_bit()); - reg_block.dma_conf.modify(|_, w| w.dma_rx_ena().set_bit()); + self.enable_dma(); self.update(); - tx.prepare_transfer( - crate::dma::gdma::DmaPeripheral::Spi2, - write_buffer_ptr, - write_buffer_len, - )?; - rx.prepare_transfer( - crate::dma::gdma::DmaPeripheral::Spi2, - read_buffer_ptr, - read_buffer_len, - )?; + tx.prepare_transfer(self.dma_peripheral(), write_buffer_ptr, write_buffer_len)?; + rx.prepare_transfer(self.dma_peripheral(), read_buffer_ptr, read_buffer_len)?; - reg_block.dma_int_clr.write(|w| { - w.dma_infifo_full_err_int_clr() - .set_bit() - .dma_outfifo_empty_err_int_clr() - .set_bit() - .trans_done_int_clr() - .set_bit() - .mst_rx_afifo_wfull_err_int_clr() - .set_bit() - .mst_tx_afifo_rempty_err_int_clr() - .set_bit() - }); + self.clear_dma_interrupts(); reg_block.cmd.modify(|_, w| w.usr().set_bit()); @@ -979,24 +1029,12 @@ where let reg_block = self.register_block(); self.configure_datalen(len as u32 * 8); - reg_block.dma_conf.modify(|_, w| w.dma_tx_ena().set_bit()); - reg_block.dma_conf.modify(|_, w| w.dma_rx_ena().set_bit()); + self.enable_dma(); self.update(); - tx.prepare_transfer(crate::dma::gdma::DmaPeripheral::Spi2, ptr, len)?; + tx.prepare_transfer(self.dma_peripheral(), ptr, len)?; - reg_block.dma_int_clr.write(|w| { - w.dma_infifo_full_err_int_clr() - .set_bit() - .dma_outfifo_empty_err_int_clr() - .set_bit() - .trans_done_int_clr() - .set_bit() - .mst_rx_afifo_wfull_err_int_clr() - .set_bit() - .mst_tx_afifo_rempty_err_int_clr() - .set_bit() - }); + self.clear_dma_interrupts(); reg_block.cmd.modify(|_, w| w.usr().set_bit()); @@ -1012,12 +1050,42 @@ where let reg_block = self.register_block(); self.configure_datalen(len as u32 * 8); + self.enable_dma(); + self.update(); + + rx.prepare_transfer(self.dma_peripheral(), ptr, len)?; + + self.clear_dma_interrupts(); + + reg_block.cmd.modify(|_, w| w.usr().set_bit()); + + return Ok(()); + } + + fn dma_peripheral(&self) -> DmaPeripheral { + match self.spi_num() { + 2 => DmaPeripheral::Spi2, + #[cfg(esp32)] + 3 => DmaPeripheral::Spi3, + _ => panic!("Illegal SPI instance"), + } + } + + #[cfg(esp32c3)] + fn enable_dma(&self) { + let reg_block = self.register_block(); reg_block.dma_conf.modify(|_, w| w.dma_tx_ena().set_bit()); reg_block.dma_conf.modify(|_, w| w.dma_rx_ena().set_bit()); - self.update(); + } - rx.prepare_transfer(crate::dma::gdma::DmaPeripheral::Spi2, ptr, len)?; + #[cfg(esp32)] + fn enable_dma(&self) { + // for non GDMA this is done in `assign_tx_device` / `assign_rx_device` + } + #[cfg(esp32c3)] + fn clear_dma_interrupts(&self) { + let reg_block = self.register_block(); reg_block.dma_int_clr.write(|w| { w.dma_infifo_full_err_int_clr() .set_bit() @@ -1030,13 +1098,50 @@ where .mst_tx_afifo_rempty_err_int_clr() .set_bit() }); + } - reg_block.cmd.modify(|_, w| w.usr().set_bit()); - - return Ok(()); + #[cfg(esp32)] + fn clear_dma_interrupts(&self) { + let reg_block = self.register_block(); + reg_block.dma_int_clr.write(|w| { + w.inlink_dscr_empty_int_clr() + .set_bit() + .outlink_dscr_error_int_clr() + .set_bit() + .inlink_dscr_error_int_clr() + .set_bit() + .in_done_int_clr() + .set_bit() + .in_err_eof_int_clr() + .set_bit() + .in_suc_eof_int_clr() + .set_bit() + .out_done_int_clr() + .set_bit() + .out_eof_int_clr() + .set_bit() + .out_total_eof_int_clr() + .set_bit() + }); } } +#[cfg(any(esp32c3, esp32))] +impl InstanceDma for crate::pac::SPI2 +where + TX: Tx, + RX: Rx, +{ +} + +#[cfg(any(esp32))] +impl InstanceDma for crate::pac::SPI3 +where + TX: Tx, + RX: Rx, +{ +} + pub trait Instance { fn register_block(&self) -> &RegisterBlock; @@ -1050,6 +1155,8 @@ pub trait Instance { fn enable_peripheral(&self, peripheral_clock_control: &mut PeripheralClockControl); + fn spi_num(&self) -> u8; + fn init(&mut self) { let reg_block = self.register_block(); reg_block.user.modify(|_, w| { @@ -1438,14 +1545,11 @@ impl Instance for crate::pac::SPI2 { fn enable_peripheral(&self, peripheral_clock_control: &mut PeripheralClockControl) { peripheral_clock_control.enable(crate::system::Peripheral::Spi2); } -} -#[cfg(any(esp32c3))] -impl InstanceDma for crate::pac::SPI2 -where - TX: Tx, - RX: Rx, -{ + #[inline(always)] + fn spi_num(&self) -> u8 { + 2 + } } #[cfg(any(esp32))] @@ -1479,6 +1583,11 @@ impl Instance for crate::pac::SPI2 { fn enable_peripheral(&self, peripheral_clock_control: &mut PeripheralClockControl) { peripheral_clock_control.enable(crate::system::Peripheral::Spi2); } + + #[inline(always)] + fn spi_num(&self) -> u8 { + 2 + } } #[cfg(any(esp32))] @@ -1512,6 +1621,11 @@ impl Instance for crate::pac::SPI3 { fn enable_peripheral(&self, peripheral_clock_control: &mut PeripheralClockControl) { peripheral_clock_control.enable(crate::system::Peripheral::Spi3) } + + #[inline(always)] + fn spi_num(&self) -> u8 { + 3 + } } #[cfg(any(esp32s2, esp32s3))] @@ -1545,6 +1659,11 @@ impl Instance for crate::pac::SPI2 { fn enable_peripheral(&self, peripheral_clock_control: &mut PeripheralClockControl) { peripheral_clock_control.enable(crate::system::Peripheral::Spi2) } + + #[inline(always)] + fn spi_num(&self) -> u8 { + 2 + } } #[cfg(any(esp32s2, esp32s3))] @@ -1578,4 +1697,19 @@ impl Instance for crate::pac::SPI3 { fn enable_peripheral(&self, peripheral_clock_control: &mut PeripheralClockControl) { peripheral_clock_control.enable(crate::system::Peripheral::Spi3) } + + #[inline(always)] + fn spi_num(&self) -> u8 { + 3 + } } + +pub trait Spi2Instance {} + +#[cfg(esp32)] +pub trait Spi3Instance {} + +impl Spi2Instance for crate::pac::SPI2 {} + +#[cfg(esp32)] +impl Spi3Instance for crate::pac::SPI3 {} diff --git a/esp-hal-common/src/system.rs b/esp-hal-common/src/system.rs index 959d36e8fe0..138e05388f2 100644 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -28,6 +28,8 @@ pub enum Peripheral { ApbSarAdc, #[cfg(esp32c3)] Gdma, + #[cfg(esp32)] + Dma, } /// Controls the enablement of peripheral clocks. @@ -92,6 +94,11 @@ impl PeripheralClockControl { perip_clk_en1.modify(|_, w| w.dma_clk_en().set_bit()); perip_rst_en1.modify(|_, w| w.dma_rst().clear_bit()); } + #[cfg(esp32)] + Peripheral::Dma => { + perip_clk_en0.modify(|_, w| w.spi_dma_clk_en().set_bit()); + perip_rst_en0.modify(|_, w| w.spi_dma_rst().clear_bit()); + } } } } @@ -106,12 +113,20 @@ pub struct CpuControl { _private: (), } +/// Controls the configuration of the chip's clocks. +#[cfg(esp32)] +pub struct Dma { + _private: (), +} + /// The SYSTEM/DPORT splitted into it's different logical parts. pub struct SystemParts { _private: (), pub peripheral_clock_control: PeripheralClockControl, pub clock_control: SystemClockControl, pub cpu_control: CpuControl, + #[cfg(esp32)] + pub dma: Dma, } /// Extension trait to split a SYSTEM/DPORT peripheral in independent logical @@ -132,6 +147,8 @@ impl SystemExt for SystemPeripheral { peripheral_clock_control: PeripheralClockControl { _private: () }, clock_control: SystemClockControl { _private: () }, cpu_control: CpuControl { _private: () }, + #[cfg(esp32)] + dma: Dma { _private: () }, } } } diff --git a/esp32-hal/examples/adc.rs b/esp32-hal/examples/adc.rs index 336df7b6976..31534be1ff4 100644 --- a/esp32-hal/examples/adc.rs +++ b/esp32-hal/examples/adc.rs @@ -39,7 +39,8 @@ fn main() -> ! { let analog = peripherals.SENS.split(); let mut adc2_config = AdcConfig::new(); - let mut pin25 = adc2_config.enable_pin(io.pins.gpio25.into_analog(), Attenuation::Attenuation11dB); + let mut pin25 = + adc2_config.enable_pin(io.pins.gpio25.into_analog(), Attenuation::Attenuation11dB); let mut adc2 = ADC::::adc(analog.adc2, adc2_config).unwrap(); let mut delay = Delay::new(&clocks); diff --git a/esp32-hal/examples/spi_loopback_dma.rs b/esp32-hal/examples/spi_loopback_dma.rs new file mode 100644 index 00000000000..6eb3c069f0e --- /dev/null +++ b/esp32-hal/examples/spi_loopback_dma.rs @@ -0,0 +1,119 @@ +//! SPI loopback test using DMA +//! +//! Folowing pins are used: +//! SCLK GPIO19 +//! MISO GPIO25 +//! MOSI GPIO23 +//! CS GPIO22 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This example transfers data via SPI. +//! Connect MISO and MOSI pins to see the outgoing data is read as incoming +//! data. + +#![no_std] +#![no_main] + +use esp32_hal::{ + clock::ClockControl, + dma::{DmaPriority, DmaTransferRxTx}, + gpio::IO, + pac::Peripherals, + pdma::Dma, + prelude::*, + spi::{dma::WithDmaSpi2, Spi, SpiMode}, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; +use xtensa_lx_rt::entry; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let mut system = peripherals.DPORT.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-C3, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt = timer_group0.wdt; + + wdt.disable(); + rtc.rwdt.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio19; + let miso = io.pins.gpio25; + let mosi = io.pins.gpio23; + let cs = io.pins.gpio22; + + let dma = Dma::new(system.dma, &mut system.peripheral_clock_control); + let dma_channel = dma.spi2channel; + + let mut descriptors = [0u32; 8 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let mut spi = Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ) + .with_dma(dma_channel.configure( + false, + &mut descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + )); + + let mut delay = Delay::new(&clocks); + + // DMA buffer require a static life-time + let mut send = buffer1(); + let mut receive = buffer2(); + let mut i = 0; + + for (i, v) in send.iter_mut().enumerate() { + *v = (i % 255) as u8; + } + + loop { + send[0] = i; + send[send.len() - 1] = i; + i = i.wrapping_add(1); + + let transfer = spi.dma_transfer(send, receive).unwrap(); + // here we could do something else while DMA transfer is in progress + // the buffers and spi is moved into the transfer and we can get it back via + // `wait` + (receive, send, spi) = transfer.wait(); + println!( + "{:x?} .. {:x?}", + &receive[..10], + &receive[receive.len() - 10..] + ); + + delay.delay_ms(250u32); + } +} + +fn buffer1() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} + +fn buffer2() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32-hal/src/lib.rs b/esp32-hal/src/lib.rs index 0c308647539..5974dd08e01 100644 --- a/esp32-hal/src/lib.rs +++ b/esp32-hal/src/lib.rs @@ -4,6 +4,8 @@ pub use embedded_hal as ehal; pub use esp_hal_common::{ clock, cpu_control::CpuControl, + dma, + dma::pdma, efuse, gpio as gpio_types, i2c, diff --git a/esp32c3-hal/examples/spi_loopback_dma.rs b/esp32c3-hal/examples/spi_loopback_dma.rs index fb0520b1475..49e933ac4b2 100644 --- a/esp32c3-hal/examples/spi_loopback_dma.rs +++ b/esp32c3-hal/examples/spi_loopback_dma.rs @@ -18,11 +18,12 @@ use esp32c3_hal::{ clock::ClockControl, - gdma::{DmaPriority, DmaTransferRxTx, Gdma}, + dma::{DmaPriority, DmaTransferRxTx}, + gdma::Gdma, gpio::IO, pac::Peripherals, prelude::*, - spi::{Spi, SpiMode}, + spi::{dma::WithDmaSpi2, Spi, SpiMode}, timer::TimerGroup, Delay, Rtc, @@ -73,14 +74,12 @@ fn main() -> ! { &mut system.peripheral_clock_control, &clocks, ) - .with_tx_rx_dma( - dma_channel - .tx - .get(&mut descriptors, false, DmaPriority::Priority0), - dma_channel - .rx - .get(&mut rx_descriptors, false, DmaPriority::Priority0), - ); + .with_dma(dma_channel.configure( + false, + &mut descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + )); let mut delay = Delay::new(&clocks); diff --git a/esp32c3-hal/src/lib.rs b/esp32c3-hal/src/lib.rs index 3bbbd1d25c6..9efac024a54 100644 --- a/esp32c3-hal/src/lib.rs +++ b/esp32c3-hal/src/lib.rs @@ -5,6 +5,7 @@ use core::arch::global_asm; pub use embedded_hal as ehal; pub use esp_hal_common::{ clock, + dma, dma::gdma, efuse, gpio as gpio_types,