* No longer publicly expose the `PeripheralClockControl` struct * Update examples as needed to get things building again * Update CHANGELOG.md * Address review feedback, fix a warning * Use a critical section for all devices other than the ESP32-C6/H2, as they modify multiple registers * Rebase and update `etm` driver to fix build errors
282 lines
7.6 KiB
Rust
282 lines
7.6 KiB
Rust
//! # Event Task Matrix (ETM)
|
||
//!
|
||
//! ## Overview
|
||
//!
|
||
//! Normally, if a peripheral X needs to notify peripheral Y of a particular
|
||
//! event, this could only be done via a CPU interrupt from peripheral X, where
|
||
//! the CPU notifies peripheral Y on behalf of peripheral X. However, in
|
||
//! time-critical applications, the latency introduced by CPU interrupts is
|
||
//! non-negligible.
|
||
//!
|
||
//! With the help of the Event Task Matrix (ETM) module, some peripherals can
|
||
//! directly notify other peripherals of events through pre-set connections
|
||
//! without the intervention of CPU interrupts. This allows precise and low
|
||
//! latency synchronization between peripherals, and lessens the CPU’s workload
|
||
//! as the CPU no longer needs to handle these events.
|
||
//!
|
||
//! The ETM module has multiple programmable channels, they are used to connect
|
||
//! a particular Event to a particular Task. When an event is activated, the ETM
|
||
//! channel will trigger the corresponding task automatically.
|
||
//!
|
||
//! More information: https://docs.espressif.com/projects/esp-idf/en/latest/esp32c6/api-reference/peripherals/etm.html
|
||
//!
|
||
//! ## Example
|
||
//! ```no_run
|
||
//! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||
//! let mut led = io.pins.gpio1.into_push_pull_output();
|
||
//! let button = io.pins.gpio9.into_pull_down_input();
|
||
//!
|
||
//! // setup ETM
|
||
//! let gpio_ext = GpioEtmChannels::new(peripherals.GPIO_SD);
|
||
//! let led_task = gpio_ext.channel0_task.toggle(&mut led);
|
||
//! let button_event = gpio_ext.channel0_event.falling_edge(button);
|
||
//!
|
||
//! let etm = Etm::new(peripherals.SOC_ETM);
|
||
//! let channel0 = etm.channel0;
|
||
//!
|
||
//! // make sure the configured channel doesn't get dropped - dropping it will
|
||
//! // disable the channel
|
||
//! let _configured_channel = channel0.setup(&button_event, &led_task);
|
||
//!
|
||
//! // the LED is controlled by the button without involving the CPU
|
||
//! loop {}
|
||
//! ```
|
||
|
||
use crate::{
|
||
peripheral::{Peripheral, PeripheralRef},
|
||
system::PeripheralClockControl,
|
||
};
|
||
|
||
/// Unconfigured EtmChannel.
|
||
#[non_exhaustive]
|
||
pub struct EtmChannel<const C: u8> {}
|
||
|
||
macro_rules! impl_etm_channel {
|
||
($channel: literal, $bank: literal) => {
|
||
paste::paste! {
|
||
impl EtmChannel<$channel> {
|
||
/// Setup the channel
|
||
///
|
||
/// Enabled the channel and configures the assigned event and task.
|
||
pub fn setup<'a, E, T>(self, event: &'a E, task: &'a T) -> EtmConfiguredChannel<'a, E,T,$channel>
|
||
where
|
||
E: EtmEvent,
|
||
T: EtmTask,
|
||
{
|
||
let etm = unsafe { crate::peripherals::SOC_ETM::steal() };
|
||
|
||
etm.[< ch $channel _evt_id >].modify(|_, w| w.[< ch $channel _evt_id >]().variant(event.id()));
|
||
etm.[< ch $channel _task_id >].modify(|_, w| w.[< ch $channel _task_id >]().variant(task.id()));
|
||
etm.[< ch_ena_ad $bank _set >].write(|w| w.[< ch_set $channel >]().set_bit());
|
||
|
||
EtmConfiguredChannel {
|
||
_event: event,
|
||
_task: task,
|
||
}
|
||
}
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
impl_etm_channel!(0, 0);
|
||
impl_etm_channel!(1, 0);
|
||
impl_etm_channel!(2, 0);
|
||
impl_etm_channel!(3, 0);
|
||
impl_etm_channel!(4, 0);
|
||
impl_etm_channel!(5, 0);
|
||
impl_etm_channel!(6, 0);
|
||
impl_etm_channel!(7, 0);
|
||
impl_etm_channel!(8, 0);
|
||
impl_etm_channel!(9, 0);
|
||
impl_etm_channel!(10, 0);
|
||
impl_etm_channel!(11, 0);
|
||
impl_etm_channel!(12, 0);
|
||
impl_etm_channel!(13, 0);
|
||
impl_etm_channel!(14, 0);
|
||
impl_etm_channel!(15, 0);
|
||
impl_etm_channel!(16, 0);
|
||
impl_etm_channel!(17, 0);
|
||
impl_etm_channel!(18, 0);
|
||
impl_etm_channel!(19, 0);
|
||
impl_etm_channel!(20, 0);
|
||
impl_etm_channel!(21, 0);
|
||
impl_etm_channel!(22, 0);
|
||
impl_etm_channel!(23, 0);
|
||
impl_etm_channel!(24, 0);
|
||
impl_etm_channel!(25, 0);
|
||
impl_etm_channel!(26, 0);
|
||
impl_etm_channel!(27, 0);
|
||
impl_etm_channel!(28, 0);
|
||
impl_etm_channel!(29, 0);
|
||
impl_etm_channel!(30, 0);
|
||
impl_etm_channel!(31, 0);
|
||
impl_etm_channel!(32, 1);
|
||
impl_etm_channel!(33, 1);
|
||
impl_etm_channel!(34, 1);
|
||
impl_etm_channel!(35, 1);
|
||
impl_etm_channel!(36, 1);
|
||
impl_etm_channel!(37, 1);
|
||
impl_etm_channel!(38, 1);
|
||
impl_etm_channel!(39, 1);
|
||
impl_etm_channel!(40, 1);
|
||
impl_etm_channel!(41, 1);
|
||
impl_etm_channel!(42, 1);
|
||
impl_etm_channel!(43, 1);
|
||
impl_etm_channel!(44, 1);
|
||
impl_etm_channel!(45, 1);
|
||
impl_etm_channel!(46, 1);
|
||
impl_etm_channel!(47, 1);
|
||
impl_etm_channel!(48, 1);
|
||
impl_etm_channel!(49, 1);
|
||
|
||
macro_rules! impl_disable_helper {
|
||
($(($channel:literal, $bank:literal)),+) => {
|
||
paste::paste! {
|
||
fn disable_channel(channel: u8) {
|
||
let etm = unsafe { crate::peripherals::SOC_ETM::steal() };
|
||
match channel {
|
||
$(
|
||
$channel => {etm.[< ch_ena_ad $bank _clr>].write(|w| w.[< ch_clr $channel >]().set_bit());},
|
||
)+
|
||
_ => panic!("Unknown channel {}", channel),
|
||
}
|
||
|
||
}
|
||
}
|
||
};
|
||
}
|
||
impl_disable_helper!(
|
||
(0, 0),
|
||
(1, 0),
|
||
(2, 0),
|
||
(3, 0),
|
||
(4, 0),
|
||
(5, 0),
|
||
(6, 0),
|
||
(7, 0),
|
||
(8, 0),
|
||
(9, 0),
|
||
(10, 0),
|
||
(11, 0),
|
||
(12, 0),
|
||
(13, 0),
|
||
(14, 0),
|
||
(15, 0),
|
||
(16, 0),
|
||
(17, 0),
|
||
(18, 0),
|
||
(19, 0),
|
||
(20, 0),
|
||
(21, 0),
|
||
(22, 0),
|
||
(23, 0),
|
||
(24, 0),
|
||
(25, 0),
|
||
(26, 0),
|
||
(27, 0),
|
||
(28, 0),
|
||
(29, 0),
|
||
(30, 0),
|
||
(31, 0),
|
||
(32, 1),
|
||
(33, 1),
|
||
(34, 1),
|
||
(35, 1),
|
||
(36, 1),
|
||
(37, 1),
|
||
(38, 1),
|
||
(39, 1),
|
||
(40, 1),
|
||
(41, 1),
|
||
(42, 1),
|
||
(43, 1),
|
||
(44, 1),
|
||
(45, 1),
|
||
(46, 1),
|
||
(47, 1),
|
||
(48, 1),
|
||
(49, 1)
|
||
);
|
||
|
||
/// A readily configured channel
|
||
///
|
||
/// The channel is enabled and event and task are configured.
|
||
#[non_exhaustive]
|
||
pub struct EtmConfiguredChannel<'a, E, T, const C: u8>
|
||
where
|
||
E: EtmEvent,
|
||
T: EtmTask,
|
||
{
|
||
_event: &'a E,
|
||
_task: &'a T,
|
||
}
|
||
|
||
impl<'a, E, T, const C: u8> Drop for EtmConfiguredChannel<'a, E, T, C>
|
||
where
|
||
E: EtmEvent,
|
||
T: EtmTask,
|
||
{
|
||
fn drop(&mut self) {
|
||
debug!("drop {}", C);
|
||
disable_channel(C);
|
||
}
|
||
}
|
||
|
||
macro_rules! create_etm_struct {
|
||
($($num:literal),+) => {
|
||
paste::paste! {
|
||
/// ETM Instance
|
||
///
|
||
/// Provides access to all the [EtmChannel]
|
||
pub struct Etm<'d> {
|
||
_peripheral: PeripheralRef<'d, crate::peripherals::SOC_ETM>,
|
||
$(pub [< channel $num >]: EtmChannel<$num>,)+
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
macro_rules! create_etm_constructor {
|
||
($($num:literal),+) => {
|
||
paste::paste! {
|
||
impl<'d> Etm<'d> {
|
||
pub fn new(peripheral: impl Peripheral<P = crate::peripherals::SOC_ETM> + 'd) -> Self {
|
||
crate::into_ref!(peripheral);
|
||
|
||
PeripheralClockControl::enable(crate::system::Peripheral::Etm);
|
||
|
||
Self {
|
||
_peripheral: peripheral,
|
||
$([< channel $num >]: EtmChannel {},)+
|
||
}
|
||
}
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
create_etm_struct!(
|
||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
|
||
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49
|
||
);
|
||
|
||
create_etm_constructor!(
|
||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
|
||
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49
|
||
);
|
||
|
||
#[doc(hidden)]
|
||
pub trait EtmEvent: private::Sealed {
|
||
fn id(&self) -> u8;
|
||
}
|
||
|
||
#[doc(hidden)]
|
||
pub trait EtmTask: private::Sealed {
|
||
fn id(&self) -> u8;
|
||
}
|
||
|
||
pub(crate) mod private {
|
||
pub trait Sealed {}
|
||
}
|