Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nRF52: watchdog timeout is not accurate #2635

Merged
merged 3 commits into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions hal/src/nRF52840/watchdog_hal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class WatchdogLock {
class Nrf52Watchdog : public Watchdog {
public:
int init(const hal_watchdog_config_t* config) {
CHECK_FALSE(initialized_, SYSTEM_ERROR_INVALID_STATE);
CHECK_FALSE(started(), SYSTEM_ERROR_INVALID_STATE);
CHECK_TRUE(config && (config->size > 0), SYSTEM_ERROR_INVALID_ARGUMENT);
CHECK_TRUE(config->timeout_ms >= WATCHDOG_MIN_TIMEOUT, SYSTEM_ERROR_INVALID_ARGUMENT);
CHECK_TRUE(config->timeout_ms <= WATCHDOG_MAX_TIMEOUT, SYSTEM_ERROR_INVALID_ARGUMENT);
Expand All @@ -80,10 +80,17 @@ class Nrf52Watchdog : public Watchdog {
} else {
nrfConfig.behaviour = NRF_WDT_BEHAVIOUR_PAUSE_SLEEP_HALT;
}
nrfx_err_t ret = nrfx_wdt_init(&nrfConfig, nrf52WatchdogEventHandler);
SPARK_ASSERT(ret == NRF_SUCCESS);
ret = nrfx_wdt_channel_alloc(&channelId_);
SPARK_ASSERT(ret == NRF_SUCCESS);
if (!initialized_) {
nrfx_err_t ret = nrfx_wdt_init(&nrfConfig, nrf52WatchdogEventHandler);
SPARK_ASSERT(ret == NRF_SUCCESS);
ret = nrfx_wdt_channel_alloc(&channelId_);
SPARK_ASSERT(ret == NRF_SUCCESS);
} else {
nrf_wdt_behaviour_set(nrfConfig.behaviour);
uint64_t ticks = (nrfConfig.reload_value * 32768ULL) / 1000;
SPARK_ASSERT(ticks <= UINT32_MAX);
nrf_wdt_reload_value_set((uint32_t)ticks);
}

memcpy(&info_.config, config, std::min(info_.config.size, config->size));
info_.state = HAL_WATCHDOG_STATE_CONFIGURED;
Expand Down
30 changes: 28 additions & 2 deletions hal/src/rtl872x/watchdog_hal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ class RtlWatchdog : public Watchdog {

WDG_InitTypeDef WDG_InitStruct = {};
uint32_t CountProcess = 0;
uint32_t DivFacProcess = 0;
WDG_Scalar(config->timeout_ms, &CountProcess, &DivFacProcess);
uint32_t DivFacProcess = 1; // Minimum requirement
calculateFactors(config->timeout_ms, &CountProcess, &DivFacProcess);
WDG_InitStruct.CountProcess = CountProcess;
WDG_InitStruct.DivFacProcess = DivFacProcess;
WDG_InitStruct.RstAllPERI = 1;
Expand Down Expand Up @@ -128,6 +128,32 @@ class RtlWatchdog : public Watchdog {

~RtlWatchdog() = default;

void calculateFactors(system_tick_t timeout, uint32_t* count, uint32_t* div) {
if (timeout == 0) {
*count = 0;
*div = 1;
return;
}
uint32_t tempDiv;
uint16_t tempCount;
bool candidate = false;
for (int8_t countId = 11; countId >= 0; countId--) {
tempCount = (0x00000001 << (countId + 1)) - 1;
tempDiv = ((timeout * 32768ULL) / tempCount / 1000);
if (tempDiv <= 1) { // minimum *div is of 1
continue;
}
if (candidate && tempDiv > 65536) {
break;
}
tempDiv = std::min(tempDiv, (uint32_t)65536);
*div = tempDiv - 1;
*count = countId;
candidate = true;
// Continue to seek smaller countId
}
}

static void rtlWatchdogEventHandler(void* context) {
WDG_IrqClear();
auto pInstance = (RtlWatchdog*)context;
Expand Down
47 changes: 47 additions & 0 deletions user/tests/wiring/watchdog/watchdog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,16 @@ test(WATCHDOG_07_notify_2) {
assertEqual(magick, 0xdeadbeef);
}

#include "nrf_wdt.h"
static void checkError(system_tick_t expectedTo) {
uint32_t reg = nrf_wdt_reload_value_get();
uint64_t actualTo = (reg * 1000ULL) / 32768;
uint32_t delta = (expectedTo > actualTo) ? (expectedTo - actualTo) : (actualTo - expectedTo);
// Serial.printlnf("expected: %ld, actual: %lu, delta: %d", expectedTo, (uint32_t)actualTo, delta);
assertLessOrEqual(delta, 1);
assertMoreOrEqual(delta, 0);
}

#endif // HAL_PLATFORM_NRF52840

#if HAL_PLATFORM_RTL872X
Expand Down Expand Up @@ -218,4 +228,41 @@ test(WATCHDOG_08_reconfigurable) {
assertEqual(0, pushMailbox(MailboxEntry().type(MailboxEntry::Type::RESET_PENDING), 5000));
}

static void checkError(system_tick_t expectedTo) {
WDG_TypeDef* WDG = ((WDG_TypeDef *)0x40002800);
uint32_t reg = WDG->VENDOR;
uint8_t countId = (reg >> 25) & 0x0000000F;
countId = std::min(countId, (uint8_t)11);
uint32_t count = (0x00000001 << (countId + 1)) - 1;
uint16_t div = reg & 0x0000FFFF;
// Serial.printlnf("id: %d, count: %d, div: %d", countId, count, div);
uint64_t actualTo = count * (div + 1) * 1000ULL / 32768;
uint32_t delta = (expectedTo > actualTo) ? (expectedTo - actualTo) : (actualTo - expectedTo);
// Serial.printlnf("expected: %ld, actual: %lu, delta: %d", expectedTo, (uint32_t)actualTo, delta);

assertLessOrEqual(delta, 200);
assertMoreOrEqual(delta, 0);
}

#endif // HAL_PLATFORM_RTL872X

test(WATCHDDOG_100_reload_value_is_calculated_correctly) {
constexpr system_tick_t TEST_LOOP_CNT = 100;

WatchdogInfo info;
assertEqual(0, Watchdog.getInfo(info));

for (uint16_t i = 1; i <= TEST_LOOP_CNT; i++) {
system_tick_t expectedTo;
if (i == 1) {
expectedTo = info.minTimeout();
} else if (i == TEST_LOOP_CNT) {
expectedTo = info.maxTimeout();
} else {
expectedTo = random(info.minTimeout(), info.maxTimeout());
}
assertEqual(0, Watchdog.init(WatchdogConfiguration().timeout(expectedTo)));

checkError(expectedTo);
}
}