← Design Verification Interviews

Interview Question · Debug

Two Drivers, One Bus

L1 JuniorL2 Mid-Levelsystemveriloginterfacesdebug

The Question

The interviewer asks

A bench has two ports, each with its own driver. Port 1 never sees traffic and port 0 shows corrupted beats. Here is the log. What do you check first?

log
@10  d0 send 11 on port0
@10  d1 send 22 on port0      <- d1 was configured for port1
@15  port0 valid=1 data=22    (beat 11 overwritten)
@40  port0 beats=1 port1 beats=0
Reveal the Model Answer

Check which interface instance each driver's virtual interface points at. Here both drivers got port0, so they overwrite each other and port1 stays idle. A virtual interface is a reference, so copying one handle shares the bus. Print each agent's interface path at the start of the run, and give every agent its own handle from the top level.

Two Drivers, One Hard-Coded Port
0123driverdrv0drv1drv0drv1port0.rvalidport1.rvaliddrv1 drives port 0
  1. Cycle 0

    drv0 drives port 0, as intended.

  2. Cycle 1

    drv1 was built for port 1, but its hard-coded path tb.port0 drives port 0 again.

  3. Cycle 3

    Port 1 never moves, so its coverage can only stay at zero.

The symptom on the waveform: every beat meant for port 1 appears on port 0. A copied handle and a hard-coded path look exactly the same.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Junior)

A good junior answer suspects the handle assignment from the log alone, before looking at the DUT.

Cross-Questions

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

WhyProve ItBreak ItCompareScale It

  1. Two Drivers, One Bus
  2. WhyWhy Classes Need Virtual InterfacesL1 Junior

    Why can't a driver class simply contain the interface instance? Why the virtual keyword?

    Reveal the Model Answer

    Interfaces are static, elaborated hierarchy, while class objects are created at run time, so a class cannot instantiate one. A virtual interface is a variable holding a reference to an existing interface instance. The bench assigns it before use, so one driver class can be pointed at any instance of the interface.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "virtual means the same as in virtual function: it can be overridden."

    Here it means a reference to an interface instance; it has nothing to do with method dispatch.

    Source IEEE 1800-2017 §25.9

  3. Break ItA Virtual Interface That Was Never SetL1 Junior

    The test crashes with a null access the first time the driver touches vif.valid. What happened and how do you fail faster?

    Reveal the Model Answer

    The virtual interface variable is null until something assigns it, and nothing did. Check it where it is received, for example right after the configuration lookup, and stop with a message naming the agent. The failure then points at the missing assignment, not at the first signal access.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "An unassigned virtual interface points at the first instance of that interface."

    It is null until assigned; there is no default binding to any instance.

    Source IEEE 1800-2017 §25.9

  4. ComparePassing the Interface by Constructor or by ConfigurationL2 Mid-Level

    Compare handing each driver its virtual interface through the constructor with fetching it from a configuration database.

    Reveal the Model Answer

    A constructor argument is explicit and checked at the call site, which suits a small hand-built bench. A configuration object or database lets deep components get the handle without every parent passing it down. UVM benches work this way. The cost is indirection: a wrong path or key leaves the handle null, so check it on receipt.

    Each Driver Reaches the Port It Was GivenThree drivers built by the top module, each given a virtual interface handle through its constructor. Arrows from the top module carry a handle into a constructor; arrows from a driver are drives through that handle to real pins.
    Each driver reaches the port it was givenThree drivers built by the top module, each given a virtual interface handle through its constructor. Arrows from the top module carry a handle into a constructor; arrows from a driver are drives through that handle to real pins. testbench tb module knows which port eachdriver gets object d0: rd_driver vif refers to port0 object d1: rd_driver vif refers to port1 object d2: rd_driver vif is null interface port0 rd_if instance interface port1 rd_if instance new(.., port0)new(.., port1)new(.., null)vif.cb drivesvif.cb drives
    1. The instances exist first

      rd_if port0 (clk); and rd_if port1 (clk); are part of the elaborated design. They exist before time 0 and never move.

    2. Hand each driver its portt=0 (illustrative)

      The top module passes port0 to d0 and port1 to d1. Each constructor stores a handle; no signals are copied.

      Follow the calls, values and waits
      1. data handle: port0: tb module → d0: rd_driver
      2. data handle: port1: tb module → d1: rd_driver
    3. A driver with no portt=0 (illustrative)

      d2 is built with null. Its handle refers to nothing until something assigns it.

      Follow the calls, values and waits
      1. data null: tb module → d2: rd_driver
      2. Waiting: d2: rd_driver — vif == null
    4. Both drivers sendt=7 (illustrative)

      Forked, both drivers wait for the edge at 5 and drive through vif.cb. With output skew 2, each pin changes at t=7, on that driver's own port.

      Follow the calls, values and waits
      1. data rvalid=1, rdata=0a: d0: rd_driver → port0
      2. data rvalid=1, rdata=1b: d1: rd_driver → port1
    5. Two ports, two beatst=15 (illustrative)

      At the edge at 15 the module prints port0 carries 0a and port1 carries 1b.

    6. The null check reports

      d2.send() checks vif first and prints drv2: no interface configured; nothing sent instead of failing on a null handle.

      Follow the calls, values and waits
      1. Waiting: d2: rd_driver — no interface configured
    Read the diagram as text
    • tb module: knows which port each driver gets
    • d0: rd_driver: vif refers to port0
    • d1: rd_driver: vif refers to port1
    • d2: rd_driver: vif is null
    • port0: rd_if instance
    • port1: rd_if instance
    • tb module → d0: rd_driver: new(.., port0)
    • tb module → d1: rd_driver: new(.., port1)
    • tb module → d2: rd_driver: new(.., null)
    • d0: rd_driver → port0: vif.cb drives
    • d1: rd_driver → port1: vif.cb drives

    One driver class serves any number of ports. Which pins a driver reaches is decided by the handle it was given.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "The config database guarantees each agent gets the right interface."

    It returns whatever matches the path and name you set. A wildcard path set once can hand the same interface to every agent.

  5. Prove ItSolve 6.4.1: Give Each Driver Its Own BusSimulator-graded

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

    Open Problem 6.4.1

  6. Scale ItBinding Sixteen Agents to Sixteen PortsL3 Senior

    The DUT grows to sixteen ports, generated by a parameter. How do you connect sixteen agents without copy-paste bugs like this one?

    Reveal the Model Answer

    Create the interfaces in a generate loop indexed by port number. Publish each handle under a name built from the same index. Build the agents in a loop that reads the handle with that index. Then add a start-of-test check that the sixteen handles are all different and not null. A binding mistake then stops the run at time zero.

    Self-Check

    Tick each key point you covered in your answer.

    What Interviewers Listen For (Senior)

    A good senior answer removes copy-paste completely, so one index drives both sides. It adds a cheap check at time zero instead of relying on review.

    Wrong Altitude

    "Review the sixteen lines carefully."

    Review does not scale with the parameter. When the port count changes, the hand-written lines become wrong.

What People Get Wrong

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

Misconception

"Each driver gets its own copy of the bus when it is constructed."

A virtual interface holds a reference to an instance, not a copy. The twin hands port0 to both drivers and port1 sees nothing.

Run It

Two drivers built from a copy-pasted line that passes port0 twice.

RUNVerilator · simulator service
Edit the code, then run it
Expected: port0 beats=1 port1 beats=0 FAIL: both drivers held a handle to port0, so port1 never saw a beat
See the Fix
interface bus_if(input logic clk);
  logic valid = 0; logic [7:0] data = 0;
endinterface
class driver;
  virtual bus_if vif;
  function new(virtual bus_if v); vif = v; endfunction
  task send(logic [7:0] d);
    @(negedge vif.clk); vif.valid = 1; vif.data = d;
    @(negedge vif.clk); vif.valid = 0;
  endtask
endclass
module tb;
  logic clk = 0; always #5 clk = ~clk;
  bus_if port0(clk), port1(clk);
  int got0 = 0, got1 = 0;
  always @(posedge clk) begin if (port0.valid) got0++; if (port1.valid) got1++; end
  initial begin
    driver d0 = new(port0);
    driver d1 = new(port1);      // the fix: each driver gets its own port
    fork d0.send(8'h11); d1.send(8'h22); join
    #20 $display("port0 beats=%0d port1 beats=%0d", got0, got1);
    if (got1 == 0) $display("FAIL: both drivers held a handle to port0, so port1 never saw a beat");
    else $display("PASS: one beat on each port");
    $finish;
  end
endmodule

Prints: port0 beats=1 port1 beats=1 PASS: one beat on each port

Delivery

Starts by debugging the DUT's port 1 logic.

The log already shows d1 sending on port0. Read the evidence first; it saves time spent in the RTL.

Wrong Altitude

"Use a global interface variable and let every driver read it."

A single global makes sharing the default and is the root cause of this bug class. Per-agent configuration objects make the binding explicit.

Learn It, Then Prove It