← Design Verification Interviews

Interview Question · Code Writing

The Driver Loop and Item Done

L0 FresherL1 Junioruvmsequencesdriver

The Question

The interviewer asks

Write the driver loop that takes items from the sequencer. What breaks if item_done() is missing?

systemverilog
class bus_driver extends uvm_driver #(bus_item);
  virtual bus_if vif;
  task run_phase(uvm_phase phase);
    // Write the loop: take an item, drive it on vif, then ...?
  endtask
endclass
Reveal the Model Answer

Loop forever: call seq_item_port.get_next_item(req), drive the item on the interface, then call seq_item_port.item_done(). get_next_item waits until a sequence's item is granted. item_done tells the sequencer the item is finished, which lets the sequence's finish_item return. Without it, the sequence waits forever in finish_item. The next get_next_item also reports an error every time.

Two Waits on Each Side of the SequencerDashed pulses are calls; solid tokens are the item and pin values; outlined markers are a call returning. The clock's falling edges are at 10, 20, 30 and 40 ns.
Two waits on each side of the sequencerDashed pulses are calls; solid tokens are the item and pin values; outlined markers are a call returning. The clock's falling edges are at 10, 20, 30 and 40 ns. sequence incr_seq.body start_item,randomize, finish_item component Sequencer Grants a waitingsequence to an askingdriver component Driver get_next_item, drive,item_done design Incrementer y = x + 1 start_item /finish_itemget_next_item /item_donevalid, xy
  1. The sequence asks first0 ns (illustrative)

    The sequence calls start_item and waits. The driver is keeping the bus idle for two falling edges and has not asked for anything.

    Follow the calls, values and waits
    1. call start_item(it): incr_seq.body → Sequencer
    2. Waiting: incr_seq.body — in start_item: no driver asking
    3. Waiting: Driver — idle until 20 ns
  2. The driver asks: grant20 ns (illustrative)

    The driver calls get_next_item. Now a driver is asking, so the sequencer grants the waiting sequence and start_item returns.

    Follow the calls, values and waits
    1. call get_next_item(req): Driver → Sequencer
    2. return grant: Sequencer → incr_seq.body
    3. Waiting: Driver — in get_next_item
    4. Released: incr_seq.body
    5. Released: Driver
  3. Randomize late, then hand over20 ns (illustrative)

    The sequence randomizes x=29 and calls finish_item. The sequencer passes that same object to the driver, and get_next_item returns it.

    Follow the calls, values and waits
    1. call finish_item(it): incr_seq.body → Sequencer
    2. data item 1: x=29: incr_seq.body → Sequencer
    3. data item 1: x=29: Sequencer → Driver
    4. Waiting: incr_seq.body — in finish_item until item_done
    5. Released: Driver
  4. Drive the pins30 ns (illustrative)

    At the next falling edge the driver sets valid and x. Simulation time passes only here, while the sequence waits.

    Follow the calls, values and waits
    1. data valid=1, x=29: Driver → Incrementer
  5. Done: finish_item returns40 ns (illustrative)

    The driver reads y=30, clears valid and calls item_done. finish_item returns in the same time step.

    Follow the calls, values and waits
    1. data y=30: Incrementer → Driver
    2. call item_done(): Driver → Sequencer
    3. return finish_item returns: Sequencer → incr_seq.body
    4. Released: incr_seq.body
  6. Item 2 in the same time step40 ns (illustrative)

    The driver asks again at once, and the sequence's next start_item is granted at 40 ns. In steady state the handshake costs no simulation time.

    Follow the calls, values and waits
    1. call get_next_item(req): Driver → Sequencer
    2. call start_item(it): incr_seq.body → Sequencer
    3. return grant: Sequencer → incr_seq.body
Read the diagram as text
  • incr_seq.body: start_item, randomize, finish_item
  • Sequencer: Grants a waiting sequence to an asking driver
  • Driver: get_next_item, drive, item_done
  • Incrementer: y = x + 1
  • incr_seq.body → Sequencer: start_item / finish_item
  • Driver → Sequencer: get_next_item / item_done
  • Driver → Incrementer: valid, x
  • Incrementer → Driver: y

The driver pulls; the sequence never pushes. Each side blocks until the other side acts.

The handshake in 10 ns steps
0123456time (ns)0102030405060sequencewait grantwait done 1wait done 2wait done 3driveridlegot item 1drive 1done 1, got 2drive 2done 2, got 3validdriver asks: grantitem_done for item 1

The sequence asks at 0 ns but is granted at 20 ns, when the driver first calls get_next_item.

One item at a time: get, drive until accepted, then report done.
task run_phase(uvm_phase phase);
  forever begin
    seq_item_port.get_next_item(req);   // wait for a granted item
    @(posedge vif.clk);
    vif.valid <= 1; vif.data <= req.data;
    do @(posedge vif.clk); while (!vif.ready);
    vif.valid <= 0;
    seq_item_port.item_done();          // finish_item in the sequence returns
  end
endtask

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Fresher)

A good fresher answer writes get, drive and done inside a forever loop. It says that item_done() is what lets the sequence move on.

Cross-Questions

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

WhyWhat IfProve ItCompare

  1. The Driver Loop and Item Done
  2. WhyWhy Randomize After Start ItemL1 Junior

    Why do sequences randomize the item after start_item() and not before?

    Reveal the Model Answer

    start_item() returns when the sequencer grants this sequence the driver. Randomizing after that, "late randomization", lets the constraints use the latest state, such as a FIFO level a monitor just reported. Randomizing earlier could pick values that were true when the item was made but are stale when it is driven.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "The order does not matter."

    It does not change legality. It changes which state the constraints see, which matters for reactive stimulus.

    Source Verification Academy, UVM Cookbook, Sequence API: late randomization (PDF p209)

  3. What IfA Randomize That Fails After Start ItemL1 JuniorL2 Mid-Level

    What if randomize() fails after start_item() returns? What must the sequence still do?

    Reveal the Model Answer

    The sequencer has granted this sequence, and the driver waits for the item. If the sequence never calls finish_item(), the driver waits forever and the test hangs. So check the return value, report an error, and either finish the item or end the test. A failed call leaves the fields at their old values.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "A failed randomize just sends new legal values anyway."

    The fields keep their old values. Sent unchecked, the item may repeat or break the protocol.

    Source Lesson 5.1 key points

  4. CompareThe uvm_do Macros vs Start ItemL1 Junior

    Compare the uvm_do macros with writing start_item, randomize and finish_item yourself.

    Reveal the Model Answer

    The macros hide create, start, randomize and finish in one line. That is short, but you cannot see or change the steps, and a failed randomize is easy to miss. Writing the three calls makes each step visible, so you can check randomize() and add code in between. Many teams ban the macros for that reason.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "The macros are faster at run time."

    They expand to the same calls. The choice is about readability and control, not speed.

    Source uvm-core 2020.3.1 (IEEE 1800.2 library) source, macros/uvm_sequence_defines.svh: uvm_do is uvm_create plus `uvm_rand_send (start_item, randomize, finish_item)

  5. What IfThe Handshake on a Pipelined BusL2 Mid-Level

    Asked if they mention a pipelined bus

    What if the bus is pipelined, with a new address every clock while earlier data is still coming back?

    Reveal the Model Answer

    Calling item_done only after the data returns would leave the address phase idle. Call item_done() when the address phase is accepted, so the sequence can send the next item. Return the data later with put_response(), carrying the request's IDs. One thread drives addresses and another collects data.

    Two Items in Flight, Two Milestones EachArrows are calls and returned transactions. Follow item 1 and item 2: each driver worker thread carries its own item through the address and data phases.
    Two items in flight, two milestones eachArrows are calls and returned transactions. Follow item 1 and item 2: each driver worker thread carries its own item through the address and data phases. sequence Sequence: sendthread start_item,finish_item per item sequencer Sequencer One requesthandshake at a time queue Addresssemaphore One key driver thread Worker A Item 1: address 0-5ns, data 5-15 ns driver thread Worker B Item 2: address 5-15ns, data 15-25 ns sequence Sequence: collectthread get_response loop dut Memory pins Address phase anddata phase overlap finish_item(item)get(): handoff ofitem 1get(): handoff ofitem 2holds key 0-5 nsholds key 5-15 nsdrives item 1drives item 2put(rsp): completion
    1. Handoff of item 1 (0 ns)

      Worker A takes the semaphore key and calls get(). The sequence's finish_item for item 1 returns at once, so it starts preparing item 2.

      Follow the calls, values and waits
      1. call get(1): Worker A → Address semaphore
      2. data key: Address semaphore → Worker A
      3. call get(): Worker A → Sequencer
      4. data item 1: Sequence: send thread → Sequencer
      5. data item 1: Sequencer → Worker A
      6. Waiting: Worker B — waiting for the key
      7. Released: Sequence: send thread
    2. Item 1's address accepted (5 ns)

      The memory accepts item 1's address phase at the 5 ns edge. Worker A clears avalid, then returns the key.

      Follow the calls, values and waits
      1. data addr 1: Worker A → Memory pins
      2. return key: Worker A → Address semaphore
      3. Released: Worker B
    3. Handoff of item 2 (5 ns)

      Worker B takes the key and item 2 and starts its address phase while worker A is in item 1's data phase.

      Follow the calls, values and waits
      1. call get(1): Worker B → Address semaphore
      2. data key: Address semaphore → Worker B
      3. call get(): Worker B → Sequencer
      4. data item 2: Sequence: send thread → Sequencer
      5. data item 2: Sequencer → Worker B
      6. Waiting: Worker A — data phase of item 1
      7. Released: Sequence: send thread
    4. Completion of item 1 (15 ns)

      Worker A reads rdata after the data edge, builds a response with set_id_info and calls put. At the same edge the memory accepts item 2's address.

      Follow the calls, values and waits
      1. data rdata 1: Memory pins → Worker A
      2. return rsp 1: Worker A → Sequence: collect thread
      3. data addr 2: Worker B → Memory pins
      4. Released: Sequence: collect thread
      5. Released: Worker A
    5. Handoff of item 3 (15 ns)

      Worker B returns the key; worker A, free again, takes it and item 3.

      Follow the calls, values and waits
      1. return key: Worker B → Address semaphore
      2. call get(1): Worker A → Address semaphore
      3. data key: Address semaphore → Worker A
      4. call get(): Worker A → Sequencer
      5. data item 3: Sequence: send thread → Sequencer
      6. data item 3: Sequencer → Worker A
      7. Waiting: Worker B — data phase of item 2
      8. Released: Sequence: send thread
    6. Serial before completionbefore 15 ns (serial) (illustrative)

      In the serial alternative, finish_item stays blocked until the data phase completes at 15 ns.

      Follow the calls, values and waits
      1. data item 1: Sequence: send thread → Sequencer
      2. Waiting: Sequence: send thread — in finish_item until 15 ns
      3. Released: Worker A
      4. Released: Worker B
    Read the diagram as text
    • Sequence: send thread: start_item, finish_item per item
    • Sequencer: One request handshake at a time
    • Address semaphore: One key
    • Worker A: Item 1: address 0-5 ns, data 5-15 ns
    • Worker B: Item 2: address 5-15 ns, data 15-25 ns
    • Sequence: collect thread: get_response loop
    • Memory pins: Address phase and data phase overlap
    • Sequence: send thread → Sequencer: finish_item(item)
    • Worker A → Sequencer: get(): handoff of item 1
    • Worker B → Sequencer: get(): handoff of item 2
    • Worker A → Address semaphore: holds key 0-5 ns
    • Worker B → Address semaphore: holds key 5-15 ns
    • Worker A → Memory pins: drives item 1
    • Worker B → Memory pins: drives item 2
    • Worker A → Sequence: collect thread: put(rsp): completion

    Handoff (the worker's get) and completion (its put) are separate events. The semaphore covers request selection and the address phase only.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "item_done must mean the whole transfer is complete."

    It means the driver is ready for the next item. On a pipelined bus that point comes before the data.

    Source Verification Academy, UVM Cookbook, Driver/Pipelined (PDF p255-258)

  6. Prove ItSolve U6.1.1: Keep a Two-Clock Memory BusySimulator-graded

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

    Open Problem U6.1.1

  7. Prove ItSolve U4.3.1: The Burst Driver That Outran Its ReceiverSimulator-graded

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

    Open Problem U4.3.1

What People Get Wrong

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

Misconception

"The next get_next_item() moves on by itself."

The sequencer still waits for item_done. The twin's sequence finishes no items, and every repeated call logs an error.

Run It

A driver loop without item_done, and a 200 ns watchdog so the hang still ends.

RUNVerilator + UVM 2020.3.1 · simulator service
Edit the code, then run it
Expected among the UVM messages: t=200 driven=19 finished=0 FAIL: without item_done the sequence waits forever in finish_item
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 four_items extends uvm_sequence #(bus_item);
  `uvm_object_utils(four_items)
  int finished;
  function new(string name = "four_items"); super.new(name); endfunction
  task body();
    repeat (4) begin
      req = bus_item::type_id::create("req");
      start_item(req);
      void'(req.randomize());
      finish_item(req);
      finished++;
    end
  endtask
