From 7934ab9eeb71a28f6556ef047545562ff8c83297 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Jun 2026 17:05:36 -0700 Subject: [PATCH 1/2] Support classes in generate blocks SystemVerilog allows class declarations as module and generate items. Currently a class declaration in a generate block triggers an assert because `pform_push_class_scope()` only records classes in `PScopeExtra` scopes. Add class storage to `PGenerate` and elaborate those classes like module and package classes. When registering task, function or class declarations, only use the current `PGenerate` object as the target if it is also the current lexical scope. This distinction matters for generated classes because `pform_cur_generate` remains set while the class body is parsed, but the current lexical scope has changed to the `PClass`. This records the class declaration in the generate block while leaving methods and constructors in the class scope. Signed-off-by: Lars-Peter Clausen --- PGenerate.h | 6 +++++- elab_scope.cc | 2 ++ elab_sig.cc | 1 + elaborate.cc | 1 + pform.cc | 27 ++++++++++++++++++--------- 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/PGenerate.h b/PGenerate.h index d2d293262..24745c7dc 100644 --- a/PGenerate.h +++ b/PGenerate.h @@ -26,10 +26,12 @@ # include # include # include +# include # include "pform_types.h" class Design; class NetScope; +class PClass; class PExpr; class PFunction; class PProcess; @@ -92,9 +94,11 @@ class PGenerate : public PNamedItem, public LexicalScope { std::list gates; void add_gate(PGate*); - // Tasks instantiated within this scheme. + // Definitions instantiated within this scheme. std::map tasks; std::mapfuncs; + std::map classes; + std::vector classes_lexical; // Generate schemes can contain further generate schemes. std::list generate_schemes; diff --git a/elab_scope.cc b/elab_scope.cc index 4b233348d..43d46de3b 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1324,6 +1324,8 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope) elaborate_scope_enumerations(des, scope, enum_sets); + elaborate_scope_classes(des, scope, classes_lexical); + // Run through the defparams for this scope and save the result // in a table for later final override. diff --git a/elab_sig.cc b/elab_sig.cc index f23145c22..5b752c617 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -607,6 +607,7 @@ bool PGenerate::elaborate_sig_(Design*des, NetScope*scope) const elaborate_sig_funcs(des, scope, funcs); elaborate_sig_tasks(des, scope, tasks); + elaborate_sig_classes(des, scope, classes); typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() diff --git a/elaborate.cc b/elaborate.cc index ba327a815..e4894fd6e 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -7228,6 +7228,7 @@ bool PGenerate::elaborate_(Design*des, NetScope*scope) const if (result_flag) { elaborate_functions(des, scope, funcs); elaborate_tasks(des, scope, tasks); + elaborate_classes(des, scope, classes); for (const auto gt : gates) gt->elaborate(des, scope); diff --git a/pform.cc b/pform.cc index 59e264326..d9249910c 100644 --- a/pform.cc +++ b/pform.cc @@ -449,6 +449,11 @@ static PScopeExtra* find_nearest_scopex(LexicalScope*scope) return scopex; } +static PGenerate* current_generate_scope() +{ + return lexical_scope == pform_cur_generate ? pform_cur_generate : nullptr; +} + static void add_local_symbol(LexicalScope*scope, perm_string name, PNamedItem*item) { assert(scope); @@ -585,12 +590,16 @@ PClass* pform_push_class_scope(const struct vlltype&loc, perm_string name) PScopeExtra*scopex = find_nearest_scopex(lexical_scope); ivl_assert(loc, scopex); - ivl_assert(loc, !pform_cur_generate); pform_set_scope_timescale(class_scope, scopex); - scopex->classes[name] = class_scope; - scopex->classes_lexical .push_back(class_scope); + if (auto generate_scope = current_generate_scope()) { + generate_scope->classes[name] = class_scope; + generate_scope->classes_lexical.push_back(class_scope); + } else { + scopex->classes[name] = class_scope; + scopex->classes_lexical.push_back(class_scope); + } lexical_scope = class_scope; return class_scope; @@ -632,9 +641,9 @@ PTask* pform_push_task_scope(const struct vlltype&loc, const char*name, pform_set_scope_timescale(task, scopex); - if (pform_cur_generate) { - add_local_symbol(pform_cur_generate, task_name, task); - pform_cur_generate->tasks[task_name] = task; + if (auto generate_scope = current_generate_scope()) { + add_local_symbol(generate_scope, task_name, task); + generate_scope->tasks[task_name] = task; } else { add_local_symbol(scopex, task_name, task); scopex->tasks[task_name] = task; @@ -667,9 +676,9 @@ PFunction* pform_push_function_scope(const struct vlltype&loc, const char*name, pform_set_scope_timescale(func, scopex); - if (pform_cur_generate) { - add_local_symbol(pform_cur_generate, func_name, func); - pform_cur_generate->funcs[func_name] = func; + if (auto generate_scope = current_generate_scope()) { + add_local_symbol(generate_scope, func_name, func); + generate_scope->funcs[func_name] = func; } else { add_local_symbol(scopex, func_name, func); From bb8b05bb5ddea26e1de3618d595e56ae33739708 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Jun 2026 17:05:56 -0700 Subject: [PATCH 2/2] Add regression test for classes in generate blocks Check that a class declared in a conditional generate block can be used. Also check that classes declared in a generate loop get separate class scopes for each generated instance. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/br_gh1384.v | 50 +++++++++++++++++++++++++++++++++ ivtest/regress-vvp.list | 1 + ivtest/vvp_tests/br_gh1384.json | 9 ++++++ 3 files changed, 60 insertions(+) create mode 100644 ivtest/ivltests/br_gh1384.v create mode 100644 ivtest/vvp_tests/br_gh1384.json diff --git a/ivtest/ivltests/br_gh1384.v b/ivtest/ivltests/br_gh1384.v new file mode 100644 index 000000000..f58f8a283 --- /dev/null +++ b/ivtest/ivltests/br_gh1384.v @@ -0,0 +1,50 @@ +// Check that classes declared inside generate blocks can be used. + +module test; + + reg failed; + + `define check(val, exp) \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end + + if (1) begin : g + class C; + int value; + + function new(int value); + this.value = value; + endfunction + endclass + + C c = new(42); + end + + for (genvar i = 0; i < 2; i = i + 1) begin : h + class C; + int value; + + function new(int value); + this.value = value + i; + endfunction + endclass + + C c = new(10); + end + + initial begin + failed = 1'b0; + + `check(g.c.value, 42); + `check(h[0].c.value, 10); + `check(h[1].c.value, 11); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index d9a8f3e7b..b66ada6b5 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -82,6 +82,7 @@ br_gh1258a vvp_tests/br_gh1258a.json br_gh1258b vvp_tests/br_gh1258b.json br_gh1286 vvp_tests/br_gh1286.json br_gh1323 vvp_tests/br_gh1323.json +br_gh1384 vvp_tests/br_gh1384.json br_gh1385 vvp_tests/br_gh1385.json br_gh1385a vvp_tests/br_gh1385a.json br_gh1385b vvp_tests/br_gh1385b.json diff --git a/ivtest/vvp_tests/br_gh1384.json b/ivtest/vvp_tests/br_gh1384.json new file mode 100644 index 000000000..2f6ac5c8b --- /dev/null +++ b/ivtest/vvp_tests/br_gh1384.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "br_gh1384.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +}