sim: support upstream Watchdog driver improvement

Fixes: https://github.com/InfiniTimeOrg/InfiniSim/issues/101
This commit is contained in:
Reinhold Gschweicher 2023-05-01 21:43:43 +02:00
parent 22c02bace6
commit 7613571f96
2 changed files with 181 additions and 35 deletions

View File

@ -1,39 +1,151 @@
#include "drivers/Watchdog.h" #include "drivers/Watchdog.h"
//#include <mdk/nrf.h>
using namespace Pinetime::Drivers; using namespace Pinetime::Drivers;
void Watchdog::Setup(uint8_t timeoutSeconds) { namespace {
resetReason = ActualResetReason(); /// 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<uint32_t>(sleepBehaviour) | static_cast<uint32_t>(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() { 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 { const char* Pinetime::Drivers::ResetReasonToString(Watchdog::ResetReason reason) {
return ResetReasons::ResetPin;
}
const char* Watchdog::ResetReasonToString(Watchdog::ResetReasons reason) {
switch (reason) { switch (reason) {
case ResetReasons::ResetPin: case Watchdog::ResetReason::ResetPin:
return "Reset pin"; return "Reset pin";
case ResetReasons::Watchdog: case Watchdog::ResetReason::Watchdog:
return "Watchdog"; return "Watchdog";
case ResetReasons::DebugInterface: case Watchdog::ResetReason::DebugInterface:
return "Debug interface"; return "Debug interface";
case ResetReasons::LpComp: case Watchdog::ResetReason::LpComp:
return "LPCOMP"; return "LPCOMP";
case ResetReasons::SystemOff: case Watchdog::ResetReason::SystemOff:
return "System OFF"; return "System OFF";
case ResetReasons::CpuLockup: case Watchdog::ResetReason::CpuLockup:
return "CPU Lock-up"; return "CPU Lock-up";
case ResetReasons::SoftReset: case Watchdog::ResetReason::SoftReset:
return "Soft reset"; return "Soft reset";
case ResetReasons::NFC: case Watchdog::ResetReason::NFC:
return "NFC"; return "NFC";
case ResetReasons::HardReset: case Watchdog::ResetReason::HardReset:
return "Hard reset"; return "Hard reset";
default: default:
return "Unknown"; return "Unknown";

View File

@ -1,34 +1,68 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
//#include <nrf52_bitfields.h>
namespace Pinetime { namespace Pinetime {
namespace Drivers { 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 { class Watchdog {
public: public:
enum class ResetReasons { ResetPin, Watchdog, SoftReset, CpuLockup, SystemOff, LpComp, DebugInterface, NFC, HardReset }; /// Indicates the reasons of a reset of the MCU
void Setup(uint8_t timeoutSeconds); 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 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; return resetReason;
} }
static const char* ResetReasonToString(ResetReasons reason);
private: private:
ResetReasons resetReason; ResetReason resetReason;
ResetReasons ActualResetReason() const;
}; };
class WatchdogView { /// Converts a reset reason to a human readable string
public: const char* ResetReasonToString(Watchdog::ResetReason reason);
WatchdogView(const Watchdog& watchdog) : watchdog {watchdog} {
}
Watchdog::ResetReasons ResetReason() const {
return watchdog.ResetReason();
}
private:
const Watchdog& watchdog;
};
} }
} }