esp_hal/etm.rs
1//! # Event Task Matrix (ETM)
2//!
3//! ## Overview
4//!
5//! Normally, if a peripheral X needs to notify peripheral Y of a particular
6//! event, this could only be done via a CPU interrupt from peripheral X, where
7//! the CPU notifies peripheral Y on behalf of peripheral X. However, in
8//! time-critical applications, the latency introduced by CPU interrupts is
9//! non-negligible.
10//!
11//! With the help of the Event Task Matrix (ETM) module, some peripherals can
12//! directly notify other peripherals of events through pre-set connections
13//! without the intervention of CPU interrupts. This allows precise and low
14//! latency synchronization between peripherals, and lessens the CPU’s workload
15//! as the CPU no longer needs to handle these events.
16//!
17//! The ETM module has multiple programmable channels, they are used to connect
18//! a particular Event to a particular Task. When an event is activated, the ETM
19//! channel will trigger the corresponding task automatically.
20//!
21//! For more information, please refer to the
22#, "/api-reference/peripherals/etm.html)")]
23//! ## Examples
24//!
25//! ### Control LED by the button via ETM
26//! ```rust, no_run
27#![doc = crate::before_snippet!()]
28//! # use esp_hal::gpio::etm::{Channels, InputConfig, OutputConfig};
29//! # use esp_hal::etm::Etm;
30//! # use esp_hal::gpio::Pull;
31//! # use esp_hal::gpio::Level;
32//!
33//! let mut led = peripherals.GPIO1;
34//! let button = peripherals.GPIO9;
35//!
36//! // setup ETM
37//! let gpio_ext = Channels::new(peripherals.GPIO_SD);
38//! let led_task = gpio_ext.channel0_task.toggle(
39//! &mut led,
40//! OutputConfig {
41//! open_drain: false,
42//! pull: Pull::None,
43//! initial_state: Level::Low,
44//! },
45//! );
46//! let button_event = gpio_ext
47//! .channel0_event
48//! .falling_edge(button, InputConfig { pull: Pull::Down });
49//!
50//! let etm = Etm::new(peripherals.SOC_ETM);
51//! let channel0 = etm.channel0;
52//!
53//! // make sure the configured channel doesn't get dropped - dropping it will
54//! // disable the channel
55//! let _configured_channel = channel0.setup(&button_event, &led_task);
56//!
57//! // the LED is controlled by the button without involving the CPU
58//! loop {}
59//! # }
60//! ```
61//!
62//! ### Control LED by the systimer via ETM
63//! ```rust, no_run
64#![doc = crate::before_snippet!()]
65//! # use esp_hal::gpio::etm::{Channels, InputConfig, OutputConfig};
66//! # use esp_hal::etm::Etm;
67//! # use esp_hal::gpio::Pull;
68//! # use esp_hal::gpio::Level;
69//! # use esp_hal::timer::systimer::{etm::Event, SystemTimer};
70//! # use esp_hal::timer::PeriodicTimer;
71//! use esp_hal::time::Duration;
72//!
73//! let syst = SystemTimer::new(peripherals.SYSTIMER);
74//! let mut alarm0 = syst.alarm0;
75//! let mut timer = PeriodicTimer::new(&mut alarm0);
76//! timer.start(Duration::from_secs(1));
77//!
78//! let mut led = peripherals.GPIO1;
79//!
80//! // setup ETM
81//! let gpio_ext = Channels::new(peripherals.GPIO_SD);
82//! let led_task = gpio_ext.channel0_task.toggle(
83//! &mut led,
84//! OutputConfig {
85//! open_drain: false,
86//! pull: Pull::None,
87//! initial_state: Level::Low,
88//! },
89//! );
90//!
91//! let timer_event = Event::new(&mut alarm0);
92//!
93//! let etm = Etm::new(peripherals.SOC_ETM);
94//! let channel0 = etm.channel0;
95//!
96//! // make sure the configured channel doesn't get dropped - dropping it will
97//! // disable the channel
98//! let _configured_channel = channel0.setup(&timer_event, &led_task);
99//!
100//! // the LED is controlled by the timer without involving the CPU
101//! loop {}
102//! # }
103//! ```
104
105use crate::{
106 peripheral::{Peripheral, PeripheralRef},
107 peripherals::SOC_ETM,
108 system::GenericPeripheralGuard,
109};
110
111/// Unconfigured EtmChannel.
112#[non_exhaustive]
113pub struct EtmChannel<const C: u8> {}
114
115impl<const C: u8> EtmChannel<C> {
116 /// Setup the channel
117 ///
118 /// Enabled the channel and configures the assigned event and task.
119 pub fn setup<'a, E, T>(self, event: &'a E, task: &'a T) -> EtmConfiguredChannel<'a, E, T, C>
120 where
121 E: EtmEvent,
122 T: EtmTask,
123 {
124 let etm = SOC_ETM::regs();
125 let guard = GenericPeripheralGuard::new();
126
127 etm.ch(C as usize)
128 .evt_id()
129 .modify(|_, w| unsafe { w.evt_id().bits(event.id()) });
130 etm.ch(C as usize)
131 .task_id()
132 .modify(|_, w| unsafe { w.task_id().bits(task.id()) });
133 if C < 32 {
134 etm.ch_ena_ad0_set().write(|w| w.ch_set(C).set_bit());
135 } else {
136 etm.ch_ena_ad1_set().write(|w| w.ch_set(C - 32).set_bit());
137 }
138
139 EtmConfiguredChannel {
140 _event: event,
141 _task: task,
142 _guard: guard,
143 }
144 }
145}
146
147fn disable_channel(channel: u8) {
148 let etm = SOC_ETM::regs();
149 if channel < 32 {
150 etm.ch_ena_ad0_clr().write(|w| w.ch_clr(channel).set_bit());
151 } else {
152 etm.ch_ena_ad1_clr()
153 .write(|w| w.ch_clr(channel - 32).set_bit());
154 }
155}
156
157/// A readily configured channel
158///
159/// The channel is enabled and event and task are configured.
160#[non_exhaustive]
161pub struct EtmConfiguredChannel<'a, E, T, const C: u8>
162where
163 E: EtmEvent,
164 T: EtmTask,
165{
166 _event: &'a E,
167 _task: &'a T,
168 _guard: GenericPeripheralGuard<{ crate::system::Peripheral::Etm as u8 }>,
169}
170
171impl<E, T, const C: u8> Drop for EtmConfiguredChannel<'_, E, T, C>
172where
173 E: EtmEvent,
174 T: EtmTask,
175{
176 fn drop(&mut self) {
177 debug!("Drop ETM channel {}", C);
178 disable_channel(C);
179 }
180}
181
182macro_rules! create_etm {
183 ($($num:literal),+) => {
184 paste::paste! {
185 /// ETM Instance
186 ///
187 /// Provides access to all the [EtmChannel]
188 pub struct Etm<'d> {
189 _peripheral: PeripheralRef<'d, crate::peripherals::SOC_ETM>,
190 $(
191 /// An individual ETM channel, identified by its index number.
192 pub [< channel $num >]: EtmChannel<$num>,
193 )+
194 }
195
196 impl<'d> Etm<'d> {
197 /// Creates a new `Etm` instance.
198 pub fn new(peripheral: impl Peripheral<P = crate::peripherals::SOC_ETM> + 'd) -> Self {
199 crate::into_ref!(peripheral);
200
201 Self {
202 _peripheral: peripheral,
203 $([< channel $num >]: EtmChannel { },)+
204 }
205 }
206 }
207 }
208 };
209}
210
211create_etm!(
212 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,
213 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49
214);
215
216#[doc(hidden)]
217pub trait EtmEvent: crate::private::Sealed {
218 fn id(&self) -> u8;
219}
220
221#[doc(hidden)]
222pub trait EtmTask: crate::private::Sealed {
223 fn id(&self) -> u8;
224}