Resize size-constrained array after solver for global-constraint owners and consolidate subobj tests
This commit is contained in:
parent
79fcd07194
commit
8bf2f62e80
|
|
@ -4893,13 +4893,9 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
// basic-randomizes FIRST and lets the solver override the constrained
|
||||
// leaves afterwards. This way a leaf that is only globally constrained
|
||||
// (not user3) is still basic-randomized when its type is randomized
|
||||
// standalone, while the owner's solver result wins.
|
||||
// KNOWN LIMITATION: if such an owner also declares its own
|
||||
// size-constrained array, the size-phase solver call is emitted above
|
||||
// (during the genp block) before these assignments, so basicFirst cannot
|
||||
// reorder it and the array size constraint is left unsolved. This
|
||||
// combination cannot be built on master (the #7833 regression blocks it),
|
||||
// so it is a capability gap, not a regression of working behavior.
|
||||
// standalone, while the owner's solver result wins. The size-only resize
|
||||
// fallback below is emitted after the solver call for such owners so an
|
||||
// owner that also size-constrains its own array still resizes correctly.
|
||||
AstVarRef* const fvarRefp = new AstVarRef{fl, fvarp, VAccess::WRITE};
|
||||
randomizep->addStmtsp(new AstAssign{
|
||||
fl, fvarRefp, basicFirst ? new AstFuncRef{fl, basicRandomizep} : beginValp});
|
||||
|
|
@ -4907,13 +4903,21 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
const auto sizeArraysIt = m_sizeConstrainedArrays.find(nodep);
|
||||
const bool needsSizePhase
|
||||
= sizeArraysIt != m_sizeConstrainedArrays.end() && !sizeArraysIt->second.empty();
|
||||
if (!needsSizePhase) {
|
||||
// Resize fallback for an array whose size is only solver-determined (size
|
||||
// constraint but no element constraint, so no two-pass size phase). It must
|
||||
// run AFTER the solver .next() that computes the size variable, otherwise it
|
||||
// resizes with a stale value. A basicFirst owner runs the solver LAST (after
|
||||
// basic randomization), so emit the resize after the second assignment;
|
||||
// otherwise the solver runs first and the resize belongs right after it.
|
||||
const auto emitResizeFallback = [&]() {
|
||||
if (needsSizePhase) return;
|
||||
if (AstTask* const resizeAllTaskp
|
||||
= VN_AS(m_memberMap.findMember(nodep, "__Vresize_constrained_arrays"), Task)) {
|
||||
AstTaskRef* const resizeTaskRefp = new AstTaskRef{fl, resizeAllTaskp};
|
||||
randomizep->addStmtsp(resizeTaskRefp->makeStmt());
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!basicFirst) emitResizeFallback();
|
||||
|
||||
AstVarRef* const fvarRefReadp = fvarRefp->cloneTree(false);
|
||||
fvarRefReadp->access(VAccess::READ);
|
||||
|
|
@ -4923,6 +4927,8 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
randomizep->addStmtsp(new AstAssign{fl, fvarRefp->cloneTree(false),
|
||||
new AstAnd{fl, fvarRefReadp, secondHalfp}});
|
||||
|
||||
if (basicFirst) emitResizeFallback();
|
||||
|
||||
// Call nested post_randomize on rand class-type members (IEEE 18.4.1)
|
||||
if (classHasRandClassMembers(nodep)) {
|
||||
AstTask* const postTaskp = getCreateNestedCallbackTask(nodep, "post");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,178 @@
|
|||
// 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
|
||||
|
||||
// Scenario 1 (issue #7833 literal): owner SA1 declares a global constraint on a
|
||||
// sub-object's type but is NEVER randomized; a standalone randomize() of the
|
||||
// holder must still randomize the nested fields.
|
||||
class C1;
|
||||
rand int x;
|
||||
rand int y;
|
||||
endclass
|
||||
|
||||
class B1;
|
||||
rand C1 c;
|
||||
function new();
|
||||
c = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class SA1;
|
||||
rand B1 b;
|
||||
constraint c_foo {b.c.x == 123;}
|
||||
endclass
|
||||
|
||||
// Scenario 2 (combined): the SAME design randomizes both the constraint owner
|
||||
// and a standalone holder of the constrained type.
|
||||
class C2;
|
||||
rand int x;
|
||||
rand int y;
|
||||
endclass
|
||||
|
||||
class B2;
|
||||
rand C2 c;
|
||||
function new();
|
||||
c = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class SA2;
|
||||
rand B2 b;
|
||||
constraint c_foo {b.c.x == 123;}
|
||||
function new();
|
||||
b = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Scenario 3 (multiple same-type sub-objects): two sub-objects of one type are
|
||||
// each pinned by a global constraint while that type is also randomized
|
||||
// standalone; the two share one underlying rand variable.
|
||||
class C3;
|
||||
rand int x;
|
||||
endclass
|
||||
|
||||
class M3;
|
||||
rand C3 c1;
|
||||
rand C3 c2;
|
||||
constraint c {
|
||||
c1.x == 11;
|
||||
c2.x == 22;
|
||||
}
|
||||
function new();
|
||||
c1 = new();
|
||||
c2 = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Scenario 4 (global constraint owner with its own size-constrained array): the
|
||||
// owner basic-randomizes first, the solver overrides last, and the size-only
|
||||
// resize fallback still resizes from the solver-determined size.
|
||||
class C4;
|
||||
rand int x;
|
||||
endclass
|
||||
|
||||
class A4;
|
||||
rand C4 c;
|
||||
rand int arr[];
|
||||
constraint c_sub {c.x == 5;}
|
||||
constraint c_size {arr.size() == 4;}
|
||||
function new();
|
||||
c = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t_constraint_global_subobj;
|
||||
B1 b1;
|
||||
SA2 a2;
|
||||
B2 b2;
|
||||
M3 m3;
|
||||
C3 s3;
|
||||
A4 a4;
|
||||
int prevx, prevy, p3;
|
||||
bit varyx, varyy, vary3;
|
||||
|
||||
initial begin
|
||||
// Scenario 1: SA1 never randomized; standalone B1 must vary both fields.
|
||||
b1 = new();
|
||||
varyx = 0;
|
||||
varyy = 0;
|
||||
if (b1.randomize() != 1) $stop;
|
||||
prevx = b1.c.x;
|
||||
prevy = b1.c.y;
|
||||
for (int i = 0; i < 20; i++) begin
|
||||
if (b1.randomize() != 1) $stop;
|
||||
if (b1.c.x != prevx) varyx = 1;
|
||||
if (b1.c.y != prevy) varyy = 1;
|
||||
prevx = b1.c.x;
|
||||
prevy = b1.c.y;
|
||||
end
|
||||
if (!varyx || !varyy) begin
|
||||
$display("ERROR: standalone holder fields stuck (varyx=%0d varyy=%0d)", varyx, varyy);
|
||||
$stop;
|
||||
end
|
||||
|
||||
// Scenario 2: randomize the owner (constraint applies) and a standalone
|
||||
// holder (fields random) in the same design.
|
||||
a2 = new();
|
||||
if (a2.randomize() != 1) $stop;
|
||||
if (a2.b.c.x != 123) begin
|
||||
$display("ERROR: owner constraint not applied, a2.b.c.x=%0d", a2.b.c.x);
|
||||
$stop;
|
||||
end
|
||||
b2 = new();
|
||||
varyx = 0;
|
||||
if (b2.randomize() != 1) $stop;
|
||||
prevx = b2.c.x;
|
||||
for (int i = 0; i < 20; i++) begin
|
||||
if (b2.randomize() != 1) $stop;
|
||||
if (b2.c.x != prevx) varyx = 1;
|
||||
prevx = b2.c.x;
|
||||
end
|
||||
if (!varyx) begin
|
||||
$display("ERROR: standalone holder x stuck while owner also randomized");
|
||||
$stop;
|
||||
end
|
||||
|
||||
// Scenario 3: two same-type sub-objects each pinned, plus standalone vary.
|
||||
m3 = new();
|
||||
if (m3.randomize() != 1) $stop;
|
||||
if (m3.c1.x != 11) begin
|
||||
$display("ERROR: m3.c1.x should be 11, got %0d", m3.c1.x);
|
||||
$stop;
|
||||
end
|
||||
if (m3.c2.x != 22) begin
|
||||
$display("ERROR: m3.c2.x should be 22, got %0d", m3.c2.x);
|
||||
$stop;
|
||||
end
|
||||
s3 = new();
|
||||
vary3 = 0;
|
||||
if (s3.randomize() != 1) $stop;
|
||||
p3 = s3.x;
|
||||
for (int i = 0; i < 20; i++) begin
|
||||
if (s3.randomize() != 1) $stop;
|
||||
if (s3.x != p3) vary3 = 1;
|
||||
p3 = s3.x;
|
||||
end
|
||||
if (!vary3) begin
|
||||
$display("ERROR: standalone same-type x stuck");
|
||||
$stop;
|
||||
end
|
||||
|
||||
// Scenario 4: owner with a global constraint AND its own size constraint.
|
||||
a4 = new();
|
||||
if (a4.randomize() != 1) $stop;
|
||||
if (a4.c.x != 5) begin
|
||||
$display("ERROR: a4.c.x should be 5, got %0d", a4.c.x);
|
||||
$stop;
|
||||
end
|
||||
if (a4.arr.size() != 4) begin
|
||||
$display("ERROR: a4.arr.size() should be 4, got %0d", a4.arr.size());
|
||||
$stop;
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
// 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
|
||||
|
||||
class C;
|
||||
rand int x;
|
||||
rand int y;
|
||||
endclass
|
||||
|
||||
class B;
|
||||
rand C c;
|
||||
function new();
|
||||
c = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class A;
|
||||
rand B b;
|
||||
constraint c_foo {b.c.x == 123;}
|
||||
function new();
|
||||
b = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t_constraint_global_subobj_combined;
|
||||
B b;
|
||||
A a;
|
||||
int px, py;
|
||||
bit xvar, yvar;
|
||||
|
||||
initial begin
|
||||
// Same compilation randomizes BOTH the sub-object holder B standalone AND the
|
||||
// global-constraint owner A. A leaf (C::x) is therefore solver-owned for A
|
||||
// but must be freely randomized for B; both have to work at once (issue
|
||||
// #7833).
|
||||
b = new();
|
||||
if (b.randomize() != 1) $stop;
|
||||
px = b.c.x;
|
||||
py = b.c.y;
|
||||
xvar = 0;
|
||||
yvar = 0;
|
||||
for (int i = 0; i < 20; i++) begin
|
||||
if (b.randomize() != 1) $stop;
|
||||
if (b.c.x != px) xvar = 1;
|
||||
if (b.c.y != py) yvar = 1;
|
||||
px = b.c.x;
|
||||
py = b.c.y;
|
||||
end
|
||||
if (!xvar) begin
|
||||
$display("ERROR: standalone b.c.x not randomized (stuck)");
|
||||
$stop;
|
||||
end
|
||||
if (!yvar) begin
|
||||
$display("ERROR: standalone b.c.y not randomized (stuck)");
|
||||
$stop;
|
||||
end
|
||||
|
||||
a = new();
|
||||
if (a.randomize() != 1) $stop;
|
||||
py = a.b.c.y;
|
||||
yvar = 0;
|
||||
for (int i = 0; i < 20; i++) begin
|
||||
if (a.randomize() != 1) $stop;
|
||||
if (a.b.c.x != 123) begin
|
||||
$display("ERROR: global constraint b.c.x == 123 not applied, got %0d", a.b.c.x);
|
||||
$stop;
|
||||
end
|
||||
if (a.b.c.y != py) yvar = 1;
|
||||
py = a.b.c.y;
|
||||
end
|
||||
if (!yvar) begin
|
||||
$display("ERROR: a.b.c.y not randomized (stuck)");
|
||||
$stop;
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
#!/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()
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
// 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
|
||||
|
||||
class C;
|
||||
rand int x;
|
||||
endclass
|
||||
|
||||
class M;
|
||||
rand C c1;
|
||||
rand C c2;
|
||||
constraint c {
|
||||
c1.x == 11;
|
||||
c2.x == 22;
|
||||
}
|
||||
function new();
|
||||
c1 = new();
|
||||
c2 = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t_constraint_global_subobj_multi;
|
||||
C s;
|
||||
M m;
|
||||
int p;
|
||||
bit vary;
|
||||
|
||||
initial begin
|
||||
// Type C is randomized standalone AND used as two distinct sub-objects of M,
|
||||
// each pinned by a global constraint. The two sub-objects share the same
|
||||
// underlying rand variable, so each must keep its own write_var (issue #7833
|
||||
// family).
|
||||
s = new();
|
||||
if (s.randomize() != 1) $stop;
|
||||
p = s.x;
|
||||
vary = 0;
|
||||
for (int i = 0; i < 20; i++) begin
|
||||
if (s.randomize() != 1) $stop;
|
||||
if (s.x != p) vary = 1;
|
||||
p = s.x;
|
||||
end
|
||||
if (!vary) begin
|
||||
$display("ERROR: standalone C.x not randomized (stuck)");
|
||||
$stop;
|
||||
end
|
||||
|
||||
m = new();
|
||||
if (m.randomize() != 1) $stop;
|
||||
if (m.c1.x != 11) begin
|
||||
$display("ERROR: m.c1.x should be 11, got %0d", m.c1.x);
|
||||
$stop;
|
||||
end
|
||||
if (m.c2.x != 22) begin
|
||||
$display("ERROR: m.c2.x should be 22, got %0d", m.c2.x);
|
||||
$stop;
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
#!/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()
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
// 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
|
||||
|
||||
class C;
|
||||
rand int x;
|
||||
rand int y;
|
||||
endclass
|
||||
|
||||
class B;
|
||||
rand C c;
|
||||
function new();
|
||||
c = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class A;
|
||||
rand B b;
|
||||
constraint c_foo {b.c.x == 123;}
|
||||
function new();
|
||||
b = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t_constraint_global_subobj_standalone;
|
||||
B b;
|
||||
int px, py;
|
||||
bit xvar, yvar;
|
||||
|
||||
initial begin
|
||||
// Issue #7833: a standalone randomize() of B must randomize the nested
|
||||
// sub-object fields c.x and c.y. Class A holds a global constraint that
|
||||
// references type C through b.c.x, which used to flag C's fields as solved
|
||||
// and skip them in B's basic randomization even though A is never
|
||||
// randomized here.
|
||||
b = new();
|
||||
if (b.randomize() != 1) $stop;
|
||||
px = b.c.x;
|
||||
py = b.c.y;
|
||||
xvar = 0;
|
||||
yvar = 0;
|
||||
for (int i = 0; i < 20; i++) begin
|
||||
if (b.randomize() != 1) $stop;
|
||||
if (b.c.x != px) xvar = 1;
|
||||
if (b.c.y != py) yvar = 1;
|
||||
px = b.c.x;
|
||||
py = b.c.y;
|
||||
end
|
||||
if (!xvar) begin
|
||||
$display("ERROR: b.c.x not randomized (stuck)");
|
||||
$stop;
|
||||
end
|
||||
if (!yvar) begin
|
||||
$display("ERROR: b.c.y not randomized (stuck)");
|
||||
$stop;
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue