← Design Verification Interviews

Interview Question · Code Reading

A Scoreboard Across a Mid-Test Reset

L2 Mid-Leveluvmresetscoreboard

The Question

The interviewer asks

A test asserts reset while a result is inside the DUT, then sends more traffic. This scoreboard ignores reset. What does it report?

systemverilog
function void write_out(incr_result r);
  bit [7:0] want;
  if (r.reset) return;   // reset is the design's business
  if (expected.size() == 0) begin
    `uvm_error("SB", $sformatf("y=%0d with no input waiting", r.y))
    return;
  end
  want = expected.pop_front();
  checked++;
  if (r.y != want) begin
    mismatches++;
    `uvm_error("SB", $sformatf("y=%0d, expected %0d", r.y, want))
  end
endfunction
Reveal the Model Answer

It reports a false mismatch. Reset destroyed the result for input 32, but its prediction, 33, stays at the front of the queue. The first result after reset, 81, is compared with 33. Then the prediction 81 waits forever, and the end check reports it missing. The scoreboard must treat reset as an event: count its open predictions as canceled and empty the queue.

What the queue holds through reset
01234time (ns)35505570105event17 checkedreset ondriver drops 48reset off81 arrivesqueue, ignoring reset3333, 8181 (vs 33: error)queue, flushed on reset33emptyempty, then 81empty (81 matches)the result 33 was destroyed by reset

The prediction outlives the result it predicted.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Mid-Level)

A good mid-level answer traces the queue through the reset. It predicts both errors: the false mismatch and the prediction left at the end.

Cross-Questions

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

