Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DMA 2/N] Split DMA IN/OUT handlers #2521

Merged
merged 5 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 52 additions & 8 deletions esp-hal/src/dma/gdma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ use embassy_sync::waitqueue::AtomicWaker;
static TX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];
static RX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];

cfg_if::cfg_if! {
if #[cfg(any(esp32c2, esp32c3))] {
use portable_atomic::AtomicBool;
static TX_IS_ASYNC: [AtomicBool; CHANNEL_COUNT] = [const { AtomicBool::new(false) }; CHANNEL_COUNT];
MabezDev marked this conversation as resolved.
Show resolved Hide resolved
static RX_IS_ASYNC: [AtomicBool; CHANNEL_COUNT] = [const { AtomicBool::new(false) }; CHANNEL_COUNT];
}
}

impl<C: GdmaChannel> crate::private::Sealed for ChannelTxImpl<C> {}

impl<C: GdmaChannel> ChannelTxImpl<C> {
Expand Down Expand Up @@ -314,6 +322,24 @@ impl<C: GdmaChannel> InterruptAccess<DmaTxInterrupt> for ChannelTxImpl<C> {
fn waker(&self) -> &'static AtomicWaker {
&TX_WAKERS[self.0.number() as usize]
}

fn is_async(&self) -> bool {
cfg_if::cfg_if! {
if #[cfg(any(esp32c2, esp32c3))] {
TX_IS_ASYNC[self.0.number() as usize].load(portable_atomic::Ordering::Acquire)
} else {
true
}
}
}

fn set_async(&self, _is_async: bool) {
cfg_if::cfg_if! {
if #[cfg(any(esp32c2, esp32c3))] {
TX_IS_ASYNC[self.0.number() as usize].store(_is_async, portable_atomic::Ordering::Release);
}
}
}
}

#[non_exhaustive]
Expand Down Expand Up @@ -513,6 +539,24 @@ impl<C: GdmaChannel> InterruptAccess<DmaRxInterrupt> for ChannelRxImpl<C> {
fn waker(&self) -> &'static AtomicWaker {
&RX_WAKERS[self.0.number() as usize]
}

fn is_async(&self) -> bool {
cfg_if::cfg_if! {
if #[cfg(any(esp32c2, esp32c3))] {
RX_IS_ASYNC[self.0.number() as usize].load(portable_atomic::Ordering::Acquire)
} else {
true
}
}
}

fn set_async(&self, _is_async: bool) {
cfg_if::cfg_if! {
if #[cfg(any(esp32c2, esp32c3))] {
RX_IS_ASYNC[self.0.number() as usize].store(_is_async, portable_atomic::Ordering::Release);
}
}
}
}

/// A Channel can be created from this
Expand Down Expand Up @@ -611,16 +655,16 @@ cfg_if::cfg_if! {
impl_channel!(2, DMA_CH2, asynch_handler::interrupt_handler_ch2);
} else if #[cfg(any(esp32c6, esp32h2))] {
const CHANNEL_COUNT: usize = 3;
impl_channel!(0, DMA_IN_CH0, asynch_handler::interrupt_handler_ch0, DMA_OUT_CH0, asynch_handler::interrupt_handler_ch0);
impl_channel!(1, DMA_IN_CH1, asynch_handler::interrupt_handler_ch1, DMA_OUT_CH1, asynch_handler::interrupt_handler_ch1);
impl_channel!(2, DMA_IN_CH2, asynch_handler::interrupt_handler_ch2, DMA_OUT_CH2, asynch_handler::interrupt_handler_ch2);
impl_channel!(0, DMA_IN_CH0, asynch_handler::interrupt_handler_in_ch0, DMA_OUT_CH0, asynch_handler::interrupt_handler_out_ch0);
impl_channel!(1, DMA_IN_CH1, asynch_handler::interrupt_handler_in_ch1, DMA_OUT_CH1, asynch_handler::interrupt_handler_out_ch1);
impl_channel!(2, DMA_IN_CH2, asynch_handler::interrupt_handler_in_ch2, DMA_OUT_CH2, asynch_handler::interrupt_handler_out_ch2);
} else if #[cfg(esp32s3)] {
const CHANNEL_COUNT: usize = 5;
impl_channel!(0, DMA_IN_CH0, asynch_handler::interrupt_handler_ch0, DMA_OUT_CH0, asynch_handler::interrupt_handler_ch0);
impl_channel!(1, DMA_IN_CH1, asynch_handler::interrupt_handler_ch1, DMA_OUT_CH1, asynch_handler::interrupt_handler_ch1);
impl_channel!(2, DMA_IN_CH2, asynch_handler::interrupt_handler_ch2, DMA_OUT_CH2, asynch_handler::interrupt_handler_ch2);
impl_channel!(3, DMA_IN_CH3, asynch_handler::interrupt_handler_ch3, DMA_OUT_CH3, asynch_handler::interrupt_handler_ch3);
impl_channel!(4, DMA_IN_CH4, asynch_handler::interrupt_handler_ch4, DMA_OUT_CH4, asynch_handler::interrupt_handler_ch4);
impl_channel!(0, DMA_IN_CH0, asynch_handler::interrupt_handler_in_ch0, DMA_OUT_CH0, asynch_handler::interrupt_handler_out_ch0);
bugadani marked this conversation as resolved.
Show resolved Hide resolved
impl_channel!(1, DMA_IN_CH1, asynch_handler::interrupt_handler_in_ch1, DMA_OUT_CH1, asynch_handler::interrupt_handler_out_ch1);
impl_channel!(2, DMA_IN_CH2, asynch_handler::interrupt_handler_in_ch2, DMA_OUT_CH2, asynch_handler::interrupt_handler_out_ch2);
impl_channel!(3, DMA_IN_CH3, asynch_handler::interrupt_handler_in_ch3, DMA_OUT_CH3, asynch_handler::interrupt_handler_out_ch3);
impl_channel!(4, DMA_IN_CH4, asynch_handler::interrupt_handler_in_ch4, DMA_OUT_CH4, asynch_handler::interrupt_handler_out_ch4);
}
}

