Skip to content

A watchdog timer is the one peripheral on the bus whose job is to survive the failure of everything else — including the firmware that programs it — so its register map is designed not to be convenient but to be hard to disable by accident. Every other peripheral you have designed in this module assumes the CPU is healthy: software writes a configuration register, the block obeys, life goes on. A watchdog assumes the opposite — that the CPU may have wandered into a fault loop, be executing garbage, or be stuck spinning — and its entire value is that it outlives that failure and forces a recovery. That single inversion drives every design decision here: a free-running down-counter that the CPU must periodically feed to prove it is alive, a two-stage escalation from interrupt to hard reset so a missed feed degrades gracefully before it nukes the system, and — the non-trivial point of this chapter — a magic-key write-protect (WdogLock, unlocked only by writing 0x1ACCE551) that gates every other register write, so that runaway code stamping random values across the address space cannot accidentally turn the watchdog off and disarm the very mechanism meant to catch it. We ground all of it in the real Arm SP805 watchdog, the part that ships in countless SoCs.

1. Problem statement

The problem is building a timer whose purpose is to detect that the CPU has stopped behaving correctly and to force a recovery — which means it must keep working precisely when the system around it has failed, and it must resist being disabled by the failing system itself.

A watchdog is a hardware dead-man's switch. The contract is simple to state and subtle to implement: the CPU promises to periodically tell the watchdog "I am still alive" (the feed or kick), and if the watchdog ever goes too long without hearing that, it concludes the CPU has hung or gone insane and forces a reset. Three obligations fall out of that contract, and each one fights the grain of an ordinary peripheral:

  • It must keep counting while the CPU is broken. An ordinary peripheral does work for software; a watchdog does work against the possibility of software's failure. Its down-counter is free-running off PCLK and decrements every cycle regardless of what the CPU is doing — the CPU's only influence is to reload it by feeding. If the CPU stops feeding (because it hung), the counter must reach zero on its own.
  • It must escalate gracefully, not just hard-reset. A single missed feed might be a transient — an interrupt service routine that ran long, a momentary stall. Slamming the whole SoC into reset on the first timeout is brutal and loses state needed to debug. So the watchdog escalates in two stages: the first timeout raises an interrupt (a chance for a still-partly-alive CPU to recover, log, and feed); only if that goes unserviced does the second timeout assert a system reset.
  • It must resist being disabled by the very failure it guards against. This is the hard part and the reason a watchdog map looks different from any other. If runaway firmware — a corrupted program counter scribbling across the peripheral region — could disable the watchdog with one stray write, the watchdog would fail in exactly the scenario it exists to catch. So every write that could weaken the watchdog is gated behind a magic-key lock: the registers are write-protected until software deliberately writes a specific 32-bit key, and any other value re-locks them.

So the job is not "a down-counter with an interrupt." It is a self-defending peripheral: a free-running counter, a two-stage interrupt-then-reset escalation, a feed mechanism, and a magic-key write-protect that assumes the thing programming it may itself be the threat.

2. Why previous knowledge is insufficient

This module has built every primitive a watchdog uses — counters live in a timer peripheral, write-protection in write-only key ports, interrupts in status registers — but none of those chapters assembled them into a block whose threat model is its own host CPU, and that assembly is what is new here.

  • Configuration registers taught a control register software trusts; the watchdog distrusts it. A normal CONTROL register assumes the writer is friendly — software sets ENABLE, the block runs. WdogControl carries the same kind of INTEN/RESEN bits, but the whole point of WdogLock is that software is not trusted by default: a write to WdogControl does nothing unless the lock was first opened with the magic key. The control-register chapter never had to defend the register against its own writer.
  • Write-only registers taught the secure-key port and the self-clearing strobe; the watchdog uses both at once, for a new reason. WdogLock is a key port (write a magic value to change behaviour) and WdogIntClr is a write-only strobe (write any value to act — clear the interrupt and reload the counter). You have built each pattern. What is new is why: the key here does not protect a secret, it protects the watchdog's liveness — it exists so the failing CPU cannot disarm the recovery mechanism. The strobe here is not a generic "go" — it is the feed that proves the CPU is alive.
  • Status registers and W1C taught raw-versus-masked interrupt bits; the watchdog adds a timeout escalation on top. WdogRIS/WdogMIS are exactly the raw/masked interrupt pair you designed for any peripheral. What previous knowledge does not give you is the two-stage semantics: the interrupt is not the end state, it is stage one of an escalation that becomes a reset if the interrupt is ignored — a peripheral whose unserviced interrupt has teeth.

