Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • RTL Foundations36
  • Combinational Design25
  • Sequential Design27
  • FSM & Control27
  • Timing & Clocking39
  • Reset & CDC34
  • Memory & Buses57
  • Advanced RTL28
  • Senior & Interview76
    • Low-Power RTL Hub
    • DFT and Scan Hub
    • Physical Awareness Hub
    • Waveform Debug Hub
    • STA and ECO Hub
    • RTL-to-Implementation Closure
    • DFT-Ready RTL
    • Capstone: Resilient Streaming Peripheral
    • Principal Design Review
    • Senior RTL Interview Questions & Model Answers
      • Interview: Combinational and Arithmetic
      • Interview: Sequential and Pipelines
      • Interview: FSM and Control
        • Choosing an FSM Encoding from Timing
        • Moore vs Mealy Outputs
        • Proving an FSM Correct
        • The Controller and Datapath Split
      • Interview: Timing and STA
      • Interview: CDC and Reset
      • Interview: Memories and Buses
      • Interview: Low-Power Design
      • Interview: Debug and ECO
      • Interview: Coding and Whiteboard
  • Glossary and Notation
Loading lesson

‹Interview: FSM and Control
  1. Topics
  2. RTL Design
  3. Senior & Interview
  4. Senior RTL Interview Questions & Model Answers
  5. Interview: FSM and Control
  6. Proving an FSM Correct

Part 9 · Senior · Senior

Proving an FSM Correct

Reachability, return-to-IDLE, and illegal-code checks on a transfer controller, all procedural.

