all
Chapter 19 of 20

Watchdog Timer — Tiva C

Eslam El Hefny Apr 19, 2025 8 min read
95% done

Watchdog Timer — Tiva C

Overview

The TM4C123GH6PM includes two independent watchdog timers: WDT0 (on the APB bus) and WDT1 (on the AHB bus, always-on in deep sleep). A watchdog timer generates a reset if software fails to periodically “pet” (reset) it within a defined timeout. This prevents the system from hanging indefinitely due to software bugs, infinite loops, or stack corruption.


Beginner Level — What & Why

What is a Watchdog Timer?

A watchdog is a hardware timer that counts down from a loaded value. If your software does not reset the timer before it reaches zero, the watchdog assumes the system has hung and issues a hardware reset. Your software must “pet the dog” (write to the reload register) regularly to prove it is still running.

Real-World Analogy

A watchdog is like a “dead man’s switch” on a train. As long as the operator pushes the button regularly, the train runs. If the operator becomes incapacitated (software hang) and stops pressing the button, the train automatically brakes (microcontroller resets). The system is safe even without any human (software) intervention.

What Problem Does It Solve?

  • Automatic recovery from software deadlocks, infinite loops, and stack corruption
  • Ensures the system returns to a known-good state without manual intervention
  • Required by safety standards (IEC 61508, ISO 26262) for reliable embedded systems

Key Terms

Term Meaning
WDT0 Watchdog Timer 0 on APB — standard peripheral
WDT1 Watchdog Timer 1 on AHB — always clocked in deep sleep
WDTLOAD Reload value register — sets timeout period
WDTVALUE Current countdown value (read-only)
WDTCTL Control register — enable interrupt and reset
WDTLOCK Write 0x1ACCE551 to unlock, any other value to lock
Pet/Feed Writing WDTLOAD to reset the countdown

Intermediate Level — How It Works

WDT0 vs WDT1

Feature WDT0 WDT1
Bus APB AHB
Clock System clock System clock
Deep sleep Stops Continues
Reset cause WDTSTAT bit 1 (WDT0) WDTSTAT bit 2 (WDT1)
Base address 0x40000000 0x40001000
Recommended use Normal operation Extended sleep protection

Key Registers

Register Offset Description
WDTLOAD 0x000 Load value (countdown starts here)
WDTVALUE 0x004 Current counter value (read-only)
WDTCTL 0x008 Control: INTEN (bit0), RESEN (bit1), INTTYPE (bit2)
WDTRIS 0x00C Raw interrupt status
WDTMIS 0x010 Masked interrupt status
WDTICR 0x00C Interrupt clear (writing clears timeout interrupt)
WDTTEST 0x418 Test register (STALL bit for CCS debug halt)
WDTLOCK 0xC00 Lock register

WDTCTL bit fields:

Bit Name Function
0 INTEN Enable watchdog interrupt (first timeout fires ISR)
1 RESEN Enable reset on second timeout
2 INTTYPE 0 = maskable IRQ, 1 = NMI (non-maskable)

Two-Stage Timeout Behavior

With INTEN=1 and RESEN=1:

  1. First timeout: Watchdog fires an interrupt (ISR can still pet the dog)
  2. Second timeout (if not cleared): Generates a system reset

Setting INTTYPE=1 makes the first timeout a Non-Maskable Interrupt (NMI) — it cannot be disabled even with IntMasterDisable().

TivaWare DriverLib API

#include "driverlib/watchdog.h"

/* Enable WDT0 clock */
SysCtlPeripheralEnable(SYSCTL_PERIPH_WDOG0);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_WDOG0));

/* Set timeout: 1 second at 80 MHz = 80,000,000 cycles */
WatchdogReloadSet(WATCHDOG0_BASE, 80000000);

/* Enable watchdog interrupt (first timeout = IRQ) */
WatchdogIntEnable(WATCHDOG0_BASE);

/* Enable reset on second timeout */
WatchdogResetEnable(WATCHDOG0_BASE);

/* Unlock registers before modifying (if previously locked) */
/* WatchdogUnlock(WATCHDOG0_BASE); */

/* Enable the watchdog — once enabled, RESEN cannot be cleared */
WatchdogEnable(WATCHDOG0_BASE);

/* Lock the watchdog to prevent accidental disable */
WatchdogLock(WATCHDOG0_BASE);

/* Enable watchdog interrupt in NVIC */
IntEnable(INT_WATCHDOG);
IntMasterEnable();

Feeding (Petting) the Watchdog

/* Reset the countdown — call this regularly to prevent timeout */
void WatchdogFeed(void)
{
    WatchdogReloadSet(WATCHDOG0_BASE, 80000000);  // reload = reset countdown
}

Advanced Level — Deep Dive

Bare-Metal Watchdog Configuration

#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_watchdog.h"

void WDT0_Init(uint32_t ui32Timeout)
{
    /* Enable WDT0 clock */
    HWREG(SYSCTL_RCGCWD) |= (1U << 0);
    while (!(HWREG(SYSCTL_PRWD) & (1U << 0)));

    /* Unlock the watchdog (write magic key 0x1ACCE551) */
    HWREG(WATCHDOG0_BASE + WDT_O_LOCK) = WDT_LOCK_UNLOCK;  // 0x1ACCE551

    /* Set reload value */
    HWREG(WATCHDOG0_BASE + WDT_O_LOAD) = ui32Timeout;

    /* Enable interrupt (INTEN=1) and reset (RESEN=1) */
    HWREG(WATCHDOG0_BASE + WDT_O_CTL) = WDT_CTL_INTEN | WDT_CTL_RESEN;

    /* Enable stall-in-debug (stops WDT when CCS halts CPU) */
    HWREG(WATCHDOG0_BASE + WDT_O_TEST) |= WDT_TEST_STALL;

    /* Lock registers to prevent accidental modification */
    HWREG(WATCHDOG0_BASE + WDT_O_LOCK) = WDT_LOCK_LOCKED;  // any value ≠ key

    /* Enable NVIC interrupt for WDT0 (IRQ 18) */
    HWREG(NVIC_EN0) = (1U << (INT_WATCHDOG - 16));
}

/* Feed the watchdog — must be called before timeout */
void WDT0_Feed(void)
{
    /* Unlock to allow writing LOAD */
    HWREG(WATCHDOG0_BASE + WDT_O_LOCK) = WDT_LOCK_UNLOCK;
    HWREG(WATCHDOG0_BASE + WDT_O_LOAD) = 80000000;  // reload 1 second
    HWREG(WATCHDOG0_BASE + WDT_O_LOCK) = WDT_LOCK_LOCKED;
}

Watchdog ISR (First Timeout Handler)

void Watchdog_Handler(void)
{
    /* Clear the watchdog interrupt (WDTICR) */
    WatchdogIntClear(WATCHDOG0_BASE);

    /* Log the event, attempt recovery */
    /* WARNING: if we return without petting the dog,
     * the second timeout (RESEN) will generate a hardware reset */

    /* Attempt emergency pet if we can safely do so */
    /* WatchdogFeed(); */

    /* Or intentionally let the second timeout reset the system */
}

Detecting Watchdog Reset at Startup

After a watchdog reset, firmware can determine the cause by reading the Reset Cause Register (RESC):

void CheckResetCause(void)
{
    uint32_t ui32ResetCause = SysCtlResetCauseGet();
    SysCtlResetCauseClear(ui32ResetCause);  // clear for next boot

    if (ui32ResetCause & SYSCTL_CAUSE_WDOG0)
    {
        /* System was reset by WDT0 — log it, notify user */
        /* Set a flag in a non-volatile RAM region preserved across resets */
    }
    else if (ui32ResetCause & SYSCTL_CAUSE_POR)
    {
        /* Power-on reset — normal startup */
    }
    else if (ui32ResetCause & SYSCTL_CAUSE_EXT)
    {
        /* External reset pin was asserted */
    }
}

Reset Cause Register (RESC) Bits

Bit Symbol Cause
0 EXT External reset pin
1 POR Power-on reset
2 BOR Brown-out reset
3 WDT0 Watchdog Timer 0 reset
4 SW Software reset (SysCtlReset)
5 WDT1 Watchdog Timer 1 reset
20 MOSCFAIL Main oscillator failure reset

Choosing the Right Timeout

System type Recommended timeout
Simple polling loop (10 ms period) 50–100 ms
RTOS with 1 ms tick 50–200 ms
ADC + control loop (100 Hz) 100–500 ms
Slow communication with host 1–5 seconds

The timeout must be shorter than the maximum acceptable hang time but longer than the maximum time between pet operations including all possible blocking calls.

