From 128ef72d6d0098aa209ad36d734d88dfed57a19a Mon Sep 17 00:00:00 2001 From: Noah Laptop Date: Wed, 8 Apr 2020 09:37:12 -0700 Subject: [PATCH] Fix watchdog feeding (address https://github.com/OPEnSLab-OSU/FeatherFault/issues/4) --- README.md | 2 ++ src/FeatherFault.cpp | 17 ++++++++++++++--- src/FeatherFault.h | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9e0c793..c0d4895 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,8 @@ FeatherFault currently handles three failure modes: hanging, [memory overflow](h Hanging detection is implemented using the SAMD watchdog timer early warning interrupt. As a result, FeatherFault will not detect hanging unless `FeatherFault::StartWDT` is called somewhere in the beginning of the sketch. Note that similar to normal watchdog operation, FeatherFaults detection must be periodically resetting using `MARK` macro; this means that the `MARK` macro must be placed such that it is called at least periodically under the timeout specified. In long operations that cannot be `MARK`ed (sleep being an example), use `FeatherFault::StopWDT` to disable the watchdog during that time. +Behind the scenes watchdog feeding is implemented in terms of a global atomic boolean which determines if the device should fault during the watchdog interrupt, as opposed to the standard register write found in SleepyDog and other libraries. This decision was made because feeding the WDT on the SAMD21 is [extremely slow (1-5ms)](https://www.avrfreaks.net/forum/c21-watchdog-syncing-too-slow), which is unacceptable for the `MARK` macro (see https://github.com/OPEnSLab-OSU/FeatherFault/issues/4). Note that due to this implementation, the watchdog interrupt happens regularly and may take an extended period of time (1-5ms), causing possible timing issues with other code. + #### Memory Overflow Detection Memory overflow detection is implemented by checking the top of the heap against the top of the stack. If the stack is overwriting the heap, memory is assumed to be corrupted and the board is immediately reset. This check is performed inside the `MARK` macro. diff --git a/src/FeatherFault.cpp b/src/FeatherFault.cpp index cbb8cf1..46b9872 100644 --- a/src/FeatherFault.cpp +++ b/src/FeatherFault.cpp @@ -34,6 +34,8 @@ typedef union { static const uint32_t pageSizes[] = { 8, 16, 32, 64, 128, 256, 512, 1024 }; +/** Global atmoic bool to check if the watchdog has been fed, we use a boolean instead of WDT_Reset because watchdog synchronization is slow */ +static volatile std::atomic_bool should_feed_watchdog(false); /** Global atomic bool to specify that last_line or last_file are being written to, determines if a fault happened while they were being written */ static volatile std::atomic_bool is_being_written(false); /** Global variable to store the last line MARKed, written by FeatherFault::_Mark and read by FeatherFault::HandleFault */ @@ -146,8 +148,14 @@ static void WDTReset() { /** WDT Handler, calls HandleFault(FeatherFault::FAULT_HUNG) */ [[ noreturn ]] __attribute__ ((interrupt ("IRQ"))) void WDT_Handler() { WDT->INTFLAG.bit.EW = 1; // Clear interrupt flag - // Handle fault! - HandleFault(FeatherFault::FAULT_HUNG); + // Check if the watchdog has been "fed", if so, reset the watchdog and continue + if (should_feed_watchdog.load()){ + should_feed_watchdog.store(false); + WDTReset(); + } + // else there's been a timeout, so fault! + else + HandleFault(FeatherFault::FAULT_HUNG); } /** HardFault Handler, calls HandleFault(FeatherFault::FAULT_HARDFAULT) */ @@ -194,6 +202,7 @@ void FeatherFault::StartWDT(const FeatherFault::WDTTimeout timeout) { // Start watchdog now! WDT->CTRL.bit.ENABLE = 1; while(WDT->STATUS.bit.SYNCBUSY); + should_feed_watchdog.load(false); } @@ -211,7 +220,9 @@ void FeatherFault::SetCallback(volatile void(*callback)()) { /* See FeatherFault.h */ void FeatherFault::_Mark(const int line, const char* file) { - WDTReset(); + // feed the watchdog + should_feed_watchdog.store(true); + // write the last marked data is_being_written.store(true); last_line = line; last_file = file; diff --git a/src/FeatherFault.h b/src/FeatherFault.h index 64d4e23..eae09e7 100644 --- a/src/FeatherFault.h +++ b/src/FeatherFault.h @@ -135,4 +135,4 @@ namespace FeatherFault { * This macro is a proxy for FeatherFault::_Mark, allowing it to * grab the line # and filename. */ -#define MARK { FeatherFault::_Mark(__LINE__, __SHORT_FILE__); } \ No newline at end of file +#define MARK { constexpr const char* const filename = __SHORT_FILE__; FeatherFault::_Mark(__LINE__, filename); } \ No newline at end of file