Skip to content

Commit 5f19f68

Browse files
authored
Refactor, document and fix the Watchdog driver (#1710)
* Refactor and document the Watchdog driver to make it more readable. Fix the configuration of the behaviours configuration that was not properly implemented (but it didn't cause any side effect since the correct value was eventually set in NRF_WDT->CONFIG). Fix the wrong interpretation of the reset reasons caused by implicit conversions of int to bool.
1 parent c22e30a commit 5f19f68

4 files changed

Lines changed: 186 additions & 68 deletions

File tree

src/displayapp/screens/SystemInfo.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,24 +101,24 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen1() {
101101
std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
102102
auto batteryPercent = batteryController.PercentRemaining();
103103
const auto* resetReason = [this]() {
104-
switch (watchdog.ResetReason()) {
105-
case Drivers::Watchdog::ResetReasons::Watchdog:
104+
switch (watchdog.GetResetReason()) {
105+
case Drivers::Watchdog::ResetReason::Watchdog:
106106
return "wtdg";
107-
case Drivers::Watchdog::ResetReasons::HardReset:
107+
case Drivers::Watchdog::ResetReason::HardReset:
108108
return "hardr";
109-
case Drivers::Watchdog::ResetReasons::NFC:
109+
case Drivers::Watchdog::ResetReason::NFC:
110110
return "nfc";
111-
case Drivers::Watchdog::ResetReasons::SoftReset:
111+
case Drivers::Watchdog::ResetReason::SoftReset:
112112
return "softr";
113-
case Drivers::Watchdog::ResetReasons::CpuLockup:
113+
case Drivers::Watchdog::ResetReason::CpuLockup:
114114
return "cpulock";
115-
case Drivers::Watchdog::ResetReasons::SystemOff:
115+
case Drivers::Watchdog::ResetReason::SystemOff:
116116
return "off";
117-
case Drivers::Watchdog::ResetReasons::LpComp:
117+
case Drivers::Watchdog::ResetReason::LpComp:
118118
return "lpcomp";
119-
case Drivers::Watchdog::ResetReasons::DebugInterface:
119+
case Drivers::Watchdog::ResetReason::DebugInterface:
120120
return "dbg";
121-
case Drivers::Watchdog::ResetReasons::ResetPin:
121+
case Drivers::Watchdog::ResetReason::ResetPin:
122122
return "rst";
123123
default:
124124
return "?";

src/drivers/Watchdog.cpp

Lines changed: 121 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,74 +2,148 @@
22
#include <mdk/nrf.h>
33
using namespace Pinetime::Drivers;
44

5-
void Watchdog::Setup(uint8_t timeoutSeconds) {
6-
NRF_WDT->CONFIG &= ~(WDT_CONFIG_SLEEP_Msk << WDT_CONFIG_SLEEP_Pos);
7-
NRF_WDT->CONFIG |= (WDT_CONFIG_HALT_Run << WDT_CONFIG_SLEEP_Pos);
5+
namespace {
6+
/// The watchdog is always driven by a 32768kHz clock
7+
constexpr uint32_t ClockFrequency = 32768;
8+
/// Write this value in the reload register to reload the watchdog
9+
constexpr uint32_t ReloadValue = 0x6E524635UL;
810

9-
NRF_WDT->CONFIG &= ~(WDT_CONFIG_HALT_Msk << WDT_CONFIG_HALT_Pos);
10-
NRF_WDT->CONFIG |= (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos);
11+
/// Configures the behaviours (pause or run) of the watchdog while the CPU is sleeping or halted by the debugger
12+
///
13+
/// @param sleepBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is sleeping
14+
/// @param haltBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is halted by the debugger
15+
void SetBehaviours(Watchdog::SleepBehaviour sleepBehaviour, Watchdog::HaltBehaviour haltBehaviour) {
16+
// NRF_WDT->CONFIG : only the 1st and 4th bits are relevant.
17+
// Bit 0 : Behavior when the CPU is sleeping
18+
// Bit 3 : Behavior when the CPU is halted by the debugger
19+
// O means that the CPU is paused during sleep/halt, 1 means that the watchdog is kept running
20+
NRF_WDT->CONFIG = static_cast<uint32_t>(sleepBehaviour) | static_cast<uint32_t>(haltBehaviour);
21+
}
22+
23+
/// Configure the timeout delay of the watchdog (called CRV, Counter Reload Value, in the documentation).
24+
///
25+
/// @param timeoutSeconds Timeout of the watchdog, expressed in seconds
26+
void SetTimeout(uint8_t timeoutSeconds) {
27+
// According to the documentation:
28+
// Clock = 32768
29+
// timeout [s] = ( CRV + 1 ) / Clock
30+
// -> CRV = (timeout [s] * Clock) -1
31+
NRF_WDT->CRV = (timeoutSeconds * ClockFrequency) - 1;
32+
}
33+
34+
/// Enables the first reload register
35+
///
36+
/// The hardware provides 8 reload registers. To reload the watchdog, all enabled
37+
/// register must be refreshed.
38+
///
39+
/// This driver only enables the first reload register.
40+
void EnableFirstReloadRegister() {
41+
// RRED (Reload Register Enable) is a bitfield of 8 bits. Each bit represent
42+
// one of the eight reload registers available.
43+
// In this case, we enable only the first one.
44+
NRF_WDT->RREN |= 1;
45+
}
46+
47+
/// Returns the reset reason provided by the POWER subsystem
48+
Watchdog::ResetReason GetResetReason() {
49+
/* NRF_POWER->RESETREAS
50+
* -------------------------------------------------------------------------------------------------------------------- *
51+
* Bit | Reason (if bit is set to 1)
52+
* ----|---------------------------------------------------------------------------------------------------------------- *
53+
* 0 | Reset from the pin reset
54+
* 1 | Reset from the watchdog
55+
* 2 | Reset from soft reset
56+
* 3 | Reset from CPU lock-up
57+
* 16 | Reset due to wake up from System OFF mode when wakeup is triggered from DETECT signal from GPIO
58+
* 17 | Reset due to wake up from System OFF mode when wakeup is triggered from ANADETECT signal from LPCOMP
59+
* 18 | Reset due to wake up from System OFF mode when wakeup is triggered from entering into debug interface mode
60+
* 19 | Reset due to wake up from System OFF mode by NFC field detect
61+
* -------------------------------------------------------------------------------------------------------------------- */
62+
const uint32_t reason = NRF_POWER->RESETREAS;
63+
NRF_POWER->RESETREAS = 0xffffffff;
64+
65+
uint32_t value = reason & 0x01; // avoid implicit conversion to bool using this temporary variable.
66+
if (value != 0) {
67+
return Watchdog::ResetReason::ResetPin;
68+
}
69+
70+
value = (reason >> 1u) & 0x01u;
71+
if (value != 0) {
72+
return Watchdog::ResetReason::Watchdog;
73+
}
74+
75+
value = (reason >> 2u) & 0x01u;
76+
if (value != 0) {
77+
return Watchdog::ResetReason::SoftReset;
78+
}
1179

12-
/* timeout (s) = (CRV + 1) / 32768 */
13-
// JF : 7500 = 7.5s
14-
uint32_t crv = (((timeoutSeconds * 1000u) << 15u) / 1000) - 1;
15-
NRF_WDT->CRV = crv;
80+
value = (reason >> 3u) & 0x01u;
81+
if (value != 0) {
82+
return Watchdog::ResetReason::CpuLockup;
83+
}
1684

17-
/* Enable reload requests */
18-
NRF_WDT->RREN = (WDT_RREN_RR0_Enabled << WDT_RREN_RR0_Pos);
85+
value = (reason >> 16u) & 0x01u;
86+
if (value != 0) {
87+
return Watchdog::ResetReason::SystemOff;
88+
}
1989

20-
resetReason = ActualResetReason();
90+
value = (reason >> 17u) & 0x01u;
91+
if (value != 0) {
92+
return Watchdog::ResetReason::LpComp;
93+
}
94+
95+
value = (reason >> 18u) & 0x01u;
96+
if (value != 0) {
97+
return Watchdog::ResetReason::DebugInterface;
98+
}
99+
100+
value = (reason >> 19u) & 0x01u;
101+
if (value != 0) {
102+
return Watchdog::ResetReason::NFC;
103+
}
104+
105+
return Watchdog::ResetReason::HardReset;
106+
}
21107
}
22108

23-
void Watchdog::Start() {
24-
NRF_WDT->TASKS_START = 1;
109+
void Watchdog::Setup(uint8_t timeoutSeconds, SleepBehaviour sleepBehaviour, HaltBehaviour haltBehaviour) {
110+
SetBehaviours(sleepBehaviour, haltBehaviour);
111+
SetTimeout(timeoutSeconds);
112+
EnableFirstReloadRegister();
113+
114+
resetReason = ::GetResetReason();
25115
}
26116

27-
void Watchdog::Kick() {
28-
NRF_WDT->RR[0] = WDT_RR_RR_Reload;
117+
void Watchdog::Start() {
118+
// Write 1 in the START task to start the watchdog
119+
NRF_WDT->TASKS_START = 1;
29120
}
30121

31-
Watchdog::ResetReasons Watchdog::ActualResetReason() const {
32-
uint32_t reason = NRF_POWER->RESETREAS;
33-
NRF_POWER->RESETREAS = 0xffffffff;
34-
35-
if (reason & 0x01u)
36-
return ResetReasons::ResetPin;
37-
if ((reason >> 1u) & 0x01u)
38-
return ResetReasons::Watchdog;
39-
if ((reason >> 2u) & 0x01u)
40-
return ResetReasons::SoftReset;
41-
if ((reason >> 3u) & 0x01u)
42-
return ResetReasons::CpuLockup;
43-
if ((reason >> 16u) & 0x01u)
44-
return ResetReasons::SystemOff;
45-
if ((reason >> 17u) & 0x01u)
46-
return ResetReasons::LpComp;
47-
if ((reason) &0x01u)
48-
return ResetReasons::DebugInterface;
49-
if ((reason >> 19u) & 0x01u)
50-
return ResetReasons::NFC;
51-
return ResetReasons::HardReset;
122+
void Watchdog::Reload() {
123+
// Write the reload value 0x6E524635UL to the reload register to reload the watchdog.
124+
// NOTE : This driver enables only the 1st reload register.
125+
NRF_WDT->RR[0] = ReloadValue;
52126
}
53127

54-
const char* Watchdog::ResetReasonToString(Watchdog::ResetReasons reason) {
128+
const char* Pinetime::Drivers::ResetReasonToString(Watchdog::ResetReason reason) {
55129
switch (reason) {
56-
case ResetReasons::ResetPin:
130+
case Watchdog::ResetReason::ResetPin:
57131
return "Reset pin";
58-
case ResetReasons::Watchdog:
132+
case Watchdog::ResetReason::Watchdog:
59133
return "Watchdog";
60-
case ResetReasons::DebugInterface:
134+
case Watchdog::ResetReason::DebugInterface:
61135
return "Debug interface";
62-
case ResetReasons::LpComp:
136+
case Watchdog::ResetReason::LpComp:
63137
return "LPCOMP";
64-
case ResetReasons::SystemOff:
138+
case Watchdog::ResetReason::SystemOff:
65139
return "System OFF";
66-
case ResetReasons::CpuLockup:
140+
case Watchdog::ResetReason::CpuLockup:
67141
return "CPU Lock-up";
68-
case ResetReasons::SoftReset:
142+
case Watchdog::ResetReason::SoftReset:
69143
return "Soft reset";
70-
case ResetReasons::NFC:
144+
case Watchdog::ResetReason::NFC:
71145
return "NFC";
72-
case ResetReasons::HardReset:
146+
case Watchdog::ResetReason::HardReset:
73147
return "Hard reset";
74148
default:
75149
return "Unknown";

src/drivers/Watchdog.h

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,68 @@
11
#pragma once
22
#include <cstdint>
3+
#include <nrf52_bitfields.h>
34

45
namespace Pinetime {
56
namespace Drivers {
7+
/// Low level driver for the watchdog based on the nRF52832 Product Specification V1.1
8+
///
9+
/// This driver initializes the timeout and sleep and halt behaviours of the watchdog
10+
/// in the method Watchdog::Setup().
11+
///
12+
/// The watchdog can then be started using the method Watchdog::Start(). At this point, the watchdog runs
13+
/// and will reset the MCU if it's not reloaded before the timeout elapses.
14+
///
15+
/// The watchdog can be reloaded using Watchdog::Kick().
16+
///
17+
/// The watchdog also provide the cause of the last reset (reset pin, watchdog, soft reset, hard reset,... See
18+
/// Watchdog::ResetReasons).
619
class Watchdog {
720
public:
8-
enum class ResetReasons { ResetPin, Watchdog, SoftReset, CpuLockup, SystemOff, LpComp, DebugInterface, NFC, HardReset };
9-
void Setup(uint8_t timeoutSeconds);
21+
/// Indicates the reasons of a reset of the MCU
22+
enum class ResetReason { ResetPin, Watchdog, SoftReset, CpuLockup, SystemOff, LpComp, DebugInterface, NFC, HardReset };
23+
24+
/// Behaviours of the watchdog when the CPU is sleeping
25+
enum class SleepBehaviour : uint8_t {
26+
/// Pause watchdog while the CPU is sleeping
27+
Pause = 0 << WDT_CONFIG_SLEEP_Pos,
28+
/// Keep the watchdog running while the CPU is sleeping
29+
Run = 1 << WDT_CONFIG_SLEEP_Pos
30+
};
31+
32+
/// Behaviours of the watchdog when the CPU is halted by the debugger
33+
enum class HaltBehaviour : uint8_t {
34+
/// Pause watchdog while the CPU is halted by the debugger
35+
Pause = 0 << WDT_CONFIG_HALT_Pos,
36+
/// Keep the watchdog running while the CPU is halted by the debugger
37+
Run = 1 << WDT_CONFIG_HALT_Pos
38+
};
39+
40+
/// Configures the watchdog with a specific timeout, behaviour when sleeping and when halted by the debugger
41+
///
42+
/// @param sleepBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is sleeping
43+
/// @param haltBehaviour Configure the watchdog to either be paused, or kept running, while the CPU is halted by the debugger
44+
void Setup(uint8_t timeoutSeconds, SleepBehaviour sleepBehaviour, HaltBehaviour haltBehaviour);
45+
46+
/// Starts the watchdog. The watchdog will reset the MCU when the timeout period is elapsed unless you call
47+
/// Watchdog::Kick before the end of the period
1048
void Start();
11-
void Kick();
1249

13-
ResetReasons ResetReason() const {
50+
/// Reloads the watchdog.
51+
///
52+
/// Ensure that you call this function regularly with a period shorter
53+
/// than the timeout period to prevent the watchdog from resetting the MCU.
54+
void Reload();
55+
56+
/// Returns the reason of the last reset
57+
ResetReason GetResetReason() const {
1458
return resetReason;
1559
}
1660

17-
static const char* ResetReasonToString(ResetReasons reason);
18-
1961
private:
20-
ResetReasons resetReason;
21-
ResetReasons ActualResetReason() const;
62+
ResetReason resetReason;
2263
};
64+
65+
/// Converts a reset reason to a human readable string
66+
const char* ResetReasonToString(Watchdog::ResetReason reason);
2367
}
2468
}

src/systemtask/SystemTask.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ void SystemTask::Process(void* instance) {
9999
void SystemTask::Work() {
100100
BootErrors bootError = BootErrors::None;
101101

102-
watchdog.Setup(7);
102+
watchdog.Setup(7, Drivers::Watchdog::SleepBehaviour::Run, Drivers::Watchdog::HaltBehaviour::Pause);
103103
watchdog.Start();
104-
NRF_LOG_INFO("Last reset reason : %s", Pinetime::Drivers::Watchdog::ResetReasonToString(watchdog.ResetReason()));
104+
NRF_LOG_INFO("Last reset reason : %s", Pinetime::Drivers::ResetReasonToString(watchdog.GetResetReason()));
105105
APP_GPIOTE_INIT(2);
106106

107107
spi.Init();
@@ -403,7 +403,7 @@ void SystemTask::Work() {
403403
dateTimeController.UpdateTime(systick_counter);
404404
NoInit_BackUpTime = dateTimeController.CurrentDateTime();
405405
if (nrf_gpio_pin_read(PinMap::Button) == 0) {
406-
watchdog.Kick();
406+
watchdog.Reload();
407407
}
408408
}
409409
#pragma clang diagnostic pop

0 commit comments

Comments
 (0)