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
PCLKand 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
CONTROLregister assumes the writer is friendly — software setsENABLE, the block runs.WdogControlcarries the same kind ofINTEN/RESENbits, but the whole point ofWdogLockis that software is not trusted by default: a write toWdogControldoes 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.
WdogLockis a key port (write a magic value to change behaviour) andWdogIntClris 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/WdogMISare 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.
WdogValuedecrements everyPCLKfromWdogLoad. The CPU does not stop it, slow it, or read-modify it cycle by cycle — its single influence is the feed: a write toWdogIntClrreloadsWdogValueback toWdogLoadand 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 (
WdogRIS→WdogMISifINTEN) 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 viaWdogIntClr), nothing more happens. But if the counter reaches zero a second time with the first interrupt still pending — meaning nobody serviced it — the watchdog assertsWDOGRES, the system reset, but only ifRESENis set. Interrupt is the warning; reset is the execution;RESENis the safety on the trigger. - The magic key gates every write that could weaken the watchdog.
WdogLockat offset0xC00is the safety catch. After reset (or any non-key write) the registers are locked: writes toWdogLoad,WdogControl, etc. are silently dropped. Writing the exact value0x1ACCE551toWdogLockunlocks them; writing any other value re-locks. A read ofWdogLockreturns 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."
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.
| Offset | Register | Access | Reset | Purpose |
|---|---|---|---|---|
0x000 | WdogLoad | RW | 0xFFFFFFFF | Reload value; written here, copied into WdogValue on a write or on timeout reload |
0x004 | WdogValue | RO | 0xFFFFFFFF | Current count — the live free-running down-counter; read-only to software |
0x008 | WdogControl | RW | 0x0 | [0] INTEN enables the counter + interrupt; [1] RESEN enables the stage-2 reset output |
0x00C | WdogIntClr | WO | — | Feed/kick: write any value to clear the interrupt and reload WdogValue from WdogLoad |
0x010 | WdogRIS | RO | 0x0 | Raw interrupt status — [0] set when the counter reaches zero (stage-1), before masking |
0x014 | WdogMIS | RO | 0x0 | Masked interrupt status — WdogRIS & INTEN; this is what the interrupt controller sees |
0xC00 | WdogLock | RW | locked | Write 0x1ACCE551 to unlock register writes; any other value locks; read returns lock status (1=locked) |
0xFE0–0xFFC | WdogPeriphID / PCellID | RO | id | Identification 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.
// ============================================================
// 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
endmoduleTwo 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 decision | Options | SP805 choice & rationale |
|---|---|---|
| How does the CPU prove liveness? | Toggle a bit · write a sequence · write a dedicated strobe | A 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 lock | A 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 action | Immediate reset · interrupt-then-reset | Two-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 enable | Gated 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 status | Lock 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 lock | Unlocked · locked | Locked. 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,
WdogValuecounts steadily down fromWdogLoadto zero with no reload event — there is no APB write toWdogIntClranywhere in the trace. At the first zero,WdogRIS[0]sets andwdog_intasserts; the counter reloads and counts down again; at the second zero,wdog_respulses and the system resets. The interval between resets equals exactly two full count-downs ofWdogLoad / 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 haltsPCLK-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 whileWdogLockwas still locked, soWdogControl'sINTENwrite was dropped — except hereINTENdid 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
WdogLockandWdogControlback 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 MentorSnippet// 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
WdogIntClris actually being written at the expected cadence, and that the period between resets equals one or two fullWdogLoadcount-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.
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_allowedwas accidentally wired towr, silently defeating the entire protection scheme.Azvya Education Pvt. Ltd.VLSI MentorSnippet// 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) reloadsWdogValuefromWdogLoadand clears the masked interrupt — both effects, since a feed that reloads but fails to clearint_pendingwould let stage-two reset fire on the next timeout despite a live, feeding CPU.Azvya Education Pvt. Ltd.VLSI MentorSnippetassert 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 whenRESEN. The key negative property is that a second timeout withRESEN=0raises/holds the interrupt but never driveswdog_res— interrupt-only mode must provably never reset.Azvya Education Pvt. Ltd.VLSI MentorSnippet// 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
- Spell the lock contract. State what value unlocks
WdogLock, what happens on any other written value, what a read ofWdogLockreturns, and the reset-default lock state — then explain why each choice makes corruption fail armed. - Trace the feed. For a watchdog with
WdogLoad = 1000andINTEN=1, describe whatWdogValuedoes 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. - Order the escalation. Write the sequence of events from the first timeout to a system reset, naming
WdogRIS,WdogMIS,int_pending, andRESEN, and identify the exact window in which a feed still saves the system. - Find the silent-drop bug. Given boot code that writes
WdogLoadandWdogControlbut never writes0x1ACCE551first, explain what the watchdog actually does and how a read-back would expose the bug. - 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
WdogIntClrthat reloads the free-running down-counter (WdogValuefromWdogLoad) 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 (
WdogRIS→WdogMISviaINTEN) and reloads; only a second consecutive timeout with the interrupt still unserviced asserts the system resetWDOGRES, and only whenRESENis 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 withRESEN=0provably never drives reset — because those silent failures are exactly the ones that ship.