Support rand dynamic arrays used inside with clause of randomize (#8246)

Signed-off-by: Kamil Danecki <[email protected]>
Signed-off-by: Igor Zaworski <[email protected]>
This commit is contained in:
Kamil Danecki
2026-09-10 10:40:30 -04:00
committed by GitHub
parent 557c21a337
commit ee792e235a
11 changed files with 432 additions and 40 deletions
+59 -40
View File
@@ -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;
}
+21
View File
@@ -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()
+112
View File
@@ -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
+21
View File
@@ -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()
@@ -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
@@ -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
+16
View File
@@ -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()
@@ -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
@@ -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
+16
View File
@@ -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()
@@ -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