Difficulty: Hard · Puzzle 2 of 6 · Topic: Debugging Riddles
A 2-bit one-hot ring FSM (legal states 01 and 10, IDLE = 01) sometimes comes out of reset as 00 and stays there forever, on gate-level or silicon-like timing. Plain zero-delay RTL runs pass. Reproduce it in simulation and fix it.
module fsm(input logic clk, rst_s0, rst_s1, output logic s0, s1);
always_ff @(posedge clk or negedge rst_s0) if (!rst_s0) s0 <= 1'b1; else s0 <= s1;
always_ff @(posedge clk or negedge rst_s1) if (!rst_s1) s1 <= 1'b0; else s1 <= s0;
endmodule
// Gate-level behaviour modelled in RTL with wire delays on the reset branches:
assign #0 rst_s0 = rst_n; // release reaches s0 at once
assign #3 rst_s1 = rst_n; // release reaches s1 3 ns later (routing skew)
// clk edges at 5, 15, ..., 45; rst_n is released at 43 ns, 2 ns before the edge at 45The reset release is an asynchronous signal reaching two flops at slightly different times relative to the clock edge.
1) Asserting reset asynchronously is safe, but deasserting it near a clock edge violates recovery/removal timing. With a skew between flops, one flop sees the release before the edge and the other after.
2) Reproduction (run in Icarus): with the delays above, s0 leaves reset before the edge at 45 and loads s1=0, while s1 is still held in reset. The state becomes 00 at 46 ns and every following edge keeps it at 00 (0 rotates forever). The simulation printed 00 at 46, 56, 66, 76.
3) Plain RTL without the wire delays releases both flops in the same delta, so the bug never shows. Real silicon can also go metastable (X in a gate-level run with SDF), but the lock-up above is the deterministic version.
4) Fix: asynchronous assert, synchronous deassert. A two-flop reset synchroniser, clocked by the FSM's clock, delivers one clean reset release that all flops of the domain see on the same edge. With it the same FSM printed 01 01 10 01 (a legal ring) from the first edge after release.
5) Also have the testbench release reset at a controlled time (for example just after a clock edge) so RTL runs are deterministic, and add a one-hot assertion on the state so a lock-up fails immediately.Answer: Bug is asynchronous reset deassert timing; synchronize reset release per clock domain.
Checked by running the code in Icarus Verilog 13; the printed output supports the answer above. Icarus: with the skewed reset release the FSM locks up in 00 and never leaves it. Icarus: with a two-flop reset synchroniser (async assert, sync deassert) the same FSM runs a legal 01 / 10 ring after release.
Trusting passing zero-delay RTL runs. Reset-release problems only appear with skew or timing, so they need a gate-level run, a delayed-reset model like this one, or a static reset-domain check.
How many sync flops do you choose for reset release and why? Model answer: two is the normal choice; the first can go metastable, the second gives it one clock period to resolve. Use three when the clock is very fast or the safety target is strict, and size it with the same MTBF = exp(Tresolve/tau) estimate as any other synchroniser.
Zero-delay RTL passes, so I suspect reset release timing. If reset is released asynchronously close to a clock edge, with skew between flops, one flop sees the release before the edge and the other after, so the one-hot state can come out as 00, and rotating 00 gives 00 forever. The fix is asynchronous assert, synchronous deassert: a two-flop reset synchroniser clocked by the FSM clock, so all flops see the release on the same edge. I would also add a one-hot assertion so a lock-up fails immediately. The trap is trusting the passing zero-delay runs.