WhyWhat IfProve ItBreak ItCompareScale It

  1. A Scoreboard Across a Mid-Test Reset
  2. What IfThe Driver's Item When Reset ArrivesL2 Mid-Level

    Asked if they fix only the scoreboard

    What should the driver do with the item it was driving when reset arrived?

    Reveal the Model Answer

    Stop driving it and drop it without calling item_done(). stop_sequences() has already emptied the sequencer's request FIFO. An item_done() after that finds no outstanding request, and uvm-core stops the run with a fatal error.

    What Each Stop Removes, with an Item in the DriverL has queued requests and its third item, L2, is in the driver. Arrows are calls and the item's path. The request FIFO holds the item the driver has taken but not finished.
    What each stop removes, with an item in the driverL has queued requests and its third item, L2, is in the driver. Arrows are calls and the item's path. The request FIFO holds the item the driver has taken but not finished. caller Test Calls the stop sequence Sequence L Body in UVM_BODY queue Arbitration queue L's waiting requestsand locks queue Request FIFO Holds L2 untilitem_done driver Driver Working on L2 until50 ns seq.kill()sqr.stop_sequences()requestsL2flush
    Read the diagram as text
    • Test: Calls the stop
    • Sequence L: Body in UVM_BODY
    • Arbitration queue: L's waiting requests and locks
    • Request FIFO: Holds L2 until item_done
    • Driver: Working on L2 until 50 ns
    • Test → Sequence L: seq.kill()
    • Test → Arbitration queue: sqr.stop_sequences()
    • Sequence L → Arbitration queue: requests
    • Request FIFO → Driver: L2
    • Arbitration queue → Request FIFO: flush

    A kill removes one sequence's queued work and leaves the driver's item alone. stop_sequences() also empties the request FIFO under a busy driver, so a later item_done() finds nothing.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "Call item_done() anyway, so the sequence is not left waiting."

    The sequence was killed and the request removed. item_done() with no outstanding request is the fatal error SQRBADITMDN.

    Source uvm-core 2020.3.1 (IEEE 1800.2 library) source, seq/uvm_sequencer.svh (item_done: SQRBADITMDN)

  3. WhyWhy Item Done After a Flush Is FatalL2 Mid-Level

    Why does an item_done() after stop_sequences() stop the run instead of being ignored?

    Reveal the Model Answer

    item_done() tells the sequencer that the driver finished the oldest outstanding request. stop_sequences() has removed every request, so nothing is left to finish. The sequencer treats that as a broken handshake and reports a fatal error. So on reset the driver drops its item without calling item_done().

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Always call item_done(), or the sequence hangs."

    Normally yes, but after stop_sequences() the request is gone. A late call has nothing to match.

    Source uvm-core 2020.3.1 (IEEE 1800.2 library) source, seq/uvm_sequencer.svh (item_done: SQRBADITMDN)

  4. CompareReset as an Event vs a Phase JumpL3 Senior

    Compare handling reset inside run_phase with jumping back to reset_phase.

    Reveal the Model Answer

    Inside run_phase, each component watches reset and cleans up its own state. A phase jump re-runs the reset and main phases for the whole phase domain. That only works if every component, including bought VIP, restarts cleanly from those phases. Many teams avoid phase jumping for that reason and treat reset as an event.

    Two Branches Between Start of Simulation and ExtractArrows show which phase starts after which. In this measured run the test objects in run_phase for 10 ns and in reset_phase for 20 ns. It objects in main_phase for 30 ns and in shutdown_phase for 10 ns.
    Two branches between start of simulation and extractArrows show which phase starts after which. In this measured run the test objects in `run_phase` for 10 ns and in `reset_phase` for 20 ns. It objects in `main_phase` for 30 ns and in `shutdown_phase` for 10 ns. component start_of_simulation Last zero-time step component run_phase Objection 0-10 ns component pre_reset ..post_reset reset objection 0-20ns component pre_configure ..post_main configure takes 0 ns;main 20-50 ns component pre_shutdown ..post_shutdown shutdown objection50-60 ns component extract Starts when bothbranches end starts at 0starts at 0ends at 20ends at 50ends at 60ends at 60
    1. Both branches start0 ns (illustrative)

      run_phase and pre_reset_phase start in the same time step. pre_reset has no objection, so it ends at once and reset_phase starts.

      Follow the calls, values and waits
      1. call start run_phase: start_of_simulation → run_phase
      2. call start pre_reset: start_of_simulation → pre_reset .. post_reset
      3. Waiting: run_phase — test objects until 10 ns
      4. Waiting: pre_reset .. post_reset — reset objection until 20 ns
    2. run_phase has dropped, but waits10 ns (illustrative)

      The test drops its run_phase objection at 10 ns. The run phase does not end yet: it shares its successor, extract, with the schedule, and waits for it.

      Follow the calls, values and waits
      1. Waiting: run_phase — objections 0; waits for the schedule
      2. Released: run_phase
    3. Reset ends; main begins20 ns (illustrative)

      The reset objection drops at 20 ns. post_reset and the three configure phases have no objection, so they pass in zero time and main_phase starts at 20 ns.

      Follow the calls, values and waits
      1. call start pre_configure: pre_reset .. post_reset → pre_configure .. post_main
      2. Waiting: pre_configure .. post_main — main objection until 50 ns
      3. Released: pre_reset .. post_reset
    4. Main ends; shutdown begins50 ns (illustrative)

      The main objection drops at 50 ns. post_main and pre_shutdown pass in zero time, and shutdown_phase holds until 60 ns.

      Follow the calls, values and waits
      1. call start pre_shutdown: pre_configure .. post_main → pre_shutdown .. post_shutdown
      2. Waiting: pre_shutdown .. post_shutdown — shutdown objection until 60 ns
      3. Released: pre_configure .. post_main
    5. Both branches end60 ns (illustrative)

      The shutdown objection drops at 60 ns. The run phase and post_shutdown both end at 60 ns, and extract starts.

      Follow the calls, values and waits
      1. return post_shutdown ended: pre_shutdown .. post_shutdown → extract
      2. return run_phase ended: run_phase → extract
      3. Released: pre_shutdown .. post_shutdown
      4. Released: run_phase
    Read the diagram as text
    • start_of_simulation: Last zero-time step
    • run_phase: Objection 0-10 ns
    • pre_reset .. post_reset: reset objection 0-20 ns
    • pre_configure .. post_main: configure takes 0 ns; main 20-50 ns
    • pre_shutdown .. post_shutdown: shutdown objection 50-60 ns
    • extract: Starts when both branches end
    • start_of_simulation → run_phase: starts at 0
    • start_of_simulation → pre_reset .. post_reset: starts at 0
    • pre_reset .. post_reset → pre_configure .. post_main: ends at 20
    • pre_configure .. post_main → pre_shutdown .. post_shutdown: ends at 50
    • pre_shutdown .. post_shutdown → extract: ends at 60
    • run_phase → extract: ends at 60

    The run phase ends at 60 ns, although its own objection dropped at 10 ns.

    Self-Check

    Tick each key point you covered in your answer.

    What Interviewers Listen For (Senior)

    A good senior answer names the condition for a phase jump: every component must support running its phases again. It treats reset as an event when bought VIP cannot do that.

  5. Break ItA Reset Test That Tests No ResetL2 Mid-LevelL3 Senior

    How could a reset test pass without testing reset at all?

    Reveal the Model Answer

    If reset arrives when nothing is in flight, the scoreboard has nothing to cancel, and even the broken scoreboard passes. Check that at least one prediction was canceled and one result was checked after reset. A ledger such as inputs = checked + canceled makes this visible in every run.

    Accepted Work and an Offered Item Need Different TreatmentThis incrementer discards unfinished results on reset. The scoreboard cancels accepted work; the driver abandons an offer that never transferred.
    Accepted work and an offered item need different treatmentThis incrementer discards unfinished results on reset. The scoreboard cancels accepted work; the driver abandons an offer that never transferred. accepted Accepted x = 32 45 ns: valid and readyare high prediction Expected y = 33 One result still owed ledger Canceled: 1 55 ns: removeexpected 33 offered Offered x = 48 No ready/validtransfer driver Drop the offer Lower valid; noitem_done after flush recovery New x = 80 Accepted at 85 ns;81 checked at 105 ns predictresetresetrestart
    1. Accept and predict45 ns (illustrative)

      The input monitor observes 32 being accepted. The model predicts 33, so accepted work has an entry in the scoreboard.

      Follow the calls, values and waits
      1. data expected 33: Accepted x = 32 → Expected y = 33
      2. Waiting: Expected y = 33 — Waiting for result 33
    2. Distinguish the offer50 ns (illustrative)

      The driver is offering 48 when reset rises. No transfer accepted 48, so it creates no prediction and contributes nothing to the accepted count.

      Follow the calls, values and waits
      1. Waiting: Offered x = 48 — No acceptance yet
    3. Apply this reset rule55 ns (illustrative)

      Reset discards the pending DUT result. The scoreboard cancels 33 once; the driver abandons 48 while the test flushes the sequencer.

      Follow the calls, values and waits
      1. data expected 33: Expected y = 33 → Canceled: 1
      2. data offer 48: Offered x = 48 → Drop the offer
      3. Released: Expected y = 33
      4. Released: Offered x = 48
    4. Demonstrate recovery85 ns (illustrative)

      After reset falls at 70 ns, a new item 80 is accepted.

      Follow the calls, values and waits
      1. data new item 80: Drop the offer → New x = 80
    5. Check the new result105 ns (illustrative)

      Its result 81 is checked at 105 ns. Neither cancellation nor dropping 48 counts as a successful comparison.

    Read the diagram as text
    • Accepted x = 32: 45 ns: valid and ready are high
    • Expected y = 33: One result still owed
    • Canceled: 1: 55 ns: remove expected 33
    • Offered x = 48: No ready/valid transfer
    • Drop the offer: Lower valid; no item_done after flush
    • New x = 80: Accepted at 85 ns; 81 checked at 105 ns
    • Accepted x = 32 → Expected y = 33: predict
    • Expected y = 33 → Canceled: 1: reset
    • Offered x = 48 → Drop the offer: reset
    • Drop the offer → New x = 80: restart

    Arrows show bookkeeping and restart actions. Together with 16 → 17 before reset, the final ledger is 3 accepted = 2 checked + 1 canceled. Offered 48 is outside that ledger.

    Self-Check

    Tick each key point you covered in your answer.

  6. Prove ItSolve U10.2.1: The Scoreboard That Remembered Through ResetSimulator-graded

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

    Open Problem U10.2.1

  7. Scale ItIndependent Reset DomainsL3 SeniorL4 Staff

    Scale it: a chip has three reset domains that reset independently. How does the bench handle reset now?

    Reveal the Model Answer

    Each agent and scoreboard listens to the reset of its own domain. A reset in one domain cancels only the predictions for traffic inside that domain. Traffic that crosses domains needs a written rule: is a request lost, retried or completed? Put that rule in the test plan and check it.

    Self-Check

    Tick each key point you covered in your answer.

    What Interviewers Listen For (Staff)

    A good staff answer first asks the architects for the rule on traffic that crosses reset domains. Then it maps each scoreboard to one reset domain.

