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();