On this page

    Why this matters

    "Simulation passes" covers reset and a few arcs. A wait state that holds for a missing acknowledge is a legal stay, so any no-deadlock claim must state what the inputs are assumed to do.

    After this lesson you can: check reachability, bounded return to IDLE and illegal-code recovery for a small FSM, and state the input assumptions behind each claim.

    The question

    "Simulation passes. How do you know this FSM is correct?" The interviewer is asking for the properties beyond directed tests, and how each one is checked.

    The complete answer names three properties, gives the check for each, and states the input assumptions the checks rest on.

    Model answer

    Three important structural checks are reachability, return to IDLE under explicit input assumptions, and illegal-code recovery. They are not a complete functional proof: also check output legality and the transaction contract. Here grant must be high only in SEND or WAIT, never while IDLE, arbitrating, retrying or DONE.

    Property

    Procedural check

    SVA shape

    Reachability

    Drive a legal input prefix to each state

    Cover each state

    No deadlock

    From each state, return to IDLE within N cycles

    Assert return under input assumptions

    Unused-code RTL recovery

    Force unused codes in this RTL encoding, expect IDLE

    Assert recovery under an explicit fault model; check mapped implementation separately

    State diagram: IDLE stays on not req and goes to ARB on req. ARB goes to SEND. SEND and WAIT, both with grant = 1, go to DONE on ack, to RETRY on err, and to WAIT otherwise. RETRY goes back to SEND. DONE returns to IDLE.
    The xfer_ctrl next-state logic from the testbench below. Grant is high only in SEND and WAIT; ack beats err; every path reaches IDLE through DONE once ack arrives.

    Read each arc as one line of the case statement. The three properties are statements about this picture.

    • Reachability: every state has an arc into it from IDLE through legal inputs.

    • Return to IDLE: from SEND, WAIT or RETRY the only way home is ack, then DONE. That is why the no-deadlock check assumes ack eventually arrives.

    • Unused codes 6 and 7 are not drawn; the default branch sends them to IDLE.

    No-deadlock claims always rest on input assumptions. A WAIT state that holds for a missing acknowledge is a legal stay, not a bug. The check drives the awaited input and requires the return.

    Fault model: the bench forces unused binary codes 6 and 7 with req low. Faults into legal states are undetected by a default, and synthesis can optimize/re-encode recovery. Safety requirements need supported safe-FSM controls, report inspection and implementation-level fault checks; see illegal states and recovery.

    design.sv
    1
    2
    3
    4
    5
    6
    // snippet
    // The two properties this lesson proves procedurally, in SVA shape:
    //   cp_reached_done: cover property (@(posedge clk) state == DONE);
    //   ap_return_home:  assert property (@(posedge clk) (state != IDLE) |-> s_eventually (state == IDLE));
    // Liveness (s_eventually) needs fairness assumptions on the inputs, so the
    // testbench below drives those inputs explicitly instead.

    Interview

    Reach states legally, check bounded return under stated peer assumptions, and test unused-code RTL behavior under a named fault model. Qualify implementation recovery separately.

    Follow-ups

    • Simulation or formal for the no-deadlock claim? Simulation shows return paths exist. Formal proves them for all inputs under the stated assumptions. Use both where the FSM guards safety.

    • What exactly is assumed of the inputs? That an awaited response eventually arrives. Without that fairness, any wait state is a correct infinite stay.

    • Which coverage metric backs the claim? State and transition coverage. The checks below visit all six states and every return arc back to IDLE.

    Common wrong answers

    • "Reset was tested, so the FSM is fine." Reset covers one state. The other five, and every arc between them, are still unproven.

    • "Full line coverage proves the FSM." Line coverage can pass while a state is never visited. State and transition coverage are the relevant metrics.

    • "The default guarantees silicon recovery." It specifies RTL unused-code behavior. Review synthesis safe-FSM settings and implemented recovery; neither a default nor unique/default diagnoses faults into another legal state.

    Common pitfall

    Forcing the state register reaches states quickly but hides reset and encoding bugs, since it visits states the real machine may never enter. Reach each state through legal inputs first, and reserve forcing for illegal-code injection only.

    Check yourself From WAIT with acknowledge held high, how many cycles to IDLE here, and what does the bound of 6 cover?

    Two cycles: WAIT to DONE, then DONE to IDLE. The bound of 6 covers the longest return path (three cycles from ARB or RETRY) with margin.

    Check yourself Why is the acknowledge assumption written down instead of left implicit?

    Without it the liveness claim is false: a missing acknowledge holds WAIT forever by design. Stating the assumption turns a hang into a checked contract on the peer block.

    Testbench

    A 6-state transfer controller plus procedural checks for reachability, bounded return to IDLE, and illegal-code recovery.

    xfer_ctrl.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    // State codes are named once, above the design, so the testbench can reuse them.
    typedef enum logic [2:0] {IDLE, ARB, SEND, WAIT, RETRY, DONE} state_t;
    
    module xfer_ctrl (
      input  logic       clk, rst_n, req, ack, err,
      output logic       grant, busy,
      output logic [2:0] state_o
    );
      state_t state, next_state;
    
      always_comb begin
        next_state = state;
        grant = 1'b0;
        busy = 1'b1;
        case (state)
          IDLE:  begin busy = 1'b0; if (req) next_state = ARB; end
          ARB:   next_state = SEND;
          SEND:  begin grant = 1'b1; if (ack) next_state = DONE; else if (err) next_state = RETRY; else next_state = WAIT; end
          WAIT:  begin grant = 1'b1; if (ack) next_state = DONE; else if (err) next_state = RETRY; else next_state = WAIT; end
          RETRY: next_state = SEND;
          DONE:  begin busy = 1'b0; next_state = IDLE; end
          default: next_state = IDLE;
        endcase
      end
    
      always_ff @(posedge clk or negedge rst_n)
        if (!rst_n) state <= IDLE;
        else        state <= next_state;
    
      // Named-state readout for the testbench: literals only, so no enum-to-vector cast.
      always_comb begin
        case (state)
          IDLE:  state_o = 3'd0;
          ARB:   state_o = 3'd1;
          SEND:  state_o = 3'd2;
          WAIT:  state_o = 3'd3;
          RETRY: state_o = 3'd4;
          DONE:  state_o = 3'd5;
          default: state_o = 3'd7;              // illegal is observable, never disguised as IDLE
        endcase
      end
    endmodule
    

    Remember

    • Reach each state through legal inputs first; forcing the state register hides reset and encoding bugs.

    • Liveness holds only under the assumption that an awaited response eventually arrives.

    • State and transition coverage back the claim; line coverage does not.

    Rule of thumb: a default branch specifies RTL behaviour for unused codes, not silicon recovery.

    How this was checked

    Bench run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It forces unused binary codes 6 and 7 with req low. Faults into legal states are not detected by a default, and synthesis can optimise or re-encode the recovery logic.

    Keep going

    • Practice: FSM verification strategy: coverage and assertions for corner cases

    • Practice: FSM deadlock and livelock debugging

    • Interview: run a mock interview on this topic

    Related topics

    • Choosing and verifying

    • Illegal states and recovery

    Previous

    323 / 350

    Next: The Controller and Datapath Split

    Up next

    Up nextThe Controller and Datapath Split

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Choosing an FSM Encoding from Timing