What People Get Wrong

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

Misconception

"Reset is the design's business; the scoreboard does not need to know."

The design drops what it held, but the scoreboard still expects it. The twin compares the first result after reset with a prediction from before it.

Run It

The lesson U10.2 bench with a scoreboard that ignores reset.

RUNVerilator + UVM 2020.3.1 · simulator service
Edit the code, then run it
Expected among the UVM messages: 35 ns scoreboard y=17 checked 50 ns test reset on 55 ns driver reset: drops x=48, no item_done 70 ns test reset off checked 2, mismatches 1, still waiting 1 FAIL: the scoreboard kept the prediction for a result that reset destroyed
See the Fix
`timescale 1ns/1ns
`include "uvm_macros.svh"
import uvm_pkg::*;

// The incrementer from U7 with a reset. It captures x at edge k (valid and ready both 1), is busy
// for two edges, and drives y = x + 1 with out_valid after edge k+1; monitors see it at edge k+2.
// Reset clears the busy count and drops a result still inside. STUCK_AFTER > 0 makes ready stay
// low for good after that many accepts (a hung design).
interface incr_if (input logic clk);
  logic       rst = 0;
  logic       valid = 0;
  logic [7:0] x = 0;
  logic       ready;
  logic       out_valid = 0;
  logic [7:0] y = 0;
