nRF54L15 watchdog first reset reason is 0x1 (power on reset) instead of 0x4 (watchdog reset detected).

Hi guys, I’m currently testing the Zephyr watchdog sample on the NRF54L15DK, and I’ve noticed a behavior that seems incorrect regarding the reported reset reason. My SDK is v3.1.1 and Toolchain version is 3.1.1 as well. On the first boot after flashing or power-on, RESETREAS reports Power-On Reset instead of indicating a watchdog reset, even though the watchdog timer should have triggered.

However, on the second reboot, the reset reason correctly shows WDT1 triggered.

This makes it appear as if the watchdog did not cause a reset on the first cycle, even though the watchdog should have expired. The behavior is consistent across multiple tests.

This is the code that I tested:

/*
 * Copyright (c) 2015 Intel Corporation
 * Copyright (c) 2018 Nordic Semiconductor
 * Copyright (c) 2019 Centaur Analytics, Inc
 * Copyright 2023 NXP
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/watchdog.h>
#include <zephyr/sys/printk.h>
#include <stdbool.h>


/* Watchdog timer */
#define WDT_MAX_WINDOW_MSEC  1000 // in milliseconds
#define WDT_MIN_WINDOW_MSEC  0   // in milliseconds
#define WDG_FEED_INTERVAL 250 // in milliseconds
#define WDT_OPT WDT_OPT_PAUSE_HALTED_BY_DBG
static const struct device *wdt;
static int wdt_channel_id;
static bool wdt_running = true;  // true = feeding active
static bool wdt_command = true; // true = correct command entered
#define LED1_NODE DT_ALIAS(led1)
static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(LED1_NODE, gpios);
uint32_t reason;

/* Check the reset reason */
 void print_reset_reason(void)
{
	// reason = reason & RESET_RESETREAS_ResetValue;
	reason = NRF_RESET->RESETREAS;
	printk("RESETREAS = 0x%08" PRIX32 "\n", reason);

	if (reason & RESET_RESETREAS_RESETPIN_Msk) {
		printk("RESETREAS = 0x%08" PRIX32 "\n", reason);
    	printk("System was reset by RESET pin!\n");
		uint32_t result = (reason & RESET_RESETREAS_RESETPIN_Msk) >> RESET_RESETREAS_RESETPIN_Pos;
		printk("reset pin detected = 0x%08" PRIX32 "\n", result);
		NRF_RESET->RESETREAS = RESET_RESETREAS_RESETPIN_Msk;
	}
	if (reason & RESET_RESETREAS_DOG0_Msk){
		printk("RESETREAS = 0x%08" PRIX32 "\n", reason);
        printk("System was reset by watchdog WDT0!\n");
		uint32_t detected = (reason & RESET_RESETREAS_DOG0_Msk) >> RESET_RESETREAS_DOG0_Pos;
		printk("wdt0 detected = 0x%08" PRIX32 "\n" ,detected);
		NRF_RESET->RESETREAS = RESET_RESETREAS_DOG0_Msk;
	}
	if (reason & RESET_RESETREAS_DOG1_Msk) {
		printk("RESETREAS = 0x%08" PRIX32 "\n", reason);
        printk("System was reset by watchdog WDT1!\n");
		uint32_t detected = (reason & RESET_RESETREAS_DOG1_Msk) >> RESET_RESETREAS_DOG1_Pos;
		printk("wdt1 detected = 0x%08" PRIX32 "\n" ,detected);
		NRF_RESET->RESETREAS = RESET_RESETREAS_DOG1_Msk;
	}
    if (reason == 0) {
		printk("RESETREAS = 0x%08" PRIX32 "\n", reason);
        printk("Reset reason unknown or power-on reset\n");
    }
	else {
		printk("reason = 0x%08" PRIX32 "\n", reason);
        printk("Reset caused by something else\n");
    }

    // Clear RESETREAS after reading 
	// reason = reason & RESET_RESETREAS_ResetValue;
	// NRF_RESET->RESETREAS &= ~(reason);
	reason = ~(reason) & NRF_RESET->RESETREAS;
	
	printk("\n");
	printk("RESETREAS should be 0x0 after clear:\n");
	printk("RESETREAS = 0x%08" PRIX32 "\n", reason);
}

/* --- Timer callback --- */
static void blink_led1_timer_handler(struct k_timer *timer)
{
    static bool led_state = false;
    gpio_pin_set_dt(&led1, led_state);
    led_state = !led_state;  // Toggle
}

/* --- Optional: Stop handler --- */
static void blink_timer_stop(struct k_timer *timer){
    gpio_pin_set_dt(&led1, 0);
    LOG_INF("Blink timer stopped");
}

static void blink_led1(){
	int ret;
    if (!gpio_is_ready_dt(&led1)) { 
        LOG_ERR("LED device not ready");
    }

    ret = gpio_pin_configure_dt(&led1, GPIO_OUTPUT_INACTIVE);
    if (ret < 0) {
        LOG_ERR("Failed to configure LED pin");
    }

    /* Create timer: handler called periodically */
    k_timer_init(&blink_timer, blink_led1_timer_handler, blink_timer_stop);
	
    /* Start the timer: first expiry 0ms, repeat every 100ms */
	for(int i =0; i<6; i++){
    	k_timer_start(&blink_timer, K_MSEC(0), K_MSEC(100));
	}
}

#if WDT_ALLOW_CALLBACK
static void wdt_callback(const struct device *wdt_dev, int channel_id)
{
	static bool handled_event;

	if (handled_event) {
		return;
	}

	wdt_feed(wdt_dev, channel_id);

	printk("Handled things..ready to reset\n");
	handled_event = true;
}
#endif /* WDT_ALLOW_CALLBACK */

static int watchdog_timer(void){
	wdt = DEVICE_DT_GET(DT_NODELABEL(wdt31));
    if (!device_is_ready(wdt)) {
        LOG_ERR("Watchdog device not ready");
        return 0;
    }

    struct wdt_timeout_cfg cfg = {
        .window.min = WDT_MIN_WINDOW_MSEC,
		.window.max = WDT_MAX_WINDOW_MSEC,
        .callback = wdt_callback,
        .flags = WDT_FLAG_RESET_SOC,
    };

    wdt_channel_id = wdt_install_timeout(wdt, &cfg);
    if (wdt_channel_id < 0) {
        LOG_ERR("WDT install failed (%d)", wdt_channel_id);
        return 0;
    }
	// if (wdt_setup(wdt, 0) < 0)
    if (wdt_setup(wdt, WDT_OPT) < 0) {
        LOG_ERR("WDT setup failed");
        return 0;
    }

    LOG_INF("Watchdog started (timeout = %d ms)", WDT_MAX_WINDOW_MSEC);
	
	printk("nRF54L15 reset reason: \n");
    print_reset_reason();
	blink_led1();
	wdt_running = true; // set back to true to start feeding
	return 0;
} 


int main(void)
{
	printk("Watchdog sample application\n");

#if WDT_ALLOW_CALLBACK
	/* Set up watchdog callback. */
	wdt_config.callback = wdt_callback;

	printk("Attempting to test pre-reset callback\n");
#else /* WDT_ALLOW_CALLBACK */
	printk("Callback in RESET_SOC disabled for this platform\n");
#endif /* WDT_ALLOW_CALLBACK */

	watchdog_timer();



	/* Waiting for the SoC reset. */
	printk("Waiting for reset...\n");

	return 0;
}

Related