ESP32-S2: implement CryptoDMA for AES (#2699)
* Changelog * Split up PDMA into modules * Add CryptoDMA implementation * Move file * Import SELECT only in the function where we use it --------- Co-authored-by: Juraj Sadel <juraj.sadel@espressif.com>
This commit is contained in:
parent
3cc8d611bd
commit
273fdc0928
@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Peripheral singletons now implement `Debug`, `PartialEq`, `defmt::Format` and `Eq` (except AnyPeripherals) (#2682)
|
||||
- `BurstConfig`, a device-specific configuration for configuring DMA transfers in burst mode (#2543)
|
||||
- `{DmaRxBuf, DmaTxBuf, DmaRxTxBuf}::set_burst_config` (#2543)
|
||||
- ESP32-S2: DMA support for AES (#2699)
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@ -227,7 +227,7 @@ pub enum Endianness {
|
||||
/// transfer, which can significantly speed up operations when dealing with
|
||||
/// large data volumes. It supports various cipher modes such as ECB, CBC, OFB,
|
||||
/// CTR, CFB8, and CFB128.
|
||||
#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||
#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s2, esp32s3))]
|
||||
pub mod dma {
|
||||
use crate::{
|
||||
aes::{Key, Mode},
|
||||
@ -449,7 +449,7 @@ pub mod dma {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(esp32c3, esp32s3))]
|
||||
#[cfg(any(esp32c3, esp32s2, esp32s3))]
|
||||
fn reset_aes(&self) {
|
||||
unsafe {
|
||||
let s = crate::peripherals::SYSTEM::steal();
|
||||
|
||||
@ -897,7 +897,7 @@ pub enum DmaPeripheral {
|
||||
Mem2Mem5 = 5,
|
||||
#[cfg(not(esp32c2))]
|
||||
Aes = 6,
|
||||
#[cfg(gdma)]
|
||||
#[cfg(any(esp32s2, gdma))]
|
||||
Sha = 7,
|
||||
#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||
Adc = 8,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
526
esp-hal/src/dma/pdma/crypto.rs
Normal file
526
esp-hal/src/dma/pdma/crypto.rs
Normal file
@ -0,0 +1,526 @@
|
||||
use portable_atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::{
|
||||
asynch::AtomicWaker,
|
||||
dma::*,
|
||||
interrupt::Priority,
|
||||
peripheral::Peripheral,
|
||||
peripherals::Interrupt,
|
||||
};
|
||||
|
||||
pub(super) type CryptoRegisterBlock = crate::peripherals::crypto_dma::RegisterBlock;
|
||||
|
||||
/// The RX half of a Crypto DMA channel.
|
||||
pub struct CryptoDmaRxChannel(pub(crate) CryptoDmaChannel);
|
||||
|
||||
impl crate::private::Sealed for CryptoDmaRxChannel {}
|
||||
impl DmaRxChannel for CryptoDmaRxChannel {}
|
||||
impl Peripheral for CryptoDmaRxChannel {
|
||||
type P = Self;
|
||||
|
||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||
Self(self.0.clone_unchecked())
|
||||
}
|
||||
}
|
||||
|
||||
/// The TX half of a Crypto DMA channel.
|
||||
pub struct CryptoDmaTxChannel(pub(crate) CryptoDmaChannel);
|
||||
|
||||
impl crate::private::Sealed for CryptoDmaTxChannel {}
|
||||
impl DmaTxChannel for CryptoDmaTxChannel {}
|
||||
impl Peripheral for CryptoDmaTxChannel {
|
||||
type P = Self;
|
||||
|
||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||
Self(self.0.clone_unchecked())
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterAccess for CryptoDmaTxChannel {
|
||||
fn reset(&self) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block.conf().modify(|_, w| {
|
||||
w.out_rst().set_bit();
|
||||
w.ahbm_rst().set_bit();
|
||||
w.ahbm_fifo_rst().set_bit()
|
||||
});
|
||||
register_block.conf().modify(|_, w| {
|
||||
w.out_rst().clear_bit();
|
||||
w.ahbm_rst().clear_bit();
|
||||
w.ahbm_fifo_rst().clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn set_burst_mode(&self, burst_mode: BurstConfig) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.conf()
|
||||
.modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled()));
|
||||
}
|
||||
|
||||
fn set_descr_burst_mode(&self, burst_mode: bool) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.conf()
|
||||
.modify(|_, w| w.outdscr_burst_en().bit(burst_mode));
|
||||
}
|
||||
|
||||
fn set_peripheral(&self, peripheral: u8) {
|
||||
use esp32s2::crypto_dma::aes_sha_select::SELECT;
|
||||
let peripheral = match peripheral {
|
||||
p if p == DmaPeripheral::Aes as u8 => SELECT::Aes,
|
||||
p if p == DmaPeripheral::Sha as u8 => SELECT::Sha,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.aes_sha_select()
|
||||
.modify(|_, w| w.select().variant(peripheral));
|
||||
}
|
||||
|
||||
fn set_link_addr(&self, address: u32) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.out_link()
|
||||
.modify(|_, w| unsafe { w.outlink_addr().bits(address) });
|
||||
}
|
||||
|
||||
fn start(&self) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.out_link()
|
||||
.modify(|_, w| w.outlink_start().set_bit());
|
||||
}
|
||||
|
||||
fn stop(&self) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.out_link()
|
||||
.modify(|_, w| w.outlink_stop().set_bit());
|
||||
}
|
||||
|
||||
fn restart(&self) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.out_link()
|
||||
.modify(|_, w| w.outlink_restart().set_bit());
|
||||
}
|
||||
|
||||
fn set_check_owner(&self, check_owner: Option<bool>) {
|
||||
if check_owner == Some(true) {
|
||||
panic!("Crypto DMA does not support checking descriptor ownership");
|
||||
}
|
||||
}
|
||||
|
||||
fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool {
|
||||
self.0.is_compatible_with(peripheral)
|
||||
}
|
||||
|
||||
#[cfg(psram_dma)]
|
||||
fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.conf1()
|
||||
.modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
|
||||
}
|
||||
|
||||
#[cfg(psram_dma)]
|
||||
fn can_access_psram(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl TxRegisterAccess for CryptoDmaTxChannel {
|
||||
fn set_auto_write_back(&self, enable: bool) {
|
||||
// there is no `auto_wrback` for SPI
|
||||
assert!(!enable);
|
||||
}
|
||||
|
||||
fn last_dscr_address(&self) -> usize {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.out_eof_des_addr()
|
||||
.read()
|
||||
.out_eof_des_addr()
|
||||
.bits() as usize
|
||||
}
|
||||
|
||||
fn peripheral_interrupt(&self) -> Option<Interrupt> {
|
||||
None
|
||||
}
|
||||
|
||||
fn async_handler(&self) -> Option<InterruptHandler> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptAccess<DmaTxInterrupt> for CryptoDmaTxChannel {
|
||||
fn enable_listen(&self, interrupts: EnumSet<DmaTxInterrupt>, enable: bool) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block.int_ena().modify(|_, w| {
|
||||
for interrupt in interrupts {
|
||||
match interrupt {
|
||||
DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable),
|
||||
DmaTxInterrupt::DescriptorError => w.out_dscr_err().bit(enable),
|
||||
DmaTxInterrupt::Eof => w.out_eof().bit(enable),
|
||||
DmaTxInterrupt::Done => w.out_done().bit(enable),
|
||||
};
|
||||
}
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn is_listening(&self) -> EnumSet<DmaTxInterrupt> {
|
||||
let mut result = EnumSet::new();
|
||||
|
||||
let register_block = self.0.register_block();
|
||||
let int_ena = register_block.int_ena().read();
|
||||
if int_ena.out_total_eof().bit_is_set() {
|
||||
result |= DmaTxInterrupt::TotalEof;
|
||||
}
|
||||
if int_ena.out_dscr_err().bit_is_set() {
|
||||
result |= DmaTxInterrupt::DescriptorError;
|
||||
}
|
||||
if int_ena.out_eof().bit_is_set() {
|
||||
result |= DmaTxInterrupt::Eof;
|
||||
}
|
||||
if int_ena.out_done().bit_is_set() {
|
||||
result |= DmaTxInterrupt::Done;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn clear(&self, interrupts: impl Into<EnumSet<DmaTxInterrupt>>) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block.int_clr().write(|w| {
|
||||
for interrupt in interrupts.into() {
|
||||
match interrupt {
|
||||
DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(),
|
||||
DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit_by_one(),
|
||||
DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(),
|
||||
DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(),
|
||||
};
|
||||
}
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn pending_interrupts(&self) -> EnumSet<DmaTxInterrupt> {
|
||||
let mut result = EnumSet::new();
|
||||
|
||||
let register_block = self.0.register_block();
|
||||
let int_raw = register_block.int_raw().read();
|
||||
if int_raw.out_total_eof().bit_is_set() {
|
||||
result |= DmaTxInterrupt::TotalEof;
|
||||
}
|
||||
if int_raw.out_dscr_err().bit_is_set() {
|
||||
result |= DmaTxInterrupt::DescriptorError;
|
||||
}
|
||||
if int_raw.out_eof().bit_is_set() {
|
||||
result |= DmaTxInterrupt::Eof;
|
||||
}
|
||||
if int_raw.out_done().bit_is_set() {
|
||||
result |= DmaTxInterrupt::Done;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn waker(&self) -> &'static AtomicWaker {
|
||||
self.0.tx_waker()
|
||||
}
|
||||
|
||||
fn is_async(&self) -> bool {
|
||||
self.0.tx_async_flag().load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
fn set_async(&self, is_async: bool) {
|
||||
self.0.tx_async_flag().store(is_async, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterAccess for CryptoDmaRxChannel {
|
||||
fn reset(&self) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block.conf().modify(|_, w| {
|
||||
w.in_rst().set_bit();
|
||||
w.ahbm_rst().set_bit();
|
||||
w.ahbm_fifo_rst().set_bit()
|
||||
});
|
||||
register_block.conf().modify(|_, w| {
|
||||
w.in_rst().clear_bit();
|
||||
w.ahbm_rst().clear_bit();
|
||||
w.ahbm_fifo_rst().clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
fn set_burst_mode(&self, _burst_mode: BurstConfig) {}
|
||||
|
||||
fn set_descr_burst_mode(&self, burst_mode: bool) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.conf()
|
||||
.modify(|_, w| w.indscr_burst_en().bit(burst_mode));
|
||||
}
|
||||
|
||||
fn set_peripheral(&self, peripheral: u8) {
|
||||
use esp32s2::crypto_dma::aes_sha_select::SELECT;
|
||||
let peripheral = match peripheral {
|
||||
p if p == DmaPeripheral::Aes as u8 => SELECT::Aes,
|
||||
p if p == DmaPeripheral::Sha as u8 => SELECT::Sha,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.aes_sha_select()
|
||||
.modify(|_, w| w.select().variant(peripheral));
|
||||
}
|
||||
|
||||
fn set_link_addr(&self, address: u32) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.in_link()
|
||||
.modify(|_, w| unsafe { w.inlink_addr().bits(address) });
|
||||
}
|
||||
|
||||
fn start(&self) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.in_link()
|
||||
.modify(|_, w| w.inlink_start().set_bit());
|
||||
}
|
||||
|
||||
fn stop(&self) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.in_link()
|
||||
.modify(|_, w| w.inlink_stop().set_bit());
|
||||
}
|
||||
|
||||
fn restart(&self) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.in_link()
|
||||
.modify(|_, w| w.inlink_restart().set_bit());
|
||||
}
|
||||
|
||||
fn set_check_owner(&self, check_owner: Option<bool>) {
|
||||
if check_owner == Some(true) {
|
||||
panic!("Crypto DMA does not support checking descriptor ownership");
|
||||
}
|
||||
}
|
||||
|
||||
fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool {
|
||||
self.0.is_compatible_with(peripheral)
|
||||
}
|
||||
|
||||
#[cfg(psram_dma)]
|
||||
fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block
|
||||
.conf1()
|
||||
.modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
|
||||
}
|
||||
|
||||
#[cfg(psram_dma)]
|
||||
fn can_access_psram(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl RxRegisterAccess for CryptoDmaRxChannel {
|
||||
fn peripheral_interrupt(&self) -> Option<Interrupt> {
|
||||
// We don't know if the channel is used by AES or SHA, so interrupt handler
|
||||
// setup is the responsibility of the peripheral driver.
|
||||
None
|
||||
}
|
||||
|
||||
fn async_handler(&self) -> Option<InterruptHandler> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptAccess<DmaRxInterrupt> for CryptoDmaRxChannel {
|
||||
fn enable_listen(&self, interrupts: EnumSet<DmaRxInterrupt>, enable: bool) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block.int_ena().modify(|_, w| {
|
||||
for interrupt in interrupts {
|
||||
match interrupt {
|
||||
DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable),
|
||||
DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable),
|
||||
DmaRxInterrupt::DescriptorError => w.in_dscr_err().bit(enable),
|
||||
DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().bit(enable),
|
||||
DmaRxInterrupt::Done => w.in_done().bit(enable),
|
||||
};
|
||||
}
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn is_listening(&self) -> EnumSet<DmaRxInterrupt> {
|
||||
let mut result = EnumSet::new();
|
||||
|
||||
let register_block = self.0.register_block();
|
||||
let int_ena = register_block.int_ena().read();
|
||||
if int_ena.in_dscr_err().bit_is_set() {
|
||||
result |= DmaRxInterrupt::DescriptorError;
|
||||
}
|
||||
if int_ena.in_dscr_err().bit_is_set() {
|
||||
result |= DmaRxInterrupt::DescriptorEmpty;
|
||||
}
|
||||
if int_ena.in_suc_eof().bit_is_set() {
|
||||
result |= DmaRxInterrupt::SuccessfulEof;
|
||||
}
|
||||
if int_ena.in_err_eof().bit_is_set() {
|
||||
result |= DmaRxInterrupt::ErrorEof;
|
||||
}
|
||||
if int_ena.in_done().bit_is_set() {
|
||||
result |= DmaRxInterrupt::Done;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn clear(&self, interrupts: impl Into<EnumSet<DmaRxInterrupt>>) {
|
||||
let register_block = self.0.register_block();
|
||||
register_block.int_clr().write(|w| {
|
||||
for interrupt in interrupts.into() {
|
||||
match interrupt {
|
||||
DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(),
|
||||
DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(),
|
||||
DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit_by_one(),
|
||||
DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit_by_one(),
|
||||
DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(),
|
||||
};
|
||||
}
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn pending_interrupts(&self) -> EnumSet<DmaRxInterrupt> {
|
||||
let mut result = EnumSet::new();
|
||||
|
||||
let register_block = self.0.register_block();
|
||||
let int_raw = register_block.int_raw().read();
|
||||
if int_raw.in_dscr_err().bit_is_set() {
|
||||
result |= DmaRxInterrupt::DescriptorError;
|
||||
}
|
||||
if int_raw.in_dscr_empty().bit_is_set() {
|
||||
result |= DmaRxInterrupt::DescriptorEmpty;
|
||||
}
|
||||
if int_raw.in_suc_eof().bit_is_set() {
|
||||
result |= DmaRxInterrupt::SuccessfulEof;
|
||||
}
|
||||
if int_raw.in_err_eof().bit_is_set() {
|
||||
result |= DmaRxInterrupt::ErrorEof;
|
||||
}
|
||||
if int_raw.in_done().bit_is_set() {
|
||||
result |= DmaRxInterrupt::Done;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn waker(&self) -> &'static AtomicWaker {
|
||||
self.0.rx_waker()
|
||||
}
|
||||
|
||||
fn is_async(&self) -> bool {
|
||||
self.0.rx_async_flag().load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn set_async(&self, _is_async: bool) {
|
||||
self.0.rx_async_flag().store(_is_async, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "DMA channel suitable for CRYPTO"]
|
||||
#[non_exhaustive]
|
||||
pub struct CryptoDmaChannel {}
|
||||
|
||||
impl crate::private::Sealed for CryptoDmaChannel {}
|
||||
|
||||
impl Peripheral for CryptoDmaChannel {
|
||||
type P = Self;
|
||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||
Self::steal()
|
||||
}
|
||||
}
|
||||
impl CryptoDmaChannel {
|
||||
#[doc = r" Unsafely constructs a new DMA channel."]
|
||||
#[doc = r""]
|
||||
#[doc = r" # Safety"]
|
||||
#[doc = r""]
|
||||
#[doc = r" The caller must ensure that only a single instance is used."]
|
||||
pub unsafe fn steal() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
impl DmaChannel for CryptoDmaChannel {
|
||||
type Rx = CryptoDmaRxChannel;
|
||||
type Tx = CryptoDmaTxChannel;
|
||||
unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) {
|
||||
(CryptoDmaRxChannel(Self {}), CryptoDmaTxChannel(Self {}))
|
||||
}
|
||||
}
|
||||
impl DmaChannelExt for CryptoDmaChannel {
|
||||
fn rx_interrupts() -> impl InterruptAccess<DmaRxInterrupt> {
|
||||
CryptoDmaRxChannel(Self {})
|
||||
}
|
||||
fn tx_interrupts() -> impl InterruptAccess<DmaTxInterrupt> {
|
||||
CryptoDmaTxChannel(Self {})
|
||||
}
|
||||
}
|
||||
impl PdmaChannel for CryptoDmaChannel {
|
||||
type RegisterBlock = CryptoRegisterBlock;
|
||||
fn register_block(&self) -> &Self::RegisterBlock {
|
||||
unsafe { &*crate::peripherals::CRYPTO_DMA::PTR }
|
||||
}
|
||||
fn tx_waker(&self) -> &'static AtomicWaker {
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
&WAKER
|
||||
}
|
||||
fn rx_waker(&self) -> &'static AtomicWaker {
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
&WAKER
|
||||
}
|
||||
fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool {
|
||||
let compatible_peripherals = [DmaPeripheral::Aes, DmaPeripheral::Sha];
|
||||
compatible_peripherals.contains(&peripheral)
|
||||
}
|
||||
fn peripheral_interrupt(&self) -> Interrupt {
|
||||
unreachable!("Crypto DMA has separate interrupts specific to AES and SHA")
|
||||
}
|
||||
fn async_handler(&self) -> InterruptHandler {
|
||||
pub(crate) extern "C" fn __esp_hal_internal_interrupt_handler() {
|
||||
super::asynch::handle_in_interrupt::<CryptoDmaChannel>();
|
||||
super::asynch::handle_out_interrupt::<CryptoDmaChannel>();
|
||||
}
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub(crate) static interrupt_handler: crate::interrupt::InterruptHandler =
|
||||
crate::interrupt::InterruptHandler::new(
|
||||
__esp_hal_internal_interrupt_handler,
|
||||
Priority::max(),
|
||||
);
|
||||
interrupt_handler
|
||||
}
|
||||
fn rx_async_flag(&self) -> &'static AtomicBool {
|
||||
static FLAG: AtomicBool = AtomicBool::new(false);
|
||||
&FLAG
|
||||
}
|
||||
fn tx_async_flag(&self) -> &'static AtomicBool {
|
||||
static FLAG: AtomicBool = AtomicBool::new(false);
|
||||
&FLAG
|
||||
}
|
||||
}
|
||||
impl DmaChannelConvert<CryptoDmaRxChannel> for CryptoDmaChannel {
|
||||
fn degrade(self) -> CryptoDmaRxChannel {
|
||||
CryptoDmaRxChannel(self)
|
||||
}
|
||||
}
|
||||
impl DmaChannelConvert<CryptoDmaTxChannel> for CryptoDmaChannel {
|
||||
fn degrade(self) -> CryptoDmaTxChannel {
|
||||
CryptoDmaTxChannel(self)
|
||||
}
|
||||
}
|
||||
436
esp-hal/src/dma/pdma/i2s.rs
Normal file
436
esp-hal/src/dma/pdma/i2s.rs
Normal file
@ -0,0 +1,436 @@
|
||||
use portable_atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::{asynch::AtomicWaker, dma::*, peripheral::Peripheral, peripherals::Interrupt};
|
||||
|
||||
pub(super) type I2sRegisterBlock = crate::peripherals::i2s0::RegisterBlock;
|
||||
|
||||
/// The RX half of an arbitrary I2S DMA channel.
|
||||
pub struct AnyI2sDmaRxChannel(pub(crate) AnyI2sDmaChannel);
|
||||
|
||||
impl crate::private::Sealed for AnyI2sDmaRxChannel {}
|
||||
impl DmaRxChannel for AnyI2sDmaRxChannel {}
|
||||
impl Peripheral for AnyI2sDmaRxChannel {
|
||||
type P = Self;
|
||||
|
||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||
Self(self.0.clone_unchecked())
|
||||
}
|
||||
}
|
||||
|
||||
/// The TX half of an arbitrary I2S DMA channel.
|
||||
pub struct AnyI2sDmaTxChannel(pub(crate) AnyI2sDmaChannel);
|
||||
|
||||
impl crate::private::Sealed for AnyI2sDmaTxChannel {}
|
||||
impl DmaTxChannel for AnyI2sDmaTxChannel {}
|
||||
impl Peripheral for AnyI2sDmaTxChannel {
|
||||
type P = Self;
|
||||
|
||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||
Self(self.0.clone_unchecked())
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterAccess for AnyI2sDmaTxChannel {
|
||||
fn reset(&self) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block.lc_conf().modify(|_, w| w.out_rst().set_bit());
|
||||
reg_block.lc_conf().modify(|_, w| w.out_rst().clear_bit());
|
||||
}
|
||||
|
||||
fn set_burst_mode(&self, burst_mode: BurstConfig) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.lc_conf()
|
||||
.modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled()));
|
||||
}
|
||||
|
||||
fn set_descr_burst_mode(&self, burst_mode: bool) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.lc_conf()
|
||||
.modify(|_, w| w.outdscr_burst_en().bit(burst_mode));
|
||||
}
|
||||
|
||||
fn set_link_addr(&self, address: u32) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.out_link()
|
||||
.modify(|_, w| unsafe { w.outlink_addr().bits(address) });
|
||||
}
|
||||
|
||||
fn set_peripheral(&self, _peripheral: u8) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
fn start(&self) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.out_link()
|
||||
.modify(|_, w| w.outlink_start().set_bit());
|
||||
}
|
||||
|
||||
fn stop(&self) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.out_link()
|
||||
.modify(|_, w| w.outlink_stop().set_bit());
|
||||
}
|
||||
|
||||
fn restart(&self) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.out_link()
|
||||
.modify(|_, w| w.outlink_restart().set_bit());
|
||||
}
|
||||
|
||||
fn set_check_owner(&self, check_owner: Option<bool>) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.lc_conf()
|
||||
.modify(|_, w| w.check_owner().bit(check_owner.unwrap_or(true)));
|
||||
}
|
||||
|
||||
fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool {
|
||||
self.0.is_compatible_with(peripheral)
|
||||
}
|
||||
|
||||
#[cfg(psram_dma)]
|
||||
fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) {
|
||||
let spi = self.0.register_block();
|
||||
spi.lc_conf()
|
||||
.modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
|
||||
}
|
||||
|
||||
#[cfg(psram_dma)]
|
||||
fn can_access_psram(&self) -> bool {
|
||||
matches!(self.0, AnyI2sDmaChannel(AnyI2sDmaChannelInner::I2s0(_)))
|
||||
}
|
||||
}
|
||||
|
||||
impl TxRegisterAccess for AnyI2sDmaTxChannel {
|
||||
fn set_auto_write_back(&self, enable: bool) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.lc_conf()
|
||||
.modify(|_, w| w.out_auto_wrback().bit(enable));
|
||||
}
|
||||
|
||||
fn last_dscr_address(&self) -> usize {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.out_eof_des_addr()
|
||||
.read()
|
||||
.out_eof_des_addr()
|
||||
.bits() as usize
|
||||
}
|
||||
|
||||
fn peripheral_interrupt(&self) -> Option<Interrupt> {
|
||||
None
|
||||
}
|
||||
|
||||
fn async_handler(&self) -> Option<InterruptHandler> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptAccess<DmaTxInterrupt> for AnyI2sDmaTxChannel {
|
||||
fn enable_listen(&self, interrupts: EnumSet<DmaTxInterrupt>, enable: bool) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block.int_ena().modify(|_, w| {
|
||||
for interrupt in interrupts {
|
||||
match interrupt {
|
||||
DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable),
|
||||
DmaTxInterrupt::DescriptorError => w.out_dscr_err().bit(enable),
|
||||
DmaTxInterrupt::Eof => w.out_eof().bit(enable),
|
||||
DmaTxInterrupt::Done => w.out_done().bit(enable),
|
||||
};
|
||||
}
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn is_listening(&self) -> EnumSet<DmaTxInterrupt> {
|
||||
let mut result = EnumSet::new();
|
||||
|
||||
let reg_block = self.0.register_block();
|
||||
let int_ena = reg_block.int_ena().read();
|
||||
if int_ena.out_total_eof().bit_is_set() {
|
||||
result |= DmaTxInterrupt::TotalEof;
|
||||
}
|
||||
if int_ena.out_dscr_err().bit_is_set() {
|
||||
result |= DmaTxInterrupt::DescriptorError;
|
||||
}
|
||||
if int_ena.out_eof().bit_is_set() {
|
||||
result |= DmaTxInterrupt::Eof;
|
||||
}
|
||||
if int_ena.out_done().bit_is_set() {
|
||||
result |= DmaTxInterrupt::Done;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn pending_interrupts(&self) -> EnumSet<DmaTxInterrupt> {
|
||||
let mut result = EnumSet::new();
|
||||
|
||||
let reg_block = self.0.register_block();
|
||||
let int_raw = reg_block.int_raw().read();
|
||||
if int_raw.out_total_eof().bit_is_set() {
|
||||
result |= DmaTxInterrupt::TotalEof;
|
||||
}
|
||||
if int_raw.out_dscr_err().bit_is_set() {
|
||||
result |= DmaTxInterrupt::DescriptorError;
|
||||
}
|
||||
if int_raw.out_eof().bit_is_set() {
|
||||
result |= DmaTxInterrupt::Eof;
|
||||
}
|
||||
if int_raw.out_done().bit_is_set() {
|
||||
result |= DmaTxInterrupt::Done;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn clear(&self, interrupts: impl Into<EnumSet<DmaTxInterrupt>>) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block.int_clr().write(|w| {
|
||||
for interrupt in interrupts.into() {
|
||||
match interrupt {
|
||||
DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(),
|
||||
DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit_by_one(),
|
||||
DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(),
|
||||
DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(),
|
||||
};
|
||||
}
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn waker(&self) -> &'static AtomicWaker {
|
||||
self.0.tx_waker()
|
||||
}
|
||||
|
||||
fn is_async(&self) -> bool {
|
||||
self.0.tx_async_flag().load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn set_async(&self, _is_async: bool) {
|
||||
self.0.tx_async_flag().store(_is_async, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterAccess for AnyI2sDmaRxChannel {
|
||||
fn reset(&self) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block.lc_conf().modify(|_, w| w.in_rst().set_bit());
|
||||
reg_block.lc_conf().modify(|_, w| w.in_rst().clear_bit());
|
||||
}
|
||||
|
||||
fn set_burst_mode(&self, _burst_mode: BurstConfig) {}
|
||||
|
||||
fn set_descr_burst_mode(&self, burst_mode: bool) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.lc_conf()
|
||||
.modify(|_, w| w.indscr_burst_en().bit(burst_mode));
|
||||
}
|
||||
|
||||
fn set_link_addr(&self, address: u32) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.in_link()
|
||||
.modify(|_, w| unsafe { w.inlink_addr().bits(address) });
|
||||
}
|
||||
|
||||
fn set_peripheral(&self, _peripheral: u8) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
fn start(&self) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.in_link()
|
||||
.modify(|_, w| w.inlink_start().set_bit());
|
||||
}
|
||||
|
||||
fn stop(&self) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block.in_link().modify(|_, w| w.inlink_stop().set_bit());
|
||||
}
|
||||
|
||||
fn restart(&self) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.in_link()
|
||||
.modify(|_, w| w.inlink_restart().set_bit());
|
||||
}
|
||||
|
||||
fn set_check_owner(&self, check_owner: Option<bool>) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block
|
||||
.lc_conf()
|
||||
.modify(|_, w| w.check_owner().bit(check_owner.unwrap_or(true)));
|
||||
}
|
||||
|
||||
fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool {
|
||||
self.0.is_compatible_with(peripheral)
|
||||
}
|
||||
|
||||
#[cfg(psram_dma)]
|
||||
fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) {
|
||||
let spi = self.0.register_block();
|
||||
spi.lc_conf()
|
||||
.modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
|
||||
}
|
||||
|
||||
#[cfg(psram_dma)]
|
||||
fn can_access_psram(&self) -> bool {
|
||||
matches!(self.0, AnyI2sDmaChannel(AnyI2sDmaChannelInner::I2s0(_)))
|
||||
}
|
||||
}
|
||||
|
||||
impl RxRegisterAccess for AnyI2sDmaRxChannel {
|
||||
fn peripheral_interrupt(&self) -> Option<Interrupt> {
|
||||
Some(self.0.peripheral_interrupt())
|
||||
}
|
||||
|
||||
fn async_handler(&self) -> Option<InterruptHandler> {
|
||||
Some(self.0.async_handler())
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptAccess<DmaRxInterrupt> for AnyI2sDmaRxChannel {
|
||||
fn enable_listen(&self, interrupts: EnumSet<DmaRxInterrupt>, enable: bool) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block.int_ena().modify(|_, w| {
|
||||
for interrupt in interrupts {
|
||||
match interrupt {
|
||||
DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable),
|
||||
DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable),
|
||||
DmaRxInterrupt::DescriptorError => w.in_dscr_err().bit(enable),
|
||||
DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().bit(enable),
|
||||
DmaRxInterrupt::Done => w.in_done().bit(enable),
|
||||
};
|
||||
}
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn is_listening(&self) -> EnumSet<DmaRxInterrupt> {
|
||||
let mut result = EnumSet::new();
|
||||
|
||||
let reg_block = self.0.register_block();
|
||||
let int_ena = reg_block.int_ena().read();
|
||||
if int_ena.in_dscr_err().bit_is_set() {
|
||||
result |= DmaRxInterrupt::DescriptorError;
|
||||
}
|
||||
if int_ena.in_dscr_empty().bit_is_set() {
|
||||
result |= DmaRxInterrupt::DescriptorEmpty;
|
||||
}
|
||||
if int_ena.in_suc_eof().bit_is_set() {
|
||||
result |= DmaRxInterrupt::SuccessfulEof;
|
||||
}
|
||||
if int_ena.in_err_eof().bit_is_set() {
|
||||
result |= DmaRxInterrupt::ErrorEof;
|
||||
}
|
||||
if int_ena.in_done().bit_is_set() {
|
||||
result |= DmaRxInterrupt::Done;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn pending_interrupts(&self) -> EnumSet<DmaRxInterrupt> {
|
||||
let mut result = EnumSet::new();
|
||||
|
||||
let reg_block = self.0.register_block();
|
||||
let int_raw = reg_block.int_raw().read();
|
||||
if int_raw.in_dscr_err().bit_is_set() {
|
||||
result |= DmaRxInterrupt::DescriptorError;
|
||||
}
|
||||
if int_raw.in_dscr_empty().bit_is_set() {
|
||||
result |= DmaRxInterrupt::DescriptorEmpty;
|
||||
}
|
||||
if int_raw.in_suc_eof().bit_is_set() {
|
||||
result |= DmaRxInterrupt::SuccessfulEof;
|
||||
}
|
||||
if int_raw.in_err_eof().bit_is_set() {
|
||||
result |= DmaRxInterrupt::ErrorEof;
|
||||
}
|
||||
if int_raw.in_done().bit_is_set() {
|
||||
result |= DmaRxInterrupt::Done;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn clear(&self, interrupts: impl Into<EnumSet<DmaRxInterrupt>>) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block.int_clr().write(|w| {
|
||||
for interrupt in interrupts.into() {
|
||||
match interrupt {
|
||||
DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(),
|
||||
DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(),
|
||||
DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit_by_one(),
|
||||
DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit_by_one(),
|
||||
DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(),
|
||||
};
|
||||
}
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn waker(&self) -> &'static AtomicWaker {
|
||||
self.0.rx_waker()
|
||||
}
|
||||
|
||||
fn is_async(&self) -> bool {
|
||||
self.0.rx_async_flag().load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn set_async(&self, _is_async: bool) {
|
||||
self.0.rx_async_flag().store(_is_async, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
crate::any_peripheral! {
|
||||
/// An I2S-compatible type-erased DMA channel.
|
||||
pub peripheral AnyI2sDmaChannel {
|
||||
I2s0(I2s0DmaChannel),
|
||||
#[cfg(i2s1)]
|
||||
I2s1(I2s1DmaChannel),
|
||||
}
|
||||
}
|
||||
|
||||
impl DmaChannel for AnyI2sDmaChannel {
|
||||
type Rx = AnyI2sDmaRxChannel;
|
||||
type Tx = AnyI2sDmaTxChannel;
|
||||
|
||||
unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) {
|
||||
(
|
||||
AnyI2sDmaRxChannel(unsafe { self.clone_unchecked() }),
|
||||
AnyI2sDmaTxChannel(unsafe { self.clone_unchecked() }),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PdmaChannel for AnyI2sDmaChannel {
|
||||
type RegisterBlock = I2sRegisterBlock;
|
||||
|
||||
delegate::delegate! {
|
||||
to match &self.0 {
|
||||
AnyI2sDmaChannelInner::I2s0(channel) => channel,
|
||||
#[cfg(i2s1)]
|
||||
AnyI2sDmaChannelInner::I2s1(channel) => channel,
|
||||
} {
|
||||
fn register_block(&self) -> &I2sRegisterBlock;
|
||||
fn tx_waker(&self) -> &'static AtomicWaker;
|
||||
fn rx_waker(&self) -> &'static AtomicWaker;
|
||||
fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool;
|
||||
fn peripheral_interrupt(&self) -> Interrupt;
|
||||
fn async_handler(&self) -> InterruptHandler;
|
||||
fn rx_async_flag(&self) -> &'static AtomicBool;
|
||||
fn tx_async_flag(&self) -> &'static AtomicBool;
|
||||
}
|
||||
}
|
||||
}
|
||||
212
esp-hal/src/dma/pdma/mod.rs
Normal file
212
esp-hal/src/dma/pdma/mod.rs
Normal file
@ -0,0 +1,212 @@
|
||||
//! # Direct Memory Access
|
||||
//!
|
||||
//! ## Overview
|
||||
//! The `pdma` module is part of the DMA driver of `ESP32` and `ESP32-S2`.
|
||||
//!
|
||||
//! This module provides efficient direct data transfer capabilities between
|
||||
//! peripherals and memory without involving the CPU. It enables bidirectional
|
||||
//! data transfers through DMA channels, making it particularly useful for
|
||||
//! high-speed data transfers, such as [SPI] and [I2S] communication.
|
||||
//!
|
||||
//! [SPI]: ../spi/index.html
|
||||
//! [I2S]: ../i2s/index.html
|
||||
|
||||
use critical_section::CriticalSection;
|
||||
use portable_atomic::AtomicBool;
|
||||
|
||||
use crate::{
|
||||
asynch::AtomicWaker,
|
||||
dma::*,
|
||||
interrupt::Priority,
|
||||
macros::handler,
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
peripherals::Interrupt,
|
||||
};
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
mod crypto;
|
||||
mod i2s;
|
||||
mod spi;
|
||||
|
||||
#[cfg(esp32s2)]
|
||||
pub use crypto::*;
|
||||
pub use i2s::*;
|
||||
pub use spi::*;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait PdmaChannel: crate::private::Sealed {
|
||||
type RegisterBlock;
|
||||
|
||||
fn register_block(&self) -> &Self::RegisterBlock;
|
||||
fn tx_waker(&self) -> &'static AtomicWaker;
|
||||
fn rx_waker(&self) -> &'static AtomicWaker;
|
||||
fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool;
|
||||
|
||||
fn peripheral_interrupt(&self) -> Interrupt;
|
||||
fn async_handler(&self) -> InterruptHandler;
|
||||
fn rx_async_flag(&self) -> &'static AtomicBool;
|
||||
fn tx_async_flag(&self) -> &'static AtomicBool;
|
||||
}
|
||||
|
||||
macro_rules! impl_pdma_channel {
|
||||
($peri:ident, $register_block:ident, $instance:ident, $int:ident, [$($compatible:ident),*]) => {
|
||||
paste::paste! {
|
||||
#[doc = concat!("DMA channel suitable for ", stringify!([< $instance:upper >]))]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct [<$instance DmaChannel>] {}
|
||||
|
||||
impl $crate::private::Sealed for [<$instance DmaChannel>] {}
|
||||
|
||||
impl Peripheral for [<$instance DmaChannel>] {
|
||||
type P = Self;
|
||||
|
||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||
Self::steal()
|
||||
}
|
||||
}
|
||||
|
||||
impl [<$instance DmaChannel>] {
|
||||
/// Unsafely constructs a new DMA channel.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that only a single instance is used.
|
||||
pub unsafe fn steal() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl DmaChannel for [<$instance DmaChannel>] {
|
||||
type Rx = [<$peri DmaRxChannel>];
|
||||
type Tx = [<$peri DmaTxChannel>];
|
||||
|
||||
unsafe fn split_internal(self, _: $crate::private::Internal) -> (Self::Rx, Self::Tx) {
|
||||
([<$peri DmaRxChannel>](Self {}.into()), [<$peri DmaTxChannel>](Self {}.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl DmaChannelExt for [<$instance DmaChannel>] {
|
||||
fn rx_interrupts() -> impl InterruptAccess<DmaRxInterrupt> {
|
||||
[<$peri DmaRxChannel>](Self {}.into())
|
||||
}
|
||||
fn tx_interrupts() -> impl InterruptAccess<DmaTxInterrupt> {
|
||||
[<$peri DmaTxChannel>](Self {}.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl PdmaChannel for [<$instance DmaChannel>] {
|
||||
type RegisterBlock = $register_block;
|
||||
|
||||
fn register_block(&self) -> &Self::RegisterBlock {
|
||||
unsafe { &*crate::peripherals::[< $instance:upper >]::PTR }
|
||||
}
|
||||
fn tx_waker(&self) -> &'static AtomicWaker {
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
&WAKER
|
||||
}
|
||||
fn rx_waker(&self) -> &'static AtomicWaker {
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
&WAKER
|
||||
}
|
||||
fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool {
|
||||
let compatible_peripherals = [$(DmaPeripheral::$compatible),*];
|
||||
compatible_peripherals.contains(&peripheral)
|
||||
}
|
||||
|
||||
fn peripheral_interrupt(&self) -> Interrupt {
|
||||
Interrupt::$int
|
||||
}
|
||||
|
||||
fn async_handler(&self) -> InterruptHandler {
|
||||
#[handler(priority = Priority::max())]
|
||||
pub(crate) fn interrupt_handler() {
|
||||
super::asynch::handle_in_interrupt::<[< $instance DmaChannel >]>();
|
||||
super::asynch::handle_out_interrupt::<[< $instance DmaChannel >]>();
|
||||
}
|
||||
|
||||
interrupt_handler
|
||||
}
|
||||
fn rx_async_flag(&self) -> &'static AtomicBool {
|
||||
static FLAG: AtomicBool = AtomicBool::new(false);
|
||||
&FLAG
|
||||
}
|
||||
fn tx_async_flag(&self) -> &'static AtomicBool {
|
||||
static FLAG: AtomicBool = AtomicBool::new(false);
|
||||
&FLAG
|
||||
}
|
||||
}
|
||||
|
||||
impl DmaChannelConvert<[<$peri DmaChannel>]> for [<$instance DmaChannel>] {
|
||||
fn degrade(self) -> [<$peri DmaChannel>] {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl DmaChannelConvert<[<$peri DmaRxChannel>]> for [<$instance DmaChannel>] {
|
||||
fn degrade(self) -> [<$peri DmaRxChannel>] {
|
||||
[<$peri DmaRxChannel>](self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl DmaChannelConvert<[<$peri DmaTxChannel>]> for [<$instance DmaChannel>] {
|
||||
fn degrade(self) -> [<$peri DmaTxChannel>] {
|
||||
[<$peri DmaTxChannel>](self.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_pdma_channel!(AnySpi, SpiRegisterBlock, Spi2, SPI2_DMA, [Spi2]);
|
||||
impl_pdma_channel!(AnySpi, SpiRegisterBlock, Spi3, SPI3_DMA, [Spi3]);
|
||||
|
||||
impl_pdma_channel!(AnyI2s, I2sRegisterBlock, I2s0, I2S0, [I2s0]);
|
||||
#[cfg(i2s1)]
|
||||
impl_pdma_channel!(AnyI2s, I2sRegisterBlock, I2s1, I2S1, [I2s1]);
|
||||
|
||||
// Specific peripherals use specific channels. Note that this may be overly
|
||||
// restrictive (ESP32 allows configuring 2 SPI DMA channels between 3 different
|
||||
// peripherals), but for the current set of restrictions this is sufficient.
|
||||
crate::dma::impl_dma_eligible!([Spi2DmaChannel] SPI2 => Spi2);
|
||||
crate::dma::impl_dma_eligible!([Spi3DmaChannel] SPI3 => Spi3);
|
||||
crate::dma::impl_dma_eligible!([I2s0DmaChannel] I2S0 => I2s0);
|
||||
#[cfg(i2s1)]
|
||||
crate::dma::impl_dma_eligible!([I2s1DmaChannel] I2S1 => I2s1);
|
||||
#[cfg(esp32s2)]
|
||||
crate::dma::impl_dma_eligible!([CryptoDmaChannel] AES => Aes);
|
||||
#[cfg(esp32s2)]
|
||||
crate::dma::impl_dma_eligible!([CryptoDmaChannel] SHA => Sha);
|
||||
|
||||
pub(super) fn init_dma(_cs: CriticalSection<'_>) {
|
||||
#[cfg(esp32)]
|
||||
{
|
||||
// (only) on ESP32 we need to configure DPORT for the SPI DMA channels
|
||||
// This assignes the DMA channels to the SPI peripherals, which is more
|
||||
// restrictive than necessary but we currently support the same
|
||||
// number of SPI peripherals as SPI DMA channels so it's not a big
|
||||
// deal.
|
||||
let dport = unsafe { crate::peripherals::DPORT::steal() };
|
||||
dport
|
||||
.spi_dma_chan_sel()
|
||||
.modify(|_, w| unsafe { w.spi2_dma_chan_sel().bits(1).spi3_dma_chan_sel().bits(2) });
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, CH, M> Channel<'d, M, CH>
|
||||
where
|
||||
CH: DmaChannel,
|
||||
M: Mode,
|
||||
{
|
||||
/// Asserts that the channel is compatible with the given peripheral.
|
||||
pub fn runtime_ensure_compatible(&self, peripheral: &PeripheralRef<'_, impl DmaEligible>) {
|
||||
assert!(
|
||||
self.tx
|
||||
.tx_impl
|
||||
.is_compatible_with(peripheral.dma_peripheral()),
|
||||
"This DMA channel is not compatible with {:?}",
|
||||
peripheral.dma_peripheral()
|
||||
);
|
||||
}
|
||||
}
|
||||
414
esp-hal/src/dma/pdma/spi.rs
Normal file
414
esp-hal/src/dma/pdma/spi.rs
Normal file
@ -0,0 +1,414 @@
|
||||
use portable_atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::{asynch::AtomicWaker, dma::*, peripheral::Peripheral, peripherals::Interrupt};
|
||||
|
||||
pub(super) type SpiRegisterBlock = crate::peripherals::spi2::RegisterBlock;
|
||||
|
||||
/// The RX half of an arbitrary SPI DMA channel.
|
||||
pub struct AnySpiDmaRxChannel(pub(crate) AnySpiDmaChannel);
|
||||
|
||||
impl crate::private::Sealed for AnySpiDmaRxChannel {}
|
||||
impl DmaRxChannel for AnySpiDmaRxChannel {}
|
||||
impl Peripheral for AnySpiDmaRxChannel {
|
||||
type P = Self;
|
||||
|
||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||
Self(self.0.clone_unchecked())
|
||||
}
|
||||
}
|
||||
|
||||
/// The TX half of an arbitrary SPI DMA channel.
|
||||
pub struct AnySpiDmaTxChannel(pub(crate) AnySpiDmaChannel);
|
||||
|
||||
impl crate::private::Sealed for AnySpiDmaTxChannel {}
|
||||
impl DmaTxChannel for AnySpiDmaTxChannel {}
|
||||
impl Peripheral for AnySpiDmaTxChannel {
|
||||
type P = Self;
|
||||
|
||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||
Self(self.0.clone_unchecked())
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterAccess for AnySpiDmaTxChannel {
|
||||
fn reset(&self) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_conf().modify(|_, w| w.out_rst().set_bit());
|
||||
spi.dma_conf().modify(|_, w| w.out_rst().clear_bit());
|
||||
}
|
||||
|
||||
fn set_burst_mode(&self, burst_mode: BurstConfig) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_conf()
|
||||
.modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled()));
|
||||
}
|
||||
|
||||
fn set_descr_burst_mode(&self, burst_mode: bool) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_conf()
|
||||
.modify(|_, w| w.outdscr_burst_en().bit(burst_mode));
|
||||
}
|
||||
|
||||
fn set_peripheral(&self, _peripheral: u8) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
fn set_link_addr(&self, address: u32) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_out_link()
|
||||
.modify(|_, w| unsafe { w.outlink_addr().bits(address) });
|
||||
}
|
||||
|
||||
fn start(&self) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_out_link()
|
||||
.modify(|_, w| w.outlink_start().set_bit());
|
||||
}
|
||||
|
||||
fn stop(&self) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_out_link().modify(|_, w| w.outlink_stop().set_bit());
|
||||
}
|
||||
|
||||
fn restart(&self) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_out_link()
|
||||
.modify(|_, w| w.outlink_restart().set_bit());
|
||||
}
|
||||
|
||||
fn set_check_owner(&self, check_owner: Option<bool>) {
|
||||
if check_owner == Some(true) {
|
||||
panic!("SPI DMA does not support checking descriptor ownership");
|
||||
}
|
||||
}
|
||||
|
||||
fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool {
|
||||
self.0.is_compatible_with(peripheral)
|
||||
}
|
||||
|
||||
#[cfg(psram_dma)]
|
||||
fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_conf()
|
||||
.modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
|
||||
}
|
||||
|
||||
#[cfg(psram_dma)]
|
||||
fn can_access_psram(&self) -> bool {
|
||||
matches!(self.0, AnySpiDmaChannel(AnySpiDmaChannelInner::Spi2(_)))
|
||||
}
|
||||
}
|
||||
|
||||
impl TxRegisterAccess for AnySpiDmaTxChannel {
|
||||
fn set_auto_write_back(&self, enable: bool) {
|
||||
// there is no `auto_wrback` for SPI
|
||||
assert!(!enable);
|
||||
}
|
||||
|
||||
fn last_dscr_address(&self) -> usize {
|
||||
let spi = self.0.register_block();
|
||||
spi.out_eof_des_addr().read().dma_out_eof_des_addr().bits() as usize
|
||||
}
|
||||
|
||||
fn peripheral_interrupt(&self) -> Option<Interrupt> {
|
||||
None
|
||||
}
|
||||
|
||||
fn async_handler(&self) -> Option<InterruptHandler> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptAccess<DmaTxInterrupt> for AnySpiDmaTxChannel {
|
||||
fn enable_listen(&self, interrupts: EnumSet<DmaTxInterrupt>, enable: bool) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block.dma_int_ena().modify(|_, w| {
|
||||
for interrupt in interrupts {
|
||||
match interrupt {
|
||||
DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable),
|
||||
DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().bit(enable),
|
||||
DmaTxInterrupt::Eof => w.out_eof().bit(enable),
|
||||
DmaTxInterrupt::Done => w.out_done().bit(enable),
|
||||
};
|
||||
}
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn is_listening(&self) -> EnumSet<DmaTxInterrupt> {
|
||||
let mut result = EnumSet::new();
|
||||
|
||||
let spi = self.0.register_block();
|
||||
let int_ena = spi.dma_int_ena().read();
|
||||
if int_ena.out_total_eof().bit_is_set() {
|
||||
result |= DmaTxInterrupt::TotalEof;
|
||||
}
|
||||
if int_ena.outlink_dscr_error().bit_is_set() {
|
||||
result |= DmaTxInterrupt::DescriptorError;
|
||||
}
|
||||
if int_ena.out_eof().bit_is_set() {
|
||||
result |= DmaTxInterrupt::Eof;
|
||||
}
|
||||
if int_ena.out_done().bit_is_set() {
|
||||
result |= DmaTxInterrupt::Done;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn clear(&self, interrupts: impl Into<EnumSet<DmaTxInterrupt>>) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_int_clr().write(|w| {
|
||||
for interrupt in interrupts.into() {
|
||||
match interrupt {
|
||||
DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(),
|
||||
DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().clear_bit_by_one(),
|
||||
DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(),
|
||||
DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(),
|
||||
};
|
||||
}
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn pending_interrupts(&self) -> EnumSet<DmaTxInterrupt> {
|
||||
let mut result = EnumSet::new();
|
||||
|
||||
let spi = self.0.register_block();
|
||||
let int_raw = spi.dma_int_raw().read();
|
||||
if int_raw.out_total_eof().bit_is_set() {
|
||||
result |= DmaTxInterrupt::TotalEof;
|
||||
}
|
||||
if int_raw.outlink_dscr_error().bit_is_set() {
|
||||
result |= DmaTxInterrupt::DescriptorError;
|
||||
}
|
||||
if int_raw.out_eof().bit_is_set() {
|
||||
result |= DmaTxInterrupt::Eof;
|
||||
}
|
||||
if int_raw.out_done().bit_is_set() {
|
||||
result |= DmaTxInterrupt::Done;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn waker(&self) -> &'static AtomicWaker {
|
||||
self.0.tx_waker()
|
||||
}
|
||||
|
||||
fn is_async(&self) -> bool {
|
||||
self.0.tx_async_flag().load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
fn set_async(&self, is_async: bool) {
|
||||
self.0.tx_async_flag().store(is_async, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterAccess for AnySpiDmaRxChannel {
|
||||
fn reset(&self) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_conf().modify(|_, w| w.in_rst().set_bit());
|
||||
spi.dma_conf().modify(|_, w| w.in_rst().clear_bit());
|
||||
}
|
||||
|
||||
fn set_burst_mode(&self, _burst_mode: BurstConfig) {}
|
||||
|
||||
fn set_descr_burst_mode(&self, burst_mode: bool) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_conf()
|
||||
.modify(|_, w| w.indscr_burst_en().bit(burst_mode));
|
||||
}
|
||||
|
||||
fn set_peripheral(&self, _peripheral: u8) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
fn set_link_addr(&self, address: u32) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_in_link()
|
||||
.modify(|_, w| unsafe { w.inlink_addr().bits(address) });
|
||||
}
|
||||
|
||||
fn start(&self) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_in_link().modify(|_, w| w.inlink_start().set_bit());
|
||||
}
|
||||
|
||||
fn stop(&self) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_in_link().modify(|_, w| w.inlink_stop().set_bit());
|
||||
}
|
||||
|
||||
fn restart(&self) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_in_link()
|
||||
.modify(|_, w| w.inlink_restart().set_bit());
|
||||
}
|
||||
|
||||
fn set_check_owner(&self, check_owner: Option<bool>) {
|
||||
if check_owner == Some(true) {
|
||||
panic!("SPI DMA does not support checking descriptor ownership");
|
||||
}
|
||||
}
|
||||
|
||||
fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool {
|
||||
self.0.is_compatible_with(peripheral)
|
||||
}
|
||||
|
||||
#[cfg(psram_dma)]
|
||||
fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_conf()
|
||||
.modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) });
|
||||
}
|
||||
|
||||
#[cfg(psram_dma)]
|
||||
fn can_access_psram(&self) -> bool {
|
||||
matches!(self.0, AnySpiDmaChannel(AnySpiDmaChannelInner::Spi2(_)))
|
||||
}
|
||||
}
|
||||
|
||||
impl RxRegisterAccess for AnySpiDmaRxChannel {
|
||||
fn peripheral_interrupt(&self) -> Option<Interrupt> {
|
||||
Some(self.0.peripheral_interrupt())
|
||||
}
|
||||
|
||||
fn async_handler(&self) -> Option<InterruptHandler> {
|
||||
Some(self.0.async_handler())
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptAccess<DmaRxInterrupt> for AnySpiDmaRxChannel {
|
||||
fn enable_listen(&self, interrupts: EnumSet<DmaRxInterrupt>, enable: bool) {
|
||||
let reg_block = self.0.register_block();
|
||||
reg_block.dma_int_ena().modify(|_, w| {
|
||||
for interrupt in interrupts {
|
||||
match interrupt {
|
||||
DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable),
|
||||
DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable),
|
||||
DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().bit(enable),
|
||||
DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().bit(enable),
|
||||
DmaRxInterrupt::Done => w.in_done().bit(enable),
|
||||
};
|
||||
}
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn is_listening(&self) -> EnumSet<DmaRxInterrupt> {
|
||||
let mut result = EnumSet::new();
|
||||
|
||||
let spi = self.0.register_block();
|
||||
let int_ena = spi.dma_int_ena().read();
|
||||
if int_ena.inlink_dscr_error().bit_is_set() {
|
||||
result |= DmaRxInterrupt::DescriptorError;
|
||||
}
|
||||
if int_ena.inlink_dscr_empty().bit_is_set() {
|
||||
result |= DmaRxInterrupt::DescriptorEmpty;
|
||||
}
|
||||
if int_ena.in_suc_eof().bit_is_set() {
|
||||
result |= DmaRxInterrupt::SuccessfulEof;
|
||||
}
|
||||
if int_ena.in_err_eof().bit_is_set() {
|
||||
result |= DmaRxInterrupt::ErrorEof;
|
||||
}
|
||||
if int_ena.in_done().bit_is_set() {
|
||||
result |= DmaRxInterrupt::Done;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn clear(&self, interrupts: impl Into<EnumSet<DmaRxInterrupt>>) {
|
||||
let spi = self.0.register_block();
|
||||
spi.dma_int_clr().modify(|_, w| {
|
||||
for interrupt in interrupts.into() {
|
||||
match interrupt {
|
||||
DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(),
|
||||
DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(),
|
||||
DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().clear_bit_by_one(),
|
||||
DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().clear_bit_by_one(),
|
||||
DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(),
|
||||
};
|
||||
}
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
fn pending_interrupts(&self) -> EnumSet<DmaRxInterrupt> {
|
||||
let mut result = EnumSet::new();
|
||||
|
||||
let spi = self.0.register_block();
|
||||
let int_raw = spi.dma_int_raw().read();
|
||||
if int_raw.inlink_dscr_error().bit_is_set() {
|
||||
result |= DmaRxInterrupt::DescriptorError;
|
||||
}
|
||||
if int_raw.inlink_dscr_empty().bit_is_set() {
|
||||
result |= DmaRxInterrupt::DescriptorEmpty;
|
||||
}
|
||||
if int_raw.in_suc_eof().bit_is_set() {
|
||||
result |= DmaRxInterrupt::SuccessfulEof;
|
||||
}
|
||||
if int_raw.in_err_eof().bit_is_set() {
|
||||
result |= DmaRxInterrupt::ErrorEof;
|
||||
}
|
||||
if int_raw.in_done().bit_is_set() {
|
||||
result |= DmaRxInterrupt::Done;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn waker(&self) -> &'static AtomicWaker {
|
||||
self.0.rx_waker()
|
||||
}
|
||||
|
||||
fn is_async(&self) -> bool {
|
||||
self.0.rx_async_flag().load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn set_async(&self, _is_async: bool) {
|
||||
self.0.rx_async_flag().store(_is_async, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
crate::any_peripheral! {
|
||||
/// An SPI-compatible type-erased DMA channel.
|
||||
pub peripheral AnySpiDmaChannel {
|
||||
Spi2(Spi2DmaChannel),
|
||||
Spi3(Spi3DmaChannel),
|
||||
}
|
||||
}
|
||||
|
||||
impl DmaChannel for AnySpiDmaChannel {
|
||||
type Rx = AnySpiDmaRxChannel;
|
||||
type Tx = AnySpiDmaTxChannel;
|
||||
|
||||
unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) {
|
||||
(
|
||||
AnySpiDmaRxChannel(unsafe { self.clone_unchecked() }),
|
||||
AnySpiDmaTxChannel(unsafe { self.clone_unchecked() }),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PdmaChannel for AnySpiDmaChannel {
|
||||
type RegisterBlock = SpiRegisterBlock;
|
||||
|
||||
delegate::delegate! {
|
||||
to match &self.0 {
|
||||
AnySpiDmaChannelInner::Spi2(channel) => channel,
|
||||
AnySpiDmaChannelInner::Spi3(channel) => channel,
|
||||
} {
|
||||
fn register_block(&self) -> &SpiRegisterBlock;
|
||||
fn tx_waker(&self) -> &'static AtomicWaker;
|
||||
fn rx_waker(&self) -> &'static AtomicWaker;
|
||||
fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool;
|
||||
fn peripheral_interrupt(&self) -> Interrupt;
|
||||
fn async_handler(&self) -> InterruptHandler;
|
||||
fn rx_async_flag(&self) -> &'static AtomicBool;
|
||||
fn tx_async_flag(&self) -> &'static AtomicBool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,5 +119,6 @@ crate::peripherals! {
|
||||
DMA_SPI2: Spi2DmaChannel,
|
||||
DMA_SPI3: Spi3DmaChannel,
|
||||
DMA_I2S0: I2s0DmaChannel,
|
||||
DMA_CRYPTO: CryptoDmaChannel,
|
||||
]
|
||||
}
|
||||
|
||||
@ -298,6 +298,7 @@ impl PeripheralClockControl {
|
||||
Peripheral::Dma => {
|
||||
perip_clk_en0.modify(|_, w| w.spi2_dma_clk_en().bit(enable));
|
||||
perip_clk_en0.modify(|_, w| w.spi3_dma_clk_en().bit(enable));
|
||||
perip_clk_en1.modify(|_, w| w.crypto_dma_clk_en().bit(enable));
|
||||
}
|
||||
#[cfg(esp32c3)]
|
||||
Peripheral::I2s0 => {
|
||||
@ -484,6 +485,8 @@ impl PeripheralClockControl {
|
||||
perip_rst_en0.modify(|_, w| w.spi2_dma_rst().clear_bit());
|
||||
perip_rst_en0.modify(|_, w| w.spi3_dma_rst().set_bit());
|
||||
perip_rst_en0.modify(|_, w| w.spi3_dma_rst().clear_bit());
|
||||
perip_rst_en1.modify(|_, w| w.crypto_dma_rst().set_bit());
|
||||
perip_rst_en1.modify(|_, w| w.crypto_dma_rst().clear_bit());
|
||||
}
|
||||
#[cfg(esp32c3)]
|
||||
Peripheral::I2s0 => {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
//! AES DMA Test
|
||||
|
||||
//% CHIPS: esp32c3 esp32c6 esp32h2 esp32s3
|
||||
//% CHIPS: esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
@ -26,7 +26,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_aes_128_dma_encryption(peripherals: Peripherals) {
|
||||
let dma_channel = peripherals.DMA_CH0;
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(esp32s2)] {
|
||||
let dma_channel = peripherals.DMA_CRYPTO;
|
||||
} else {
|
||||
let dma_channel = peripherals.DMA_CH0;
|
||||
}
|
||||
}
|
||||
|
||||
let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE);
|
||||
|
||||
@ -64,7 +70,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_aes_128_dma_decryption(peripherals: Peripherals) {
|
||||
let dma_channel = peripherals.DMA_CH0;
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(esp32s2)] {
|
||||
let dma_channel = peripherals.DMA_CRYPTO;
|
||||
} else {
|
||||
let dma_channel = peripherals.DMA_CH0;
|
||||
}
|
||||
}
|
||||
|
||||
let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE);
|
||||
|
||||
@ -101,7 +113,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_aes_256_dma_encryption(peripherals: Peripherals) {
|
||||
let dma_channel = peripherals.DMA_CH0;
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(esp32s2)] {
|
||||
let dma_channel = peripherals.DMA_CRYPTO;
|
||||
} else {
|
||||
let dma_channel = peripherals.DMA_CH0;
|
||||
}
|
||||
}
|
||||
|
||||
let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE);
|
||||
|
||||
@ -139,7 +157,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_aes_256_dma_decryption(peripherals: Peripherals) {
|
||||
let dma_channel = peripherals.DMA_CH0;
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(esp32s2)] {
|
||||
let dma_channel = peripherals.DMA_CRYPTO;
|
||||
} else {
|
||||
let dma_channel = peripherals.DMA_CH0;
|
||||
}
|
||||
}
|
||||
|
||||
let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user