← Design Verification Interviews

Interview Question · Code Writing

Covering a Stall with Transition Bins

L1 JuniorL2 Mid-Levelfunctional coveragetransitionshandshake

The Question

The interviewer asks

A sink can hold ready low for one or two cycles. Write coverage that proves both stall lengths were seen, not just that a stall happened.

Reveal the Model Answer

First reduce the handshake to one state per clock: IDLE when valid is 0, WAIT when ready is 0, and XFER when both are 1. Then write transition bins on that state. Use (IDLE, XFER => WAIT => XFER) for a one-cycle stall and (IDLE, XFER => WAIT => WAIT => XFER) for two. Anchoring the start stops a longer stall from also matching the shorter bin. A value bin on WAIT only says some stall happened.

Two stalls, then a transfer
01234validreadystateIDLEWAITXFERIDLEIDLE => WAIT => WAIT => XFER

One column per rising edge. A two-cycle stall is the order IDLE, WAIT, WAIT, XFER. A value bin on WAIT cannot tell it from a one-cycle stall.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Junior)

A good junior answer first builds one state per clock from valid and ready. It then writes transitions that start from the state before the stall.

Cross-Questions

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

WhyBreak ItCompareScale It

  1. Covering a Stall with Transition Bins
  2. WhyWhy Reduce the Handshake to One StateL1 Junior

    Why convert valid and ready into one state before writing transitions? Why not cross them?

    Reveal the Model Answer

    A transition bin follows one coverpoint's value from one sample to the next. A cross only pairs values within the same sample and has no memory of earlier ones. So valid crossed with ready shows that a stalled cycle happened, but not how long the stall lasted. One state per clock gives the transition something to follow.

    Four phases, five transition bins
    0123456789101112131415161718192021validreadystateIDLEXFERIDLEWAITXFERIDLEWAITXFERIDLEXFERWAITXFERIDLEbins covered000001000010010110101111011111coverage (%)020406080100phase A: 40%phase B: 60%phase C: 80%phase D: 100%

    Every state value appears early. Only the order of the samples decides which transition bins fill.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "A cross of valid and ready already shows the stall."

    It shows the stalled combination appeared at least once. It cannot tell a one-cycle stall from a five-cycle one.

    Source IEEE 1800-2017 §19.6

  3. CompareTransition Bins vs a Stall-Length CounterL2 Mid-Level

    Compare transition bins with a counter of stall cycles, sampled at each transfer. What does this print?

    systemverilog
    module tb;
      logic clk = 0;
      always #5 clk = ~clk;
      bit valid, ready;
      int stall_len = 0;
      covergroup cg with function sample(int n);
        len: coverpoint n { bins none = {0}; bins one = {1}; bins two = {2}; bins long_ = {[3:16]}; }
      endgroup
      cg c = new();
      // valid/ready per clock: stalls of 0, 1 and 5 cycles.
      bit [1:0] vr[$] = '{2'b11, 2'b10, 2'b11, 2'b10, 2'b10, 2'b10, 2'b10, 2'b10, 2'b11, 2'b00};
      initial begin
        foreach (vr[i]) begin
          {valid, ready} = vr[i];
          @(posedge clk);
          if (valid && !ready) stall_len++;
          if (valid && ready) begin c.sample(stall_len); $display("transfer after %0d stall cycles", stall_len); stall_len = 0; end
        end
        $display("stall-length coverage=%0.2f", c.get_coverage());
        $finish;
      end
    endmodule
    
    Reveal the Model Answer

    It prints three transfers, after 0, 1 and 5 stall cycles, then 75% coverage, because no two-cycle stall happened. A counter handles any stall length with one coverpoint, and a range bin like [3:16] covers long stalls. Transition bins grow one bin per length and get unreadable past three or four cycles. Use transitions for short, exact patterns and a counter for lengths.

    RUNVerilator (coverage build) · simulator service
    Edit the code, then run it
    Expected: transfer after 0 stall cycles transfer after 1 stall cycles transfer after 5 stall cycles stall-length coverage=75.00

    Self-Check

    Tick each key point you covered in your answer.

    Missed Corner

    "A counter needs no care; just sample it every clock."

    Sampled every clock it counts the stall growing, so a five-cycle stall also covers 1, 2, 3 and 4. Sample it once, at the transfer.

    Source Mehta, SystemVerilog Assertions and Functional Coverage (3rd ed.), §26.19 guidelines (PDF p476)

  4. Scale ItStall Coverage on Twelve PortsL2 Mid-LevelL3 Senior

    An interconnect has 12 ports with the same handshake. How do you cover stalls on all of them without 12 copies of the code?

    Reveal the Model Answer

    Write the covergroup once, in the interface or in a coverage class, and create one instance per port. Set option.per_instance = 1 so the report shows each port. Remember that the type total averages the instances. Eleven busy ports and one dead port still look like 92%, so review the per-port numbers, not just the total.

    Self-Check

    Tick each key point you covered in your answer.

    What Interviewers Listen For (Senior)

    A good senior answer asks for a report per port before trusting a combined number. It also asks which ports carry real traffic.

    Missed Corner

    "One shared instance for all ports is simpler."

    A shared instance merges the ports. A port that never stalls is hidden by the others, which is the hole you wanted to find.

    Source Spear & Tumbush, SystemVerilog for Verification (3rd ed.), §9.10.1 (PDF p392)

  5. WhyWhy the Type Figure Is an AverageL2 Mid-Level

    Why does the type figure read 50% when the instances together hit every bin?

    Reveal the Model Answer

    With merging off, each instance is scored against its own bins, and the type is the weighted average of those scores. Port A scores 50 and port B scores 50, so the type is 50. The rule asks "how well was each instance covered", not "was each bin hit somewhere". One idle instance therefore pulls the type figure down.

    FigureBins hitScore
    Instance Ab[0], b[1]50%
    Instance Bb[2], b[3]50%
    Type, default (average)each instance scored on its own50%
    Type, merge_instances = 1 (union)all four, from either instance100%

    The same samples as the root question. Only the rule for combining instances changes.

    Self-Check

    Tick each key point you covered in your answer.

    Tool as Truth

    "The average rule is a tool bug."

    It is the standard's default. Tools that show the union have merge_instances set, or show a merged database view.

    Source Mehta, Introduction to SystemVerilog, Table 15.2 (PDF p613)

  6. Break ItTransition Bins Sampled on the Wrong ClockL2 Mid-Level

    How could these transition bins still mislead you? Suppose the covergroup samples on a core clock twice as fast as the bus.

    Reveal the Model Answer

    Every bus cycle is sampled twice, so each state appears twice in a row. A one-cycle stall then looks like WAIT => WAIT, and it covers the two-cycle bin instead. Transition bins count samples, not bus cycles. Sample on the bus clock, or call sample() once per bus cycle.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "Sampling more often only makes coverage more accurate."

    Transitions are counted between consecutive samples. Extra samples insert repeated states, which changes which sequences appear. The twin covers the wrong bin.

    Run It

    A one-cycle stall, sampled twice per bus cycle.

    RUNVerilator (coverage build) · simulator service
    Edit the code, then run it
    Expected: one_stall=0.00 two_stall=100.00 FAIL: two samples per bus cycle turned the one-cycle stall into a two-cycle one
    See the Fix
    module tb;
      typedef enum bit [1:0] {IDLE, WAIT, XFER} st_t;
      st_t st;
      st_t bus[$] = '{IDLE, WAIT, XFER, IDLE};   // one entry per bus cycle: a one-cycle stall
      covergroup one_cg;
        cp: coverpoint st { bins one_stall = (IDLE, XFER => WAIT => XFER); }
      endgroup
      covergroup two_cg;
        cp: coverpoint st { bins two_stall = (IDLE, XFER => WAIT => WAIT => XFER); }
      endgroup
      one_cg one = new();
      two_cg two = new();
      initial begin
        foreach (bus[i]) begin
          st = bus[i];
          repeat (1) begin one.sample(); two.sample(); end   // the fix: one sample per bus cycle
        end
        $display("one_stall=%0.2f two_stall=%0.2f", one.get_coverage(), two.get_coverage());
        if (two.get_coverage() > 0) $display("FAIL: two samples per bus cycle turned the one-cycle stall into a two-cycle one");
        else $display("PASS: one sample per bus cycle covers the one-cycle stall only");
      end
    endmodule
    

    Prints: one_stall=100.00 two_stall=0.00 PASS: one sample per bus cycle covers the one-cycle stall only

