diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index f44e1745b..246650488 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -80,6 +80,13 @@ static AstVar* getRandModeVarFromClass(AstNodeModule* classp) { return nullptr; } +// Walk extends chain to find random generator variable (stored in AstClass::user3p). +static AstVar* getRandomGenerator(AstClass* const classp) { + if (classp->user3p()) return VN_AS(classp->user3p(), Var); + if (classp->extendsp()) return getRandomGenerator(classp->extendsp()->classp()); + return nullptr; +} + // ###################################################################### // Establishes the target of a rand_mode() call @@ -2459,22 +2466,18 @@ class ConstraintExprVisitor final : public VNVisitor { VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } void visit(AstConstraintUnique* nodep) override { - if (!m_classp) { - nodep->v3warn(CONSTRAINTIGN, - "Unsupported: Unique constraint in std::randomize() with {}"); - pushDeletep(nodep->unlinkFrBack()); - return; - } - + AstNodeFTask* initTaskp; + AstVar* genVarp = m_genp; FileLine* const fl = nodep->fileline(); + AstNodeModule* genModp = nullptr; - AstNodeFTask* const initTaskp = VN_AS(m_memberMap.findMember(m_classp, "new"), NodeFTask); - UASSERT_OBJ(initTaskp, nodep, "Class has no init Task"); - - AstVar* const genVarp = [](const AstClass* classp) { - while (classp->extendsp()) classp = classp->extendsp()->classp(); - return VN_AS(classp->user3p(), Var); - }(m_classp); + if (m_classp) { + initTaskp = VN_AS(m_memberMap.findMember(m_classp, "new"), NodeFTask); + genVarp = getRandomGenerator(m_classp); + } else { + initTaskp = m_inlineInitTaskp; + } + UASSERT_OBJ(initTaskp, nodep, "No init Task for unique constraint"); // UASSERT_OBJ(genVarp, nodep, "No generator variable"); if (!genVarp) { @@ -2483,9 +2486,10 @@ class ConstraintExprVisitor final : public VNVisitor { pushDeletep(nodep->unlinkFrBack()); return; } - - AstNodeModule* const genModp = VN_AS(genVarp->user2p(), NodeModule); - UASSERT_OBJ(genModp, nodep, "genVarp has no NodeModule set"); + if (m_classp) { + genModp = VN_AS(genVarp->user2p(), NodeModule); + UASSERT_OBJ(genModp, nodep, "genVarp inside m_classp has no NodeModule set"); + } // Registration calls emitted where the unique statement stood, so they end up // in the constraint setup task and re-run on every randomize() @@ -2556,7 +2560,9 @@ class ConstraintExprVisitor final : public VNVisitor { AstCMethodHard* const writeVarCallp = new AstCMethodHard{ fl, new AstVarRef{fl, genModp, genVarp, VAccess::READ}, VCMethod::RANDOMIZER_WRITE_VAR}; - writeVarCallp->addPinsp(new AstVarRef{fl, varModp, varp, VAccess::READ}); + AstVarRef* const argVarRefp = new AstVarRef{fl, varp, VAccess::READ}; + if (m_classp) argVarRefp->classOrPackagep(varModp); + writeVarCallp->addPinsp(argVarRefp); writeVarCallp->addPinsp(new AstConst{fl, AstConst::Unsized64{}, elemWidth}); writeVarCallp->addPinsp(varnamep); writeVarCallp->addPinsp(new AstConst{fl, 1}); // Dimension @@ -3631,11 +3637,6 @@ class RandomizeVisitor final : public VNVisitor { } return it->second; } - AstVar* getRandomGenerator(AstClass* const classp) { - if (classp->user3p()) return VN_AS(classp->user3p(), Var); - if (classp->extendsp()) return getRandomGenerator(classp->extendsp()->classp()); - return nullptr; - } AstTask* getCreateConstraintSetupFunc(AstClass* classp) { static const char* const name = "__Vsetup_constraints"; AstTask* setupAllTaskp = VN_AS(m_memberMap.findMember(classp, name), Task); diff --git a/test_regress/t/t_std_randomize_unsup_unq_arr.py b/test_regress/t/t_std_randomize_unq_arr.py similarity index 76% rename from test_regress/t/t_std_randomize_unsup_unq_arr.py rename to test_regress/t/t_std_randomize_unq_arr.py index 18ef27714..db1adb3f9 100755 --- a/test_regress/t/t_std_randomize_unsup_unq_arr.py +++ b/test_regress/t/t_std_randomize_unq_arr.py @@ -9,8 +9,13 @@ import vltest_bootstrap -test.scenarios('vlt') +test.scenarios('simulator') -test.lint(fails=test.vlt_all, expect_filename=test.golden_filename) +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() test.passes() diff --git a/test_regress/t/t_std_randomize_unq_arr.v b/test_regress/t/t_std_randomize_unq_arr.v new file mode 100644 index 000000000..7f584d954 --- /dev/null +++ b/test_regress/t/t_std_randomize_unq_arr.v @@ -0,0 +1,92 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Antmicro +// 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); +`define check_rand_unq(arr,min,max,arr_size) \ +begin \ + int ok[arr_size]; \ + int prev_arr[arr_size]; \ + for (int idx=0; idx<11; idx++) begin \ + if (std::randomize(arr) with { \ + unique {arr}; \ + foreach (arr[i]) { \ + arr[i] <= max; \ + arr[i] >= min; \ + } \ + } != 1) $stop; \ + foreach (arr[i]) begin \ + foreach (arr[j]) begin \ + if (i == j) continue; \ + if (arr[i] == arr[j]) $stop; \ + end \ + if (arr[i] > max || arr[i] < min) $stop; \ + if (arr[i] != prev_arr[i] && idx != 0) ok[i] = 1; \ + prev_arr[i] = arr[i]; \ + end \ + end \ + foreach (ok[i]) begin \ + if (ok[i] != 1) $stop; \ + end \ +end +// verilog_format: on + +// std::randomize with unique constraint on fixed size array inside class +class FixedSizeArr; + int arr[10]; + static int static_arr[10]; + + function void test(); + `check_rand_unq(arr, 1, 10, 10); + `check_rand_unq(static_arr, 1, 10, 10); + endfunction +endclass + +// std::randomize with unique constraint on dynamic array inside class +class DynArr; + int arr[]; + static int static_arr[]; + + function void test(); + arr = new[10]; + static_arr = new[20]; + + `check_rand_unq(arr, 12, 100, 10); + `check_rand_unq(static_arr, 1, 30, 20); + endfunction +endclass + +module t; + FixedSizeArr fixed; + DynArr dyn; + + int dyn_arr[]; + static int dyn_static_arr[]; + int fix_arr[10]; + static int fix_static_arr[10]; + + initial begin + fixed = new; + fixed.test(); + + dyn = new; + dyn.test(); + + // std::randomize with unique constraint on dynamic array outside class + dyn_arr = new[10]; + dyn_static_arr = new[10]; + `check_rand_unq(dyn_arr, 1, 10, 10); + `check_rand_unq(dyn_static_arr, 1, 10, 10); + + // std::randomize with unique constraint on fixed size array outside class + `check_rand_unq(fix_arr, 1, 10, 10); + `check_rand_unq(fix_static_arr, 1, 10, 10); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_std_randomize_unsup_unq_arr.out b/test_regress/t/t_std_randomize_unsup_unq_arr.out deleted file mode 100644 index 94e7ccd26..000000000 --- a/test_regress/t/t_std_randomize_unsup_unq_arr.out +++ /dev/null @@ -1,7 +0,0 @@ -%Warning-CONSTRAINTIGN: t/t_std_randomize_unsup_unq_arr.v:10:39: Unsupported: Unique constraint in std::randomize() with {} - : ... note: In instance 't' - 10 | if (!bit'(std::randomize(x) with {unique {x};}) || x[0] == x[1]) $stop; - | ^~~~~~ - ... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest - ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message. -%Error: Exiting due to diff --git a/test_regress/t/t_std_randomize_unsup_unq_arr.v b/test_regress/t/t_std_randomize_unsup_unq_arr.v deleted file mode 100644 index b0f6af776..000000000 --- a/test_regress/t/t_std_randomize_unsup_unq_arr.v +++ /dev/null @@ -1,14 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2026 Antmicro -// SPDX-License-Identifier: CC0-1.0 - -module t; - initial begin - bit x[2]; - if (!bit'(std::randomize(x) with {unique {x};}) || x[0] == x[1]) $stop; - $write("*-* All Finished *-*\n"); - $finish; - end -endmodule