← Design Verification Interviews

Interview Question · Concept

Which Method Runs Through a Base Handle?

L0 FresherL1 Juniorsystemverilogooppolymorphism
  • Asked at Google
  • Asked at Apple

The Question

The interviewer asks

A base class txn and a derived wr_txn both define show(). You call it through a txn handle that points to a wr_txn object. Which version runs, and what changes if show() is virtual?

Reveal the Model Answer

Without virtual, the call is bound by the handle's declared type, so txn::show() runs. With virtual, it is resolved at run time by the object's type, so wr_txn::show() runs. This run-time choice is called polymorphism. It lets a driver written for base handles keep working when a test adds new derived transactions.

One Base Handle, Two Kinds of CallA base handle that points to a derived object. Dashed badges are calls; solid badges are the answers coming back. The arrow from the handle to the object is a reference.
One base handle, two kinds of callA base handle that points to a derived object. Dashed badges are calls; solid badges are the answers coming back. The arrow from the handle to the object is a reference. caller Scoreboard loop calls throughexp_q[1] handle exp_q[1] handle type: axi_txn object Object, ID 2 object type: err_txn method axi_txn version resp_nonvirtual:OKAY method err_txn version expected_resp:SLVERR callrefers topicked by handle typepicked by object type
  1. A base handle on a derived object

    The queue entry is declared axi_txn, but it refers to the err_txn object with ID 2. The compiler knows only the handle type.

  2. Non-virtual call

    For resp_nonvirtual() the method was picked when the code compiled, from the handle type. The base version runs and answers OKAY.

    Follow the calls, values and waits
    1. call resp_nonvirtual(): Scoreboard loop → exp_q[1]
    2. call resp_nonvirtual(): exp_q[1] → axi_txn version
    3. data OKAY: axi_txn version → exp_q[1]
    4. data OKAY: exp_q[1] → Scoreboard loop
  3. Virtual call

    For expected_resp() the choice waits until the call runs. It follows the handle to the object, whose class is err_txn, and that version answers SLVERR.

    Follow the calls, values and waits
    1. call expected_resp(): Scoreboard loop → exp_q[1]
    2. call expected_resp(): exp_q[1] → Object, ID 2
    3. call expected_resp(): Object, ID 2 → err_txn version
    4. data SLVERR: err_txn version → Object, ID 2
    5. data SLVERR: Object, ID 2 → exp_q[1]
    6. data SLVERR: exp_q[1] → Scoreboard loop
Read the diagram as text
  • Scoreboard loop: calls through exp_q[1]
  • exp_q[1]: handle type: axi_txn
  • Object, ID 2: object type: err_txn
  • axi_txn version: resp_nonvirtual: OKAY
  • err_txn version: expected_resp: SLVERR
  • Scoreboard loop → exp_q[1]: call
  • exp_q[1] → Object, ID 2: refers to
  • exp_q[1] → axi_txn version: picked by handle type
  • Object, ID 2 → err_txn version: picked by object type

A non-virtual call follows the handle type. A virtual call follows the object type.

The same base handle calls a plain method and a virtual one.

RUNVerilator · simulator service
Edit the code, then run it
Expected: txn::show wr_txn::vshow

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Fresher)

A good fresher answer says which method runs in each case. It names the two types involved: the type of the handle and the type of the object.

Cross-Questions

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

