sim: support upstream Watchdog driver improvement
Fixes: https://github.com/InfiniTimeOrg/InfiniSim/issues/101
This commit is contained in:
		
							parent
							
								
									22c02bace6
								
							
						
					
					
						commit
						7613571f96
					
				@ -1,39 +1,151 @@
 | 
			
		||||
#include "drivers/Watchdog.h"
 | 
			
		||||
//#include <mdk/nrf.h>
 | 
			
		||||
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<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() {
 | 
			
		||||
  // 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";
 | 
			
		||||
 | 
			
		||||
@ -1,34 +1,68 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
//#include <nrf52_bitfields.h>
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user