endclass
class bus_driver extends uvm_driver #(bus_item);
  `uvm_component_utils(bus_driver)
  int driven;
  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 driven++;                   // drive the pins
      seq_item_port.item_done();      // the fix: tell the sequencer this item is finished
    end
  endtask
endclass
class hs_test extends uvm_test;
  `uvm_component_utils(hs_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);
    four_items seq = four_items::type_id::create("seq");
    phase.raise_objection(this);
    fork
      seq.start(sqr);
      #200;                           // a watchdog, so a hang still ends
    join_any
    $display("t=%0t driven=%0d finished=%0d", $time, drv.driven, seq.finished);
    if (seq.finished < 4) $display("FAIL: without item_done the sequence waits forever in finish_item");
    else $display("PASS: all four items completed the handshake");
    phase.drop_objection(this);
  endtask
endclass
module tb;
  initial run_test("hs_test");
endmodule

Prints: t=40 driven=4 finished=4 PASS: all four items completed the handshake

Half-Truth

"get() and get_next_item() are the same."

get() takes the item and completes the handshake at once, so the sequence moves on before the item is driven. That is fine only when nothing waits for the result.

Source Verification Academy, UVM Cookbook, Driver/Sequence API: get_next_item and get (PDF p213-215)

Wrong Altitude

"Randomize the item in the driver, just before driving it."

Scenario control belongs to the sequence. Randomize between start_item and finish_item, so constraints can depend on the current state.

Source Verification Academy, UVM Cookbook, Sequence API: late randomization (PDF p209)

Learn It, Then Prove It