WhyWhat IfProve ItBreak ItCompareScale It

  1. Which Method Runs Through a Base Handle?
  2. ComparePure Virtual Methods vs Empty Virtual MethodsL1 Junior

    Compare a pure virtual method in a virtual class with a virtual method that has an empty body.

    Reveal the Model Answer

    A pure virtual method has no body. Its class must be declared virtual, which means abstract: nobody can construct it. A subclass that does not implement the method cannot be constructed either. An empty virtual body compiles everywhere and silently does nothing when a subclass forgets the override. Use pure virtual for methods every subclass must provide, and an empty body for optional hooks.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "An abstract class cannot have any implemented methods."

    A virtual class may mix implemented methods with pure virtual ones; only construction is forbidden.

    Source IEEE 1800-2017 §8.21

  3. What IfConstructors Along the Class ChainL1 Junior

    Asked if they mention constructors

    How do constructors work along txn and rd_txn extends txn? What happens if rd_txn::new never calls super.new?

    Reveal the Model Answer

    Constructing a rd_txn runs the base constructor first, through super.new(...), which must be the first statement. If you leave it out, the compiler inserts super.new() with no arguments. That fails to compile when the base constructor has a required argument, and silently uses the defaults when the argument has one.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Forgetting super.new is a compile error, so it cannot get past."

    Only when the base argument has no default. With a default, the implicit call compiles and the base fields keep their defaults.

    Source IEEE 1800-2017 §8.17

  4. Break ItThe Transaction Whose Id Was Always ZeroL1 JuniorL2 Mid-Level

    Every rd_txn in the log has id=0, although the test constructs them with new(7, 'h40). The constructor sets addr correctly. What is missing?

    systemverilog
    class txn;
      int id;
      function new(int id = 0); this.id = id; endfunction
    endclass
    class rd_txn extends txn;
      int addr;
      function new(int id, int addr);
        this.addr = addr;
      endfunction
    endclass
    Reveal the Model Answer

    The derived constructor never calls super.new(id), so the compiler inserted super.new(), which used the default id = 0. The id argument of rd_txn::new is just a local name that nothing reads. Add super.new(id); as the first statement.

    Who Sets Each PropertyA derived constructor and the base constructor it must call. Dashed badges are calls; solid badges are values written into the new object, or the handle given back to the caller.
    Who sets each propertyA derived constructor and the base constructor it must call. Dashed badges are calls; solid badges are values written into the new object, or the handle given back to the caller. caller Test e = new(5, 'h80, 2) method err_txn::new derived constructor method axi_txn::new base constructor object The new object id, addr, err_beat new(...)super.new(...)id, addrerr_beat
    Read the diagram as text
    • Test: e = new(5, 'h80, 2)
    • err_txn::new: derived constructor
    • axi_txn::new: base constructor
    • The new object: id, addr, err_beat
    • Test → err_txn::new: new(...)
    • err_txn::new → axi_txn::new: super.new(...)
    • axi_txn::new → The new object: id, addr
    • err_txn::new → The new object: err_beat

    Each class sets what it declares. The derived class reaches the base part only through super.new.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "The base constructor sees the derived constructor's id argument because the names match."

    Arguments are local to their method; matching names connect nothing. The twin prints id=0 until super.new(id) is added.

    Run It

    A derived constructor that sets its own field but never calls super.new.

    RUNVerilator · simulator service
    Edit the code, then run it
    Expected: id=0 addr=00000040 FAIL: without super.new(id), an implicit super.new() ran with the default id 0
    See the Fix
    class txn;
      int id;
      function new(int id = 0); this.id = id; endfunction
    endclass
    class rd_txn extends txn;
      int addr;
      function new(int id, int addr);
        super.new(id);             // the fix: pass id to the base constructor
        this.addr = addr;
      endfunction
    endclass
    module tb;
      rd_txn r;
      initial begin
        r = new(7, 'h40);
        $display("id=%0d addr=%h", r.id, r.addr);
        if (r.id == 7) $display("PASS: the base constructor received the id");
        else $display("FAIL: without super.new(id), an implicit super.new() ran with the default id 0");
      end
    endmodule
    

    Prints: id=7 addr=00000040 PASS: the base constructor received the id

  5. WhyWhy Methods Are Not Virtual by DefaultL1 JuniorL2 Mid-Level

    If virtual methods are so useful, why doesn't SystemVerilog make every method virtual?

    Reveal the Model Answer

    A non-virtual method is a promise from the base class. Callers that use a base handle always get that exact behaviour, whatever a subclass does. It also lets the compiler call the method directly instead of looking it up per object. The base-class author decides which behaviour is meant to be replaced, and marks only those methods virtual.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "Non-virtual methods cannot be overridden."

    They can be redefined in a subclass, and calls through a derived handle see the new version. Only base-handle calls ignore it.

    Source IEEE 1800-2017 §8.14

  6. Scale ItSwapping Transaction Types Without Editing the DriverL2 Mid-LevelL3 Senior

    A test wants error-injecting transactions on one agent only, without editing the driver or the generator. How do you design for that?

    Reveal the Model Answer

    The generator creates every transaction in one place that a test can override, such as a factory. The driver only calls virtual methods on base handles. The test extends the transaction, overrides the virtual hooks that inject the error, and registers the subclass for that one agent. UVM's factory with an instance override is this pattern packaged, which is the main reason to use it at scale.

    Self-Check

    Tick each key point you covered in your answer.

    What Interviewers Listen For (Senior)

    A good senior answer says the one place that constructs objects must be open to override. It states the price: every method a test may change must be virtual from the start.

    Wrong Altitude

    "Add an inject_error flag to the base transaction and test it in the driver."

    Each new error kind then edits the base class and the driver, the two files shared by every test. Overrides keep test-specific behaviour in the test.

  7. Prove ItSolve 4.7.1: Factory by OpcodeSimulator-graded

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

    Open Problem 4.7.1

What People Get Wrong

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

Misconception

"If the derived class overrides the method, the derived version runs."

Only for virtual methods. The twin overrides a plain method and the base handle still calls the base one.

Run It

A derived override of a non-virtual method, called through a base handle.

RUNVerilator · simulator service
Edit the code, then run it
Expected: FAIL: beats() is not virtual, so the base handle called txn::beats
See the Fix
class txn;
  virtual function int beats(); return 1; endfunction
endclass
class burst_txn extends txn;
  virtual function int beats(); return 4; endfunction
endclass
module tb;
  txn h;
  burst_txn b;
  initial begin
    b = new;
    h = b;
    // The fix: beats() is virtual, so the object picks the version.
    if (h.beats() == 4) $display("PASS: the call reached burst_txn::beats");
    else $display("FAIL: beats() is not virtual, so the base handle called txn::beats");
  end
endmodule

Prints: PASS: the call reached burst_txn::beats

Tool as Truth

"It compiled, so my override is in place."

Verilator 5.052 accepts an override whose arguments differ from the virtual base method, with no warning. The twin shows the base handle never reaching it.

Run It

The override adds an argument and changes int to byte. It compiles.

RUNVerilator · simulator service
Edit the code, then run it
Expected: len=2 FAIL: the override's arguments differ from the base, so the call never reached it
See the Fix
class txn;
  virtual function int len(int beats); return beats; endfunction
endclass
class wide_txn extends txn;
  // The fix: the same argument list as the base method.
  virtual function int len(int beats); return beats * 4; endfunction
endclass
module tb;
  txn h;
  wide_txn w;
  initial begin
    w = new;
    h = w;
    $display("len=%0d", h.len(2));
    if (h.len(2) == 8) $display("PASS: the base handle reached the override");
    else $display("FAIL: the override's arguments differ from the base, so the call never reached it");
  end
endmodule

Prints: len=8 PASS: the base handle reached the override

Spec Misquote

"Each derived class must repeat virtual, or the method stops being virtual."

Once a method is virtual it stays virtual in every subclass, with or without the keyword. Repeating it is style.

Source IEEE 1800-2017 §8.20

Buzzword Without Mechanism

Recites "inheritance, encapsulation, polymorphism" without saying which method runs.

The interviewer asked for a prediction. A pass names the method and the rule that picked it.

Learn It, Then Prove It