Part 2 · OOP for Verification · Intermediate

extends & super

Derived classes, method overriding, super.method(), and the base bus_txn → axi_txn pattern.

What extends gives you

Writing class axi_txn extends bus_txn; makes axi_txn a specialized version of bus_txn: every property, method, and constraint of the base is part of the derived class automatically. The derived class then adds new members and overrides methods whose behavior must change. In memory, a derived object is one contiguous object containing the base part plus the derived extensions — not two linked objects. SystemVerilog supports single inheritance only: one parent per class (interface classes, covered later, recover multiple contracts).

diagram
ONE OBJECT, LAYERED LAYOUT

  axi_txn t = new();

       ┌──────────────────────────┐
       │  bus_txn part            │   inherited: addr, data, write,
       │   addr / data / write    │   print(), compare(), c_align
       ├──────────────────────────┤
       │  axi_txn part            │   added: id, burst, qos
       │   id / burst / qos       │   overridden: print()
       └──────────────────────────┘

  Single inheritance chain:  axi_txn ─extends─► bus_txn

Base bus_txn → derived axi_txn

systemverilog
class bus_txn;
  rand bit [31:0] addr;
  rand bit [31:0] data;
  rand bit        write;

  constraint c_align { addr[1:0] == 2'b00; }

  function new(string name = "bus_txn");
  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);
    return (addr == rhs.addr) && (data == rhs.data)
        && (write == rhs.write);
  endfunction
endclass

class axi_txn extends bus_txn;
  rand bit [3:0] id;
  rand bit [1:0] burst;     // FIXED / INCR / WRAP
  rand bit [3:0] qos;

  constraint c_burst { burst inside {[0:2]}; }   // ADDS to c_align

  function new(string name = "axi_txn");
    super.new(name);
  endfunction

  // OVERRIDE: extend, don't replace — reuse base via super
  virtual function void print();
    super.print();                       // base fields first
    $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;   // base fields must match
    if (!$cast(arhs, rhs))   return 0;   // rhs not an axi_txn
    return (id == arhs.id) && (burst == arhs.burst);
  endfunction
endclass

What is inherited and what combines

  • Properties and methods — all inherited; derived code uses them as its own.

  • Constraints — inherited AND combined: axi_txn::randomize() solves c_align and c_burst together. A same-named constraint in the derived class replaces the base one.

  • Constructors — NOT inherited; each class defines new() and chains with super.new() (see Constructors lesson).


super — extending instead of replacing

super.method() calls the immediate parent's version of a method from inside an override. The dominant testbench idiom is extend-then-add : call super first so base behavior (printing base fields, comparing base fields, base initialization) always runs, then layer the derived additions. Overrides that forget super silently drop base behavior — a derived compare() that skips super.compare(rhs) stops checking addr/data entirely, and the scoreboard goes blind to base-field mismatches while appearing to work.

Override signature rules

  • An override must match the base signature (name, arguments, return type) — for virtual methods the LRM requires it.

  • Matching signatures keep base-handle calls valid: callers compiled against the base prototype work on any derived object.

  • You cannot reduce visibility in an override (a base-visible method must stay callable through the base).

Interview angle

  • 'What does a derived class inherit?' — everything except constructors; constraints merge, same-named constraints override.

  • 'Why call super.print() in an override?' — extend-not-replace; otherwise base fields vanish from logs and compares.

  • 'How many parents can a class have?' — one (single inheritance); interface classes provide multiple contracts.

Key takeaways

  • extends builds one object with a base layer plus derived extensions — single inheritance only.

  • Constraints from base and derived are solved together; same-named constraints override.

  • super.method() reaches the parent's version — the extend-then-add idiom keeps base behavior alive in overrides.

  • Overrides must match the base signature so base-handle callers stay valid.

Common pitfalls

  • Overriding compare()/print() without calling super — base fields silently dropped from checks and logs.

  • Reusing a base constraint's name unintentionally — replaces the base constraint instead of adding to it.

  • Expecting constructors to be inherited — every derived class must define new() and chain super.new().

  • Changing argument lists in an override — breaks polymorphic calls through base handles.