Expand Down
155 changes: 98 additions & 57 deletions esp-hal/src/dma/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1679,6 +1679,13 @@ where
// channel was previously used for a mem2mem transfer.
rx_impl.set_mem2mem_mode(false);

if let Some(interrupt) = rx_impl.peripheral_interrupt() {
for cpu in Cpu::all() {
crate::interrupt::disable(cpu, interrupt);
}
}
rx_impl.set_async(false);

Self {
burst_mode: false,
rx_impl,
Expand All @@ -1691,6 +1698,7 @@ where
if let Some(handler) = self.rx_impl.async_handler() {
self.set_interrupt_handler(handler);
}
self.rx_impl.set_async(true);
ChannelRx {
burst_mode: self.burst_mode,
rx_impl: self.rx_impl,
Expand Down Expand Up @@ -1724,6 +1732,7 @@ where
if let Some(interrupt) = self.rx_impl.peripheral_interrupt() {
crate::interrupt::disable(Cpu::current(), interrupt);
}
self.rx_impl.set_async(false);
ChannelRx {
burst_mode: self.burst_mode,
rx_impl: self.rx_impl,
Expand Down Expand Up @@ -1960,6 +1969,13 @@ where
CH: DmaChannel,
{
fn new(tx_impl: CH::Tx) -> Self {
if let Some(interrupt) = tx_impl.peripheral_interrupt() {
for cpu in Cpu::all() {
crate::interrupt::disable(cpu, interrupt);
}
}
tx_impl.set_async(false);

Self {
burst_mode: false,
tx_impl,
Expand All @@ -1972,6 +1988,7 @@ where
if let Some(handler) = self.tx_impl.async_handler() {
self.set_interrupt_handler(handler);
}
self.tx_impl.set_async(true);
ChannelTx {
burst_mode: self.burst_mode,
tx_impl: self.tx_impl,
Expand Down Expand Up @@ -2005,6 +2022,7 @@ where
if let Some(interrupt) = self.tx_impl.peripheral_interrupt() {
crate::interrupt::disable(Cpu::current(), interrupt);
}
self.tx_impl.set_async(false);
ChannelTx {
burst_mode: self.burst_mode,
tx_impl: self.tx_impl,
Expand Down Expand Up @@ -2259,6 +2277,9 @@ pub trait InterruptAccess<T: EnumSetType>: crate::private::Sealed {
fn clear(&self, interrupts: impl Into<EnumSet<T>>);
fn pending_interrupts(&self) -> EnumSet<T>;
fn waker(&self) -> &'static embassy_sync::waitqueue::AtomicWaker;

fn is_async(&self) -> bool;
fn set_async(&self, is_async: bool);
}

/// DMA Channel
Expand Down Expand Up @@ -2982,9 +3003,12 @@ pub(crate) mod asynch {
}
}

fn handle_interrupt<CH: DmaChannelExt>() {
fn handle_in_interrupt<CH: DmaChannelExt>() {
let rx = CH::rx_interrupts();
let tx = CH::tx_interrupts();

if !rx.is_async() {
return;
}

if rx.pending_interrupts().is_disjoint(
DmaRxInterrupt::DescriptorError
Expand All @@ -3001,16 +3025,6 @@ pub(crate) mod asynch {
rx.waker().wake()
}

if tx
.pending_interrupts()
.contains(DmaTxInterrupt::DescriptorError)
{
tx.unlisten(
DmaTxInterrupt::DescriptorError | DmaTxInterrupt::TotalEof | DmaTxInterrupt::Done,
);
tx.waker().wake()
}

if rx
.pending_interrupts()
.contains(DmaRxInterrupt::SuccessfulEof)
Expand All @@ -3023,6 +3037,24 @@ pub(crate) mod asynch {
rx.unlisten(DmaRxInterrupt::Done);
rx.waker().wake()
}
}

fn handle_out_interrupt<CH: DmaChannelExt>() {
let tx = CH::tx_interrupts();

if !tx.is_async() {
return;
}

if tx
.pending_interrupts()
.contains(DmaTxInterrupt::DescriptorError)
{
tx.unlisten(
DmaTxInterrupt::DescriptorError | DmaTxInterrupt::TotalEof | DmaTxInterrupt::Done,
);
tx.waker().wake()
}

if tx.pending_interrupts().contains(DmaTxInterrupt::TotalEof)
&& tx.is_listening().contains(DmaTxInterrupt::TotalEof)
Expand All @@ -3037,69 +3069,78 @@ pub(crate) mod asynch {
}
}

#[cfg(not(any(esp32, esp32s2)))]
#[cfg(gdma)]
pub(crate) mod interrupt {
use procmacros::handler;

use super::*;

#[handler(priority = crate::interrupt::Priority::max())]
pub(crate) fn interrupt_handler_ch0() {
handle_interrupt::<DmaChannel0>();
use crate::{interrupt::Priority, macros::handler};

// Single interrupt handler for IN and OUT
#[cfg(any(esp32c2, esp32c3))]
macro_rules! interrupt_handler {
($ch:literal) => {
paste::paste! {
#[handler(priority = Priority::max())]
pub(crate) fn [<interrupt_handler_ch $ch>]() {
handle_in_interrupt::<[< DmaChannel $ch >]>();
handle_out_interrupt::<[< DmaChannel $ch >]>();
}
}
};
}

#[cfg(not(esp32c2))]
#[handler(priority = crate::interrupt::Priority::max())]
pub(crate) fn interrupt_handler_ch1() {
handle_interrupt::<DmaChannel1>();
}
#[cfg(not(any(esp32c2, esp32c3)))]
macro_rules! interrupt_handler {
($ch:literal) => {
paste::paste! {
#[handler(priority = Priority::max())]
pub(crate) fn [<interrupt_handler_in_ch $ch>]() {
handle_in_interrupt::<[< DmaChannel $ch >]>();
}

#[cfg(not(esp32c2))]
#[handler(priority = crate::interrupt::Priority::max())]
pub(crate) fn interrupt_handler_ch2() {
handle_interrupt::<DmaChannel2>();
#[handler(priority = Priority::max())]
pub(crate) fn [<interrupt_handler_out_ch $ch>]() {
handle_out_interrupt::<[< DmaChannel $ch >]>();
}
}
};
}

interrupt_handler!(0);
#[cfg(not(esp32c2))]
interrupt_handler!(1);
#[cfg(not(esp32c2))]
interrupt_handler!(2);
#[cfg(esp32s3)]
#[handler(priority = crate::interrupt::Priority::max())]
pub(crate) fn interrupt_handler_ch3() {
handle_interrupt::<DmaChannel3>();
}

interrupt_handler!(3);
#[cfg(esp32s3)]
#[handler(priority = crate::interrupt::Priority::max())]
pub(crate) fn interrupt_handler_ch4() {
handle_interrupt::<DmaChannel4>();
}
interrupt_handler!(4);
}

#[cfg(any(esp32, esp32s2))]
#[cfg(pdma)]
pub(crate) mod interrupt {
use procmacros::handler;

use super::*;

#[handler(priority = crate::interrupt::Priority::max())]
pub(crate) fn interrupt_handler_spi2_dma() {
handle_interrupt::<Spi2DmaChannel>();
use crate::{interrupt::Priority, macros::handler};

// Single interrupt handler for IN and OUT
macro_rules! interrupt_handler {
($ch:ident) => {
paste::paste! {
#[handler(priority = Priority::max())]
pub(crate) fn [<interrupt_handler_ $ch:lower _dma>]() {
handle_in_interrupt::<[< $ch DmaChannel >]>();
handle_out_interrupt::<[< $ch DmaChannel >]>();
}
}
};
}

interrupt_handler!(Spi2);
#[cfg(spi3)]
#[handler(priority = crate::interrupt::Priority::max())]
pub(crate) fn interrupt_handler_spi3_dma() {
handle_interrupt::<Spi3DmaChannel>();
}
interrupt_handler!(Spi3);

#[cfg(i2s0)]
#[handler(priority = crate::interrupt::Priority::max())]
pub(crate) fn interrupt_handler_i2s0_dma() {
handle_interrupt::<I2s0DmaChannel>();
}

interrupt_handler!(I2s0);
#[cfg(i2s1)]
#[handler(priority = crate::interrupt::Priority::max())]
pub(crate) fn interrupt_handler_i2s1_dma() {
handle_interrupt::<I2s1DmaChannel>();
}
interrupt_handler!(I2s1);
}
}
Loading