endinterface

module incr_pipe #(parameter int STUCK_AFTER = 0) (incr_if bus);
  logic [1:0] busy = 0;
  logic       staged = 0;
  logic [7:0] stage = 0;
  int         taken = 0;
  assign bus.ready = (busy == 0) && !bus.rst && !(STUCK_AFTER > 0 && taken >= STUCK_AFTER);
  always @(posedge bus.clk) begin
    bus.out_valid <= staged && !bus.rst;
    bus.y         <= stage;
    staged        <= bus.valid && bus.ready;
    stage         <= bus.x + 1;
    if (bus.rst)                     busy <= 0;
    else if (bus.valid && bus.ready) begin busy <= 2; taken <= taken + 1; end
    else if (busy != 0)              busy <= busy - 1;
  end
endmodule

function automatic void log_event(string who, string what);
  $display("%3d ns  %-11s%s", $time, who, what);
endfunction

class incr_item extends uvm_sequence_item;
  `uvm_object_utils(incr_item)
  bit [7:0] x;
  function new(string name = "incr_item"); super.new(name); endfunction
endclass

// What the monitor sees coming out; reset = 1 marks an edge with reset held.
class incr_result extends uvm_sequence_item;
  `uvm_object_utils(incr_result)
  bit [7:0] y;
  bit       reset;
  function new(string name = "incr_result"); super.new(name); endfunction
endclass

// Sends the values in the queue, one item each.
class incr_seq extends uvm_sequence #(incr_item);
  `uvm_object_utils(incr_seq)
  bit [7:0] values[$];
  string    lane = "";
  function new(string name = "incr_seq"); super.new(name); values = '{16, 32, 48}; endfunction
  task body();
    foreach (values[i]) begin
      incr_item it = incr_item::type_id::create("it");
      start_item(it);
      it.x = values[i];
      finish_item(it);
    end
  endtask
endclass

class incr_driver extends uvm_driver #(incr_item);
  `uvm_component_utils(incr_driver)
  virtual incr_if vif;
  function new(string name, uvm_component parent); super.new(name, parent); endfunction
  task run_phase(uvm_phase phase);
    forever begin
      @(negedge vif.clk);
      if (vif.rst) begin                     // no new item while reset is held
        vif.valid <= 0;
        continue;
      end
      seq_item_port.try_next_item(req);
      if (req == null) begin
        vif.valid <= 0;
        continue;
      end
      vif.valid <= 1;
      vif.x     <= req.x;
      do @(posedge vif.clk); while (!vif.ready && !vif.rst);
      if (vif.rst) begin                     // reset: the sequencer has forgotten this item
        vif.valid <= 0;
        log_event("driver", $sformatf("reset: drops x=%0d, no item_done", req.x));
        continue;
      end
      seq_item_port.item_done();
    end
  endtask
endclass