Gotchas

  • Once WDTCTL is enabled (INTEN=1), it cannot be disabled by software. Only a reset clears it. This is intentional — a misbehaving program cannot disable its own watchdog.
  • Locking the watchdog with WatchdogLock() prevents any register writes (including LOAD). You must unlock before petting if you locked.
  • CCS debug halt problem: When you halt the CPU in CCS, the watchdog continues counting and will reset the MCU. Set the STALL bit in WDTTEST to pause the watchdog during debug halts.
  • WDT1 and deep sleep: Use WDT1 if the system enters deep sleep, as WDT0 stops when the APB clock is gated.
  • Do not pet the dog in an ISR (unless absolutely necessary) — if the main loop hangs and only ISRs run, the dog being fed by an ISR gives a false “healthy” indication.

Step-by-Step Example

/*
 * watchdog_demo.c
 * WDT0 configured for 1-second timeout.
 * Main loop pets the dog every 500 ms.
 * SW1 (PF4) held down simulates a hang — WDT resets the system.
 * After reset, the firmware detects the WDT cause and blinks blue LED.
 * Board  : TM4C123GXL EK LaunchPad
 * SDK    : TivaWare_C_Series-2.2.x
 */

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/watchdog.h"
#include "driverlib/interrupt.h"

static bool g_bWdtReset = false;

void Watchdog_Handler(void)
{
    WatchdogIntClear(WATCHDOG0_BASE);
    /* Do not pet — allow second timeout to reset system */
}

int main(void)
{
    /* Step 1: 80 MHz clock */
    SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
                   SYSCTL_OSC_MAIN   | SYSCTL_XTAL_16MHZ);

    /* Step 2: Check if last reset was caused by WDT */
    uint32_t ui32Cause = SysCtlResetCauseGet();
    SysCtlResetCauseClear(ui32Cause);
    if (ui32Cause & SYSCTL_CAUSE_WDOG0)
        g_bWdtReset = true;

    /* Step 3: Configure LEDs and SW1 */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,
                          GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
    GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_4);
    GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4,
                     GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);

    /* Step 4: Indicate WDT reset with blue LED on boot */
    if (g_bWdtReset)
    {
        /* Blink blue LED 5 times to signal WDT reset */
        int i;
        for (i = 0; i < 5; i++)
        {
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);
            SysCtlDelay(SysCtlClockGet() / 6);  // 500 ms
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);
            SysCtlDelay(SysCtlClockGet() / 6);
        }
    }

    /* Step 5: Configure WDT0 for 1-second timeout */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_WDOG0);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_WDOG0));

    WatchdogReloadSet(WATCHDOG0_BASE, SysCtlClockGet());  // 1 second
    WatchdogIntEnable(WATCHDOG0_BASE);
    WatchdogResetEnable(WATCHDOG0_BASE);

    /* Pause watchdog during CCS debug halt */
    WatchdogStallEnable(WATCHDOG0_BASE);

    WatchdogEnable(WATCHDOG0_BASE);
    WatchdogLock(WATCHDOG0_BASE);

    IntEnable(INT_WATCHDOG);
    IntMasterEnable();

    /* Step 6: Normal operation — pet the dog every 500 ms
     * Hold SW1 to simulate a hang (no pet → WDT resets) */
    while (1)
    {
        /* Normal heartbeat: green LED on */
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, GPIO_PIN_3);
        SysCtlDelay(SysCtlClockGet() / 6);  // 500 ms

        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0);
        SysCtlDelay(SysCtlClockGet() / 6);  // 500 ms

        /* If SW1 is held: do not pet → WDT fires and resets */
        if (!GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4))
        {
            /* Simulate hang: Red LED on, no pet */
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
            while (!GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4));
            /* When WDT fires, Watchdog_Handler clears ISR but does NOT pet
               → second timeout → hardware reset */
        }

        /* Pet the watchdog (unlock → reload → lock) */
        WatchdogUnlock(WATCHDOG0_BASE);
        WatchdogReloadSet(WATCHDOG0_BASE, SysCtlClockGet());
        WatchdogLock(WATCHDOG0_BASE);
    }
}

Summary

Key Point Details
Modules WDT0 (APB) and WDT1 (AHB, deep-sleep capable)
Timeout WDTLOAD / system_clock = seconds
Two-stage First timeout = IRQ, second timeout = reset
Lock key 0x1ACCE551 to unlock, any other to lock
Debug stall WDTTEST STALL bit — pauses WDT on CCS halt
Cannot disable INTEN/RESEN cannot be cleared after INTEN set
Reset detection SysCtlResetCauseGet() checks SYSCTL_CAUSE_WDOG0
Pet operation WatchdogReloadSet (after unlock)

Next Chapter

Error Handling

Share: