From 0d965a9fc66153db0be37d3a27e92176fac59aa3 Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Fri, 11 Oct 2024 14:31:10 +0200 Subject: [PATCH] Make `RWDT` API work correctly, make `RWDT` and `MWDT`APIs same --- esp-hal/src/lib.rs | 6 +- esp-hal/src/rtc_cntl/mod.rs | 171 ++++++++++++++++++++----------- esp-hal/src/timer/timg.rs | 75 +++++++++----- examples/src/bin/rtc_watchdog.rs | 6 +- examples/src/bin/watchdog.rs | 2 +- 5 files changed, 170 insertions(+), 90 deletions(-) diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index e84d25c48d5..16d6292e51a 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -505,7 +505,7 @@ pub fn init(config: Config) -> Peripherals { match config.watchdog.rwdt { WatchdogStatus::Enabled(duration) => { rtc.rwdt.enable(); - rtc.rwdt.set_timeout(duration); + rtc.rwdt.set_timeout(0, duration).unwrap(); } WatchdogStatus::Disabled => { rtc.rwdt.disable(); @@ -516,7 +516,7 @@ pub fn init(config: Config) -> Peripherals { WatchdogStatus::Enabled(duration) => { let mut timg0_wd = crate::timer::timg::Wdt::::new(); timg0_wd.enable(); - timg0_wd.set_timeout(duration); + timg0_wd.set_timeout(0, duration).unwrap(); } WatchdogStatus::Disabled => { crate::timer::timg::Wdt::::new().disable(); @@ -528,7 +528,7 @@ pub fn init(config: Config) -> Peripherals { WatchdogStatus::Enabled(duration) => { let mut timg1_wd = crate::timer::timg::Wdt::::new(); timg1_wd.enable(); - timg1_wd.set_timeout(duration); + timg1_wd.set_timeout(0, duration).unwrap(); } WatchdogStatus::Disabled => { crate::timer::timg::Wdt::::new().disable(); diff --git a/esp-hal/src/rtc_cntl/mod.rs b/esp-hal/src/rtc_cntl/mod.rs index 0824ac6494f..01d5dc2344b 100644 --- a/esp-hal/src/rtc_cntl/mod.rs +++ b/esp-hal/src/rtc_cntl/mod.rs @@ -198,7 +198,7 @@ impl<'d> Rtc<'d> { let this = Self { _inner: rtc_cntl.into_ref(), - rwdt: Rwdt::default(), + rwdt: Rwdt::new(), #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] swd: Swd::new(), }; @@ -785,7 +785,7 @@ pub enum RwdtStageAction { Interrupt = 1, /// Reset the CPU core. ResetCpu = 2, - /// Reset the main system (MWDT, CPU, and all peripherals). + /// Reset the main system. /// The power management unit and RTC peripherals will not be reset. ResetCore = 3, /// Reset the main system, power management unit and RTC peripherals. @@ -793,33 +793,31 @@ pub enum RwdtStageAction { } /// RWDT related errors. +#[derive(Debug)] pub enum RwdtError { /// Trying to configure the wrong stage. InvalidStage, } /// RTC Watchdog Timer. -pub struct Rwdt { - stg0_action: RwdtStageAction, - stg1_action: RwdtStageAction, - stg2_action: RwdtStageAction, - stg3_action: RwdtStageAction, -} +pub struct Rwdt; impl Default for Rwdt { fn default() -> Self { - Self { - stg0_action: RwdtStageAction::ResetSystem, - stg1_action: RwdtStageAction::Off, - stg2_action: RwdtStageAction::Off, - stg3_action: RwdtStageAction::Off, - } + Self::new() } } /// RTC Watchdog Timer driver. impl Rwdt { + /// Create a new RTC watchdog timer instance + pub fn new() -> Self { + Self + } + /// Enable the watchdog timer instance. + /// Watchdog starts with default settings (`stage 0`` resets the system, the + /// others are deactivated) pub fn enable(&mut self) { self.set_enabled(true); } @@ -829,42 +827,38 @@ impl Rwdt { self.set_enabled(false); } - /// Listen for interrupts. + /// Listen for interrupts on stage 0. pub fn listen(&mut self) { #[cfg(not(any(esp32c6, esp32h2)))] let rtc_cntl = unsafe { &*LPWR::PTR }; #[cfg(any(esp32c6, esp32h2))] let rtc_cntl = unsafe { &*LP_WDT::PTR }; - self.stg0_action = RwdtStageAction::Interrupt; - self.set_write_protection(false); // Configure STAGE0 to trigger an interrupt upon expiration rtc_cntl .wdtconfig0() - .modify(|_, w| unsafe { w.wdt_stg0().bits(self.stg0_action as u8) }); + .modify(|_, w| unsafe { w.wdt_stg0().bits(RwdtStageAction::Interrupt as u8) }); rtc_cntl.int_ena().modify(|_, w| w.wdt().set_bit()); self.set_write_protection(true); } - /// Stop listening for interrupts. + /// Stop listening for interrupts on stage 0. pub fn unlisten(&mut self) { #[cfg(not(any(esp32c6, esp32h2)))] let rtc_cntl = unsafe { &*LPWR::PTR }; #[cfg(any(esp32c6, esp32h2))] let rtc_cntl = unsafe { &*LP_WDT::PTR }; - self.stg0_action = RwdtStageAction::ResetSystem; - self.set_write_protection(false); // Configure STAGE0 to reset the main system and the RTC upon expiration. rtc_cntl .wdtconfig0() - .modify(|_, w| unsafe { w.wdt_stg0().bits(self.stg0_action as u8) }); + .modify(|_, w| unsafe { w.wdt_stg0().bits(RwdtStageAction::ResetSystem as u8) }); rtc_cntl.int_ena().modify(|_, w| w.wdt().clear_bit()); @@ -918,7 +912,7 @@ impl Rwdt { rtc_cntl.wdtwprotect().write(|w| unsafe { w.bits(wkey) }); } - fn set_enabled(&mut self, enable: bool) { + fn set_enabled(&mut self, enabled: bool) { #[cfg(not(any(esp32c6, esp32h2)))] let rtc_cntl = unsafe { &*LPWR::PTR }; #[cfg(any(esp32c6, esp32h2))] @@ -926,15 +920,47 @@ impl Rwdt { self.set_write_protection(false); - rtc_cntl - .wdtconfig0() - .modify(|_, w| w.wdt_en().bit(enable).wdt_flashboot_mod_en().bit(enable)); + if !enabled { + rtc_cntl.wdtconfig0().modify(|_, w| unsafe { w.bits(0) }); + } else { + rtc_cntl + .wdtconfig0() + .write(|w| w.wdt_flashboot_mod_en().bit(false)); + + rtc_cntl + .wdtconfig0() + .modify(|_, w| w.wdt_en().bit(enabled).wdt_pause_in_slp().bit(enabled)); + + // Apply default settings for WDT + unsafe { + rtc_cntl.wdtconfig0().modify(|_, w| { + w.wdt_stg0() + .bits(RwdtStageAction::ResetSystem as u8) + .wdt_cpu_reset_length() + .bits(7) + .wdt_sys_reset_length() + .bits(7) + .wdt_stg1() + .bits(RwdtStageAction::Off as u8) + .wdt_stg2() + .bits(RwdtStageAction::Off as u8) + .wdt_stg3() + .bits(RwdtStageAction::Off as u8) + .wdt_en() + .set_bit() + }); + } + } self.set_write_protection(true); } - /// Configure timeout value in ms. - pub fn set_timeout(&mut self, timeout: MicrosDurationU64) { + /// Configure timeout value in ms for the selected stage. + pub fn set_timeout( + &mut self, + stage: usize, + timeout: MicrosDurationU64, + ) -> Result<(), RwdtError> { #[cfg(not(any(esp32c6, esp32h2)))] let rtc_cntl = unsafe { &*LPWR::PTR }; #[cfg(any(esp32c6, esp32h2))] @@ -945,41 +971,68 @@ impl Rwdt { unsafe { #[cfg(esp32)] - rtc_cntl - .wdtconfig1() - .modify(|_, w| w.wdt_stg0_hold().bits(timeout_raw)); + match stage { + 0 => rtc_cntl + .wdtconfig1() + .modify(|_, w| w.wdt_stg0_hold().bits(timeout_raw)), + 1 => rtc_cntl + .wdtconfig2() + .modify(|_, w| w.wdt_stg1_hold().bits(timeout_raw)), + 2 => rtc_cntl + .wdtconfig3() + .modify(|_, w| w.wdt_stg2_hold().bits(timeout_raw)), + 3 => rtc_cntl + .wdtconfig4() + .modify(|_, w| w.wdt_stg3_hold().bits(timeout_raw)), + _ => return Err(RwdtError::InvalidStage), + } #[cfg(any(esp32c6, esp32h2))] - rtc_cntl.config1().modify(|_, w| { - w.wdt_stg0_hold() - .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) - }); + match stage { + 0 => rtc_cntl.config1().modify(|_, w| { + w.wdt_stg0_hold() + .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) + }), + 1 => rtc_cntl.config2().modify(|_, w| { + w.wdt_stg1_hold() + .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) + }), + 2 => rtc_cntl.config3().modify(|_, w| { + w.wdt_stg2_hold() + .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) + }), + 3 => rtc_cntl.config4().modify(|_, w| { + w.wdt_stg3_hold() + .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) + }), + _ => return Err(RwdtError::InvalidStage), + } #[cfg(not(any(esp32, esp32c6, esp32h2)))] - rtc_cntl.wdtconfig1().modify(|_, w| { - w.wdt_stg0_hold() - .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) - }); - - rtc_cntl.wdtconfig0().modify(|_, w| { - w.wdt_stg0() - .bits(self.stg0_action as u8) - .wdt_cpu_reset_length() - .bits(7) - .wdt_sys_reset_length() - .bits(7) - .wdt_stg1() - .bits(self.stg1_action as u8) - .wdt_stg2() - .bits(self.stg2_action as u8) - .wdt_stg3() - .bits(self.stg3_action as u8) - .wdt_en() - .set_bit() - }); + match stage { + 0 => rtc_cntl.wdtconfig1().modify(|_, w| { + w.wdt_stg0_hold() + .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) + }), + 1 => rtc_cntl.wdtconfig2().modify(|_, w| { + w.wdt_stg1_hold() + .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) + }), + 2 => rtc_cntl.wdtconfig3().modify(|_, w| { + w.wdt_stg2_hold() + .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) + }), + 3 => rtc_cntl.wdtconfig4().modify(|_, w| { + w.wdt_stg3_hold() + .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) + }), + _ => return Err(RwdtError::InvalidStage), + } } self.set_write_protection(true); + + Ok(()) } /// Set the action for a specific stage. @@ -997,25 +1050,21 @@ impl Rwdt { match stage { 0 => { - self.stg0_action = action; rtc_cntl .wdtconfig0() .modify(|_, w| unsafe { w.wdt_stg0().bits(action as u8) }); } 1 => { - self.stg1_action = action; rtc_cntl .wdtconfig0() .modify(|_, w| unsafe { w.wdt_stg1().bits(action as u8) }); } 2 => { - self.stg2_action = action; rtc_cntl .wdtconfig0() .modify(|_, w| unsafe { w.wdt_stg2().bits(action as u8) }); } 3 => { - self.stg3_action = action; rtc_cntl .wdtconfig0() .modify(|_, w| unsafe { w.wdt_stg3().bits(action as u8) }); @@ -1044,7 +1093,7 @@ impl embedded_hal_02::watchdog::WatchdogEnable for Rwdt { where T: Into, { - self.set_timeout(period.into()); + self.set_timeout(0, period.into()).unwrap(); } } diff --git a/esp-hal/src/timer/timg.rs b/esp-hal/src/timer/timg.rs index a2e4b953cbf..5d0092f27b3 100644 --- a/esp-hal/src/timer/timg.rs +++ b/esp-hal/src/timer/timg.rs @@ -983,7 +983,34 @@ where if !enabled { reg_block.wdtconfig0().write(|w| unsafe { w.bits(0) }); } else { + reg_block + .wdtconfig0() + .write(|w| w.wdt_flashboot_mod_en().bit(false)); + reg_block.wdtconfig0().write(|w| w.wdt_en().bit(true)); + + #[cfg_attr(esp32, allow(unused_unsafe))] + reg_block.wdtconfig0().write(|w| unsafe { + w.wdt_en() + .bit(true) + .wdt_stg0() + .bits(MwdtStageAction::Off as u8) + .wdt_cpu_reset_length() + .bits(7) + .wdt_sys_reset_length() + .bits(7) + .wdt_stg1() + .bits(MwdtStageAction::Off as u8) + .wdt_stg2() + .bits(MwdtStageAction::Off as u8) + .wdt_stg3() + .bits(MwdtStageAction::Off as u8) + }); + + #[cfg(any(esp32c2, esp32c3, esp32c6))] + reg_block + .wdtconfig0() + .modify(|_, w| w.wdt_conf_update_en().set_bit()); } self.set_write_protection(true); @@ -1011,7 +1038,11 @@ where } /// Set the timeout, in microseconds, of the watchdog timer - pub fn set_timeout(&mut self, timeout: MicrosDurationU64) { + pub fn set_timeout( + &mut self, + stage: usize, + timeout: MicrosDurationU64, + ) -> Result<(), MwdtError> { let timeout_raw = (timeout.to_nanos() * 10 / 125) as u32; let reg_block = unsafe { &*TG::register_block() }; @@ -1022,27 +1053,23 @@ where .wdtconfig1() .write(|w| unsafe { w.wdt_clk_prescale().bits(1) }); - reg_block - .wdtconfig2() - .write(|w| unsafe { w.wdt_stg0_hold().bits(timeout_raw) }); - - #[cfg_attr(esp32, allow(unused_unsafe))] - reg_block.wdtconfig0().write(|w| unsafe { - w.wdt_en() - .bit(true) - .wdt_stg0() - .bits(MwdtStageAction::ResetSystem as u8) - .wdt_cpu_reset_length() - .bits(1) - .wdt_sys_reset_length() - .bits(1) - .wdt_stg1() - .bits(MwdtStageAction::Off as u8) - .wdt_stg2() - .bits(MwdtStageAction::Off as u8) - .wdt_stg3() - .bits(MwdtStageAction::Off as u8) - }); + unsafe { + match stage { + 0 => reg_block + .wdtconfig2() + .write(|w| w.wdt_stg0_hold().bits(timeout_raw)), + 1 => reg_block + .wdtconfig3() + .write(|w| w.wdt_stg1_hold().bits(timeout_raw)), + 2 => reg_block + .wdtconfig4() + .write(|w| w.wdt_stg2_hold().bits(timeout_raw)), + 3 => reg_block + .wdtconfig5() + .write(|w| w.wdt_stg3_hold().bits(timeout_raw)), + _ => return Err(MwdtError::InvalidStage), + } + } #[cfg(any(esp32c2, esp32c3, esp32c6))] reg_block @@ -1050,6 +1077,8 @@ where .modify(|_, w| w.wdt_conf_update_en().set_bit()); self.set_write_protection(true); + + Ok(()) } /// Set the stage action of the MWDT for a specific stage. @@ -1137,7 +1166,7 @@ where T: Into, { self.enable(); - self.set_timeout(period.into()); + self.set_timeout(0, period.into()).unwrap(); } } diff --git a/examples/src/bin/rtc_watchdog.rs b/examples/src/bin/rtc_watchdog.rs index 255f0bf1225..3a232e1f4b7 100644 --- a/examples/src/bin/rtc_watchdog.rs +++ b/examples/src/bin/rtc_watchdog.rs @@ -28,7 +28,9 @@ fn main() -> ! { let mut rtc = Rtc::new(peripherals.LPWR); rtc.set_interrupt_handler(interrupt_handler); - rtc.rwdt.set_timeout(2000.millis()); + + rtc.rwdt.enable(); + rtc.rwdt.set_timeout(0, 2000.millis()).unwrap(); rtc.rwdt.listen(); critical_section::with(|cs| RWDT.borrow_ref_mut(cs).replace(rtc.rwdt)); @@ -47,7 +49,7 @@ fn interrupt_handler() { println!("Restarting in 5 seconds..."); - rwdt.set_timeout(5000.millis()); + rwdt.set_timeout(0, 5000.millis()).unwrap(); rwdt.unlisten(); }); } diff --git a/examples/src/bin/watchdog.rs b/examples/src/bin/watchdog.rs index ee01618c217..37b43cad05d 100644 --- a/examples/src/bin/watchdog.rs +++ b/examples/src/bin/watchdog.rs @@ -21,7 +21,7 @@ fn main() -> ! { let timg0 = TimerGroup::new_async(peripherals.TIMG0); let mut wdt0 = timg0.wdt; wdt0.enable(); - wdt0.set_timeout(2u64.secs()); + wdt0.set_timeout(0, 2u64.secs()).unwrap(); loop { wdt0.feed();