SV: parse ANSI class parameter port lists

Allow class C #(type T = int, ...) so parameterized classes with defaults
elaborate; add smoke examples and hello_uvm Makefile.
This commit is contained in:
mjoekhan 2026-07-21 16:25:09 +05:00
parent 4fc765f912
commit 372e93db3a
7 changed files with 115 additions and 6 deletions

View File

@ -12,8 +12,8 @@ Last updated: 2026-07-21
|------|--------|
| Icarus Verilog tree | Fork of `steveicarus/iverilog` (`master` @ merge of SV array ordering work) |
| [`uvm/`](../uvm/) | Seeded from IVL_UVM (VerifWorks) — messaging, CLP, stub phases, poor-mans mailbox/semaphore; **not** Accellera-compatible |
| [`examples/hello_uvm`](../examples/hello_uvm) | Smoke TB for the seeded library |
| Parameterized classes | **In progress** on `feat/param-classes` |
| [`examples/hello_uvm`](../examples/hello_uvm) | Smoke TB for the seeded library (`Makefile` included) |
| Parameterized classes | **Partial** on `feat/param-classes`: ANSI `class C #(type T = int, parameter int W = 8);` parses into the class scope and elaborates with **defaults**. Explicit specializations `C#(byte)` / overrides not done yet. See [`examples/param_classes`](../examples/param_classes). |
| Associative arrays | Missing |
| Virtual interfaces | Missing |
| Constraints / randomize | Missing |

View File

@ -0,0 +1,24 @@
# Build/run hello_uvm against the seeded uvm/ (IVL_UVM) library.
# Requires a configured install under ../../install (or IVERILOG/VVP on PATH).
ROOT ?= $(abspath ../..)
IVL_UVM_HOME ?= $(ROOT)/uvm
PREFIX ?= $(ROOT)/install
IVERILOG ?= $(PREFIX)/bin/iverilog
VVP ?= $(PREFIX)/bin/vvp
VVP_MOD ?= $(PREFIX)/lib/ivl
OUT := hello_uvm.vvp
.PHONY: all run clean
all: $(OUT)
$(OUT): hello_uvm.sv flist.f
IVL_UVM_HOME=$(IVL_UVM_HOME) $(IVERILOG) -g2012 -o $@ -c flist.f
run: $(OUT)
$(VVP) -M $(VVP_MOD) $(OUT)
clean:
rm -f $(OUT)

View File

@ -0,0 +1,29 @@
// Smoke: ANSI class parameter port list with defaults (no explicit #() override).
class box #(type T = int, parameter int W = 8);
T val;
bit [W-1:0] data;
function new(T v);
val = v;
data = v;
endfunction
function T get();
return val;
endfunction
endclass
module top;
box b;
initial begin
b = new(42);
if (b.get() !== 42) begin
$display("FAIL: get=%0d", b.get());
$finish(1);
end
if (b.data !== 8'h2a) begin
$display("FAIL: data=%0h", b.data);
$finish(1);
end
$display("PASS box_default val=%0d data=%0h", b.val, b.data);
$finish;
end
endmodule

View File

@ -0,0 +1,21 @@
// Regression: plain (non-parameterized) class still works.
class counter;
int n;
function new(); n = 0; endfunction
function void inc(); n++; endfunction
endclass
module top;
counter c;
initial begin
c = new;
c.inc();
c.inc();
if (c.n !== 2) begin
$display("FAIL n=%0d", c.n);
$finish(1);
end
$display("PASS plain_class n=%0d", c.n);
$finish;
end
endmodule

15
parse.y
View File

@ -1237,7 +1237,9 @@ assertion_item_label_opt
;
class_declaration /* IEEE1800-2005: A.1.2 */
: K_virtual_opt K_class lifetime_opt identifier_name class_declaration_extends_opt ';'
/* Push the class scope before the optional #(...) parameter port list so
parameters land in the class LexicalScope (same idea as modules). */
: K_virtual_opt K_class lifetime_opt identifier_name
{ /* Up to 1800-2017 the grammar in the LRM allowed an optional lifetime
* qualifier for class declarations. But the LRM never specified what
* this qualifier should do. Starting with 1800-2023 the qualifier has
@ -1253,15 +1255,20 @@ class_declaration /* IEEE1800-2005: A.1.2 */
class_type_t *class_type= new class_type_t(name);
FILE_NAME(class_type, @4);
pform_set_typedef(@4, name, class_type, nullptr);
pform_start_class_declaration(@2, class_type, $5.type, $5.args, $1);
/* Base class is filled in after parameter_port_list / extends. */
pform_start_class_declaration(@2, class_type, 0, 0, $1);
}
module_parameter_port_list_opt
class_declaration_extends_opt ';'
{ pform_set_class_extends(@7, $7.type, $7.args);
}
class_items_opt K_endclass
{ // Process a class.
pform_end_class_declaration(@9);
pform_end_class_declaration(@11);
}
label_opt
{ // Wrap up the class.
check_end_label(@11, "class", $4, $11);
check_end_label(@13, "class", $4, $13);
delete[] $4;
}
;

View File

@ -191,6 +191,9 @@ extern void pform_class_property(const struct vlltype&loc,
extern void pform_set_this_class(const struct vlltype&loc, PTaskFunc*net);
extern void pform_set_constructor_return(PFunction*net);
extern void pform_set_class_extends(const struct vlltype&loc,
data_type_t*base_type,
std::list<named_pexpr_t> *base_args);
extern void pform_end_class_declaration(const struct vlltype&loc);
extern bool pform_in_class();

View File

@ -64,6 +64,31 @@ void pform_start_class_declaration(const struct vlltype&loc,
}
}
void pform_set_class_extends(const struct vlltype&loc,
data_type_t*base_type,
list<named_pexpr_t> *base_args)
{
ivl_assert(loc, pform_cur_class);
class_type_t*type = pform_cur_class->type;
ivl_assert(loc, type);
if (base_type == 0 && (base_args == 0 || base_args->empty())) {
if (base_args)
delete base_args;
return;
}
ivl_assert(loc, type->base_type == 0);
type->base_type.reset(base_type);
ivl_assert(loc, type->base_args.empty());
if (base_args) {
type->base_args.insert(type->base_args.begin(), base_args->begin(),
base_args->end());
delete base_args;
}
}
void pform_class_property(const struct vlltype&loc,
property_qualifier_t property_qual,
data_type_t*data_type,