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.
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.
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.
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
endmoduleBUS WR addr=1000 data=deadbeef
AXI id=3 burst=1 qos=7
PASS: extends compare checks base fields, qos, and null rhsAll base fields. Only the derived-only comparisons remain, so addr and data mismatches pass silently.
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.
One way to write it:
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
good '{'h22, 'ha5, 'h3c, 'hbb}
bad '{'h22, 'ha5, 'h3c, 'h44}
PASS: err_pkt reuses pkt_txn's bytes and flips only the check byteRun 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.