-
Notifications
You must be signed in to change notification settings - Fork 1.2k
[mspm0] wwdt reset detection #4732
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[mspm0] wwdt reset detection #4732
Conversation
- 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.
I think it would belong in
The message should be buffered in defmt's internal buffer. I wonder if it has something to do with the hardware racing defmt, or if defmt assumes the contents of the buffer are garbage and clears it before reading after reattach. Is there some way to put a barrier so that execution doesn't advance until something has attached to defmt? |
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), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a fun little trick you can use here given that ResetCause
is repr(u8)
If you write something like the following for each branch you only need to declare the u8 value once.
match value {
_ if value == NoReset as u8 => Ok(NoReset),
....
}
CpurstSwTriggered = 0x1D, | ||
} | ||
|
||
impl TryFrom<u8> for ResetCause { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if this is public API that we want to guarantee. Since ResetCause
is a public enum, it would expose TryFrom
in public API.
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, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not all of these can be produced on every chip. But cfg guarding when a reset cause is available is going to involve some metapac work. Either another big entry in parts.yaml
or we try to parse the SVDs to figure out which reset causes are available (but this might require us to fix the errors that cause some SYSCTL modules erroring when extracted).
I'd be fine with just leaving all variants to exist on every chip.
/// 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<ResetCause, u8> { | ||
let cause_raw = pac::SYSCTL.rstcause().read().id(); | ||
ResetCause::try_from(cause_raw as u8) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does open up an interesting safety question: Should we allow the user to call this more than once? With peripheral init it makes sense to do that since otherwise memory safety errors would happen.
In this case it would just produce the wrong reset value. At minimum probably a good idea to mark this function as #[must_use = "Reading reset cause will clear it"]
so that the compiler can warn if you try to use it and ignore the result.
Thanks for the feedback and pointers! Will update tomorrow or next week. |
First-time opening a PR in this project, not sure if this is the right place to put the code.
I want to be able to determine when a watchdog reset has happened in my firmware. So I added a function to check the reset cause. It probably doesn't belong in wwdt.rs, but wasn't sure if I should make another module.
I also tried implementing a
TryFrom(mspm0_metapac::sysctl::vals::Id)
but I couldn't sufficiently inspect the type or figure out where it is defined in order to properly use it, so I ended up with anas u8
approach.Behavior
On a launchpad MSPM0L1306
Unfortunately, I'm not seeing the logs after reset that woudl confirm that the register is getting set to
SysrstWwdt0Violation
properly. Is there a way forprobe-rs
anddefmt
to come back up with the reset-MCU's log-stream?