class incr_monitor extends uvm_monitor;
  `uvm_component_utils(incr_monitor)
  virtual incr_if vif;
  uvm_analysis_port #(incr_item)   in_ap;
  uvm_analysis_port #(incr_result) out_ap;
  function new(string name, uvm_component parent);
    super.new(name, parent);
    in_ap  = new("in_ap", this);
    out_ap = new("out_ap", this);
  endfunction
  task run_phase(uvm_phase phase);
    forever begin
      @(posedge vif.clk);
      if (vif.rst) begin                       // reset is part of what happened
        incr_result r = incr_result::type_id::create("r");
        r.reset = 1;
        out_ap.write(r);
        continue;
      end
      if (vif.valid && vif.ready) begin
        incr_item t = incr_item::type_id::create("t");
        t.x = vif.x;
        in_ap.write(t);
      end
      if (vif.out_valid) begin
        incr_result r = incr_result::type_id::create("r");
        r.y = vif.y;
        out_ap.write(r);
      end
    end
  endtask
endclass

`uvm_analysis_imp_decl(_in)
`uvm_analysis_imp_decl(_out)

class incr_scoreboard extends uvm_component;
  `uvm_component_utils(incr_scoreboard)
  uvm_analysis_imp_in  #(incr_item, incr_scoreboard)   in_imp;
  uvm_analysis_imp_out #(incr_result, incr_scoreboard) out_imp;
  bit [7:0] expected[$];
  int inputs, checked, canceled, mismatches;
  function new(string name, uvm_component parent);
    super.new(name, parent);
    in_imp  = new("in_imp", this);
    out_imp = new("out_imp", this);
  endfunction
  function void write_in(incr_item t);
    inputs++;
    expected.push_back(t.x + 1);
  endfunction
  function void write_out(incr_result r);
    bit [7:0] want;
    if (r.reset) begin     // the fix: the design dropped these results, so drop the predictions
      canceled += expected.size();
      expected.delete();
      return;
    end
    if (expected.size() == 0) begin
      `uvm_error("SB", $sformatf("result y=%0d with no input waiting", r.y))
      return;
    end
    want = expected.pop_front();
    checked++;
    if (r.y != want) begin
      mismatches++;
      `uvm_error("SB", $sformatf("y=%0d, expected %0d", r.y, want))
    end
    else log_event("scoreboard", $sformatf("y=%0d checked", r.y));
  endfunction
  function void check_phase(uvm_phase phase);
    if (expected.size() != 0)
      `uvm_error("SB", $sformatf("%0d expected results never arrived", expected.size()))
  endfunction
endclass

class reset_test extends uvm_test;
  `uvm_component_utils(reset_test)
  uvm_sequencer #(incr_item) sqr;
  incr_driver     drv;
  incr_monitor    mon;
  incr_scoreboard sb;
  virtual incr_if vif;
  function new(string name, uvm_component parent); super.new(name, parent); endfunction
  function void build_phase(uvm_phase phase);
    sqr = uvm_sequencer #(incr_item)::type_id::create("sqr", this);
    drv = incr_driver::type_id::create("drv", this);
    mon = incr_monitor::type_id::create("mon", this);
    sb  = incr_scoreboard::type_id::create("sb", this);
    if (!uvm_config_db #(virtual incr_if)::get(this, "", "vif", vif))
      `uvm_fatal("NOVIF", "no virtual interface")
    drv.vif = vif;
    mon.vif = vif;
  endfunction
  function void connect_phase(uvm_phase phase);
    drv.seq_item_port.connect(sqr.seq_item_export);
    mon.in_ap.connect(sb.in_imp);
    mon.out_ap.connect(sb.out_imp);
  endfunction
  task run_phase(uvm_phase phase);
    incr_seq first  = incr_seq::type_id::create("first");
    incr_seq second = incr_seq::type_id::create("second");
    second.values = '{80};
    phase.raise_objection(this);
    fork first.start(sqr); join_none
    repeat (5) @(negedge vif.clk);
    vif.rst <= 1;                            // reset in the middle of traffic
    log_event("test", "reset on");
    @(posedge vif.clk);
    sqr.stop_sequences();                    // kill the sequences, flush the sequencer
    repeat (2) @(negedge vif.clk);
    vif.rst <= 0;
    log_event("test", "reset off");
    second.start(sqr);                       // fresh traffic after reset
    fork
      wait (sb.expected.size() == 0);
      #300;                                  // give up: a result is still owed
    join_any
    phase.drop_objection(this);
  endtask
  function void report_phase(uvm_phase phase);
    $display("checked %0d, mismatches %0d, still waiting %0d", sb.checked, sb.mismatches, sb.expected.size());
    if (sb.mismatches == 0 && sb.expected.size() == 0)
      $display("PASS: reset canceled %0d prediction, and the result after reset matched", sb.canceled);
  endfunction
endclass

module tb;
  bit clk;
  always #5 clk = ~clk;
  incr_if bus (clk);
  incr_pipe dut (bus);
  initial begin
      uvm_config_db #(virtual incr_if)::set(null, "uvm_test_top", "vif", bus);
    run_test("reset_test");
  end
endmodule

Prints: 70 ns test reset off 105 ns scoreboard y=81 checked checked 2, mismatches 0, still waiting 0 PASS: reset canceled 1 prediction, and the result after reset matched UVM_ERROR : 0

Missed Corner

"Kill the sequences on reset and you are done."

Killing sequences stops new stimulus only. The driver may still hold an item, and the scoreboard still holds predictions. Each needs its own reset path.

Source Lesson U10.2 example: the driver drops x=48 and the scoreboard cancels one prediction

Half-Truth

"UVM has a reset phase, so a mid-test reset is handled for you."

reset_phase runs once, before the main phases. A reset during run_phase needs your own handling, or a phase jump that every component must support.

Learn It, Then Prove It