diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 7c7f94186..4d3d26c4c 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -4157,17 +4157,28 @@ class RandomizeVisitor final : public VNVisitor { void makeModeInit(AstVar* modeVarp, AstClass* classp, uint32_t modeCount) { AstNodeModule* const modeVarModp = VN_AS(modeVarp->user2p(), NodeModule); FileLine* fl = modeVarp->fileline(); + AstVar* const oldSizeVarp = new AstVar{ + fl, VVarType::BLOCKTEMP, m_uniqueNames.get("__VoldSize"), modeVarp->findUInt32DType()}; + oldSizeVarp->funcLocal(true); + oldSizeVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); + AstCMethodHard* const oldSizep + = new AstCMethodHard{fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::READ}, + VCMethod::DYN_SIZE, nullptr}; + oldSizep->dtypeSetUInt32(); AstCMethodHard* const dynarrayNewp = new AstCMethodHard{fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE}, VCMethod::DYN_RESIZE, new AstConst{fl, modeCount}}; dynarrayNewp->dtypeSetVoid(); AstNodeFTask* const ctorNewp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask); UASSERT_OBJ(ctorNewp, classp, "No new() in class"); - // Build init chain: resize -> set-all-to-1 loop - AstNode* const initFirstp = dynarrayNewp->makeStmt(); + // Preserve inherited modes set by super.new() and initialize only newly added entries. + AstNode* const initFirstp = oldSizeVarp; initFirstp->addNext( - makeModeSetLoop(fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE}, - new AstConst{fl, 1}, true)); + new AstAssign{fl, new AstVarRef{fl, oldSizeVarp, VAccess::WRITE}, oldSizep}); + initFirstp->addNext(dynarrayNewp->makeStmt()); + initFirstp->addNext(makeModeSetLoop( + fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE}, new AstConst{fl, 1}, + true, new AstVarRef{fl, oldSizeVarp, VAccess::READ})); // Prepend init code before user statements in constructor body, but after // var declarations and super.new(). This ensures that user's constraint_mode() // or rand_mode() calls in the constructor execute after mode arrays are initialized. @@ -4216,7 +4227,7 @@ class RandomizeVisitor final : public VNVisitor { newp->addStmtsp(ifp); } AstNode* makeModeSetLoop(FileLine* const fl, AstNodeExpr* const lhsp, AstNodeExpr* const rhsp, - bool inTask) { + bool inTask, AstNodeExpr* const startp = nullptr) { AstVar* const iterVarp = new AstVar{fl, VVarType::BLOCKTEMP, "i", lhsp->findUInt32DType()}; iterVarp->funcLocal(inTask); iterVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); @@ -4227,8 +4238,8 @@ class RandomizeVisitor final : public VNVisitor { new AstVarRef{fl, iterVarp, VAccess::READ}}; setp->dtypeSetUInt32(); AstNode* const stmtsp = iterVarp; - stmtsp->addNext( - new AstAssign{fl, new AstVarRef{fl, iterVarp, VAccess::WRITE}, new AstConst{fl, 0}}); + stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, iterVarp, VAccess::WRITE}, + startp ? startp : new AstConst{fl, 0}}); AstLoop* const loopp = new AstLoop{fl}; stmtsp->addNext(loopp); diff --git a/test_regress/t/t_constraint_randmode_inherited.py b/test_regress/t/t_constraint_randmode_inherited.py new file mode 100755 index 000000000..73bd07159 --- /dev/null +++ b/test_regress/t/t_constraint_randmode_inherited.py @@ -0,0 +1,19 @@ +#!/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_constraint_randmode_inherited.v b/test_regress/t/t_constraint_randmode_inherited.v new file mode 100644 index 000000000..238f3dc5b --- /dev/null +++ b/test_regress/t/t_constraint_randmode_inherited.v @@ -0,0 +1,93 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// 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 + +typedef enum int { + FIRST, + SECOND, + THIRD +} Kind; + +class Base; + rand Kind kind; + + constraint c_kind { kind inside {FIRST, SECOND, THIRD}; } +endclass + +class Intermediate extends Base; + rand int value; + + constraint c_value { value == 31; } + + function new; + kind = THIRD; + kind.rand_mode(0); + endfunction +endclass + +class Derived extends Intermediate; + rand int extra; + + constraint c_extra { extra == 15; } +endclass + +class CombinedModes; + rand int value; + + constraint c_value { value == 7; } + + function new; + value = 9; + value.rand_mode(0); + c_value.constraint_mode(0); + endfunction +endclass + +module t; + initial begin + automatic Intermediate intermediate = new; + automatic Derived derived = new; + automatic CombinedModes combined_modes = new; + automatic int randomize_result; + + `checkd(combined_modes.value, 9) + `checkd(combined_modes.value.rand_mode(), 0) + `checkd(combined_modes.c_value.constraint_mode(), 0) + randomize_result = combined_modes.randomize(); + `checkd(randomize_result, 1) + `checkd(combined_modes.value, 9) + + `checkd(intermediate.kind, THIRD) + `checkd(intermediate.kind.rand_mode(), 0) + `checkd(derived.kind, THIRD) + `checkd(derived.kind.rand_mode(), 0) + + repeat (20) begin + randomize_result = intermediate.randomize(); + `checkd(randomize_result, 1) + `checkd(intermediate.kind, THIRD) + `checkd(intermediate.kind.rand_mode(), 0) + `checkd(intermediate.value, 31) + `checkd(intermediate.value.rand_mode(), 1) + + randomize_result = derived.randomize(); + `checkd(randomize_result, 1) + `checkd(derived.kind, THIRD) + `checkd(derived.kind.rand_mode(), 0) + `checkd(derived.value, 31) + `checkd(derived.value.rand_mode(), 1) + `checkd(derived.extra, 15) + `checkd(derived.extra.rand_mode(), 1) + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule