← RTL Design Interviews

Interview Question · Debug

The Sequencer Stuck After an Upset

L2 Mid-LevelL3 Seniorfsmdebugreliability

The Question

The interviewer asks

In a beam test, an upset hits a 3-bit state register. The sequencer then ignores start until reset. Here is the log. Explain it and fix it.

log
beam run 14, sequencer seq5 (3-bit state, binary)
  3.200 ms  upset event logged near the state register
  3.200 ms  st_q = 110
  3.201 ms  start = 1   ... no LOAD
  9.870 ms  start = 1   ... no LOAD
 12.000 ms  rst_n pulsed; st_q = 000; next start -> LOAD
Reveal the Model Answer

The enum names five codes, but three bits hold eight. The upset wrote 110, which has no arm. The block starts with st_d = st_q, so the machine holds 110 forever. Add a default arm that goes back to IDLE. Then check synthesis kept it, because a tool that re-encodes the FSM can drop arms for unreachable codes.

Five Named States and the Codes Without a NameArrows are transitions at a rising edge. The dashed box stands for the three binary codes the enum does not name.
Five named states and the codes without a nameArrows are transitions at a rising edge. The dashed box stands for the three binary codes the enum does not name. state IDLE 000 waits for start state LOAD 001 one cycle state RUN 010 until last state DRAIN 011 one cycle state DONE 100 one cycle queue 101, 110, 111 no name, no transitioninto them startnext edgelastnext edgenext edgedefault arm
  1. A job

    IDLE, LOAD, RUN, DRAIN, DONE and back: the only codes a correct run ever visits.

  2. An upset

    Only an upset, a glitch or a missed reset puts the register in 101, 110 or 111. The default arm is the only way back.

Read the diagram as text
  • IDLE 000: waits for start
  • LOAD 001: one cycle
  • RUN 010: until last
  • DRAIN 011: one cycle
  • DONE 100: one cycle
  • 101, 110, 111: no name, no transition into them
  • IDLE 000 → LOAD 001: start
  • LOAD 001 → RUN 010: next edge
  • RUN 010 → DRAIN 011: last
  • DRAIN 011 → DONE 100: next edge
  • DONE 100 → IDLE 000: next edge
  • 101, 110, 111 → IDLE 000: default arm

Without the default arm, the dashed box has no way out.

The Sequencer After an UpsetThe second job runs normally. Between edges 8 and 9 the bench forces the state register to 110 for 1 ns. From then on, start is high and nothing happens.
0123456clkstartstIDLELOADRUN110load
  1. Cycle 1

    Edge 7: start was high, so the state is LOAD and load is 1.

  2. Cycle 2

    Edge 8: RUN, code 010. The upset follows: bit 2 flips and the register holds 110.

  3. Cycle 3

    Edge 9: no arm matches 110, so st_d = st_q stands. The register reloads 110.

  4. Cycle 6

    Edges 10 to 12: start is high and the machine still holds 110. Only reset gets it out.

Measured on the bench, cycles e6 to e12. The run prints "2 jobs started": the third never begins.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Mid-Level)

A good mid-level answer counts the codes that have no name. It finds the hold in st_d = st_q and adds the default arm.

Cross-Questions

Where the interviewer goes next. Open a node to practise that follow-up.

WhyProve ItCompareScale It

  1. The Sequencer Stuck After an Upset
  2. WhyWhy a State Register Has Unnamed CodesL1 Junior

    Why are there codes with no name at all?

    Reveal the Model Answer

    The register is just N flops, so it can hold 2^N codes. The enum gives names to only the codes you use: five of eight here. The hardware does not know which codes have names. Only your next-state logic decides what happens in the other three.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "An enum variable can only hold its named values."

    In simulation a cast or a force can put any value in it, and in silicon the flops can hold any pattern. The type does not add hardware checks.

    Source Sutherland, RTL Modeling with SystemVerilog (2017) ch. 4.4 Enumerated types

  3. CompareA Default Arm vs Keeping the EncodingL2 Mid-LevelL3 Senior

    Compare the two fixes: adding a default arm, and telling the tool to keep the encoding. Do you need both?

    Reveal the Model Answer

    Yes, you need both. The default arm says what unnamed codes must do. Keeping the encoding makes sure the netlist still contains those codes and that arm. On this flow, the default alone was dropped by re-encoding, and the attribute alone kept a machine with no way out. Other tools offer a "safe FSM" option for the same job.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "A default arm makes the logic bigger, so leave it out."

    Measured here, it did the opposite. With the encoding kept, the default arm cut the machine from 17 cells to 13.

    Source Measured: the root twin and its fix, Yosys 0.70+290 (OSS CAD Suite 2026-11-02), slang front end, synth -flatten with ABC, ltp -noff

  4. Prove ItProving the Netlist Still RecoversL2 Mid-LevelL3 Senior

    Prove it: how do you show that the recovery survived synthesis?

    Reveal the Model Answer

    Check the netlist, not the RTL. Simulate the gates, force each unnamed code into the state flops, and check the machine reaches IDLE within a few cycles. Read the FSM section of the synthesis log and the flop count too. They show whether the machine was extracted and re-encoded.

    Self-Check

    Tick each key point you covered in your answer.

    Tool as Truth

    "The log shows no warning, so the encoding was kept."

    Re-encoding is a normal step, not a warning. It shows up in the FSM section of the log and in the flop count.

    Source Yosys 0.70+290 command help, help fsm_detect and help fsm_recode

  5. Scale ItFSM Recovery for a Safety-Critical BlockL3 SeniorL4 Staff

    The block goes into a car. What do you require of every FSM, and how do you prove it?

    Reveal the Model Answer

    Every machine must leave any illegal code in a bounded number of cycles, to a known safe state. It should also flag the event, so the system knows something went wrong. Prove the recovery on the netlist, not the RTL: check the encoding was kept and run upset tests at gate level. A formal check that every code reaches IDLE covers all codes at once.

    Self-Check

    Tick each key point you covered in your answer.

    What Interviewers Listen For (Staff)

    A good staff answer treats recovery as a requirement with a time bound. It asks for detection and reporting, and proves recovery on the netlist.

    Tool as Truth

    "Our RTL tests force illegal states and the FSM recovers."

    RTL tests prove the RTL. Re-encoding can remove the recovery path in the netlist, so the proof must be on the netlist.

    Source Measured: the sequencer with and without the default arm, no attribute: both 11 cells, Yosys 0.70+290 (OSS CAD Suite 2026-11-02), slang front end, synth -flatten with ABC, ltp -noff

  6. Prove ItSolve R3.2.1: The Burst That Never StartedSimulator-graded

    Show it, don't just say it: this Silicon Lab problem is graded by the simulator.

    Open Problem R3.2.1

