diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c61be52c3b..ae1eebc44cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Async support for I2S (#801) - Async support for PARL_IO (#807) - Implement `embeded_hal_async::delay::DelayUs` trait for `SYSTIMER` alarms (#812) +- ETM driver, GPIO ETM (#819) ### Changed diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index 8a1f216dc1e..bff2ab44d4b 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -58,9 +58,10 @@ ufmt-write = { version = "0.1.0", optional = true } esp32 = { version = "0.26.0", features = ["critical-section"], optional = true } esp32c2 = { version = "0.14.0", features = ["critical-section"], optional = true } esp32c3 = { version = "0.17.0", features = ["critical-section"], optional = true } -esp32c6 = { version = "0.7.0", features = ["critical-section"], optional = true } +# until new C6 pacs are relesed, required because of a wrong base address of GPIO_SD +esp32c6 = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32c6", rev = "1b1ce7ba631cb349a8255f5959e768d5c52fad40", version = "0.7.0", features = ["critical-section"], optional = true } # until new H2 pacs are relesed, required because of https://github.com/esp-rs/esp-pacs/pull/156 -esp32h2 = { git = "https://github.com/esp-rs/esp-pacs.git", rev = "2545b66f4c11dd78dc02a3c77c44a78fa5893beb", features = ["critical-section"], optional = true } +esp32h2 = { git = "https://github.com/esp-rs/esp-pacs.git", rev = "1b1ce7ba631cb349a8255f5959e768d5c52fad40", features = ["critical-section"], optional = true } esp32s2 = { version = "0.17.0", features = ["critical-section"], optional = true } esp32s3 = { git = "https://github.com/esp-rs/esp-pacs", rev = "c1283ca", features = ["critical-section",], optional = true } diff --git a/esp-hal-common/devices/esp32h2/device.toml b/esp-hal-common/devices/esp32h2/device.toml index 1e6e8257549..3bc52035745 100644 --- a/esp-hal-common/devices/esp32h2/device.toml +++ b/esp-hal-common/devices/esp32h2/device.toml @@ -12,6 +12,7 @@ peripherals = [ "ecc", "efuse", "gpio", + "gpio_sd", "hmac", "hp_apm", "hp_sys", diff --git a/esp-hal-common/src/etm.rs b/esp-hal-common/src/etm.rs new file mode 100644 index 00000000000..b974a18a86a --- /dev/null +++ b/esp-hal-common/src/etm.rs @@ -0,0 +1,283 @@ +//! # 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, &mut system.peripheral_clock_control); +//! 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 {} + +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

+ 'd, + peripheral_clock_control: &mut PeripheralClockControl, + ) -> Self { + crate::into_ref!(peripheral); + peripheral_clock_control.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 {} +} diff --git a/esp-hal-common/src/gpio.rs b/esp-hal-common/src/gpio.rs index ecc6b46d98f..75bf7e703fa 100644 --- a/esp-hal-common/src/gpio.rs +++ b/esp-hal-common/src/gpio.rs @@ -1612,6 +1612,332 @@ macro_rules! analog { } } +#[cfg(soc_etm)] +pub mod etm { + //! # Event Task Matrix Function + //! + //! ## Overview + //! + //! GPIO supports ETM function, that is, the ETM task of GPIO can be + //! triggered by the ETM event of any peripheral, or the ETM task of any + //! peripheral can be triggered by the ETM event of GPIO. + //! + //! The GPIO ETM provides eight task channels. The ETM tasks that each task + //! channel can receive are: + //! - SET: GPIO goes high when triggered + //! - CLEAR: GPIO goes low when triggered + //! - TOGGLE: GPIO toggle level when triggered. + //! + //! GPIO has eight event channels, and the ETM events that each event + //! channel can generate are: + //! - RISE_EDGE: Indicates that the output signal of the corresponding GPIO + //! has a rising edge + //! - FALL_EDGE: Indicates that the output signal of the corresponding GPIO + //! has a falling edge + //! - ANY_EDGE: Indicates that the output signal of the corresponding GPIO + //! is reversed + //! + //! ## Example + //! ```no_run + //! let led_task = gpio_ext.channel0_task.toggle(&mut led); + //! let button_event = gpio_ext.channel0_event.falling_edge(button); + //! ``` + + use crate::peripheral::{Peripheral, PeripheralRef}; + + /// All the GPIO ETM channels + #[non_exhaustive] + pub struct GpioEtmChannels<'d> { + _gpio_sd: PeripheralRef<'d, crate::peripherals::GPIO_SD>, + pub channel0_task: GpioEtmTaskChannel<0>, + pub channel0_event: GpioEtmEventChannel<0>, + pub channel1_task: GpioEtmTaskChannel<1>, + pub channel1_event: GpioEtmEventChannel<1>, + pub channel2_task: GpioEtmTaskChannel<2>, + pub channel2_event: GpioEtmEventChannel<2>, + pub channel3_task: GpioEtmTaskChannel<3>, + pub channel3_event: GpioEtmEventChannel<3>, + pub channel4_task: GpioEtmTaskChannel<4>, + pub channel4_event: GpioEtmEventChannel<4>, + pub channel5_task: GpioEtmTaskChannel<5>, + pub channel5_event: GpioEtmEventChannel<5>, + pub channel6_task: GpioEtmTaskChannel<6>, + pub channel6_event: GpioEtmEventChannel<6>, + pub channel7_task: GpioEtmTaskChannel<7>, + pub channel7_event: GpioEtmEventChannel<7>, + } + + impl<'d> GpioEtmChannels<'d> { + pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(peripheral); + + Self { + _gpio_sd: peripheral, + channel0_task: GpioEtmTaskChannel {}, + channel0_event: GpioEtmEventChannel {}, + channel1_task: GpioEtmTaskChannel {}, + channel1_event: GpioEtmEventChannel {}, + channel2_task: GpioEtmTaskChannel {}, + channel2_event: GpioEtmEventChannel {}, + channel3_task: GpioEtmTaskChannel {}, + channel3_event: GpioEtmEventChannel {}, + channel4_task: GpioEtmTaskChannel {}, + channel4_event: GpioEtmEventChannel {}, + channel5_task: GpioEtmTaskChannel {}, + channel5_event: GpioEtmEventChannel {}, + channel6_task: GpioEtmTaskChannel {}, + channel6_event: GpioEtmEventChannel {}, + channel7_task: GpioEtmTaskChannel {}, + channel7_event: GpioEtmEventChannel {}, + } + } + } + + /// An ETM controlled GPIO event + pub struct GpioEtmEventChannel {} + + impl GpioEtmEventChannel { + /// Trigger at rising edge + pub fn rising_edge<'d, PIN>( + self, + pin: impl Peripheral

+ 'd, + ) -> GpioEtmEventChannelRising<'d, PIN, C> + where + PIN: super::Pin, + { + crate::into_ref!(pin); + enable_event_channel(C, pin.number()); + GpioEtmEventChannelRising { _pin: pin } + } + + /// Trigger at falling edge + pub fn falling_edge<'d, PIN>( + self, + pin: impl Peripheral

+ 'd, + ) -> GpioEtmEventChannelFalling<'d, PIN, C> + where + PIN: super::Pin, + { + crate::into_ref!(pin); + enable_event_channel(C, pin.number()); + GpioEtmEventChannelFalling { _pin: pin } + } + + /// Trigger at any edge + pub fn any_edge<'d, PIN>( + self, + pin: impl Peripheral

+ 'd, + ) -> GpioEtmEventChannelAny<'d, PIN, C> + where + PIN: super::Pin, + { + crate::into_ref!(pin); + enable_event_channel(C, pin.number()); + GpioEtmEventChannelAny { _pin: pin } + } + } + + /// Event for rising edge + #[non_exhaustive] + pub struct GpioEtmEventChannelRising<'d, PIN, const C: u8> + where + PIN: super::Pin, + { + _pin: PeripheralRef<'d, PIN>, + } + + impl<'d, PIN, const C: u8> crate::etm::private::Sealed for GpioEtmEventChannelRising<'d, PIN, C> where + PIN: super::Pin + { + } + + impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelRising<'d, PIN, C> + where + PIN: super::Pin, + { + fn id(&self) -> u8 { + 1 + C + } + } + + /// Event for falling edge + #[non_exhaustive] + pub struct GpioEtmEventChannelFalling<'d, PIN, const C: u8> + where + PIN: super::Pin, + { + _pin: PeripheralRef<'d, PIN>, + } + + impl<'d, PIN, const C: u8> crate::etm::private::Sealed for GpioEtmEventChannelFalling<'d, PIN, C> where + PIN: super::Pin + { + } + + impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelFalling<'d, PIN, C> + where + PIN: super::Pin, + { + fn id(&self) -> u8 { + 9 + C + } + } + + /// Event for any edge + #[non_exhaustive] + pub struct GpioEtmEventChannelAny<'d, PIN, const C: u8> + where + PIN: super::Pin, + { + _pin: PeripheralRef<'d, PIN>, + } + + impl<'d, PIN, const C: u8> crate::etm::private::Sealed for GpioEtmEventChannelAny<'d, PIN, C> where + PIN: super::Pin + { + } + + impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelAny<'d, PIN, C> + where + PIN: super::Pin, + { + fn id(&self) -> u8 { + 17 + C + } + } + + /// An ETM controlled GPIO task + pub struct GpioEtmTaskChannel {} + + impl GpioEtmTaskChannel { + // In theory we could have multiple pins assigned to the same task. Not sure how + // useful that would be. If we want to support it, the easiest would be + // to offer additional functions like `set2`, `set3` etc. where the + // number is the pin-count + + /// Task to set a high level + pub fn set<'d, PIN>(self, pin: impl Peripheral

+ 'd) -> GpioEtmTaskSet<'d, PIN, C> + where + PIN: super::Pin, + { + crate::into_ref!(pin); + enable_task_channel(C, pin.number()); + GpioEtmTaskSet { _pin: pin } + } + + /// Task to set a low level + pub fn clear<'d, PIN>( + self, + pin: impl Peripheral

+ 'd, + ) -> GpioEtmTaskClear<'d, PIN, C> + where + PIN: super::Pin, + { + crate::into_ref!(pin); + enable_task_channel(C, pin.number()); + GpioEtmTaskClear { _pin: pin } + } + + /// Task to toggle the level + pub fn toggle<'d, PIN>( + self, + pin: impl Peripheral

+ 'd, + ) -> GpioEtmTaskToggle<'d, PIN, C> + where + PIN: super::Pin, + { + crate::into_ref!(pin); + enable_task_channel(C, pin.number()); + GpioEtmTaskToggle { _pin: pin } + } + } + + /// Task for set operation + #[non_exhaustive] + pub struct GpioEtmTaskSet<'d, PIN, const C: u8> + where + PIN: super::Pin, + { + _pin: PeripheralRef<'d, PIN>, + } + + impl<'d, PIN, const C: u8> crate::etm::private::Sealed for GpioEtmTaskSet<'d, PIN, C> where + PIN: super::Pin + { + } + + impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskSet<'d, PIN, C> + where + PIN: super::Pin, + { + fn id(&self) -> u8 { + 1 + C + } + } + + /// Task for clear operation + #[non_exhaustive] + pub struct GpioEtmTaskClear<'d, PIN, const C: u8> { + _pin: PeripheralRef<'d, PIN>, + } + + impl<'d, PIN, const C: u8> crate::etm::private::Sealed for GpioEtmTaskClear<'d, PIN, C> where + PIN: super::Pin + { + } + + impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskClear<'d, PIN, C> + where + PIN: super::Pin, + { + fn id(&self) -> u8 { + 9 + C + } + } + + /// Task for toggle operation + #[non_exhaustive] + pub struct GpioEtmTaskToggle<'d, PIN, const C: u8> { + _pin: PeripheralRef<'d, PIN>, + } + + impl<'d, PIN, const C: u8> crate::etm::private::Sealed for GpioEtmTaskToggle<'d, PIN, C> where + PIN: super::Pin + { + } + + impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskToggle<'d, PIN, C> + where + PIN: super::Pin, + { + fn id(&self) -> u8 { + 17 + C + } + } + + fn enable_task_channel(channel: u8, pin: u8) { + let gpio_sd = unsafe { crate::peripherals::GPIO_SD::steal() }; + let ptr = unsafe { gpio_sd.etm_task_p0_cfg.as_ptr().add(pin as usize / 4) }; + let shift = 8 * (pin as usize % 4); + // bit 0 = en, bit 1-3 = channel + unsafe { + ptr.write_volatile( + ptr.read_volatile() & !(0xf << shift) + | 1 << shift + | (channel as u32) << (shift + 1), + ); + } + } + + fn enable_event_channel(channel: u8, pin: u8) { + let gpio_sd = unsafe { crate::peripherals::GPIO_SD::steal() }; + gpio_sd.etm_event_ch_cfg[channel as usize].modify(|_, w| w.etm_ch0_event_en().clear_bit()); + gpio_sd.etm_event_ch_cfg[channel as usize] + .modify(|_, w| w.etm_ch0_event_sel().variant(pin)); + gpio_sd.etm_event_ch_cfg[channel as usize].modify(|_, w| w.etm_ch0_event_en().set_bit()); + } +} + #[cfg(lp_io)] pub mod lp_gpio { use core::marker::PhantomData; diff --git a/esp-hal-common/src/hmac.rs b/esp-hal-common/src/hmac.rs index d65bbce1554..e29112f5363 100644 --- a/esp-hal-common/src/hmac.rs +++ b/esp-hal-common/src/hmac.rs @@ -137,6 +137,13 @@ impl<'d> Hmac<'d> { self.hmac .set_para_finish .write(|w| w.set_para_end().set_bit()); + + // TODO align `query_check` in SVDs/PACs + #[cfg(esp32h2)] + if self.hmac.query_error.read().query_check().bit_is_set() { + return Err(nb::Error::Other(Error::KeyPurposeMismatch)); + } + #[cfg(not(esp32h2))] if self.hmac.query_error.read().qurey_check().bit_is_set() { return Err(nb::Error::Other(Error::KeyPurposeMismatch)); } diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index 45ed76949ad..18f1d1ba902 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -96,6 +96,8 @@ pub mod dma; pub mod ecc; #[cfg(feature = "embassy")] pub mod embassy; +#[cfg(soc_etm)] +pub mod etm; #[cfg(gpio)] pub mod gpio; #[cfg(hmac)] diff --git a/esp-hal-common/src/soc/esp32h2/peripherals.rs b/esp-hal-common/src/soc/esp32h2/peripherals.rs index 44cd85746ba..5ff14c13a51 100644 --- a/esp-hal-common/src/soc/esp32h2/peripherals.rs +++ b/esp-hal-common/src/soc/esp32h2/peripherals.rs @@ -36,6 +36,7 @@ crate::peripherals! { ECC => true, EFUSE => true, GPIO => true, + GPIO_SD => true, HMAC => true, HP_APM => true, HP_SYS => true, diff --git a/esp-hal-common/src/system.rs b/esp-hal-common/src/system.rs index 3b2be338bd3..c358ae101d0 100755 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -108,6 +108,8 @@ pub enum Peripheral { Hmac, #[cfg(ecc)] Ecc, + #[cfg(soc_etm)] + Etm, } pub struct SoftwareInterruptControl { @@ -553,6 +555,11 @@ impl PeripheralClockControl { system.ecc_conf.modify(|_, w| w.ecc_clk_en().set_bit()); system.ecc_conf.modify(|_, w| w.ecc_rst_en().clear_bit()); } + #[cfg(soc_etm)] + Peripheral::Etm => { + system.etm_conf.modify(|_, w| w.etm_clk_en().set_bit()); + system.etm_conf.modify(|_, w| w.etm_rst_en().clear_bit()); + } } } } diff --git a/esp32c6-hal/examples/etm_gpio.rs b/esp32c6-hal/examples/etm_gpio.rs new file mode 100644 index 00000000000..a9625e03f20 --- /dev/null +++ b/esp32c6-hal/examples/etm_gpio.rs @@ -0,0 +1,42 @@ +//! Control LED on GPIO 1 by the BOOT-BUTTON via ETM + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + etm::Etm, + gpio::{etm::GpioEtmChannels, IO}, + peripherals::Peripherals, + prelude::*, +}; +use esp_backtrace as _; + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + 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(); + + led.set_high().unwrap(); + + // 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, &mut system.peripheral_clock_control); + 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 {} +} diff --git a/esp32h2-hal/examples/etm_gpio.rs b/esp32h2-hal/examples/etm_gpio.rs new file mode 100644 index 00000000000..166279d2919 --- /dev/null +++ b/esp32h2-hal/examples/etm_gpio.rs @@ -0,0 +1,42 @@ +//! Control LED on GPIO 1 by the BOOT-BUTTON via ETM + +#![no_std] +#![no_main] + +use esp32h2_hal::{ + clock::ClockControl, + etm::Etm, + gpio::{etm::GpioEtmChannels, IO}, + peripherals::Peripherals, + prelude::*, +}; +use esp_backtrace as _; + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + 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(); + + led.set_high().unwrap(); + + // 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, &mut system.peripheral_clock_control); + 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 {} +}