diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5aab8cb34ae..b8bfdd4f903 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Implement `embeded_hal_async::delay::DelayUs` trait for `SYSTIMER` alarms (#812)
- ETM driver, GPIO ETM (#819)
- (G)DMA AES support (#821)
+- SYSTIMER ETM functionality (#828)
### Changed
diff --git a/esp-hal-common/src/systimer.rs b/esp-hal-common/src/systimer.rs
index f4dcdf30dd2..46403025d02 100644
--- a/esp-hal-common/src/systimer.rs
+++ b/esp-hal-common/src/systimer.rs
@@ -65,6 +65,10 @@ impl<'d> SystemTimer<'d> {
pub fn new(p: impl Peripheral
+ 'd) -> Self {
crate::into_ref!(p);
+
+ #[cfg(soc_etm)]
+ etm::enable_etm();
+
Self {
_inner: p,
alarm0: Alarm::new(),
@@ -365,3 +369,60 @@ mod asynch {
WAKERS[2].wake();
}
}
+
+#[cfg(soc_etm)]
+pub mod etm {
+ //! # Event Task Matrix Function
+ //!
+ //! ## Overview
+ //!
+ //! The system timer supports the Event Task Matrix (ETM) function, which
+ //! allows the system timer’s ETM events to trigger any
+ //! peripherals’ ETM tasks.
+ //!
+ //! The system timer can generate the following ETM events:
+ //! - SYSTIMER_EVT_CNT_CMPx: Indicates the alarm pulses generated by
+ //! COMPx
+ //!
+ //! ## Example
+ //! ```no_run
+ //! let syst = SystemTimer::new(peripherals.SYSTIMER);
+ //! let mut alarm0 = syst.alarm0.into_periodic();
+ //! alarm0.set_period(1u32.secs());
+ //!
+ //! let timer_event = SysTimerEtmEvent::new(&mut alarm0);
+ //! ```
+
+ use super::*;
+
+ /// An ETM controlled SYSTIMER event
+ pub struct SysTimerEtmEvent<'a, M, const N: u8> {
+ alarm: &'a mut Alarm,
+ }
+
+ impl<'a, M, const N: u8> SysTimerEtmEvent<'a, M, N> {
+ /// Creates an ETM event from the given [Alarm]
+ pub fn new(alarm: &'a mut Alarm) -> Self {
+ Self { alarm }
+ }
+
+ /// Execute closure f with mutable access to the wrapped [Alarm].
+ pub fn with(&self, f: impl FnOnce(&&'a mut Alarm) -> R) -> R {
+ let alarm = &self.alarm;
+ f(alarm)
+ }
+ }
+
+ impl<'a, M, const N: u8> crate::etm::private::Sealed for SysTimerEtmEvent<'a, M, N> {}
+
+ impl<'a, M, const N: u8> crate::etm::EtmEvent for SysTimerEtmEvent<'a, M, N> {
+ fn id(&self) -> u8 {
+ 50 + N
+ }
+ }
+
+ pub(super) fn enable_etm() {
+ let syst = unsafe { crate::peripherals::SYSTIMER::steal() };
+ syst.conf.modify(|_, w| w.etm_en().set_bit());
+ }
+}
diff --git a/esp32c6-hal/examples/etm_blinky_systimer.rs b/esp32c6-hal/examples/etm_blinky_systimer.rs
new file mode 100644
index 00000000000..8e1be7fc1b4
--- /dev/null
+++ b/esp32c6-hal/examples/etm_blinky_systimer.rs
@@ -0,0 +1,44 @@
+//! Control LED on GPIO 1 by the systimer via ETM
+
+#![no_std]
+#![no_main]
+
+use esp32c6_hal::{
+ clock::ClockControl,
+ etm::Etm,
+ gpio::{etm::GpioEtmChannels, IO},
+ peripherals::Peripherals,
+ prelude::*,
+ systimer::{etm::SysTimerEtmEvent, SystemTimer},
+};
+use esp_backtrace as _;
+
+#[entry]
+fn main() -> ! {
+ let peripherals = Peripherals::take();
+ let system = peripherals.PCR.split();
+ let _clocks = ClockControl::boot_defaults(system.clock_control).freeze();
+
+ let syst = SystemTimer::new(peripherals.SYSTIMER);
+ let mut alarm0 = syst.alarm0.into_periodic();
+ alarm0.set_period(1u32.secs());
+
+ let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
+ let mut led = io.pins.gpio1.into_push_pull_output();
+
+ // setup ETM
+ let gpio_ext = GpioEtmChannels::new(peripherals.GPIO_SD);
+ let led_task = gpio_ext.channel0_task.toggle(&mut led);
+
+ let timer_event = SysTimerEtmEvent::new(&mut alarm0);
+
+ 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(&timer_event, &led_task);
+
+ // the LED is controlled by the timer without involving the CPU
+ loop {}
+}
diff --git a/esp32c6-hal/examples/etm_gpio.rs b/esp32c6-hal/examples/etm_gpio.rs
index 4f6f95f848f..7b1ffb62c73 100644
--- a/esp32c6-hal/examples/etm_gpio.rs
+++ b/esp32c6-hal/examples/etm_gpio.rs
@@ -14,7 +14,6 @@ use esp_backtrace as _;
#[entry]
fn main() -> ! {
- esp_println::logger::init_logger_from_env();
let peripherals = Peripherals::take();
let system = peripherals.PCR.split();
let _clocks = ClockControl::boot_defaults(system.clock_control).freeze();
diff --git a/esp32h2-hal/examples/etm_blinky_systimer.rs b/esp32h2-hal/examples/etm_blinky_systimer.rs
new file mode 100644
index 00000000000..758b8c02e04
--- /dev/null
+++ b/esp32h2-hal/examples/etm_blinky_systimer.rs
@@ -0,0 +1,44 @@
+//! Control LED on GPIO 1 by the systimer via ETM
+
+#![no_std]
+#![no_main]
+
+use esp32h2_hal::{
+ clock::ClockControl,
+ etm::Etm,
+ gpio::{etm::GpioEtmChannels, IO},
+ peripherals::Peripherals,
+ prelude::*,
+ systimer::{etm::SysTimerEtmEvent, SystemTimer},
+};
+use esp_backtrace as _;
+
+#[entry]
+fn main() -> ! {
+ let peripherals = Peripherals::take();
+ let system = peripherals.PCR.split();
+ let _clocks = ClockControl::boot_defaults(system.clock_control).freeze();
+
+ let syst = SystemTimer::new(peripherals.SYSTIMER);
+ let mut alarm0 = syst.alarm0.into_periodic();
+ alarm0.set_period(1u32.secs());
+
+ let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
+ let mut led = io.pins.gpio1.into_push_pull_output();
+
+ // setup ETM
+ let gpio_ext = GpioEtmChannels::new(peripherals.GPIO_SD);
+ let led_task = gpio_ext.channel0_task.toggle(&mut led);
+
+ let timer_event = SysTimerEtmEvent::new(&mut alarm0);
+
+ 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(&timer_event, &led_task);
+
+ // the LED is controlled by the timer without involving the CPU
+ loop {}
+}
diff --git a/esp32h2-hal/examples/etm_gpio.rs b/esp32h2-hal/examples/etm_gpio.rs
index 64e64987124..4b8b4276a24 100644
--- a/esp32h2-hal/examples/etm_gpio.rs
+++ b/esp32h2-hal/examples/etm_gpio.rs
@@ -14,7 +14,6 @@ use esp_backtrace as _;
#[entry]
fn main() -> ! {
- esp_println::logger::init_logger_from_env();
let peripherals = Peripherals::take();
let system = peripherals.PCR.split();
let _clocks = ClockControl::boot_defaults(system.clock_control).freeze();