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

TermMeaning
WDT0Watchdog Timer 0 on APB — standard peripheral
WDT1Watchdog Timer 1 on AHB — always clocked in deep sleep
WDTLOADReload value register — sets timeout period
WDTVALUECurrent countdown value (read-only)
WDTCTLControl register — enable interrupt and reset
WDTLOCKWrite 0x1ACCE551 to unlock, any other value to lock
Pet/FeedWriting WDTLOAD to reset the countdown

Intermediate Level — How It Works

WDT0 vs WDT1

FeatureWDT0WDT1
BusAPBAHB
ClockSystem clockSystem clock
Deep sleepStopsContinues
Reset causeWDTSTAT bit 1 (WDT0)WDTSTAT bit 2 (WDT1)
Base address0x400000000x40001000
Recommended useNormal operationExtended sleep protection

Key Registers

RegisterOffsetDescription
WDTLOAD0x000Load value (countdown starts here)
WDTVALUE0x004Current counter value (read-only)
WDTCTL0x008Control: INTEN (bit0), RESEN (bit1), INTTYPE (bit2)
WDTRIS0x00CRaw interrupt status
WDTMIS0x010Masked interrupt status
WDTICR0x00CInterrupt clear (writing clears timeout interrupt)
WDTTEST0x418Test register (STALL bit for CCS debug halt)
WDTLOCK0xC00Lock register

WDTCTL bit fields:

BitNameFunction
0INTENEnable watchdog interrupt (first timeout fires ISR)
1RESENEnable reset on second timeout
2INTTYPE0 = 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

BitSymbolCause
0EXTExternal reset pin
1PORPower-on reset
2BORBrown-out reset
3WDT0Watchdog Timer 0 reset
4SWSoftware reset (SysCtlReset)
5WDT1Watchdog Timer 1 reset
20MOSCFAILMain oscillator failure reset

Choosing the Right Timeout

System typeRecommended timeout
Simple polling loop (10 ms period)50–100 ms
RTOS with 1 ms tick50–200 ms
ADC + control loop (100 Hz)100–500 ms
Slow communication with host1–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 PointDetails
ModulesWDT0 (APB) and WDT1 (AHB, deep-sleep capable)
TimeoutWDTLOAD / system_clock = seconds
Two-stageFirst timeout = IRQ, second timeout = reset
Lock key0x1ACCE551 to unlock, any other to lock
Debug stallWDTTEST STALL bit — pauses WDT on CCS halt
Cannot disableINTEN/RESEN cannot be cleared after INTEN set
Reset detectionSysCtlResetCauseGet() checks SYSCTL_CAUSE_WDOG0
Pet operationWatchdogReloadSet (after unlock)

Next Chapter

Error Handling

Share: