From f42df8c25b166ac0bd3bee232dd04a53050f1ff9 Mon Sep 17 00:00:00 2001 From: Charles Guan <3221512+charlesincharge@users.noreply.github.com> Date: Thu, 2 Oct 2025 07:00:52 -0700 Subject: [PATCH] feat(wwdt): Add reset cause detection functionality - Add ResetCause enum with all MSPM0 reset causes from TRM Table 2-9 - Add read_reset_cause() function to read SYSCTL.RSTCAUSE register - Include helper methods to check for watchdog-specific resets - Update wwdt example to demonstrate reset cause detection - Register is automatically cleared after reading per MSPM0 spec Fixes watchdog reset detection for debugging applications. --- embassy-mspm0/src/wwdt.rs | 86 +++++++++++++++++++++++++++++ examples/mspm0l1306/src/bin/wwdt.rs | 29 +++++++++- 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/embassy-mspm0/src/wwdt.rs b/embassy-mspm0/src/wwdt.rs index e5c62c6602..86020b4fe3 100644 --- a/embassy-mspm0/src/wwdt.rs +++ b/embassy-mspm0/src/wwdt.rs @@ -240,6 +240,82 @@ impl ClosedWindowPercentage { } } +/// Reset cause values from SYSCTL.RSTCAUSE register. +/// Based on MSPM0 Technical Reference Manual Table 2-9. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum ResetCause { + /// No reset since last read + NoReset = 0x00, + /// VDD < POR- violation, PMU trim parity fault, or SHUTDNSTOREx parity fault + PorHwFailure = 0x01, + /// NRST pin reset (>1s) + PorExternalNrst = 0x02, + /// Software-triggered POR + PorSwTriggered = 0x03, + /// VDD < BOR- violation + BorSupplyFailure = 0x04, + /// Wake from SHUTDOWN + BorWakeFromShutdown = 0x05, + /// Non-PMU trim parity fault + BootrstNonPmuParityFault = 0x08, + /// Fatal clock fault + BootrstClockFault = 0x09, + /// NRST pin reset (<1s) + BootrstExternalNrst = 0x0C, + /// Software-triggered BOOTRST + BootrstSwTriggered = 0x0D, + /// WWDT0 violation - This is the main watchdog reset cause we're interested in + SysrstWwdt0Violation = 0x0E, + /// BSL exit (if present) + SysrstBslExit = 0x10, + /// BSL entry (if present) + SysrstBslEntry = 0x11, + /// Uncorrectable flash ECC error (if present) + SysrstFlashEccError = 0x14, + /// CPU lockup violation + SysrstCpuLockupViolation = 0x15, + /// Debug-triggered SYSRST + SysrstDebugTriggered = 0x1A, + /// Software-triggered SYSRST + SysrstSwTriggered = 0x1B, + /// Debug-triggered CPURST + CpurstDebugTriggered = 0x1C, + /// Software-triggered CPURST + CpurstSwTriggered = 0x1D, +} + +impl TryFrom for ResetCause { + type Error = u8; + + fn try_from(value: u8) -> Result { + use ResetCause::*; + match value { + 0x00 => Ok(NoReset), + 0x01 => Ok(PorHwFailure), + 0x02 => Ok(PorExternalNrst), + 0x03 => Ok(PorSwTriggered), + 0x04 => Ok(BorSupplyFailure), + 0x05 => Ok(BorWakeFromShutdown), + 0x08 => Ok(BootrstNonPmuParityFault), + 0x09 => Ok(BootrstClockFault), + 0x0C => Ok(BootrstExternalNrst), + 0x0D => Ok(BootrstSwTriggered), + 0x0E => Ok(SysrstWwdt0Violation), + 0x10 => Ok(SysrstBslExit), + 0x11 => Ok(SysrstBslEntry), + 0x14 => Ok(SysrstFlashEccError), + 0x15 => Ok(SysrstCpuLockupViolation), + 0x1A => Ok(SysrstDebugTriggered), + 0x1B => Ok(SysrstSwTriggered), + 0x1C => Ok(CpurstDebugTriggered), + 0x1D => Ok(CpurstSwTriggered), + other => Err(other), + } + } +} + #[non_exhaustive] #[derive(Clone, Copy, PartialEq, Eq, Debug)] /// Watchdog Config @@ -324,6 +400,16 @@ impl Watchdog { } } +/// Read the reset cause from the SYSCTL.RSTCAUSE register. +/// +/// This function reads the reset cause register which indicates why the last +/// system reset occurred. The register is automatically cleared after being read, +/// so this should be called only once per application startup. +pub fn read_reset_cause() -> Result { + let cause_raw = pac::SYSCTL.rstcause().read().id(); + ResetCause::try_from(cause_raw as u8) +} + pub(crate) trait SealedInstance { fn regs() -> &'static Regs; } diff --git a/examples/mspm0l1306/src/bin/wwdt.rs b/examples/mspm0l1306/src/bin/wwdt.rs index b8fb1a1cd4..f2f94806ce 100644 --- a/examples/mspm0l1306/src/bin/wwdt.rs +++ b/examples/mspm0l1306/src/bin/wwdt.rs @@ -8,7 +8,9 @@ use defmt::*; use embassy_executor::Spawner; use embassy_mspm0::gpio::{Level, Output}; -use embassy_mspm0::wwdt::{ClosedWindowPercentage, Config, Timeout, Watchdog}; +use embassy_mspm0::wwdt::{ + read_reset_cause, ClosedWindowPercentage, Config, ResetCause, Timeout, Watchdog, +}; use embassy_time::Timer; use {defmt_rtt as _, panic_halt as _}; @@ -20,6 +22,8 @@ async fn main(_spawner: Spawner) -> ! { let mut conf = Config::default(); conf.timeout = Timeout::Sec1; + log_reset_cause(); + // watchdog also resets the system if the pet comes too early, // less than 250 msec == 25% from 1 sec conf.closed_window = ClosedWindowPercentage::TwentyFive; @@ -52,3 +56,26 @@ async fn main(_spawner: Spawner) -> ! { // Timer::after_millis(200).await; // } } + +fn log_reset_cause() { + // Check reset cause early in startup (register is cleared after reading) + match read_reset_cause() { + Ok(ResetCause::SysrstWwdt0Violation) => { + error!( + "🐕 System was reset due to WWDT0 timeout! Previous run failed to pet watchdog." + ); + } + Ok(ResetCause::NoReset) => { + info!("✅ No reset since last read (first boot or register already read)"); + } + Ok(ResetCause::PorExternalNrst) => { + info!("🔄 System reset via NRST pin"); + } + Ok(other) => { + info!("🔄 System reset cause: {:?}", other); + } + Err(_) => { + warn!("❓ Unknown reset cause detected (reserved value)"); + } + } +}