The model to add is the watchdog as a liveness contract enforced against a possibly-hostile host: a free-running counter (timer), a feed strobe (write-only), a raw/masked interrupt (status), a magic-key lock (secure key) — composed so the assembled block defends its own ability to fire. The natural neighbours are the interrupt controller that routes the stage-one interrupt and the slave reset behavior the stage-two reset must respect.

3. Mental model

The model: a watchdog is a dead-man's switch with a deliberately stiff safety catch. The down-counter is the dead-man's switch — left alone, it runs to zero and trips. The CPU's feed is the operator's hand keeping the switch from tripping: as long as a healthy CPU periodically reloads the counter, the switch never fires. The magic-key lock is the stiff safety catch on the arming controls: you cannot bump the watchdog off by accident, because changing anything that would weaken it requires a deliberate, specific motion (writing 0x1ACCE551) that random flailing will never produce.

Three refinements make it precise and silicon-accurate:

  • The counter is free-running and the CPU's only lever is reload. WdogValue decrements every PCLK from WdogLoad. The CPU does not stop it, slow it, or read-modify it cycle by cycle — its single influence is the feed: a write to WdogIntClr reloads WdogValue back to WdogLoad and clears any pending interrupt. Feed often enough and the counter never hits zero. Stop feeding (because you hung) and it counts down to zero on its own, exactly as intended. The feed is the proof of life; the counter is the patience.
  • Timeout escalates in two stages: interrupt, then reset. When the counter reaches zero the first time, the watchdog raises an interrupt (WdogRISWdogMIS if INTEN) and reloads the counter to start counting down again. This is the warning shot — a chance for the CPU to notice, recover, and feed. If the CPU services it (feeds via WdogIntClr), nothing more happens. But if the counter reaches zero a second time with the first interrupt still pending — meaning nobody serviced it — the watchdog asserts WDOGRES, the system reset, but only if RESEN is set. Interrupt is the warning; reset is the execution; RESEN is the safety on the trigger.
  • The magic key gates every write that could weaken the watchdog. WdogLock at offset 0xC00 is the safety catch. After reset (or any non-key write) the registers are locked: writes to WdogLoad, WdogControl, etc. are silently dropped. Writing the exact value 0x1ACCE551 to WdogLock unlocks them; writing any other value re-locks. A read of WdogLock returns the lock status (1 = locked, 0 = unlocked), not the key. The brilliance is the asymmetry: unlocking takes a specific 32-bit value that runaway code will essentially never stumble onto, but any stray write re-locks — so the failure mode of corruption is "watchdog stays armed," never "watchdog silently disabled."
A block diagram of an APB watchdog. An APB write port feeds a WdogLock magic-key gate that only admits writes when 0x1ACCE551 has unlocked it; behind it, WdogLoad drives a free-running down-counter WdogValue that decrements each PCLK. A feed path from WdogIntClr reloads the counter and clears the interrupt. The first zero raises a stage-1 interrupt gated by INTEN; a second zero asserts the stage-2 reset WDOGRES gated by RESEN.
Figure 1 — the SP805-style watchdog as a self-defending APB peripheral. An APB write enters the CSR block and passes through the WdogLock magic-key gate: the write only reaches a protected register if the lock was previously opened by writing 0x1ACCE551 (unlocked = 1); any other value re-locks and the write is silently dropped. Behind the gate, WdogLoad sets the reload value for the free-running down-counter (WdogValue), which decrements every PCLK. The feed path — a write to WdogIntClr — reloads the counter and clears the interrupt, proving the CPU is alive. When the counter reaches zero the first time it raises the stage-1 interrupt (WdogRIS/WdogMIS, gated by INTEN) and reloads; if it reaches zero a second time with the interrupt still pending, it asserts the stage-2 system reset WDOGRES, but only when RESEN is set. The figure shows the central insight: the magic-key gate sits between the bus and every register that could weaken the watchdog, so runaway firmware cannot disarm the recovery mechanism with a stray write.