What People Get Wrong

Common wrong answers and why they fail. Run It shows the wrong belief failing on a real simulator.

Missed Corner

"Codes outside the enum can never appear, so the case covers everything."

The register is plain bits. An upset, a glitch or a missed reset can write any code, and with no arm for it the machine holds it.

Run It

The sequencer without a default arm, upset into 110, then with the default arm.

RUNIcarus Verilog · in your browser
Edit the code, then run it
Expected: jobs started: 1, state now 110 FAIL: the code 110 has no arm, so st_d = st_q holds it forever; the FSM is stuck
See the Fix
module seq5 (input logic clk, rst_n, start, last, output logic load, run, done);
  typedef enum logic [2:0] {IDLE, LOAD, RUN, DRAIN, DONE} state_e;   // binary 0 to 4
  (* fsm_encoding = "none" *) state_e st_q;
  state_e st_d;
  always_ff @(posedge clk or negedge rst_n)
    if (!rst_n) st_q <= IDLE;
    else        st_q <= st_d;
  always_comb begin
    st_d = st_q;                      // stay, unless an arm moves
    case (st_q)
      IDLE:  if (start) st_d = LOAD;
      LOAD:  st_d = RUN;
      RUN:   if (last) st_d = DRAIN;
      DRAIN: st_d = DONE;
      DONE:  st_d = IDLE;
      default: st_d = IDLE;         // any unnamed code: back to IDLE
    endcase
  end
  assign load = (st_q == LOAD);
  assign run  = (st_q == RUN);
  assign done = (st_q == DONE);
endmodule
module tb;
  logic clk = 0, rst_n = 1, start = 0, last = 0, load, run, done;
  int jobs = 0;
  seq5 dut (.clk(clk), .rst_n(rst_n), .start(start), .last(last), .load(load), .run(run), .done(done));
  always #5 clk = ~clk;
  always @(posedge clk) if (load) jobs++;
  initial begin
    #1 rst_n = 0; #1 rst_n = 1;
    start = 1; @(posedge clk); #1 start = 0;      // job 1 starts: LOAD
    repeat (2) @(posedge clk); #1;                // RUN
    force dut.st_q = 3'b110; #1 release dut.st_q; // an upset writes a code with no name
    start = 1; last = 1;                          // ask for another job
    repeat (8) @(posedge clk); #1;
    $display("jobs started: %0d, state now %b", jobs, dut.st_q);
    if (jobs >= 2) $display("PASS: the default arm brought the FSM back to IDLE");
    $finish;
  end
endmodule

Prints: jobs started: 3, state now 010 PASS: the default arm brought the FSM back to IDLE

Tool as Truth

"The default arm is in the RTL, so the chip recovers."

With re-encoding on, this flow built the same 11-cell netlist with and without the default arm. The recovery arm was gone.

Source Measured: the sequencer with and without the default arm, no attribute: both 11 cells, 5 flops, Yosys 0.70+290 (OSS CAD Suite 2026-11-02), slang front end, synth -flatten with ABC, ltp -noff

Half-Truth

"Use one-hot and any upset is easy to recover from."

One flipped bit gives two hot bits or none. Without logic that checks for that, a one-hot machine can stall just the same.

Source Lesson R3.2 measurement: the re-encoded one-hot netlist, simulated as gates, stayed at 00000 after an upset

Learn It, Then Prove It