← Design Verification Interviews
Interview Question · Code Writing
Ending a Test While Results Are in Flight
The Question
The interviewer asks
This test drops its objection as soon as the sequence returns. The DUT has a three-clock pipeline. Write an end of test that still checks the last results.
task run_phase(uvm_phase phase);
three_items seq = three_items::type_id::create("seq");
phase.raise_objection(this, "three items");
seq.start(env.sqr); // returns after the driver's last item_done
phase.drop_objection(this, "sequence done");
endtask
// The DUT answers y = x + 1 three clocks after it accepts x.You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
The sequence returns when the driver finishes the last item, but two results are still in the pipe. So check_phase sees three sent and one checked. A drain time keeps the phase open a little longer, but it is a guess. A better fix lets the scoreboard object while it still owes results. It raises when its expected queue becomes non-empty and drops when it empties. Keep an end check for anything still outstanding.
Results arrive at 45, 65 and 85 ns. Without a drain time, the run phase ends at 60 with two results still inside the design.
- The last drop60 ns (illustrative)
The sequence returns and the test drops. The total reaches 0, so UVM calls
phase_ready_to_endon every component before it ends the phase.Follow the calls, values and waits
- call test drops: total 0: Test → run objection
- call phase_ready_to_end: run objection → Scoreboard
- Results are owed: raise60 ns (illustrative)
Two results are still expected, so the scoreboard raises the objection again and starts a thread that waits for its queue to empty.
Follow the calls, values and waits
- call sb raises: total 1: Scoreboard → run objection
- Waiting: Scoreboard — Owes 2 results
- The second result65 ns (illustrative)
The monitor publishes y=21. The scoreboard checks it and now owes one result.
Follow the calls, values and waits
- data y=21: Pipeline → Monitor
- data y=21: Monitor → Scoreboard
- Waiting: Scoreboard — Owes 1 result
- Released: Scoreboard
- The last result, then the drop85 ns (illustrative)
y=31 arrives and the queue is empty. The thread drops at 85 ns; UVM asks again, nothing is owed, and the run phase ends at 85 ns with 3 checked.
Follow the calls, values and waits
- data y=31: Pipeline → Monitor
- data y=31: Monitor → Scoreboard
- call sb drops: total 0: Scoreboard → run objection
- call phase_ready_to_end: run objection → Scoreboard
- Released: Scoreboard
Read the diagram as text
- Test: Drops when its sequence returns
- run objection: Total at the root
- Scoreboard: expected queue: owed results
- Pipeline: Three cycles of latency
- Monitor: Publishes each result
- Test → run objection: drop_objection
- run objection → Scoreboard: phase_ready_to_end
- Scoreboard → run objection: raise / drop_objection
- Pipeline → Monitor: y at out_valid
- Monitor → Scoreboard: write(result)
The end of the test follows the last result at 85 ns, not a guessed drain time.
Self-Check
Tick each key point you covered in your answer.
What Interviewers Listen For (Junior)
A good junior answer sees that "the sequence is done" does not mean "the DUT is done". It names a drain time as the first fix.
What Interviewers Listen For (Mid-Level)
A good mid-level answer raises the scoreboard objection when its queue gets its first entry and drops it when the queue empties. It does not raise and drop for every item.
What Interviewers Listen For (Senior)
A good senior answer counts what drain times cost across a large regression. It also adds a watchdog that fails when a result never comes.
Cross-Questions
Where the interviewer goes next. Open a node to practise that follow-up.
What IfProve ItCompareScale It
- Ending a Test While Results Are in Flight
-
CompareDrain Time vs Phase Ready to EndL2 Mid-Level
Compare a drain time, a scoreboard objection and
phase_ready_to_end(). When would you pick each?2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
A drain time is a fixed wait after the last drop: simple, but a guess. A scoreboard objection follows the real outstanding work.
phase_ready_to_end()is a callback that runs when the count first reaches zero. There a busy component can raise again until it is idle, without objecting during normal traffic.Self-Check
Tick each key point you covered in your answer.
Half-Truth
"
phase_ready_to_endruns at the end of every phase automatically, so nothing else is needed."It is only a hook. The component must still raise an objection inside it, and drop it later, to delay the end.
Source Verification Academy, UVM Cookbook, End of Test: phase_ready_to_end (PDF p191)
-
What IfA Ready-to-End Raise That Never DropsL2 Mid-LevelL3 Senior
What if
phase_ready_to_end()raises an objection but never drops it?2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
The phase never ends, and the test hangs until the global timeout. So the component must drop the objection when its work is done, on every path, including errors. The callback can also run again each time the count returns to zero, up to a limit. A timeout with a clear message keeps a hang from looking like a slow test.
Self-Check
Tick each key point you covered in your answer.
Misconception
"
phase_ready_to_end()runs once, so raising there is always safe."It runs each time the count returns to zero, up to a set number of times. A raise without a drop hangs the phase.
Source uvm-core 2020.3.1 (IEEE 1800.2 library) source, base/uvm_phase.svh (max_ready_to_end_iterations, default 20)
-
What IfA Result That Never ArrivesL2 Mid-Level
What if the DUT loses one result, so the scoreboard's queue never empties?
2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
The scoreboard's objection never drops, so the test hangs until the timeout. Give the scoreboard a bound: a watchdog that fails after the longest legal latency with the outstanding items listed. That turns a silent hang into a precise error message.
Self-Check
Tick each key point you covered in your answer.
Missed Corner
"The global timeout will report it."
It reports a timeout after hours of simulated time, not which item was lost.
Source uvm-core 2020.3.1 (IEEE 1800.2 library) source, macros/uvm_global_defines.svh, default timeout 9200 s
-
Scale ItEnding a Test on Many InterfacesL3 Senior
At subsystem level you have six interfaces and an interrupt that may fire long after traffic stops. Who decides when the test ends?
2:30You have two and a half minutes. Speak or type, then press Get Feedback under your answer. No credits needed.
Speech-to-text runs in your browser. Chrome and Edge may send audio to their own speech service.
You can edit your answer before you send it. Feedback uses AI and checks your answer only against our written answer. No credits are used and your answer is not saved.
Reveal the Model Answer
No single test can know every latency, so give each checker its own "still busy" rule. Each scoreboard objects or uses
phase_ready_to_end()while it owes results. The interrupt checker waits a bounded time for a pending interrupt. The test only owns its stimulus. Add a heartbeat or watchdog, so a stuck component fails with its name instead of hanging.Self-Check
Tick each key point you covered in your answer.
Wrong Altitude
"Give the subsystem test one big drain time."
It hides which component was still busy, and it grows every time someone adds a slower path.
Source Verification Academy, UVM Cookbook, End Of Test Mechanisms (PDF p190-194)
-
Prove ItSolve U15.2.1: The Poison Test with a Short LedgerSimulator-graded
Show it, don't just say it: this Silicon Lab problem is graded by the simulator.
What People Get Wrong
Common wrong answers and why they fail. Run It shows the wrong belief failing on a real simulator.
Misconception
"When the sequence returns, the DUT has finished."
The sequence returns after the driver's last handshake. The pipeline still holds results, and the phase ends before they come out.
Run It
The interview's test on a three-clock incrementer, with an end check that counts results.
check_phase at 60 ns: sent=3 checked=1 FAIL: the test dropped its objection when the last item was sent, with 2 results still in the pipeSee the Fix
`timescale 1ns/1ns
`include "uvm_macros.svh"
import uvm_pkg::*;
interface incr_if (input logic clk);
logic in_valid = 0, out_valid;
logic [7:0] x = 0, y;
endinterface
module incr_pipe (incr_if bus);
logic v[3]; logic [7:0] d[3];
always_ff @(posedge bus.clk) begin
v[0] <= bus.in_valid; d[0] <= bus.x + 1;
v[1] <= v[0]; d[1] <= d[0];
v[2] <= v[1]; d[2] <= d[1];
end
assign bus.out_valid = v[2];
assign bus.y = d[2];
endmodule
class incr_item extends uvm_sequence_item;
`uvm_object_utils(incr_item)
bit [7:0] x, y;
function new(string name = "incr_item"); super.new(name); endfunction
endclass
class three_items extends uvm_sequence #(incr_item);
`uvm_object_utils(three_items)
function new(string name = "three_items"); super.new(name); endfunction
task body();
for (int i = 1; i <= 3; i++) begin
req = incr_item::type_id::create("req");
start_item(req);
req.x = 8'(10 * i);
finish_item(req);
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
seq_item_port.get_next_item(req);
@(negedge vif.clk) begin vif.in_valid <= 1; vif.x <= req.x; end
@(negedge vif.clk) vif.in_valid <= 0;
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, out_ap;
function new(string name, uvm_component parent); super.new(name, parent); endfunction
function void build_phase(uvm_phase phase); 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.in_valid) begin incr_item t = incr_item::type_id::create("in"); t.x = vif.x; in_ap.write(t); end
if (vif.out_valid) begin incr_item t = incr_item::type_id::create("out"); t.y = vif.y; out_ap.write(t); end
end
endtask
endclass
`uvm_analysis_imp_decl(_in)
`uvm_analysis_imp_decl(_out)
class incr_scoreboard extends uvm_scoreboard;
`uvm_component_utils(incr_scoreboard)
uvm_analysis_imp_in #(incr_item, incr_scoreboard) in_imp;
uvm_analysis_imp_out #(incr_item, incr_scoreboard) out_imp;
bit [7:0] expected[$];
int sent, checked;
function new(string name, uvm_component parent); super.new(name, parent); endfunction
function void build_phase(uvm_phase phase); in_imp = new("in_imp", this); out_imp = new("out_imp", this); endfunction
function void write_in(incr_item t); expected.push_back(t.x + 1); sent++; endfunction
function void write_out(incr_item t);
bit [7:0] want = expected.pop_front();
checked++;
if (t.y !== want) `uvm_error("MISMATCH", $sformatf("y=%0d, expected %0d", t.y, want))
endfunction
// The fix: object while results are still owed.
task run_phase(uvm_phase phase);
forever begin
wait (expected.size() > 0);
phase.raise_objection(this, "results owed");
wait (expected.size() == 0);
phase.drop_objection(this, "all results back");
end
endtask
endclass
class incr_env extends uvm_env;
`uvm_component_utils(incr_env)
uvm_sequencer #(incr_item) sqr;
incr_driver drv; incr_monitor mon; incr_scoreboard sb;
function new(string name, uvm_component parent); super.new(name, parent); endfunction
function void build_phase(uvm_phase phase);
virtual incr_if vif;
if (!uvm_config_db #(virtual incr_if)::get(this, "", "vif", vif)) `uvm_fatal("NOVIF", "no virtual interface")
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);
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
endclass
class incr_test extends uvm_test;
`uvm_component_utils(incr_test)
incr_env env;
function new(string name, uvm_component parent); super.new(name, parent); endfunction
function void build_phase(uvm_phase phase); env = incr_env::type_id::create("env", this); endfunction
task run_phase(uvm_phase phase);
three_items seq = three_items::type_id::create("seq");
phase.raise_objection(this, "three items");
seq.start(env.sqr);
phase.drop_objection(this, "sequence done");
endtask
function void check_phase(uvm_phase phase);
$display("check_phase at %0t ns: sent=%0d checked=%0d", $time, env.sb.sent, env.sb.checked);
if (env.sb.checked < env.sb.sent) $display("FAIL: the test dropped its objection when the last item was sent, with %0d results still in the pipe", env.sb.sent - env.sb.checked);
else $display("PASS: every result came back before the run phase ended");
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.env", "vif", bus);
run_test("incr_test");
end
endmodule
Prints: check_phase at 85 ns: sent=3 checked=3 PASS: every result came back before the run phase ended
Half-Truth
"Set a large drain time, like 1 ms, to be safe."
It works, but every test in the regression now runs 1 ms longer. It is still a guess if back-pressure stretches the latency.
Source IEEE 1800.2-2020 §10.5.1.3.7 set_drain_time (uvm-core base/uvm_objection.svh)
Half-Truth
"The scoreboard should raise and drop an objection for every item."
Each raise and drop travels up the tree. Objecting only on the empty and non-empty edges does the same job with far fewer calls.
Source Verification Academy, UVM Cookbook, End of Test: transactors cannot object per transaction, for performance (PDF p191)