4. Real SoC implementation

The Arm SP805 is the canonical APB watchdog. Its register map is small and every offset earns its place — note that the lock register lives far away at 0xC00, deliberately separated from the functional registers so it reads as a distinct protection mechanism, not just another control bit.

OffsetRegisterAccessResetPurpose
0x000WdogLoadRW0xFFFFFFFFReload value; written here, copied into WdogValue on a write or on timeout reload
0x004WdogValueRO0xFFFFFFFFCurrent count — the live free-running down-counter; read-only to software
0x008WdogControlRW0x0[0] INTEN enables the counter + interrupt; [1] RESEN enables the stage-2 reset output
0x00CWdogIntClrWOFeed/kick: write any value to clear the interrupt and reload WdogValue from WdogLoad
0x010WdogRISRO0x0Raw interrupt status — [0] set when the counter reaches zero (stage-1), before masking
0x014WdogMISRO0x0Masked interrupt status — WdogRIS & INTEN; this is what the interrupt controller sees
0xC00WdogLockRWlockedWrite 0x1ACCE551 to unlock register writes; any other value locks; read returns lock status (1=locked)
0xFE0–0xFFCWdogPeriphID / PCellIDROidIdentification registers (PrimeCell) — read-only, outside the lock

The RTL below shows the magic-key gate, the free-running down-counter, the two-stage interrupt→reset escalation, and the WdogIntClr feed, with real APB signal names. The decode is the centre of gravity: wr_allowed folds the unlock state into the APB write qualifier, so a single term gates every protected register.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ============================================================
// SP805-style APB watchdog — magic-key gated, two-stage escalation
// ============================================================
// Real APB slave port: pclk, presetn, psel, penable, pwrite, paddr,
// pwdata, prdata, pready, pslverr.  Outputs: wdog_int, wdog_res.
module apb_watchdog (
  input  logic        pclk,
  input  logic        presetn,
  input  logic        psel,
  input  logic        penable,
  input  logic        pwrite,
  input  logic [11:0] paddr,      // 0x000..0xC00 reachable
  input  logic [31:0] pwdata,
  output logic [31:0] prdata,
  output logic        pready,     // single-cycle slave: always ready in ACCESS
  output logic        pslverr,
  output logic        wdog_int,   // stage-1 interrupt to the interrupt controller
  output logic        wdog_res    // stage-2 system reset request (gated by RESEN)
);
  // ---- register offsets (SP805) ----
  localparam logic [11:0] LOAD=12'h000, VALUE=12'h004, CTRL =12'h008,
                          INTCLR=12'h00C, RIS=12'h010, MIS  =12'h014,
                          LOCK =12'hC00;
  localparam logic [31:0] MAGIC = 32'h1ACCE551;   // the SP805 unlock key
 
  logic [31:0] wdog_load, wdog_value;
  logic        inten, resen;          // WdogControl[1:0]
  logic        int_pending;           // raw interrupt latch (WdogRIS[0])
  logic        locked;                // 1 = register writes are blocked
 
  // APB single-cycle handshake: ACCESS phase = psel & penable.
  assign pready  = 1'b1;                       // no wait states
  assign pslverr = 1'b0;
  wire   wr      = psel && penable && pwrite;  // a write committing this cycle
  wire   acc     = psel && penable;            // any access committing this cycle
 
  // ---- THE MAGIC-KEY GATE -------------------------------------------------
  // A protected write only lands when the lock is OPEN. 'locked' is the safety
  // catch: set out of reset, opened ONLY by writing MAGIC to WdogLock, and
  // re-armed by ANY other value written there. wr_allowed folds the unlock
  // state into the APB write qualifier so one term protects every register.
  always_ff @(posedge pclk or negedge presetn)
    if (!presetn)                 locked <= 1'b1;            // armed out of reset
    else if (wr && paddr==LOCK)   locked <= (pwdata != MAGIC); // key opens; else re-locks
  wire wr_allowed = !locked && wr;   // <-- gate: protected writes require unlock
 
  // ---- WdogLoad (protected) ----------------------------------------------
  always_ff @(posedge pclk or negedge presetn)
    if (!presetn)                         wdog_load <= 32'hFFFF_FFFF;
    else if (wr_allowed && paddr==LOAD)   wdog_load <= pwdata;
 
  // ---- WdogControl: INTEN / RESEN (protected) ----------------------------
  always_ff @(posedge pclk or negedge presetn)
    if (!presetn)                         {resen, inten} <= 2'b00;
    else if (wr_allowed && paddr==CTRL)   {resen, inten} <= pwdata[1:0];
 
  // ---- WdogIntClr = the FEED/KICK (protected write-only strobe) -----------
  // Writing ANY value reloads the counter AND clears the pending interrupt.
  // This single strobe is the CPU's "I am still alive" proof.
  wire feed = wr_allowed && (paddr==INTCLR);
 
  // ---- free-running down-counter + two-stage escalation -------------------
  // The counter runs only when INTEN is set. On a feed OR a load-write it
  // reloads from wdog_load. Reaching zero is the timeout event.
  wire reload = feed || (wr_allowed && paddr==LOAD);
  wire at_zero = (wdog_value == 32'h0);
 
  always_ff @(posedge pclk or negedge presetn) begin
    if (!presetn) begin
      wdog_value  <= 32'hFFFF_FFFF;
      int_pending <= 1'b0;
      wdog_res    <= 1'b0;
    end else if (reload) begin
      wdog_value  <= wdog_load;      // FEED: reload, ...
      int_pending <= 1'b0;           // ... and clear the interrupt (serviced)
      wdog_res    <= 1'b0;
    end else if (inten) begin
      if (at_zero) begin
        // timeout reached. STAGE 1: if no interrupt pending, raise it + reload.
        // STAGE 2: if an interrupt was ALREADY pending (unserviced), reset.
        if (!int_pending) begin
          int_pending <= 1'b1;       // stage-1 interrupt
          wdog_value  <= wdog_load;  // reload to count the 2nd interval
        end else begin
          wdog_res    <= resen;      // stage-2 reset — ONLY if RESEN enabled
        end
      end else begin
        wdog_value <= wdog_value - 32'h1;   // tick down every PCLK
      end
    end
  end
 
  assign wdog_int = int_pending && inten;   // WdogMIS[0] = WdogRIS & INTEN
 
  // ---- read mux: WdogLock returns LOCK STATUS, never the key --------------
  always_comb begin
    unique case (paddr)
      LOAD:   prdata = wdog_load;
      VALUE:  prdata = wdog_value;                 // live count, RO
      CTRL:   prdata = {30'h0, resen, inten};
      RIS:    prdata = {31'h0, int_pending};       // raw interrupt
      MIS:    prdata = {31'h0, (int_pending && inten)}; // masked interrupt
      LOCK:   prdata = {31'h0, locked};            // 1 = locked, 0 = unlocked
      default: prdata = 32'h0;                     // WdogIntClr is WO -> reads 0
    endcase
  end
endmodule

Two facts make this design and not just coding. First, the magic key is a gate on the write qualifier, not a per-register if-statement. wr_allowed = !locked && psel && penable && pwrite is computed once and ANDed into every protected register's enable, so the protection is structural and uniform — you cannot forget to protect a register, and you cannot accidentally protect the lock register itself (the lock write must always be honoured, which is why locked updates on wr, not wr_allowed). Second, the two-stage escalation is encoded in int_pending: a timeout with no pending interrupt is stage one (raise + reload); a timeout with a pending interrupt is stage two (assert reset). The feed clears int_pending, which is exactly why feeding after the warning interrupt — but before the second timeout — still saves the system. The whole behaviour hinges on that one latch.

5. Engineering tradeoffs

The watchdog's design space is a set of deliberate contract choices — how to feed, how to protect, how aggressively to escalate. The table is the engineering artifact: each row is a decision, the SP805's choice, and why.

Design decisionOptionsSP805 choice & rationale
How does the CPU prove liveness?Toggle a bit · write a sequence · write a dedicated strobeA write of any value to WdogIntClr (a write-only strobe). Simple, atomic, one bus access; the value is don't-care so corrupted data still feeds correctly — what matters is that the CPU reached the feed instruction
What protects the config from stray writes?None · a single enable bit · a magic-key lockA magic-key lock (WdogLock = 0x1ACCE551). A single enable bit is one stray write from off; a 32-bit key is statistically unreachable by runaway code, and any wrong value re-locks — the asymmetry that makes corruption fail safe
First timeout actionImmediate reset · interrupt-then-resetTwo-stage: interrupt first (a recoverable warning), reset only on a second missed timeout. Preserves state for debug and tolerates a transient stall without nuking the SoC
Is the reset output always live?Always reset on timeout · gated by an enableGated by RESEN. Lets a system run the watchdog in interrupt-only mode (e.g. during bring-up/debug) without the reset teeth, then enable RESEN for production
What does a read of the lock return?The key · undefined · lock statusLock status (1=locked). Returning the key would defeat the protection; returning status lets a driver check "am I unlocked?" before writing config
Reset default of the lockUnlocked · lockedLocked. The watchdog must come out of reset protected, so a glitch or early stray write during boot cannot disable it before firmware deliberately unlocks, configures, and re-locks

The throughline: every choice is biased toward "fail armed." The feed is value-agnostic so corruption still feeds; the lock is statistically unreachable to open but trivial to re-arm; the escalation warns before it kills; and the reset-default is locked. A watchdog that is easy to disable is worse than no watchdog, because it gives false confidence — so the SP805 spends real design effort making the disable path hard and the armed path the default. The one genuine tradeoff is escalation aggressiveness: two stages buy graceful recovery and debug visibility at the cost of one extra timeout interval before reset — almost always the right trade, but a hard-real-time safety system might choose INTEN=0 semantics or a shorter window.

6. Common RTL mistakes

7. Debugging scenario

The signature watchdog escape is a board that boot-loops every few seconds in the field but runs forever on the bench — and the root cause is almost always one of two things: firmware that configured a watchdog it never feeds, or config writes that were silently dropped because the lock was never opened.

  • Observed symptom: a production unit resets itself roughly every 2–3 seconds, endlessly. On the bench, attached to a debugger with breakpoints, it never resets — because halting at a breakpoint stops the CPU but the engineer never sees the connection. The reset reason register reads "watchdog." Firmware logs show the application starting, then nothing, then a fresh boot banner — a clean restart loop with no crash, no exception.

  • Waveform/trace clue: on a bus capture, WdogValue counts steadily down from WdogLoad to zero with no reload event — there is no APB write to WdogIntClr anywhere in the trace. At the first zero, WdogRIS[0] sets and wdog_int asserts; the counter reloads and counts down again; at the second zero, wdog_res pulses and the system resets. The interval between resets equals exactly two full count-downs of WdogLoad / PCLK. The interrupt was raised — but no ISR ever serviced it (or the ISR existed but a different bug kept it from running), so stage two fired on schedule.

  • Root cause: the boot code enabled the watchdog (INTEN=1, RESEN=1) but the main loop never issued the periodic feed — either the feed routine was never wired into the scheduler, or a refactor moved the feed into a code path that an early-return skipped. The watchdog did exactly its job: it detected that the "alive" signal stopped and forced recovery. (The bench masked it because a halted CPU under the debugger also halts PCLK-relative expectations, or the engineer simply never let it free-run long enough.) A related variant: the feed was present, but the watchdog was enabled while WdogLock was still locked, so WdogControl's INTEN write was dropped — except here INTEN did take, so this is the un-fed case.

  • Correct firmware/RTL discipline: feed from a single, well-defined place that runs only when the system is genuinely healthy (e.g. the main supervisory loop after it has verified subsystems), never from a timer interrupt that keeps firing even when the application is hung — a feed from a free-running tick ISR defeats the watchdog by feeding it while the app is dead. And always unlock → configure → (optionally re-lock), then read WdogLock and WdogControl back to confirm the writes landed.

  • Verification assertion: prove that a feed actually reloads and clears, and that a stray locked write does nothing:

    Azvya Education Pvt. Ltd.VLSI Mentor
    Snippet
    // A feed (write-any to WdogIntClr while unlocked) reloads WdogValue and clears MIS.
    assert property (@(posedge pclk) disable iff (!presetn)
      (!locked && psel && penable && pwrite && paddr==12'h00C)
        |=> (wdog_value == $past(wdog_load)) && !wdog_int);
  • Debug habit: when a board resets on a fixed period with a "watchdog" reset reason, do not chase the application crash first — chase the feed. Confirm on a trace that WdogIntClr is actually being written at the expected cadence, and that the period between resets equals one or two full WdogLoad count-downs (one = un-fed in interrupt-only escalation collapsed; two = the full two-stage interval). A perfectly periodic reset with no crash is the fingerprint of an unfed dog, not a code bug — the dog is the messenger.

Two stacked waveforms over eight PCLK cycles. The top green trace shows WdogValue counting down then jumping back to WdogLoad on an APB write to WdogIntClr (the feed), never reaching zero. The bottom red trace shows WdogValue counting to zero with no feed: the first zero asserts wdog_int and reloads, and the second zero asserts wdog_res, the system reset, because RESEN is set.
Figure 2 — feeding the watchdog versus missing a feed, over about eight PCLK cycles. The top (green) trace shows a healthy CPU: WdogValue ticks down each cycle, and before it reaches zero an APB write to WdogIntClr (the feed/kick) reloads it back to WdogLoad — the counter never reaches zero, no interrupt, no reset. The bottom (red) trace shows a missed feed: no write to WdogIntClr arrives, so WdogValue counts all the way to zero. At the first zero the stage-1 interrupt (wdog_int) asserts and the counter reloads; with the interrupt still unserviced, the second zero asserts the stage-2 system reset wdog_res (because RESEN is set). The figure marks the feed edge, the two timeout-to-zero events, and the interrupt-then-reset escalation, teaching that the feed is the proof of life and that a missed feed degrades through an interrupt warning before forcing a reset.

8. Verification perspective

A watchdog is verified against its liveness-defended-against-the-host contract: the magic key actually gates writes, the feed actually reloads and clears, the escalation goes interrupt-then-reset in that order, and the reset is gated by RESEN. Each is a property the APB assertion suite can carry, and the negative cases (locked writes silently dropped) matter as much as the positive ones.

  • The lock genuinely gates protected writes. The headline safety property: a write to a protected register while locked must have no effect. Prove the register holds its value across a locked write — this is the property that catches a decode bug where wr_allowed was accidentally wired to wr, silently defeating the entire protection scheme.

    Azvya Education Pvt. Ltd.VLSI Mentor
    Snippet
    // A protected write while LOCKED does not change the register (here WdogLoad).
    assert property (@(posedge pclk) disable iff (!presetn)
      (locked && psel && penable && pwrite && paddr==12'h000)
        |=> wdog_load == $past(wdog_load));
  • The feed reloads and clears in one stroke. Assert that a write to WdogIntClr (while unlocked) reloads WdogValue from WdogLoad and clears the masked interrupt — both effects, since a feed that reloads but fails to clear int_pending would let stage-two reset fire on the next timeout despite a live, feeding CPU.

    Azvya Education Pvt. Ltd.VLSI Mentor
    Snippet
    assert property (@(posedge pclk) disable iff (!presetn)
      (!locked && psel && penable && pwrite && paddr==12'h00C)
        |=> (wdog_value == $past(wdog_load)) && !wdog_int);
  • Escalation is ordered, and reset obeys RESEN. Cover and assert the sequence: a first timeout raises the interrupt without asserting reset; only a second timeout with the interrupt still pending asserts reset, and only when RESEN. The key negative property is that a second timeout with RESEN=0 raises/holds the interrupt but never drives wdog_res — interrupt-only mode must provably never reset.

    Azvya Education Pvt. Ltd.VLSI Mentor
    Snippet
    // Second consecutive timeout asserts reset ONLY if RESEN is set.
    assert property (@(posedge pclk) disable iff (!presetn)
      (inten && int_pending && (wdog_value==32'h0)) |-> (wdog_res == resen));

The point: verify the watchdog as a defended liveness contract — locked writes are provably no-ops, the feed provably reloads-and-clears, and the escalation is provably interrupt-then-reset with the reset gated by RESEN. The negative properties (no effect when locked, no reset when RESEN=0) are the ones that catch the catastrophic escapes, because those are exactly the failures that ship silently.

9. Interview discussion

"How does a watchdog's write-protect work, and why does it use a magic key instead of an enable bit?" is a sharp senior question because a weak answer says "it has a lock bit," while a strong answer reveals the threat model — that the thing programming the watchdog may itself be the failure.

Lead with the inversion: a watchdog is the one peripheral whose threat model includes its own host CPU, so it is designed to survive and resist the CPU's failure, not to serve the CPU conveniently. Frame the three pillars: a free-running down-counter (WdogValue from WdogLoad) the CPU can only influence by feeding; a feed/kick (a write-any-value strobe to WdogIntClr) that reloads the counter and clears the interrupt as proof of life; and a two-stage escalation — first timeout raises an interrupt (recoverable warning + reload), second consecutive timeout asserts a reset (WDOGRES), gated by RESEN. Then deliver the depth the question is really probing: the magic key (WdogLock = 0x1ACCE551) gates every config write, and the reason it is a 32-bit key rather than a single enable bit is asymmetry under corruption — a single enable bit is one stray write from disabling the watchdog, but a 32-bit key is statistically unreachable by runaway code, and crucially any wrong value re-locks, so corruption fails armed, never disabled. Close with the two real-silicon flourishes: "the feed value is don't-care on purpose, so even a CPU corrupting data can still prove liveness by reaching the feed instruction," and "the reset default is locked, so the watchdog comes out of reset protected before firmware ever runs." That sequence — threat model, three pillars, key-vs-bit asymmetry, fail-armed defaults — is what separates someone who has integrated a watchdog from someone who has only read its register map.

10. Practice

  1. Spell the lock contract. State what value unlocks WdogLock, what happens on any other written value, what a read of WdogLock returns, and the reset-default lock state — then explain why each choice makes corruption fail armed.
  2. Trace the feed. For a watchdog with WdogLoad = 1000 and INTEN=1, describe what WdogValue does over time if the CPU feeds every 800 cycles versus every 1200 cycles, and at which point each stage (interrupt, reset) fires in the second case.
  3. Order the escalation. Write the sequence of events from the first timeout to a system reset, naming WdogRIS, WdogMIS, int_pending, and RESEN, and identify the exact window in which a feed still saves the system.
  4. Find the silent-drop bug. Given boot code that writes WdogLoad and WdogControl but never writes 0x1ACCE551 first, explain what the watchdog actually does and how a read-back would expose the bug.
  5. Justify interrupt-only mode. Explain when you would run INTEN=1, RESEN=0, why that is not the same as a disabled watchdog, and the assertion that proves no reset fires in that mode.

11. Q&A

12. Key takeaways

  • A watchdog is the one peripheral whose threat model includes its own host CPU — it must keep counting while the CPU is broken and resist being disabled by the failing CPU, which is why its register map (SP805) is designed to be hard to disable, not convenient.
  • The feed/kick is a write-any-value strobe to WdogIntClr that reloads the free-running down-counter (WdogValue from WdogLoad) and clears the pending interrupt — the value is don't-care on purpose, so even a CPU corrupting data can still prove liveness by reaching the feed instruction.
  • Timeout escalates in two stages: the first zero raises a recoverable interrupt (WdogRISWdogMIS via INTEN) and reloads; only a second consecutive timeout with the interrupt still unserviced asserts the system reset WDOGRES, and only when RESEN is set — so interrupt-only mode (RESEN=0) provably never resets.
  • The magic-key lock (WdogLock = 0x1ACCE551) gates every config write, and a 32-bit key beats an enable bit because of asymmetry under corruption: opening is statistically unreachable by runaway code while any wrong value re-locks, so corruption fails armed; the reset default is locked and a read returns lock status, never the key.
  • The signature field bug is an unfed dog: a perfectly periodic reset with a "watchdog" reason and no crash means the feed stopped reaching WdogIntClr — chase the feed, not the application; feed only from a place that runs when the system is genuinely healthy, never from a free-running tick ISR.
  • Verify the defended-liveness contract with negative properties first — a locked write is provably a no-op, a feed provably reloads-and-clears WdogValue/WdogMIS, and a second timeout with RESEN=0 provably never drives reset — because those silent failures are exactly the ones that ship.