What People Get Wrong

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

Misconception

"A bin on WAIT proves the stall cases were covered."

A value bin counts cycles, not sequences. The twin has only a one-cycle stall and the value bins still read 100%.

Run It

A one-cycle stall, then back-to-back transfers.

RUNVerilator (coverage build) · simulator service
Edit the code, then run it
Expected: coverage=100.00 FAIL: the value bins read 100% but a two-cycle stall never happened
See the Fix
module tb;
  typedef enum bit [1:0] {IDLE, WAIT, XFER} st_t;
  st_t st;
  bit valid, ready;
  // valid/ready per clock: a one-cycle stall, then back-to-back transfers.
  bit [1:0] vr[$] = '{2'b00, 2'b10, 2'b11, 2'b11, 2'b11, 2'b00};
  covergroup cg;
    cp: coverpoint st {
      bins one_stall = (IDLE, XFER => WAIT => XFER);
      bins two_stall = (IDLE, XFER => WAIT => WAIT => XFER);
      bins back2back = (XFER => XFER);
    }
  endgroup
  cg c = new();
  initial begin
    foreach (vr[i]) begin
      {valid, ready} = vr[i];
      st = !valid ? IDLE : (!ready ? WAIT : XFER);
      c.sample();
    end
    $display("coverage=%0.2f", c.get_coverage());
    if (c.get_coverage() == 100.0) $display("FAIL: the value bins read 100%% but a two-cycle stall never happened");
    else $display("PASS: the transition bins show the two-cycle stall is still missing");
  end
