From 7613571f96cd821bce38568c88c4795baf6b5b38 Mon Sep 17 00:00:00 2001 From: Reinhold Gschweicher Date: Mon, 1 May 2023 21:43:43 +0200 Subject: [PATCH] sim: support upstream Watchdog driver improvement Fixes: https://github.com/InfiniTimeOrg/InfiniSim/issues/101 --- sim/drivers/Watchdog.cpp | 146 ++++++++++++++++++++++++++++++++++----- sim/drivers/Watchdog.h | 70 ++++++++++++++----- 2 files changed, 181 insertions(+), 35 deletions(-) diff --git a/sim/drivers/Watchdog.cpp b/sim/drivers/Watchdog.cpp index 4f913bd..a550c09 100644 --- a/sim/drivers/Watchdog.cpp +++ b/sim/drivers/Watchdog.cpp @@ -1,39 +1,151 @@ #include "drivers/Watchdog.h" +//#include using namespace Pinetime::Drivers; -void Watchdog::Setup(uint8_t timeoutSeconds) { - resetReason = ActualResetReason(); +namespace { + /// The watchdog is always driven by a 32768kHz clock + constexpr uint32_t ClockFrequency = 32768; + /// Write this value in the reload register to reload the watchdog + constexpr uint32_t ReloadValue = 0x6E524635UL; + + /// Configures the behaviours (pause or run) of the watchdog while the CPU is sleeping or halted by the debugger + /// + /// @param sleepBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is sleeping + /// @param haltBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is halted by the debugger + void SetBehaviours(Watchdog::SleepBehaviour sleepBehaviour, Watchdog::HaltBehaviour haltBehaviour) { + // NRF_WDT->CONFIG : only the 1st and 4th bits are relevant. + // Bit 0 : Behavior when the CPU is sleeping + // Bit 3 : Behavior when the CPU is halted by the debugger + // O means that the CPU is paused during sleep/halt, 1 means that the watchdog is kept running +// NRF_WDT->CONFIG = static_cast(sleepBehaviour) | static_cast(haltBehaviour); + } + + /// Configure the timeout delay of the watchdog (called CRV, Counter Reload Value, in the documentation). + /// + /// @param timeoutSeconds Timeout of the watchdog, expressed in seconds + void SetTimeout(uint8_t timeoutSeconds) { + // According to the documentation: + // Clock = 32768 + // timeout [s] = ( CRV + 1 ) / Clock + // -> CRV = (timeout [s] * Clock) -1 +// NRF_WDT->CRV = (timeoutSeconds * ClockFrequency) - 1; + } + + /// Enables the first reload register + /// + /// The hardware provides 8 reload registers. To reload the watchdog, all enabled + /// register must be refreshed. + /// + /// This driver only enables the first reload register. + void EnableFirstReloadRegister() { + // RRED (Reload Register Enable) is a bitfield of 8 bits. Each bit represent + // one of the eight reload registers available. + // In this case, we enable only the first one. +// NRF_WDT->RREN |= 1; + } + + /// Returns the reset reason provided by the POWER subsystem + Watchdog::ResetReason GetResetReason() { + /* NRF_POWER->RESETREAS + * -------------------------------------------------------------------------------------------------------------------- * + * Bit | Reason (if bit is set to 1) + * ----|---------------------------------------------------------------------------------------------------------------- * + * 0 | Reset from the pin reset + * 1 | Reset from the watchdog + * 2 | Reset from soft reset + * 3 | Reset from CPU lock-up + * 16 | Reset due to wake up from System OFF mode when wakeup is triggered from DETECT signal from GPIO + * 17 | Reset due to wake up from System OFF mode when wakeup is triggered from ANADETECT signal from LPCOMP + * 18 | Reset due to wake up from System OFF mode when wakeup is triggered from entering into debug interface mode + * 19 | Reset due to wake up from System OFF mode by NFC field detect + * -------------------------------------------------------------------------------------------------------------------- */ +// const uint32_t reason = NRF_POWER->RESETREAS; +// NRF_POWER->RESETREAS = 0xffffffff; + // sim: always return ResetPin + const uint32_t reason = 0x01; + + uint32_t value = reason & 0x01; // avoid implicit conversion to bool using this temporary variable. + if (value != 0) { + return Watchdog::ResetReason::ResetPin; + } + + value = (reason >> 1u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::Watchdog; + } + + value = (reason >> 2u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::SoftReset; + } + + value = (reason >> 3u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::CpuLockup; + } + + value = (reason >> 16u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::SystemOff; + } + + value = (reason >> 17u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::LpComp; + } + + value = (reason >> 18u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::DebugInterface; + } + + value = (reason >> 19u) & 0x01u; + if (value != 0) { + return Watchdog::ResetReason::NFC; + } + + return Watchdog::ResetReason::HardReset; + } +} + +void Watchdog::Setup(uint8_t timeoutSeconds, SleepBehaviour sleepBehaviour, HaltBehaviour haltBehaviour) { + SetBehaviours(sleepBehaviour, haltBehaviour); + SetTimeout(timeoutSeconds); + EnableFirstReloadRegister(); + + resetReason = ::GetResetReason(); } void Watchdog::Start() { + // Write 1 in the START task to start the watchdog +// NRF_WDT->TASKS_START = 1; } -void Watchdog::Kick() { +void Watchdog::Reload() { + // Write the reload value 0x6E524635UL to the reload register to reload the watchdog. + // NOTE : This driver enables only the 1st reload register. +// NRF_WDT->RR[0] = ReloadValue; } -Watchdog::ResetReasons Watchdog::ActualResetReason() const { - return ResetReasons::ResetPin; -} - -const char* Watchdog::ResetReasonToString(Watchdog::ResetReasons reason) { +const char* Pinetime::Drivers::ResetReasonToString(Watchdog::ResetReason reason) { switch (reason) { - case ResetReasons::ResetPin: + case Watchdog::ResetReason::ResetPin: return "Reset pin"; - case ResetReasons::Watchdog: + case Watchdog::ResetReason::Watchdog: return "Watchdog"; - case ResetReasons::DebugInterface: + case Watchdog::ResetReason::DebugInterface: return "Debug interface"; - case ResetReasons::LpComp: + case Watchdog::ResetReason::LpComp: return "LPCOMP"; - case ResetReasons::SystemOff: + case Watchdog::ResetReason::SystemOff: return "System OFF"; - case ResetReasons::CpuLockup: + case Watchdog::ResetReason::CpuLockup: return "CPU Lock-up"; - case ResetReasons::SoftReset: + case Watchdog::ResetReason::SoftReset: return "Soft reset"; - case ResetReasons::NFC: + case Watchdog::ResetReason::NFC: return "NFC"; - case ResetReasons::HardReset: + case Watchdog::ResetReason::HardReset: return "Hard reset"; default: return "Unknown"; diff --git a/sim/drivers/Watchdog.h b/sim/drivers/Watchdog.h index 03807d6..66c278c 100644 --- a/sim/drivers/Watchdog.h +++ b/sim/drivers/Watchdog.h @@ -1,34 +1,68 @@ #pragma once #include +//#include namespace Pinetime { namespace Drivers { + /// Low level driver for the watchdog based on the nRF52832 Product Specification V1.1 + /// + /// This driver initializes the timeout and sleep and halt behaviours of the watchdog + /// in the method Watchdog::Setup(). + /// + /// The watchdog can then be started using the method Watchdog::Start(). At this point, the watchdog runs + /// and will reset the MCU if it's not reloaded before the timeout elapses. + /// + /// The watchdog can be reloaded using Watchdog::Kick(). + /// + /// The watchdog also provide the cause of the last reset (reset pin, watchdog, soft reset, hard reset,... See + /// Watchdog::ResetReasons). class Watchdog { public: - enum class ResetReasons { ResetPin, Watchdog, SoftReset, CpuLockup, SystemOff, LpComp, DebugInterface, NFC, HardReset }; - void Setup(uint8_t timeoutSeconds); + /// Indicates the reasons of a reset of the MCU + enum class ResetReason { ResetPin, Watchdog, SoftReset, CpuLockup, SystemOff, LpComp, DebugInterface, NFC, HardReset }; + + /// Behaviours of the watchdog when the CPU is sleeping + enum class SleepBehaviour : uint8_t { + /// Pause watchdog while the CPU is sleeping + Pause = 0, // << WDT_CONFIG_SLEEP_Pos, + /// Keep the watchdog running while the CPU is sleeping + Run = 1 // << WDT_CONFIG_SLEEP_Pos + }; + + /// Behaviours of the watchdog when the CPU is halted by the debugger + enum class HaltBehaviour : uint8_t { + /// Pause watchdog while the CPU is halted by the debugger + Pause = 0, // << WDT_CONFIG_HALT_Pos, + /// Keep the watchdog running while the CPU is halted by the debugger + Run = 1 // << WDT_CONFIG_HALT_Pos + }; + + /// Configures the watchdog with a specific timeout, behaviour when sleeping and when halted by the debugger + /// + /// @param sleepBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is sleeping + /// @param haltBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is halted by the debugger + void Setup(uint8_t timeoutSeconds, SleepBehaviour sleepBehaviour, HaltBehaviour haltBehaviour); + + /// Starts the watchdog. The watchdog will reset the MCU when the timeout period is elapsed unless you call + /// Watchdog::Kick before the end of the period void Start(); - void Kick(); - ResetReasons ResetReason() const { + + /// Reloads the watchdog. + /// + /// Ensure that you call this function regularly with a period shorter + /// than the timeout period to prevent the watchdog from resetting the MCU. + void Reload(); + + /// Returns the reason of the last reset + ResetReason GetResetReason() const { return resetReason; } - static const char* ResetReasonToString(ResetReasons reason); private: - ResetReasons resetReason; - ResetReasons ActualResetReason() const; + ResetReason resetReason; }; - class WatchdogView { - public: - WatchdogView(const Watchdog& watchdog) : watchdog {watchdog} { - } - Watchdog::ResetReasons ResetReason() const { - return watchdog.ResetReason(); - } - - private: - const Watchdog& watchdog; - }; + /// Converts a reset reason to a human readable string + const char* ResetReasonToString(Watchdog::ResetReason reason); } }