I am trying to enable a watchdog and then stop it. i closely followed the datasheet way to do this. the problem is that the watchdog once enabled will always fire. i want to enable then disable the watchdog. See code:
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/hwinfo.h>
#include <zephyr/drivers/watchdog.h>
#include <zephyr/device.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
#include <hal/nrf_clock.h>
#include <zephyr/sys/reboot.h>
#include <stdint.h>
#include <stdbool.h>
#include <zephyr/drivers/timer/nrf_grtc_timer.h>
#include <haly/nrfy_grtc.h>
#include <errno.h>
#include <zephyr/sys/util.h> // BIT()
#define WDT_TIMEOUT_MS 5000
/* wdt30 base (GLOBAL): 0x50108000 */
#define WDT30_BASE 0x50108000UL
#define REG32(o) (*(volatile uint32_t *)(WDT30_BASE + (o)))
#define TASKS_START 0x000
#define TASKS_STOP 0x004
#define EVENTS_STOPPED 0x104
#define RUNSTATUS 0x400
#define CRV 0x504
#define RREN 0x508
#define CONFIG_REG 0x50C
#define TSEN 0x520
#define TSEN_KEY 0x6E524635
#define RR0 0x600
#define STOPEN_BIT 2u /* CONFIG bit that allows STOP */
/* ---- GRTC helpers: 1 tick = 1 us ---- */
#define GRTC_BITS 48
#define GRTC_MASK ((1ULL << GRTC_BITS) - 1)
static inline uint64_t grtc_now(void) { return nrfy_grtc_sys_counter_get(NRF_GRTC) & GRTC_MASK; }
static inline uint64_t grtc_delta(uint64_t now, uint64_t ref) { return (now - ref) & GRTC_MASK; }
static void print_reset_cause(uint32_t cause)
{
if (cause == 0)
{
printk("Cold boot\n");
return;
}
printk("Reset cause: 0x%08x\n", cause);
if (cause & RESET_SOFTWARE)
printk(" - software reset\n");
if (cause & RESET_WATCHDOG)
printk(" - watchdog reset\n");
if (cause & RESET_PIN)
printk(" - pin reset\n");
if (cause & RESET_CLOCK)
printk(" - System OFF wake by GRTC\n");
if (cause & RESET_LOW_POWER_WAKE)
printk(" - System OFF wake by GPIO\n");
}
static void ensure_lfclk_xtal(void)
{
const struct device *clk = DEVICE_DT_GET(DT_NODELABEL(clock));
if (!device_is_ready(clk))
{
printk("clock not ready!\n");
return;
}
int rc = clock_control_on(clk, CLOCK_CONTROL_NRF_SUBSYS_LF);
if (rc && rc != -EALREADY)
{
printk("clock_control_on rc=%d\n", rc);
}
while (clock_control_get_status(clk, CLOCK_CONTROL_NRF_SUBSYS_LF) != CLOCK_CONTROL_STATUS_ON)
{
k_sleep(K_MSEC(1));
}
bool running = nrf_clock_lf_is_running(NRF_CLOCK);
nrf_clock_lfclk_t src = nrf_clock_lf_src_get(NRF_CLOCK); // 0=RC,1=XTAL,2=SYNTH (most nRFs)
printk("LFCLK running=%d src=%d (0=RC,1=XTAL,2=SYNTH)\n", running, src);
}
static inline uint32_t ms_to_crv(uint32_t ms)
{
/* T = (CRV+1)/32768. Clamp to >=16 cycles and do ceil to avoid shorter than requested. */
uint64_t cycles = (uint64_t)ms * 32768u + 999u; /* ceil(ms*32768/1000) */
cycles /= 1000u;
if (cycles < 16u) cycles = 16u;
return (uint32_t)(cycles - 1u);
}
static inline bool wdt_is_running(void)
{
return (REG32(RUNSTATUS) & 1u) != 0;
}
static inline bool wdt_is_stoppable(void)
{
return (REG32(CONFIG_REG) & (1u << STOPEN_BIT)) != 0;
}
static void wdt_start_stoppable(uint32_t timeout_ms)
{
if (wdt_is_running()) return; /* already running */
/* Set STOPEN before START, preserve other bits */
uint32_t cfg = REG32(CONFIG_REG);
cfg |= (1u << STOPEN_BIT);
REG32(CONFIG_REG) = cfg;
REG32(CRV) = ms_to_crv(timeout_ms);
REG32(RREN) = 1u; /* enable RR0 */
REG32(TASKS_START) = 1u;
}
static bool wdt_stop(uint32_t timeout_ms)
{
if (!wdt_is_running()) return true; /* nothing to do */
// if (!wdt_is_stoppable()) return false; /* started earlier w/ STOPEN=0 → cannot stop */
REG32(TSEN) = TSEN_KEY; /* arm STOP */
REG32(EVENTS_STOPPED) = 0;
REG32(TASKS_STOP) = 1;
k_sleep(K_MSEC(timeout_ms));
REG32(TSEN) = 0; /* disarm on timeout */
return (REG32(RUNSTATUS) & 1u) == 0;
}
/* static int start_wdt_zephyr(void)
{
// Ensure the DT node is OKAY at build-time; see overlay below
const struct device *wdt = DEVICE_DT_GET(DT_NODELABEL(wdt30));
if (!device_is_ready(wdt))
{
printk("WDT '%s' not ready\n", wdt->name);
return -ENODEV;
}
struct wdt_timeout_cfg cfg = {
.window = {.min = 0, .max = WDT_TIMEOUT_MS},
.callback = NULL, // no bark ISR, go straight to reset
.flags = WDT_FLAG_RESET_SOC,
};
int ch = wdt_install_timeout(wdt, &cfg);
if (ch < 0)
{
printk("wdt_install_timeout failed: %d\n", ch);
return ch;
}
int rc = wdt_setup(wdt, 0); // do NOT pause in sleep/debug
if (rc < 0)
{
printk("wdt_setup failed: %d\n", rc);
return rc;
}
printk("WDT armed on dev='%s' for %u ms; not feeding…\n", wdt->name, WDT_TIMEOUT_MS);
return 0;
} */
int main(void)
{
ensure_lfclk_xtal();
bool ok = wdt_stop(100);
wdt_start_stoppable(WDT_TIMEOUT_MS);
uint32_t cause = 0;
hwinfo_get_reset_cause(&cause);
print_reset_cause(cause);
hwinfo_clear_reset_cause();
uint64_t startup_ticks = z_nrf_grtc_timer_startup_value_get();
uint64_t now_ticks = grtc_now();
uint64_t since_start = grtc_delta(now_ticks, startup_ticks);
printk("GRTC at startup: %llu | now: %llu | (+%llu us since boot)\n",
(unsigned long long)startup_ticks,
(unsigned long long)now_ticks,
(unsigned long long)since_start);
// if (start_wdt_zephyr() == 0)
// {
k_sleep(K_SECONDS(2)); /* don’t feed; WDT will reset us */
ok = wdt_stop(100);
k_sleep(K_MSEC(5));
sys_reboot(SYS_REBOOT_COLD);
// }
return 0;
}