← Design Verification Interviews

Interview Question · Waveform

A Covergroup That Counted Idle Cycles

L1 JuniorL2 Mid-Levelfunctional coveragesamplinghandshake

The Question

The interviewer asks

A covergroup on the packet kind is clocked with @(posedge clk). The trace shows only WRITE and CONFIG transfers, yet READ is covered. Why?

Reveal the Model Answer

A clocked covergroup samples at every clock edge, including idle cycles. While valid is 0, the bus parks kind at 0, and 0 is READ. So the idle cycles cover READ, though no read ever moved. Sample only on a transfer instead. Call sample() when valid && ready is true, or add iff (valid && ready) to the coverpoint.

Two transfers, three idle edges
01234edge (ns)515253545validkindREADWRITECONFIGREADclocked sampleREADWRITECONFIGREADtransfer sample-WRITECONFIG-idle edge covers READ

The clocked covergroup reads 100%. Sampling on transfers reads 66.67% and shows READ is missing.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Junior)

A good junior answer points at the idle edges in the trace. It then moves the sample to the transfer, when valid and ready are both 1.

Cross-Questions

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

WhyWhat IfProve ItCompareScale It

  1. A Covergroup That Counted Idle Cycles
  2. CompareClocked Covergroups vs Calling SampleL1 Junior

    Compare a clocked covergroup with one you sample by calling sample(). When is a clocking event the right choice?

    Reveal the Model Answer

    A clocking event fits values that matter every cycle, such as an FSM state or a FIFO level. Calling sample() fits events, such as transfers, packets and register writes. A class-based bench usually calls sample() from the code that sees each transaction. A clocked covergroup on event data counts idle cycles as if they were events.

    Two covergroups watch the same link
    012345678validreadykindREADWRITEREADCONFIGREADWRITEREADcg_every bins100110111cg_xfer bins000010011cg_xfer (%)033.3366.67t=35t=55t=75

    Both covergroups read the same kind. Only the one sampled on transfers reports what the link carried.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Clocked covergroups are always wrong."

    For state that lives every cycle, a clocked covergroup is the natural model. The error is using it for things that happen only on some cycles.

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

  3. What IfWhat a Clocked Covergroup Sees at the EdgeL2 Mid-Level

    Asked if they keep a clocked covergroup

    What if the covergroup is clocked on the same edge that updates the flop it covers? What does this print?

    systemverilog
    module tb;
      logic clk = 0;
      always #5 clk = ~clk;
      logic [1:0] q = 0;
      always_ff @(posedge clk) q <= q + 1;     // q is 0 only before the first edge
      covergroup cg @(posedge clk);
        cp: coverpoint q { bins zero = {0}; }
      endgroup
      cg c = new();
      initial begin
        repeat (3) @(posedge clk);
        #1 $display("q=%0d zero-bin coverage=%0.2f", q, c.get_coverage());
        $finish;
      end
    endmodule
    
    Reveal the Model Answer

    It prints zero-bin coverage=100.00. The covergroup samples when the edge event fires. The flop's nonblocking update lands later in that time step, so the sample sees the value from before the edge. That is the same value the flop held during the previous cycle. Plan bins with that one-cycle view in mind.

    The Covergroup Samples Before the Update
    012edge (ns)51525q seen by the covergroup012q after the edge123zero binhit-

    One column per rising edge. At 5 ns the sample sees 0, the value from before the edge, so the zero bin is covered.

    RUNVerilator (coverage build) · simulator service
    Edit the code, then run it
    Expected: q=3 zero-bin coverage=100.00

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "It samples the new value, because the flop updates on that edge."

    Nonblocking updates happen in the NBA region, after the edge event wakes the covergroup. The run covers the bin for 0, a value held only before the first edge.

    Source IEEE 1800-2017 §4.4.2.4

  4. Prove ItSolve 6.1.1: Count the Real TransfersSimulator-graded

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

    Open Problem 6.1.1

  5. Scale ItCoverage That Slowed the RegressionL2 Mid-LevelL3 Senior

    Your regression got 30% slower after the coverage model was added. What do you look at first?

    Reveal the Model Answer

    Look first for covergroups that sample every clock but only need events. Then look for large crosses and for coverage inside objects that are created by the thousand. Sample once per transaction, guard coverpoints with iff, and cut crosses down to the cells in the plan. Keep a switch that turns coverage off in soak tests that are not measuring it.

    Self-Check

    Tick each key point you covered in your answer.

    What Interviewers Listen For (Senior)

    A good senior answer measures before changing anything, for example the run time with coverage off. It says which tests still need coverage on for sign-off.

    Wrong Altitude

    "Turn coverage off in the regression and turn it on before tape-out."

    Coverage closure needs the data all along. Holes found at the end leave no time to write the missing tests.

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

  6. WhyA Covergroup Inside the Transaction ClassL2 Mid-LevelL3 Senior

    A teammate embeds the covergroup in the transaction class, and the test creates a million transactions. What happens?

    Reveal the Model Answer

    Each transaction object builds its own covergroup instance in its constructor. That is a million covergroup instances, each sampled once or twice, and memory and run time grow with them. Put covergroups in components that live for the whole run, such as a coverage collector, and pass each transaction to them.

    Self-Check

    Tick each key point you covered in your answer.

    What Interviewers Listen For (Senior)

    A good senior answer turns this into a review rule. Covergroups live in components that last the whole run, never in transactions or sequences.

    Misconception

    "The covergroup is shared by all objects of the class, like a static member."

    An embedded covergroup is an instance member. Each object that runs its constructor creates its own instance.

    Source IEEE 1800-2017 §19.4

What People Get Wrong

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

Misconception

"A covergroup clocked on posedge clk counts each transfer."

It counts each edge. The idle value lands in a real bin, so the twin reads 100% with no READ transfer at all.

Run It

One WRITE and one CONFIG transfer, with idle cycles around them.

RUNVerilator (coverage build) · simulator service
Edit the code, then run it
Expected: kind coverage=100.00 after one WRITE and one CONFIG transfer FAIL: the clocked covergroup counted idle cycles, so READ looks covered though no read moved
See the Fix
module tb;
  typedef enum bit [1:0] {READ, WRITE, CONFIG} kind_t;
  logic clk = 0;
  always #5 clk = ~clk;
  bit valid = 0, ready = 1;
  kind_t kind = READ;            // the idle bus parks kind at 0, which is READ
  covergroup cg;                 // the fix: sample only on a transfer
    cp: coverpoint kind;
  endgroup
  cg c = new();
  always @(posedge clk) if (valid && ready) c.sample();
  initial begin
    @(negedge clk) begin valid = 1; kind = WRITE; end
    @(negedge clk) kind = CONFIG;
    @(negedge clk) begin valid = 0; kind = READ; end
    repeat (2) @(negedge clk);
    $display("kind coverage=%0.2f after one WRITE and one CONFIG transfer", c.get_coverage());
    if (c.get_coverage() == 100.0) $display("FAIL: the clocked covergroup counted idle cycles, so READ looks covered though no read moved");
    else $display("PASS: sampling on transfers shows READ never moved");
    $finish;
  end
endmodule

Prints: kind coverage=66.67 after one WRITE and one CONFIG transfer PASS: sampling on transfers shows READ never moved

Buzzword Without Mechanism

"Use type_option.strobe = 1 and the idle cycles go away."

Strobe moves the sample to the end of the time step; it still samples every edge. On this site's coverage build it is also ignored.

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

Learn It, Then Prove It