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)
|
- 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)
|
- `BurstConfig`, a device-specific configuration for configuring DMA transfers in burst mode (#2543)
|
||||||
- `{DmaRxBuf, DmaTxBuf, DmaRxTxBuf}::set_burst_config` (#2543)
|
- `{DmaRxBuf, DmaTxBuf, DmaRxTxBuf}::set_burst_config` (#2543)
|
||||||
|
- ESP32-S2: DMA support for AES (#2699)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
@ -227,7 +227,7 @@ pub enum Endianness {
|
|||||||
/// transfer, which can significantly speed up operations when dealing with
|
/// transfer, which can significantly speed up operations when dealing with
|
||||||
/// large data volumes. It supports various cipher modes such as ECB, CBC, OFB,
|
/// large data volumes. It supports various cipher modes such as ECB, CBC, OFB,
|
||||||
/// CTR, CFB8, and CFB128.
|
/// CTR, CFB8, and CFB128.
|
||||||
#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))]
|
#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s2, esp32s3))]
|
||||||
pub mod dma {
|
pub mod dma {
|
||||||
use crate::{
|
use crate::{
|
||||||
aes::{Key, Mode},
|
aes::{Key, Mode},
|
||||||
@ -449,7 +449,7 @@ pub mod dma {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(esp32c3, esp32s3))]
|
#[cfg(any(esp32c3, esp32s2, esp32s3))]
|
||||||
fn reset_aes(&self) {
|
fn reset_aes(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let s = crate::peripherals::SYSTEM::steal();
|
let s = crate::peripherals::SYSTEM::steal();
|
||||||
|
|||||||
@ -897,7 +897,7 @@ pub enum DmaPeripheral {
|
|||||||
Mem2Mem5 = 5,
|
Mem2Mem5 = 5,
|
||||||
#[cfg(not(esp32c2))]
|
#[cfg(not(esp32c2))]
|
||||||
Aes = 6,
|
Aes = 6,
|
||||||
#[cfg(gdma)]
|
#[cfg(any(esp32s2, gdma))]
|
||||||
Sha = 7,
|
Sha = 7,
|
||||||
#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))]
|
#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||||
Adc = 8,
|
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_SPI2: Spi2DmaChannel,
|
||||||
DMA_SPI3: Spi3DmaChannel,
|
DMA_SPI3: Spi3DmaChannel,
|
||||||
DMA_I2S0: I2s0DmaChannel,
|
DMA_I2S0: I2s0DmaChannel,
|
||||||
|
DMA_CRYPTO: CryptoDmaChannel,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -298,6 +298,7 @@ impl PeripheralClockControl {
|
|||||||
Peripheral::Dma => {
|
Peripheral::Dma => {
|
||||||
perip_clk_en0.modify(|_, w| w.spi2_dma_clk_en().bit(enable));
|
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_en0.modify(|_, w| w.spi3_dma_clk_en().bit(enable));
|
||||||
|
perip_clk_en1.modify(|_, w| w.crypto_dma_clk_en().bit(enable));
|
||||||
}
|
}
|
||||||
#[cfg(esp32c3)]
|
#[cfg(esp32c3)]
|
||||||
Peripheral::I2s0 => {
|
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.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().set_bit());
|
||||||
perip_rst_en0.modify(|_, w| w.spi3_dma_rst().clear_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)]
|
#[cfg(esp32c3)]
|
||||||
Peripheral::I2s0 => {
|
Peripheral::I2s0 => {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
//! AES DMA Test
|
//! AES DMA Test
|
||||||
|
|
||||||
//% CHIPS: esp32c3 esp32c6 esp32h2 esp32s3
|
//% CHIPS: esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
@ -26,7 +26,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_aes_128_dma_encryption(peripherals: Peripherals) {
|
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);
|
let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE);
|
||||||
|
|
||||||
@ -64,7 +70,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_aes_128_dma_decryption(peripherals: Peripherals) {
|
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);
|
let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE);
|
||||||
|
|
||||||
@ -101,7 +113,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_aes_256_dma_encryption(peripherals: Peripherals) {
|
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);
|
let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE);
|
||||||
|
|
||||||
@ -139,7 +157,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_aes_256_dma_decryption(peripherals: Peripherals) {
|
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);
|
let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user