Every class has exactly one constructor, named new. It cannot be overloaded, so default argument values cover the overloaded cases.
A constructor puts the object into a valid initial state. It stores identity, builds owned sub-objects, and captures references passed in.
The bench below builds three configurations from one constructor. Arguments can be given by position, or by name, which skips the ones in between.
class fifo_cfg;
string name;
int unsigned depth;
bit strict;
// One constructor; defaults stand in for overloads.
function new(string name = "cfg", int unsigned depth = 4, bit strict = 1);
this.name = name;
this.depth = depth;
this.strict = strict;
endfunction
endclass
module tb_cfg_defaults;
int errors = 0;
initial begin
fifo_cfg a;
fifo_cfg b;
fifo_cfg c;
a = new(); // every default
b = new("big", 16); // strict keeps its default
c = new(.name("loose"), .strict(0)); // named arguments skip depth
if (a.name != "cfg" || a.depth != 4 || a.strict != 1) errors++;
if (b.name != "big" || b.depth != 16 || b.strict != 1) errors++;
if (c.name != "loose" || c.depth != 4 || c.strict != 0) errors++;
$display("%s depth=%0d strict=%0d", a.name, a.depth, a.strict);
$display("%s depth=%0d strict=%0d", b.name, b.depth, b.strict);
$display("%s depth=%0d strict=%0d", c.name, c.depth, c.strict);
if (errors == 0) $display("PASS: one constructor with defaults built three different configurations");
else $fatal(1, "FAIL: %0d constructor default checks failed", errors);
$finish;
end
endmodulecfg depth=4 strict=1
big depth=16 strict=1
loose depth=4 strict=0
PASS: one constructor with defaults built three different configurationsclass axi_req extends base_req makes axi_req a derived class: it has everything base_req has, plus its own fields. The next topic teaches inheritance properly; here only one rule matters, how the two constructors run.
Constructing a derived object must also initialize its base part. The derived constructor therefore calls super.new as its first statement.
An omitted call inserts an implicit super.new with no arguments. That call works only when the parent takes no arguments or every argument has a default.
Parent constructor | Child omits super.new | Result |
|---|---|---|
Has a required argument | Implicit super.new supplies none | Compile error |
All arguments defaulted | Implicit super.new uses defaults | Builds, with possibly wrong values |
Explicit super.new(name) | Nothing omitted | Parent initialized from real values |
// bad-example
class axi_bad extends base_req;
int unsigned qos;
function new(string name = "axi_bad");
this.qos = 0; // compile error: implicit super.new() supplies no argument
endfunction
endclassnew(string name = "UNNAMED"). A child's constructor forgets super.new(name). What is the name after new("s0")?UNNAMED. The child's constructor never passed s0 on, so the implicit super.new() ran with the parent's default. It compiles, it runs, and every object carries the wrong name.
A compile error points at the missing call. Defaulted parent arguments instead build an object that carries the wrong name or config and keeps simulating.
Child constructor | Base name after new("s0") | Verdict |
|---|---|---|
Omits super.new |
| Silent trap: the forwarded name is lost |
Calls super.new(name) |
| Correct: the parent sees the real value |
One self-contained block holds both hierarchies and the checks. Forwarding works, the constructor body sees initialized base state, and the silent variant keeps its default.
class base_req;
string name;
function new(string name); // REQUIRED argument, no default
this.name = name;
endfunction
endclass
class axi_req extends base_req;
int unsigned qos;
int order_ok;
function new(string name = "axi_req");
super.new(name);
if (this.name != name) order_ok = 0;
else order_ok = 1;
this.qos = 0;
endfunction
endclass
class base_dflt;
string name;
function new(string name = "UNNAMED");
this.name = name;
endfunction
endclass
class child_dflt extends base_dflt;
function new(string name = "child2");
// forgot super.new(name): implicit super.new() runs instead
// object is built, but base name stays "UNNAMED"
endfunction
endclass
class child_fwd extends base_dflt;
function new(string name = "child2");
super.new(name);
endfunction
endclass
module tb_l3_chain;
int errors = 0;
initial begin
axi_req a;
axi_req d;
child_dflt s;
child_fwd f;
a = new("a0");
if (a.name != "a0") errors++;
if (a.qos != 0) errors++;
if (a.order_ok != 1) errors++;
d = new();
if (d.name != "axi_req") errors++;
s = new("s0");
if (s.name != "UNNAMED") errors++;
f = new("f0");
if (f.name != "f0") errors++;
if (errors == 0) $display("PASS: super.new forwarding and silent-default trap behave as specified");
else $fatal(1, "FAIL: constructor chain checks");
$finish;
end
endmodulePASS: super.new forwarding and silent-default trap behave as specifiedA compile error. The implicit super.new supplies no arguments.
It compiles. The parent initializes from defaults, which silently carries the wrong name or config.
Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown, with no warnings. Icarus 13 cannot run the ones that compare class handles or keep a queue inside a class.