← Design Verification Interviews

Interview Question · Code Reading

Responses That Went Missing

L2 Mid-LevelL3 Senioruvmsequencesresponses

The Question

The interviewer asks

The driver returns a response for every read with item_done(rsp). How many responses does this sequence collect?

systemverilog
task body();                       // in ten_reads, a uvm_sequence #(bus_item)
  repeat (10) begin
    req = bus_item::type_id::create("req");
    start_item(req);
    void'(req.randomize());
    finish_item(req);                // the driver answers with item_done(rsp)
  end
  // now collect every response
  fork
    forever begin get_response(rsp); got++; end
    #100;
  join_any
  disable fork;
  $display("sent 10 reads, got %0d responses", got);
endtask
Reveal the Model Answer

It collects 8, not 10. Each sequence keeps its responses in a queue that holds 8 by default. This sequence reads none until all ten are sent, so the ninth and tenth are dropped. In uvm-core 2020.3.1 the overflow error is off by default, so nothing is reported. Call get_response() after each finish_item, or use a response handler.

Two Sequences, One Driver, Responses by Sequence IDSolid tokens are requests and responses; each label names its sequence and its transaction ID. The driver calls item_done(rsp) two clock cycles after it takes each item.
Two sequences, one driver, responses by sequence IDSolid tokens are requests and responses; each label names its sequence and its transaction ID. The driver calls `item_done(rsp)` two clock cycles after it takes each item. sequence low Sends 10 and 11;get_response by tid sequence high Sends 100 and 101;get_response by tid component Sequencer Stamps the sequenceID; routes responses component Driver New rsp;set_id_info(req);item_done(rsp) finish_item(it)finish_item(it)get_next_item /item_done(rsp)low's queuehigh's queue
  1. Both ask; low goes first

    Both sequences call start_item as the test forks them. The sequencer grants low, stamps its item with low's sequence ID, and the driver takes it.

    Follow the calls, values and waits
    1. data low tid=1: x=10: low → Sequencer
    2. data low tid=1: x=10: Sequencer → Driver
    3. Waiting: low — in finish_item
    4. Waiting: high — waiting for a grant
  2. A response addressed to low20 ns (illustrative)

    The driver builds a new response, copies both IDs with set_id_info(req) and calls item_done(rsp). The sequencer reads the sequence ID and queues it for low.

    Follow the calls, values and waits
    1. data rsp for low tid=1: y=11: Driver → Sequencer
    2. data rsp for low tid=1: y=11: Sequencer → low
    3. Released: low
  3. high is granted next20 ns (illustrative)

    The driver asks again in the same time step. high has waited longest, so it is granted; its item also carries transaction ID 1.

    Follow the calls, values and waits
    1. data high tid=1: x=100: high → Sequencer
    2. data high tid=1: x=100: Sequencer → Driver
    3. Waiting: high — in finish_item
    4. Waiting: low — waiting for a grant
    5. Released: high
  4. tid=1 again, but for high40 ns (illustrative)

    high's response is routed to high's queue. The driver then takes low's second item.

    Follow the calls, values and waits
    1. data rsp for high tid=1: y=101: Driver → Sequencer
    2. data rsp for high tid=1: y=101: Sequencer → high
    3. data low tid=2: x=11: low → Sequencer
    4. data low tid=2: x=11: Sequencer → Driver
    5. Waiting: low — in finish_item
    6. Waiting: high — waiting for a grant
    7. Released: high
    8. Released: low
  5. low's second answer60 ns (illustrative)

    low's tid=2 response reaches low, and high's second item goes out.

    Follow the calls, values and waits
    1. data rsp for low tid=2: y=12: Driver → Sequencer
    2. data rsp for low tid=2: y=12: Sequencer → low
    3. data high tid=2: x=101: high → Sequencer
    4. data high tid=2: x=101: Sequencer → Driver
    5. Waiting: high — in finish_item
    6. Released: low
    7. Released: high
  6. high's second answer80 ns (illustrative)

    The last response reaches high. Each sequence printed only its own two answers.

    Follow the calls, values and waits
    1. data rsp for high tid=2: y=102: Driver → Sequencer
    2. data rsp for high tid=2: y=102: Sequencer → high
    3. Released: high
Read the diagram as text
  • low: Sends 10 and 11; get_response by tid
  • high: Sends 100 and 101; get_response by tid
  • Sequencer: Stamps the sequence ID; routes responses
  • Driver: New rsp; set_id_info(req); item_done(rsp)
  • low → Sequencer: finish_item(it)
  • high → Sequencer: finish_item(it)
  • Driver → Sequencer: get_next_item / item_done(rsp)
  • Sequencer → low: low's queue
  • Sequencer → high: high's queue

The sequence ID picks the queue; the transaction ID picks the response inside it.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Mid-Level)

A good mid-level answer knows that each sequence keeps its own response queue, and predicts 8. It fixes the code by reading each response inside the loop.

Cross-Questions

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

WhyWhat IfBreak ItCompareScale It

  1. Responses That Went Missing
  2. WhyWhy Responses Need Set ID InfoL1 Junior

    Why must the driver call rsp.set_id_info(req) before returning a response?

    Reveal the Model Answer

    Several sequences can share one sequencer, so a response has to say whose it is. set_id_info() copies the sequence ID and transaction ID from the request into the response. The sequencer uses them to put the response in the right sequence's queue.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "The sequencer remembers which request is in flight, so IDs are optional."

    With several requests outstanding, or several sequences, the IDs are the only link back.

    Source IEEE 1800.2-2020 §14.1.2.4 set_id_info (uvm-core seq/uvm_sequence_item.svh)

  3. What IfA Response Without Its IDsL1 JuniorL2 Mid-Level

    What if the driver creates the response with new and forgets set_id_info()?

    Reveal the Model Answer

    The response then has no sequence ID, so the sequencer cannot route it. The uvm-core library stops the run with a fatal error: "Driver put a response with null sequence_id". So the mistake shows at once, not as a hang. Call rsp.set_id_info(req) right after creating the response.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "Without IDs, the response goes to the first sequence on the sequencer."

    The sequencer does not guess. In uvm-core a response with no sequence ID is a fatal error.

    Source uvm-core 2020.3.1 (IEEE 1800.2 library) source, seq/uvm_sequencer_param_base.svh (SQRPUT)

  4. CompareResponse Handler vs Get ResponseL2 Mid-Level

    Compare calling get_response() with use_response_handler(1) and a response_handler() function.

    Reveal the Model Answer

    get_response() blocks the sequence until a response arrives, so each request waits for its answer. With use_response_handler(1), the sequencer calls your response_handler() function for each response as it arrives. The sequence keeps sending without waiting, which suits pipelined traffic. Responses then bypass the queue, so it cannot overflow.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "The response handler can wait for the next clock."

    It is a function, called in the sequencer's context, so it cannot consume time.

    Source uvm-core 2020.3.1 (IEEE 1800.2 library) source, seq/uvm_sequence_base.svh, virtual function void response_handler

  5. Break ItTwo Sequences Waiting for ResponsesL2 Mid-Level

    How could two sequences on one sequencer end up waiting on each other's responses?

    Reveal the Model Answer

    They cannot, as long as the driver copies the IDs: each response goes to its own sequence's queue. The problem appears when a driver builds the response from a stale request or a shared handle. Then one sequence gets two responses and the other hangs. Within one sequence, get_response(rsp, id) waits for a specific transaction.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "All responses go to the sequencer, and any sequence can take the next one."

    Each sequence has its own response queue, and routing uses the sequence ID.

    Source Verification Academy, UVM Cookbook, Sequences: response routing (PDF p198)

  6. Scale ItAn Unbounded Response QueueL3 Senior

    Why not set set_response_queue_depth(-1) in the base sequence and forget about overflow?

    Reveal the Model Answer

    It hides a sequence that never reads its responses. In a long test, those responses pile up in memory, and nobody checks them. Prefer reading responses or a response handler. If you do raise the depth, also enable the overflow report, so a real problem still shows.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Unbounded is always safer."

    It trades a visible drop for silent memory growth and unchecked data.

    Source uvm-core 2020.3.1 (IEEE 1800.2 library) source, seq/uvm_sequence_base.svh, set_response_queue_depth: -1 means arbitrarily deep

