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

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
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. extends and super
On this page

    Part 2 of 10, lesson 6 of 30Part 2 of 10/OOP for Verification/Lesson 6 of 30Intermediate

    extends and super

    Derived classes, super calls, and the bus to AXI transaction pattern.

    A derived transaction's compare() checked its new fields and quietly stopped checking the old ones. The override replaced the base method instead of extending it.

    After this lesson you can: derive a class with extends, call the parent with super, and say what a derived class inherits.

    One object with two layers

    Writing class axi_txn extends bus_txn makes axi_txn a specialized bus_txn. Every property, method, and constraint of the base becomes part of the derived class.

    A derived object is one contiguous object with a base layer plus derived extensions. SystemVerilog allows single inheritance only: one parent per class.

    A derived object is one object: the base part plus the derived part.

    Extend, do not replace

    An override should call its parent version first through super.method(), then layer derived behavior on top. Forgetting super silently drops base behavior from prints and compares.

    Constructors are not inherited. Each class defines new() and chains with super.new(), so construction runs base-first.

    Member

    What the derived class gets

    Properties and methods

    All inherited; derived code uses them as its own.

    Constructors

    Not inherited; each new() chains with super.new().

    Constraints are inherited too, and a derived constraint with the same name replaces the base one. Constraint Override via Inheritance covers that once randomization is familiar.

    Testbench

    Predict first The bench ends with a.print(). axi_txn's print() calls super.print() first. What two lines appear?

    BUS WR addr=1000 data=deadbeef, then AXI id=3 burst=1 qos=7. The base part prints its fields, and the derived part adds its own below.

    The transaction pair plus a testbench that proves base fields, qos, and null handling.

    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
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    class bus_txn;
      bit [31:0] addr;
      bit [31:0] data;
      bit        write;
    
      function new();
      endfunction
    
      virtual function void print();
        $display("BUS %s addr=%0h data=%0h", write ? "WR" : "RD", addr, data);
      endfunction
    
      virtual function bit compare(bus_txn rhs);
        if (rhs == null) return 0;
        return (addr == rhs.addr) && (data == rhs.data) && (write == rhs.write);
      endfunction
    endclass
    
    class axi_txn extends bus_txn;
      bit [3:0] id;
      bit [1:0] burst;
      bit [3:0] qos;
    
      function new();
        super.new();
      endfunction
    
      virtual function void print();
        super.print();
        $display("  AXI id=%0d burst=%0d qos=%0d", id, burst, qos);
      endfunction
    
      virtual function bit compare(bus_txn rhs);
        axi_txn arhs;
        if (!super.compare(rhs)) return 0;
        if (!$cast(arhs, rhs)) return 0;
        return (id == arhs.id) && (burst == arhs.burst) && (qos == arhs.qos);
      endfunction
    endclass
    
    module tb_extends;
      int errors = 0;
      axi_txn a;
      axi_txn b;
      bus_txn bh;
      initial begin
        a = new(); b = new();
        a.addr = 32'h1000; a.data = 32'hdead_beef; a.write = 1;
        a.id = 4'd3; a.burst = 2'd1; a.qos = 4'd7;
        b.addr = 32'h1000; b.data = 32'hdead_beef; b.write = 1;
        b.id = 4'd3; b.burst = 2'd1; b.qos = 4'd7;
        if (!a.compare(b)) errors++;
        b.qos = 4'd0;
        if (a.compare(b)) errors++;
        b.qos = 4'd7;
        if (a.compare(null)) errors++;
        bh = a;
        if (!bh.compare(b)) errors++;
        a.print();
        if (errors == 0) $display("PASS: extends compare checks base fields, qos, and null rhs");
        else $fatal(1, "FAIL: %0d compare checks wrong", errors);
        $finish;
      end
    endmodule
    Terminal
    BUS WR addr=1000 data=deadbeef
      AXI id=3 burst=1 qos=7
    PASS: extends compare checks base fields, qos, and null rhs

    Common mistakes

    Overriding compare() or print() without calling super drops every base field from the check or the log. The checking code keeps passing while blind to base-field mismatches.

    Likely questions: what does a derived class inherit, why call super.print() in an override, and how many parents can a class have.

    • Everything except constructors.

    • Extend-not-replace keeps base fields in logs and compares.

    • One parent; interface classes add extra contracts without extra parents.

    Check yourself A derived compare() skips super.compare(rhs). Which fields stop being checked?

    All base fields. Only the derived-only comparisons remain, so addr and data mismatches pass silently.

    Check yourself axi_txn's new() leaves out super.new(). Does bus_txn's constructor still run?

    Yes. The compiler inserts a call to super.new() with no arguments. That only works because bus_txn's new() takes none; a required argument would make the missing call a compile error.

    Derive err_pkt from pkt_txn for error injection: give pkt_txn a virtual to_bytes() that returns header, payload and check byte, and have err_pkt call super.to_bytes() and then flip the check byte. Through a pkt_txn handle, the corrupt bytes must still come out.

    One way to write it:

    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
    typedef enum bit [1:0] {DATA = 2'd0, CTRL = 2'd1, ERR = 2'd2} kind_e;
    typedef bit [7:0] bytes_t[$];
    
    class pkt_txn;
      kind_e     kind;
      bit [1:0]  dest;
      bit [3:0]  len;
      bit [7:0]  payload[$];
    
      function bit [7:0] header();
        return {kind, dest, len};
      endfunction
    
      function bit [7:0] check_byte();
        bit [7:0] c = header();
        foreach (payload[i]) c ^= payload[i];
        return c;
      endfunction
    
      // The bytes that go on the wire: header, payload, check byte.
      virtual function bytes_t to_bytes();
        bytes_t b = {header()};
        foreach (payload[i]) b.push_back(payload[i]);
        b.push_back(check_byte());
        return b;
      endfunction
    endclass
    
    // A packet that lies about its check byte, for error-injection tests.
    class err_pkt extends pkt_txn;
      bit corrupt = 1;
    
      virtual function bytes_t to_bytes();
        bytes_t b = super.to_bytes();          // extend, do not replace
        if (corrupt) b[b.size() - 1] = ~b[b.size() - 1];
        return b;
      endfunction
    endclass
    
    Terminal
    good '{'h22, 'ha5, 'h3c, 'hbb}
    bad  '{'h22, 'ha5, 'h3c, 'h44}
    PASS: err_pkt reuses pkt_txn's bytes and flips only the check byte
    • A derived object is one object: the base part plus the derived part.

    • Everything is inherited except constructors; new() chains with super.new().

    • An override that should add behavior calls super.method() first.

    Rule of thumb: extend the parent's behavior; do not replace it by accident.

    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: Virtual dispatch

    Previous

    40 / 259

    Next: Virtual dispatch

    Up next

    Up nextVirtual dispatch

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Casting with $cast
    • Contracts and polymorphic containers