endmodule

Prints: coverage=66.67 PASS: the transition bins show the two-cycle stall is still missing

Missed Corner

"(WAIT => XFER) counts only one-cycle stalls."

It matches the last two samples of any stall. The twin has only a two-cycle stall, and the one-cycle bin is covered anyway.

Run It

Only a two-cycle stall happens.

RUNVerilator (coverage build) · simulator service
Edit the code, then run it
Expected: coverage=100.00 with only a two-cycle stall FAIL: (WAIT => XFER) also matched the end of the two-cycle stall, so one_stall looks covered
See the Fix
module tb;
  typedef enum bit [1:0] {IDLE, WAIT, XFER} st_t;
  st_t st;
  st_t trace_[$] = '{IDLE, WAIT, WAIT, XFER, IDLE};   // only a two-cycle stall happens
  covergroup cg;
    cp: coverpoint st {
      bins one_stall = (IDLE, XFER => WAIT => XFER);          // the fix: anchor the start
      bins two_stall = (IDLE, XFER => WAIT => WAIT => XFER);
    }
  endgroup
  cg c = new();
  initial begin
    foreach (trace_[i]) begin st = trace_[i]; c.sample(); end
    $display("coverage=%0.2f with only a two-cycle stall", c.get_coverage());
    if (c.get_coverage() == 100.0) $display("FAIL: (WAIT => XFER) also matched the end of the two-cycle stall, so one_stall looks covered");
    else $display("PASS: anchored bins report the one-cycle stall as missing");
  end
endmodule

Prints: coverage=50.00 with only a two-cycle stall PASS: anchored bins report the one-cycle stall as missing

Tool as Truth

"Repetition like WAIT [*2] is not legal in transition bins; my simulator rejects it."

The standard allows repetition in transitions. Some tools, including this site's coverage build, reject it, so spell the sequence out.

Source IEEE 1800-2017 §19.5.2; ASIC Point engine notes (Verilator master b8e7f1e coverage build)

Learn It, Then Prove It