Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
    • Classes, Objects & Handles
    • Inheritance and Polymorphism
      • extends and super
      • Virtual dispatch
      • Casting with $cast
      • Contracts and polymorphic containers
    • Static, Encapsulation & Parameterized Classes
    • Virtual Interfaces: the Class-to-Pins Bridge
    • Events, Semaphores, Mailboxes and Processes
    • OOP Patterns for Testbenches
  • Constraint Randomization — Interview-Critical Deep Dive36
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹Inheritance and Polymorphism
  1. Topics
  2. SystemVerilog
  3. Object-Oriented SystemVerilog for Verification
  4. Inheritance and Polymorphism
  5. Virtual dispatch

Part 2 · OOP for Verification · Intermediate

Virtual dispatch

Declared-type versus object-type calls, and why extension hooks are virtual.

On this page

    Why this matters

    An override compiled cleanly and never ran. The base method was not virtual, so every call through a base handle kept running the base body.

    After this lesson you can: predict which body a call runs from the handle type and the object type, and make extension hooks virtual.

    Two types in every call

    Every call through a handle involves a declared type known at compile time and an object type known only at runtime. The virtual keyword decides which one resolves the call.

    Non-virtual calls bind to the declared type before simulation starts. Virtual calls read the actual object at call time and run the most-derived override.

    Call kind

    Who picks the body

    When decided

    Non-virtual

    Handle's declared type

    Compile time (static dispatch).

    Virtual

    Object's runtime type

    Call time (dynamic dispatch).

    Once virtual

    Every descendant stays virtual

    Repeating the keyword is documentation.

    Hooks belong to the base

    Reusable code is written once against base types: it calls comp.run() or txn.compare() through base handles. For derived behavior to take effect, those base methods must be virtual.

    A missing virtual is a silent failure. The derived override compiles cleanly and never runs through base handles.

    Virtual calls follow the object; non-virtual calls follow the handle.

    Common mistakes

    Common pitfall

    Same-name methods do not imply dispatch. If the base method is not virtual, the base body runs through base handles no matter what the object is. Forgetting virtual is trap one.

    Common pitfall

    Properties never dispatch. A derived property with a base property's name creates two coexisting fields, and the handle type picks which one each access touches. Never shadow base data; wrap varying access in virtual methods.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    // snippet
    class cfg_base;
      int mode = 1;
    endclass
    class cfg_child extends cfg_base;
      int mode = 2;  // hides: the object carries two integers named mode
    endclass

    Interview

    Trap answers to rehearse: non-virtual override, hidden data, and virtual calls in constructors.

    • Non-virtual resolves on handle type; virtual resolves on object type.

    • Data never dispatches; only the declared type of the access matters.

    • A virtual call from a constructor reaches the derived override before derived state is initialized, so constructors should only initialize their own level.

    Check yourself A base handle points at a child object and both classes define non-virtual report(). Which body runs?

    The base body. Non-virtual calls bind to the declared handle type at compile time.

    Check yourself A child declares int mode while the base already has int mode. How many integers does the object hold?

    Two. The child field hides the base field instead of overriding it, and each handle view sees its own copy.

    Testbench

    Predict first In the bench, b is a base handle pointing at a child object. What do b.who_nv() and b.who_v() return?

    1 and 2. who_nv is not virtual, so the handle's type picks base's body; who_v is virtual, so the object's type picks child's body.

    Dispatch codes plus a driver-hook pair with a testbench that proves which body runs.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    class base;
      function int unsigned who_nv();
        return 1;
      endfunction
      virtual function int unsigned who_v();
        return 1;
      endfunction
    endclass
    
    class child extends base;
      function int unsigned who_nv();
        return 2;
      endfunction
      virtual function int unsigned who_v();
        return 2;
      endfunction
    endclass
    
    class bus_pkt;
      bit [31:0] addr;
    endclass
    
    class base_driver;
      int unsigned n_pre = 0;
      virtual task pre_drive(inout bit [31:0] addr);
        n_pre++;
      endtask
      virtual task drive(inout bit [31:0] addr);
        addr = addr + 4;
      endtask
    endclass
    
    class err_driver extends base_driver;
      virtual task pre_drive(inout bit [31:0] addr);
        n_pre++;
        addr[1:0] = 2'b01;
      endtask
    endclass
    
    Terminal
    PASS: static calls use handle type, virtual calls and hooks use object type

    Remember

    • Non-virtual calls are fixed by the handle's declared type.

    • Virtual calls follow the object to its class.

    • Properties never dispatch: a same-named field in a child is a second field.

    Rule of thumb: anything a subclass may change is virtual in the base.

    How this was checked

    Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown, with no warnings. These class benches were run on Verilator only.

    Related topics

    • Next: Casting with $cast

    Previous

    41 / 259

    Next: Casting with $cast

    Up next

    Up nextCasting with $cast

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Contracts and polymorphic containers