What People Get Wrong

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

Misconception

"Responses wait in the queue until the sequence asks for them."

The queue holds 8. Extra responses are dropped. The twin collects 8 of 10 with UVM_ERROR : 0.

Run It

The interview's sequence and a driver that answers every read.

RUNVerilator + UVM 2020.3.1 · simulator service
Edit the code, then run it
Expected among the UVM messages: sent 10 reads, got 8 responses FAIL: the response queue holds 8; the rest were dropped without an error
See the Fix
`timescale 1ns/1ns
`include "uvm_macros.svh"
import uvm_pkg::*;
class bus_item extends uvm_sequence_item;
  `uvm_object_utils(bus_item)
  rand bit [7:0] data;
  function new(string name = "bus_item"); super.new(name); endfunction
endclass
class ten_reads extends uvm_sequence #(bus_item);
  `uvm_object_utils(ten_reads)
  int got;
  function new(string name = "ten_reads"); super.new(name); endfunction
  task body();
    repeat (10) begin
      req = bus_item::type_id::create("req");
      start_item(req);
      void'(req.randomize());
      finish_item(req);
      get_response(rsp);              // the fix: collect each response as it comes
      got++;
    end
    $display("sent 10 reads, got %0d responses", got);
    if (got < 10) $display("FAIL: the response queue holds 8; the rest were dropped without an error");
    else $display("PASS: every read got its response");
  endtask
endclass
class bus_driver extends uvm_driver #(bus_item);
  `uvm_component_utils(bus_driver)
  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);
      #10;
      rsp = bus_item::type_id::create("rsp");
      rsp.set_id_info(req);           // route the response back to its sequence
      rsp.data = ~req.data;
      seq_item_port.item_done(rsp);
    end
  endtask
endclass
class rsp_test extends uvm_test;
  `uvm_component_utils(rsp_test)
  uvm_sequencer #(bus_item) sqr;
  bus_driver drv;
  function new(string name, uvm_component parent); super.new(name, parent); endfunction
  function void build_phase(uvm_phase phase);
    sqr = uvm_sequencer #(bus_item)::type_id::create("sqr", this);
    drv = bus_driver::type_id::create("drv", this);
  endfunction
  function void connect_phase(uvm_phase phase); drv.seq_item_port.connect(sqr.seq_item_export); endfunction
  task run_phase(uvm_phase phase);
    ten_reads seq = ten_reads::type_id::create("seq");
    phase.raise_objection(this);
    seq.start(sqr);
    phase.drop_objection(this);
  endtask
endclass
module tb;
  initial run_test("rsp_test");
endmodule

Prints: sent 10 reads, got 10 responses PASS: every read got its response

Tool as Truth

"UVM always reports an error when the response queue overflows."

The source comment says so, but the flag that enables the report is a bit that starts at 0 in uvm-core 2020.3.1. Only set_response_queue_error_report_enabled(1) turns it on.

Source uvm-core 2020.3.1 (IEEE 1800.2 library) source, seq/uvm_sequence_base.svh: response_queue_error_report_enabled (line 174, never set) and put_base_response

Missed Corner

The driver builds rsp but skips rsp.set_id_info(req).

The sequencer routes a response by the sequence and transaction IDs copied from the request. Without them, it cannot find the sequence.

Source Verification Academy, UVM Cookbook, Sequences: routing responses with set_id_info (PDF p198, p218)

Learn It, Then Prove It