A watchdog guards against a stuck system. A periodic kick clears its counter; silence for the full limit raises a timeout used to reset the system.
The counter clears on kick or while disabled, and sets a sticky timeout after TIMEOUT kick-free cycles. The exact interval is tested to the cycle in the testbench below.
Each slot is one clock cycle, sampled just after its rising edge; en is high throughout. A kick drawn in slot k acts at the edge that opens slot k+1.
Slots 0 to 2: the count climbs 0, 1, 2; the kick in slot 2 clears it to 0 in slot 3.
Slots 4 to 6: no kick, so the count runs 1, 2, 3. In slot 6 it equals TIMEOUT − 1.
Slot 7: the edge that found 3 sets timeout, four edges after the kick edge. The count stops at 3.
Slots 8 to 10: a late kick clears the count, but timeout stays high. Only reset clears it.
TIMEOUT may be 1 (fires after one enabled cycle without a kick): the counter width is guarded as localparam int CW = (TIMEOUT > 1) ? $clog2(TIMEOUT) : 1; because $clog2(1) is 0.
Question | Typical choice |
|---|---|
Sticky timeout or pulse? | Sticky: the system must see the event even if it is busy; clear it by reset. |
What does a kick do? | Clears the counter. Some designs require a specific kick pattern so a runaway program cannot keep feeding it by accident. |
Can software disable it? | Often only before the first kick or via a protected register, so a bug cannot silently turn it off. |
Window watchdog? | A kick that arrives too early is also an error, which detects a loop that kicks too fast. |
Which clock runs it? | Ideally an independent, always-on clock so a clock failure in the main system is still detected. |
timeout rise, and why does the counter compare against TIMEOUT−1?timeout rises 16 clock cycles after the kick. The counter is cleared to 0 by the kick and increments each cycle, so it reaches 15 (= TIMEOUT−1) after 15 cycles, and the 16th edge sets timeout. Comparing against TIMEOUT itself would make the interval one cycle too long.
The bounded test checks kicking, exact firing time, disable clearing a partial interval, and sticky timeout under later kick and disable. Inputs are driven on falling edges; every rising-edge result is checked 1 ns after NBA updates, including every edge before and at timeout. Only reset clears a fired timeout.
module watchdog #(parameter int TIMEOUT = 16) ( // TIMEOUT must be at least 1
input logic clk, rst_n,
input logic en, // watchdog enabled
input logic kick, // feed the dog
output logic timeout // sticky: stays high until reset
);
// TIMEOUT = 1 guard ($clog2(1) is 0, so [$clog2(TIMEOUT)-1:0] would be [-1:0]).
localparam int CW = (TIMEOUT > 1) ? $clog2(TIMEOUT) : 1;
logic [CW-1:0] cnt;
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) begin
cnt <= '0;
timeout <= 1'b0;
end else if (!en || kick) begin
cnt <= '0; // a kick (or disable) restarts the interval
end else if (cnt == TIMEOUT - 1) begin
timeout <= 1'b1; // TIMEOUT cycles passed with no kick
end else begin
cnt <= cnt + 1'b1;
end
endmodule