From 6c96ce4b407d755967246fd4646b1c0af63274eb Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Sun, 24 May 2026 21:02:01 +0800 Subject: [PATCH] Fix inherited rand array with .size + foreach constraint (#7650) Fixes #7558. --- src/V3Randomize.cpp | 20 +++- .../t_randomize_class_inherit_size_foreach.py | 21 +++++ .../t_randomize_class_inherit_size_foreach.v | 93 +++++++++++++++++++ 3 files changed, 130 insertions(+), 4 deletions(-) create mode 100755 test_regress/t/t_randomize_class_inherit_size_foreach.py create mode 100644 test_regress/t/t_randomize_class_inherit_size_foreach.v diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index b88cddbfe..3fcaf5535 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -4818,7 +4818,12 @@ class RandomizeVisitor final : public VNVisitor { pinp->addPinsp(namep); pinp->addPinsp(new AstConst{fl, AstConst::Unsized64{}, static_cast(sizeVarp->width())}); - pinp->addPinsp(new AstVarRef{fl, sizeVarp, VAccess::READ}); + // sizeVarp may live in a base class when the constrained + // array is inherited; route VarRef through its declaring + // class so V3Scope can resolve it. + AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ}; + sizeVarRefp->classOrPackagep(VN_AS(sizeVarp->user2p(), NodeModule)); + pinp->addPinsp(sizeVarRefp); randomizep->addStmtsp(pinp->makeStmt()); } @@ -5265,12 +5270,18 @@ class RandomizeVisitor final : public VNVisitor { bool wasCreated = false; AstVar* const sizeVarp = createOrGetSizeVar(classp, arrVarp, fl, methodp->findIntDType(), wasCreated); + // arrVarp and sizeVarp may live in a base class when the + // array is inherited; route VarRefs through their declaring + // class so V3Scope can resolve them. + AstNodeModule* const arrClassp = VN_AS(arrVarp->user2p(), NodeModule); + AstNodeModule* const sizeClassp = VN_AS(sizeVarp->user2p(), NodeModule); if (wasCreated) { // Generate resize for dynamic arrays/queues (not assoc arrays) if (!VN_IS(arrVarp->dtypep()->skipRefp(), AssocArrayDType)) { AstCMethodHard* const resizep = new AstCMethodHard{ - fl, new AstVarRef{fl, classp, arrVarp, VAccess::READWRITE}, - VCMethod::DYN_RESIZE, new AstVarRef{fl, sizeVarp, VAccess::READ}}; + fl, new AstVarRef{fl, arrClassp, arrVarp, VAccess::READWRITE}, + VCMethod::DYN_RESIZE, + new AstVarRef{fl, sizeClassp, sizeVarp, VAccess::READ}}; resizep->dtypep(methodp->findVoidDType()); inlineResizeStmtsp = AstNode::addNext(inlineResizeStmtsp, new AstStmtExpr{fl, resizep}); @@ -5279,7 +5290,8 @@ class RandomizeVisitor final : public VNVisitor { // Append size >= 0 constraint so ConstraintExprVisitor processes it capturedTreep->addNext(createSizeGteZeroConstraint(fl, sizeVarp)); } - AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ}; + AstVarRef* const sizeVarRefp + = new AstVarRef{fl, sizeClassp, sizeVarp, VAccess::READ}; sizeVarRefp->user1(true); methodp->replaceWith(sizeVarRefp); VL_DO_DANGLING(methodp->deleteTree(), methodp); diff --git a/test_regress/t/t_randomize_class_inherit_size_foreach.py b/test_regress/t/t_randomize_class_inherit_size_foreach.py new file mode 100755 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_randomize_class_inherit_size_foreach.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_class_inherit_size_foreach.v b/test_regress/t/t_randomize_class_inherit_size_foreach.v new file mode 100644 index 000000000..a144c635a --- /dev/null +++ b/test_regress/t/t_randomize_class_inherit_size_foreach.v @@ -0,0 +1,93 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 PlanV GmbH +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +// Scenario A: minimized reproducer from issue #7558 reporter (2026-05-15). +// Dynamic array, parameterized virtual class wrapping the rand class, +// 3-level extends, randomize() reached indirectly via `req` field. +package uvm_pkg; + virtual class uvm_sequence #(type REQ = int); + REQ req; + endclass +endpackage + +package s_t_pkg; + import uvm_pkg::*; + parameter S_D_WIDTH = 64; + + class s_txn_base; + rand logic [S_D_WIDTH-1:0] p_d[]; + rand int d_s_dw; + constraint d_s_dw_c { + p_d.size() == d_s_dw / 16; + d_s_dw inside {[16 : 80]}; + } + endclass + + class s_t_txn extends s_txn_base; + constraint d_incremental { + foreach (p_d[i]) if (i == 0) p_d[i] == '0; + } + endclass + + class t_base_sequence extends uvm_sequence#(s_t_txn); + endclass + + class t_base_port_seq extends t_base_sequence; + endclass + + class p_c_seq extends t_base_port_seq; + virtual task body(); + void'(req.randomize()); + endtask + endclass +endpackage + +// Scenario B: same bug class but on a queue (rand int q[$]) -- exercises +// the queue dtype dispatch in V3Randomize, distinct from dynamic array. +package q_pkg; + class q_base; + rand int q[$]; + constraint c_size {q.size() == 3;} + endclass + + class q_derived extends q_base; + constraint c_inc {foreach (q[i]) q[i] == i * 10;} + endclass +endpackage + +module t; + import s_t_pkg::*; + import q_pkg::*; + + initial begin + // Scenario A: solomatnikov reproducer + static p_c_seq seq = new(); + seq.req = new(); + repeat (5) begin + seq.body(); + `checkd(seq.req.p_d.size(), seq.req.d_s_dw / 16) + if (seq.req.p_d.size() > 0) `checkd(seq.req.p_d[0], 0) + end + + // Scenario B: queue extension + begin + automatic q_derived qd = new(); + repeat (5) begin + `checkd(qd.randomize(), 1) + `checkd(qd.q.size(), 3) + foreach (qd.q[i]) `checkd(qd.q[i], i * 10) + end + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule