From ee792e235ae417eff8567c48c10b7e663507f7ea Mon Sep 17 00:00:00 2001 From: Kamil Danecki Date: Thu, 10 Sep 2026 16:40:30 +0200 Subject: [PATCH] Support rand dynamic arrays used inside `with` clause of randomize (#8246) Signed-off-by: Kamil Danecki Signed-off-by: Igor Zaworski --- src/V3Randomize.cpp | 99 +++++++++------- test_regress/t/t_randomize_with_dyn_arr.py | 21 ++++ test_regress/t/t_randomize_with_dyn_arr.v | 112 ++++++++++++++++++ .../t/t_randomize_with_dyn_arr_multi_dyn.py | 21 ++++ .../t/t_randomize_with_dyn_arr_multi_dyn.v | 92 ++++++++++++++ ...randomize_with_dyn_arr_multi_dyn_unsup.out | 5 + ..._randomize_with_dyn_arr_multi_dyn_unsup.py | 16 +++ ...t_randomize_with_dyn_arr_multi_dyn_unsup.v | 43 +++++++ .../t/t_randomize_with_dyn_arr_unsup.out | 5 + .../t/t_randomize_with_dyn_arr_unsup.py | 16 +++ .../t/t_randomize_with_dyn_arr_unsup.v | 42 +++++++ 11 files changed, 432 insertions(+), 40 deletions(-) create mode 100755 test_regress/t/t_randomize_with_dyn_arr.py create mode 100644 test_regress/t/t_randomize_with_dyn_arr.v create mode 100755 test_regress/t/t_randomize_with_dyn_arr_multi_dyn.py create mode 100644 test_regress/t/t_randomize_with_dyn_arr_multi_dyn.v create mode 100644 test_regress/t/t_randomize_with_dyn_arr_multi_dyn_unsup.out create mode 100755 test_regress/t/t_randomize_with_dyn_arr_multi_dyn_unsup.py create mode 100644 test_regress/t/t_randomize_with_dyn_arr_multi_dyn_unsup.v create mode 100644 test_regress/t/t_randomize_with_dyn_arr_unsup.out create mode 100755 test_regress/t/t_randomize_with_dyn_arr_unsup.py create mode 100644 test_regress/t/t_randomize_with_dyn_arr_unsup.v diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index ca895c4ad..382de6c54 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1465,7 +1465,7 @@ class ConstraintExprVisitor final : public VNVisitor { AstMemberSel* const memberselp, const std::string& smtName, AstNodeModule* const classOrPackagep) const { uint32_t unpackedDims = 0; - if (varp->dtypep()->isNonPackedArray()) { + if (varp->dtypeSkipRefp()->isNonPackedArray()) { unpackedDims = varp->dtypep()->dimensions(false).second; } if (VN_IS(varp->dtypeSkipRefp(), StructDType) @@ -2688,47 +2688,66 @@ class ConstraintExprVisitor final : public VNVisitor { if (editFormat(nodep)) return; FileLine* const fl = nodep->fileline(); - if (nodep->method() == VCMethod::ARRAY_AT && nodep->fromp()->user1()) { - // Queue/dynamic element: pre-edit clone for the rand_mode hoist, non-rand index only. - // A std::randomize() with-clause argument carries no rand qualifier - // of its own but is still part of the solve. - bool indexIsRand = false; - if (nodep->pinsp()) { - nodep->pinsp()->foreach([&](const AstNodeVarRef* vrefp) { - if (vrefp->varp()->rand().isRandomizable() - || vrefp->varp()->isStdRandomizeArg()) { - indexIsRand = true; + if (nodep->method() == VCMethod::ARRAY_AT) { + const bool indexIsRand = nodep->pinsp()->exists([](const AstNodeVarRef* const vrefp) { + return vrefp->varp()->rand().isRandomizable(); + }); + if (nodep->fromp()->user1()) { + // Queue/dynamic element: pre-edit clone for the rand_mode hoist, non-rand index + // only. A std::randomize() with-clause argument carries no rand qualifier of its + // own but is still part of the solve. + { + const AstNode* const basep = nodep->baseFromp(true); + if (basep->name() == "__Vthis" + && VN_AS(basep->backp(), MemberSel)->varp()->isRand()) { + nodep->fromp()->v3warn(E_UNSUPPORTED, + "Unsupported: Complex expression captured from " + "current scope with randomized variable"); + return; } - }); - } - AstNodeExpr* const origp = indexIsRand ? nullptr : nodep->cloneTree(false); - AstCMethodHard* const sizep - = m_structSel ? new AstCMethodHard{fl, nodep->fromp()->cloneTreePure(false), - VCMethod::DYN_SIZE} - : nullptr; - AstNodeExpr* const originalPinp = nodep->pinsp(); - iterateChildren(nodep); - AstNodeExpr* const pinp = nodep->pinsp()->unlinkFrBack(); - if (VN_IS(pinp, SFormatF) && m_structSel) VN_AS(pinp, SFormatF)->name("%x"); - AstSFormatF* newp; - if (m_structSel) { - AstNodeExpr* const argsp = AstNode::addNext(nodep->fromp()->unlinkFrBack(), pinp); - sizep->dtypeSetInt(); - AstLogAnd* const condp = new AstLogAnd{ - fl, - new AstLteS{ - fl, new AstConst{fl, AstConst::WidthedValue{}, originalPinp->width(), 0}, - originalPinp->cloneTreePure(false)}, - new AstLtS{fl, originalPinp->cloneTreePure(false), sizep}}; - m_conditionp = m_conditionp ? new AstLogAnd{fl, m_conditionp, condp} : condp; - newp = new AstSFormatF{fl, "%s.%s", false, argsp}; + } + AstNodeExpr* const origp = indexIsRand ? nullptr : nodep->cloneTree(false); + AstCMethodHard* const sizep + = m_structSel ? new AstCMethodHard{fl, nodep->fromp()->cloneTreePure(false), + VCMethod::DYN_SIZE} + : nullptr; + AstNodeExpr* const originalPinp = nodep->pinsp(); + iterateChildren(nodep); + AstNodeExpr* const pinp = nodep->pinsp()->unlinkFrBack(); + AstSFormatF* newp; + if (m_structSel) { + if (AstSFormatF* const sformatfp = VN_CAST(pinp, SFormatF)) { + sformatfp->name("%x"); + } + AstNodeExpr* const argsp + = AstNode::addNext(nodep->fromp()->unlinkFrBack(), pinp); + sizep->dtypeSetInt(); + AstLogAnd* const condp + = new AstLogAnd{fl, + new AstLteS{fl, + new AstConst{fl, AstConst::WidthedValue{}, + originalPinp->width(), 0}, + originalPinp->cloneTreePure(false)}, + new AstLtS{fl, originalPinp->cloneTreePure(false), sizep}}; + m_conditionp = m_conditionp ? new AstLogAnd{fl, m_conditionp, condp} : condp; + newp = new AstSFormatF{fl, "%s.%s", false, argsp}; + } else { + newp = createSolverArrDerefp(fl, nodep->fromp()->unlinkFrBack(), pinp); + } + nodep->replaceWith(newp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + if (origp && !hoistRandModeOverSelect(newp, origp)) { + VL_DO_DANGLING(origp->deleteTree(), origp); + } + + } else if (indexIsRand) { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported: Randomization of an index to a non-random variable"); + } else { - newp = createSolverArrDerefp(fl, nodep->fromp()->unlinkFrBack(), pinp); - } - nodep->replaceWith(newp); - VL_DO_DANGLING(nodep->deleteTree(), nodep); - if (origp && !hoistRandModeOverSelect(newp, origp)) { - VL_DO_DANGLING(origp->deleteTree(), origp); + nodep->user1(false); + UASSERT_OBJ(editFormat(nodep), nodep, + "editFormat should return true when user1 is false"); } return; } diff --git a/test_regress/t/t_randomize_with_dyn_arr.py b/test_regress/t/t_randomize_with_dyn_arr.py new file mode 100755 index 000000000..1cee58d17 --- /dev/null +++ b/test_regress/t/t_randomize_with_dyn_arr.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 + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_with_dyn_arr.v b/test_regress/t/t_randomize_with_dyn_arr.v new file mode 100644 index 000000000..f3d37a3c6 --- /dev/null +++ b/test_regress/t/t_randomize_with_dyn_arr.v @@ -0,0 +1,112 @@ +// 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_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d-%0d\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); `stop; end while(0); +`define checkd_ne(gotv,expv) do if ((gotv) === (expv)) begin $write("%%Error: %s:%0d: got=%0d expected any other value\n", `__FILE__,`__LINE__, (gotv)); `stop; end while(0); +// verilog_format: on + +typedef int int_arr_t[]; + +class A; + rand int x; +endclass + +class Cls; + rand int_arr_t arr; + function int foo(); + return arr[0]; + endfunction + task body(); + int ok; + A a; + int prev = -1; + a = new; + arr = new[2]; + arr[0] = 111112300; + arr[1] = 123000000; + repeat (40) begin + ok = a.randomize() with {x >= 0; x <= arr[1];}; + `checkd(ok, 1); + `check_range(a.x, 0, 123000000); + `checkd_ne(a.x, prev); + prev = a.x; + end + prev = -1; + repeat (40) begin + ok = a.randomize() with {x >= 0; x <= foo();}; + `checkd(ok, 1); + `check_range(a.x, 0, 111112300); + `checkd_ne(a.x, prev); + prev = a.x; + end + endtask +endclass + +class B; + rand int x; + rand int_arr_t arr; +endclass + +class Cls2; + task body(); + int ok; + B b; + int prev = -1; + b = new; + b.arr = new[2]; + b.x = 1; + b.arr[1] = 200000; + repeat (40) begin + ok = b.randomize() with {x == arr[1];}; + `checkd(ok, 1); + `checkd(b.x, b.arr[1]); + `checkd_ne(b.x, prev); + prev = b.x; + end + endtask +endclass + +class Cls3; + rand int_arr_t arr; + task body(); + int ok; + B b; + int prev = -1; + b = new; + b.arr = new[2]; + b.x = 1; + b.arr[1] = 30000000; + arr = new[2]; + arr[1] = 3; + repeat (40) begin + ok = b.randomize() with {x == arr[1];}; + `checkd(ok, 1); + `checkd(arr[1], 3); + `checkd(b.x, b.arr[1]); + `checkd_ne(b.x, prev); + prev = b.x; + end + endtask +endclass + +module t; + Cls c; + Cls2 c2; + Cls3 c3; + initial begin + c = new; + c2 = new; + c3 = new; + c.body(); + c2.body(); + c3.body(); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_with_dyn_arr_multi_dyn.py b/test_regress/t/t_randomize_with_dyn_arr_multi_dyn.py new file mode 100755 index 000000000..1cee58d17 --- /dev/null +++ b/test_regress/t/t_randomize_with_dyn_arr_multi_dyn.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 + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_with_dyn_arr_multi_dyn.v b/test_regress/t/t_randomize_with_dyn_arr_multi_dyn.v new file mode 100644 index 000000000..a5e125add --- /dev/null +++ b/test_regress/t/t_randomize_with_dyn_arr_multi_dyn.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); +// verilog_format: on + +typedef int int_arr_t[]; + +class A; + rand int x; +endclass + +class Cls; + int_arr_t arr[]; + task body(); + int ok; + A a; + a = new; + arr = new[2]; + arr[1] = new[2]; + arr[1][1] = 123; + repeat (40) begin + ok = a.randomize() with {x == arr[1][1];}; + `checkd(ok, 1); + `checkd(a.x, arr[1][1]); + end + endtask +endclass + +class B; + rand int x; + rand int arr[][]; +endclass + +class Cls2; + task body(); + int ok; + B b; + b = new; + b.arr = new[2]; + b.arr[1] = new[2]; + b.x = 1; + b.arr[1][1] = 2; + repeat (40) begin + ok = b.randomize() with {x == arr[1][1];}; + `checkd(ok, 1); + `checkd(b.x, b.arr[1][1]); + end + endtask +endclass + +class Cls3; + rand int_arr_t arr; + task body(); + int ok; + B b; + b = new; + b.arr = new[2]; + b.arr[1] = new[2]; + b.x = 1; + b.arr[1][1] = 2; + arr = new[2]; + arr[1] = 3; + repeat (40) begin + ok = b.randomize() with {x == arr[1][1];}; + `checkd(ok, 1); + `checkd(b.x, b.arr[1][1]); + `checkd(arr[1], 3); + end + endtask +endclass + +module t; + Cls c; + Cls2 c2; + Cls3 c3; + initial begin + c = new; + c2 = new; + c3 = new; + c.body(); + c2.body(); + c3.body(); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_with_dyn_arr_multi_dyn_unsup.out b/test_regress/t/t_randomize_with_dyn_arr_multi_dyn_unsup.out new file mode 100644 index 000000000..d23b4aef2 --- /dev/null +++ b/test_regress/t/t_randomize_with_dyn_arr_multi_dyn_unsup.out @@ -0,0 +1,5 @@ +%Error-UNSUPPORTED: t/t_randomize_with_dyn_arr_multi_dyn_unsup.v:28:40: Unsupported: Complex expression captured from current scope with randomized variable + 28 | ok = a.randomize() with {x == arr[1][1];}; + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_randomize_with_dyn_arr_multi_dyn_unsup.py b/test_regress/t/t_randomize_with_dyn_arr_multi_dyn_unsup.py new file mode 100755 index 000000000..38cf36b43 --- /dev/null +++ b/test_regress/t/t_randomize_with_dyn_arr_multi_dyn_unsup.py @@ -0,0 +1,16 @@ +#!/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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_randomize_with_dyn_arr_multi_dyn_unsup.v b/test_regress/t/t_randomize_with_dyn_arr_multi_dyn_unsup.v new file mode 100644 index 000000000..cb1ea29aa --- /dev/null +++ b/test_regress/t/t_randomize_with_dyn_arr_multi_dyn_unsup.v @@ -0,0 +1,43 @@ +// 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); +// verilog_format: on + +typedef int int_arr_t[]; + +class A; + rand int x; +endclass + +class Cls; + rand int_arr_t arr[]; + task body(); + int ok; + A a; + a = new; + arr = new[2]; + arr[1] = new[2]; + arr[1][1] = 123; + repeat (40) begin + ok = a.randomize() with {x == arr[1][1];}; + `checkd(ok, 1); + `checkd(a.x, arr[1][1]); + end + endtask +endclass + +module t; + Cls c; + initial begin + c = new; + c.body(); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_with_dyn_arr_unsup.out b/test_regress/t/t_randomize_with_dyn_arr_unsup.out new file mode 100644 index 000000000..c8d927167 --- /dev/null +++ b/test_regress/t/t_randomize_with_dyn_arr_unsup.out @@ -0,0 +1,5 @@ +%Error-UNSUPPORTED: t/t_randomize_with_dyn_arr_unsup.v:28:59: Unsupported: Randomization of an index to a non-random variable + 28 | if (c.randomize() with {solve foo before x; x == arr[bit'(foo)]; foo <= 1;} != 1) $stop; + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_randomize_with_dyn_arr_unsup.py b/test_regress/t/t_randomize_with_dyn_arr_unsup.py new file mode 100755 index 000000000..38cf36b43 --- /dev/null +++ b/test_regress/t/t_randomize_with_dyn_arr_unsup.py @@ -0,0 +1,16 @@ +#!/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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_randomize_with_dyn_arr_unsup.v b/test_regress/t/t_randomize_with_dyn_arr_unsup.v new file mode 100644 index 000000000..701176580 --- /dev/null +++ b/test_regress/t/t_randomize_with_dyn_arr_unsup.v @@ -0,0 +1,42 @@ +// 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); +// verilog_format: on + +typedef int int_arr_t[]; + +class C; + rand int x; + rand bit [1:0] foo; +endclass + +class Cls4; + int_arr_t arr; + task body(); + C c; + c = new; + arr = new[2]; + arr[0] = 123; + arr[1] = 124; + repeat (40) begin + if (c.randomize() with {solve foo before x; x == arr[bit'(foo)]; foo <= 1;} != 1) $stop; + `checkd(c.x, arr[bit'(c.foo)]); + end + endtask +endclass + +module t; + Cls4 c4; + initial begin + c4 = new; + c4.body(); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule