This commit is contained in:
parent
be0f4a507e
commit
d57324e5fb
|
|
@ -3572,6 +3572,32 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
UASSERT_OBJ(VN_IS(exprp, VarRef), exprp, "Should be a VarRef");
|
||||
return new AstVarRef{exprp->fileline(), VN_AS(varp->user2p(), Class), varp, access};
|
||||
}
|
||||
// Get or create a size variable for a constrained dynamic/queue/assoc array.
|
||||
// Returns the size variable. Sets wasCreated=true if a new variable was made.
|
||||
AstVar* createOrGetSizeVar(AstClass* const classp, AstVar* const arrVarp, FileLine* const fl,
|
||||
AstNodeDType* const signed32DTypep, bool& wasCreated) {
|
||||
AstVar* sizeVarp = VN_CAST(arrVarp->user4p(), Var);
|
||||
wasCreated = false;
|
||||
if (!sizeVarp) {
|
||||
sizeVarp = new AstVar{fl, VVarType::BLOCKTEMP, "__V" + arrVarp->name() + "_size",
|
||||
signed32DTypep};
|
||||
classp->addMembersp(sizeVarp);
|
||||
m_memberMap.insert(classp, sizeVarp);
|
||||
sizeVarp->user2p(classp);
|
||||
arrVarp->user4p(sizeVarp);
|
||||
wasCreated = true;
|
||||
}
|
||||
return sizeVarp;
|
||||
}
|
||||
// Build a size >= 0 constraint expression (signed int size must be non-negative).
|
||||
// Returns a new AstConstraintExpr with user1 bits set for solver visibility.
|
||||
AstConstraintExpr* createSizeGteZeroConstraint(FileLine* const fl, AstVar* const sizeVarp) {
|
||||
AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ};
|
||||
sizeVarRefp->user1(true);
|
||||
AstGteS* const sizeGtep = new AstGteS{fl, sizeVarRefp, new AstConst{fl, 0}};
|
||||
sizeGtep->user1(true);
|
||||
return new AstConstraintExpr{fl, sizeGtep};
|
||||
}
|
||||
AstNodeExpr* getFromp(AstNodeExpr* const exprp) {
|
||||
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
|
||||
return memberSelp->fromp();
|
||||
|
|
@ -4719,6 +4745,57 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
// Generate constraint setup code and a hardcoded call to the solver
|
||||
AstNode* const capturedTreep = withp->exprp()->unlinkFrBackWithNext();
|
||||
randomizeFuncp->addStmtsp(capturedTreep);
|
||||
|
||||
// Pre-scan captured tree for .size on dynamic/assoc arrays and replace
|
||||
// with size variables, using shared createOrGetSizeVar helper.
|
||||
// After CaptureVisitor, fromp() of .size is AstMemberSel (not VarRef),
|
||||
// so extract the underlying variable from the MemberSel chain.
|
||||
// Collect resize statements to add after solver call, guarded by success.
|
||||
AstNode* inlineResizeStmtsp = nullptr;
|
||||
{
|
||||
std::vector<AstCMethodHard*> sizeMethodps;
|
||||
capturedTreep->foreachAndNext([&](AstCMethodHard* methodp) {
|
||||
if (methodp->method() == VCMethod::DYN_SIZE
|
||||
|| methodp->method() == VCMethod::ASSOC_SIZE) {
|
||||
sizeMethodps.push_back(methodp);
|
||||
}
|
||||
});
|
||||
for (AstCMethodHard* const methodp : sizeMethodps) {
|
||||
// Extract array variable from fromp (VarRef or MemberSel after capture)
|
||||
AstVar* arrVarp = nullptr;
|
||||
if (AstVarRef* const varRefp = VN_CAST(methodp->fromp(), VarRef)) {
|
||||
arrVarp = varRefp->varp();
|
||||
} else if (AstMemberSel* const memberSelp = VN_CAST(methodp->fromp(), MemberSel)) {
|
||||
arrVarp = memberSelp->varp();
|
||||
}
|
||||
if (!arrVarp) continue;
|
||||
// Only handle rand-declared dynamic/assoc array variables
|
||||
if (!arrVarp->rand().isRandomizable()) continue;
|
||||
FileLine* const fl = methodp->fileline();
|
||||
bool wasCreated = false;
|
||||
AstVar* const sizeVarp = createOrGetSizeVar(
|
||||
classp, arrVarp, fl, methodp->findSigned32DType(), wasCreated);
|
||||
if (wasCreated) {
|
||||
// Generate resize for dynamic arrays/queues (not assoc arrays)
|
||||
if (!VN_IS(arrVarp->dtypep()->skipRefp(), AssocArrayDType)) {
|
||||
AstCMethodHard* const resizep = new AstCMethodHard{
|
||||
fl, new AstVarRef{fl, classp, arrVarp, VAccess::READWRITE},
|
||||
VCMethod::DYN_RESIZE, new AstVarRef{fl, sizeVarp, VAccess::READ}};
|
||||
resizep->dtypep(methodp->findVoidDType());
|
||||
inlineResizeStmtsp
|
||||
= AstNode::addNext(inlineResizeStmtsp, new AstStmtExpr{fl, resizep});
|
||||
}
|
||||
|
||||
// Append size >= 0 constraint so ConstraintExprVisitor processes it
|
||||
capturedTreep->addNext(createSizeGteZeroConstraint(fl, sizeVarp));
|
||||
}
|
||||
AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ};
|
||||
sizeVarRefp->user1(true);
|
||||
methodp->replaceWith(sizeVarRefp);
|
||||
VL_DO_DANGLING(methodp->deleteTree(), methodp);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
expandUniqueElementList(capturedTreep);
|
||||
ConstraintExprVisitor{classp, m_memberMap, capturedTreep, randomizeFuncp,
|
||||
|
|
@ -4735,6 +4812,16 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var), VAccess::WRITE},
|
||||
new AstAnd{nodep->fileline(), basicRandomizeFuncCallp, solverCallp}});
|
||||
|
||||
// Resize constrained arrays only when solver succeeds, mirroring
|
||||
// the class-level if(__Vsize_ok) guard pattern
|
||||
if (inlineResizeStmtsp) {
|
||||
AstVar* const retVarp = VN_AS(randomizeFuncp->fvarp(), Var);
|
||||
AstIf* const ifp = new AstIf{nodep->fileline(),
|
||||
new AstVarRef{nodep->fileline(), retVarp, VAccess::READ},
|
||||
inlineResizeStmtsp, nullptr};
|
||||
randomizeFuncp->addStmtsp(ifp);
|
||||
}
|
||||
|
||||
// Call nested post_randomize on rand class-type members (IEEE 18.4.1)
|
||||
if (classHasRandClassMembers(classp)) {
|
||||
AstTask* const postTaskp = getCreateNestedCallbackTask(classp, "post");
|
||||
|
|
@ -4774,16 +4861,10 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
return;
|
||||
}
|
||||
AstVar* const queueVarp = queueVarRefp->varp();
|
||||
AstVar* sizeVarp = VN_CAST(queueVarp->user4p(), Var);
|
||||
if (!sizeVarp) {
|
||||
sizeVarp = new AstVar{fl, VVarType::BLOCKTEMP, "__V" + queueVarp->name() + "_size",
|
||||
nodep->findSigned32DType()};
|
||||
classp->addMembersp(sizeVarp);
|
||||
m_memberMap.insert(classp, sizeVarp);
|
||||
sizeVarp->user2p(classp);
|
||||
|
||||
queueVarp->user4p(sizeVarp);
|
||||
|
||||
bool wasCreated = false;
|
||||
AstVar* const sizeVarp = createOrGetSizeVar(classp, queueVarp, fl,
|
||||
nodep->findSigned32DType(), wasCreated);
|
||||
if (wasCreated) {
|
||||
// Associative arrays have no resize(); only generate resize
|
||||
// for dynamic arrays and queues
|
||||
if (!VN_IS(queueVarp->dtypep()->skipRefp(), AssocArrayDType)) {
|
||||
|
|
@ -4802,11 +4883,7 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
|
||||
// Since size variable is signed int, we need additional constraint
|
||||
// to make sure it is always >= 0.
|
||||
AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ};
|
||||
sizeVarRefp->user1(true);
|
||||
AstGteS* const sizeGtep = new AstGteS{fl, sizeVarRefp, new AstConst{fl, 0}};
|
||||
sizeGtep->user1(true);
|
||||
m_constraintp->addItemsp(new AstConstraintExpr{fl, sizeGtep});
|
||||
m_constraintp->addItemsp(createSizeGteZeroConstraint(fl, sizeVarp));
|
||||
}
|
||||
AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ};
|
||||
sizeVarRefp->user1(true);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
if not test.have_solver:
|
||||
test.skip("No constraint solver installed")
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// 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
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
class Packet;
|
||||
rand int unsigned m_length;
|
||||
rand byte m_data[];
|
||||
rand byte m_mask[];
|
||||
|
||||
constraint c_length { m_length inside {[1:16]}; }
|
||||
endclass
|
||||
|
||||
class MultiArray;
|
||||
rand int unsigned m_len_a;
|
||||
rand int unsigned m_len_b;
|
||||
rand int m_arr_a[];
|
||||
rand int m_arr_b[];
|
||||
|
||||
constraint c_lens {
|
||||
m_len_a inside {[2:8]};
|
||||
m_len_b inside {[3:10]};
|
||||
}
|
||||
endclass
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
automatic Packet pkt = new;
|
||||
automatic MultiArray ma = new;
|
||||
automatic int ok;
|
||||
|
||||
// Scenario 1: single class with two dynamic arrays sized by one field
|
||||
repeat (5) begin
|
||||
ok = pkt.randomize() with {
|
||||
m_data.size == m_length;
|
||||
m_mask.size == m_length;
|
||||
foreach (m_mask[i]) m_mask[i] inside {8'h00, 8'hFF};
|
||||
};
|
||||
`checkh(ok, 1)
|
||||
`checkh(pkt.m_data.size(), pkt.m_length)
|
||||
`checkh(pkt.m_mask.size(), pkt.m_length)
|
||||
// Verify mask values are constrained
|
||||
for (int i = 0; i < pkt.m_mask.size(); i++) begin
|
||||
if (pkt.m_mask[i] !== 8'h00 && pkt.m_mask[i] !== 8'hFF) begin
|
||||
$write("%%Error: m_mask[%0d]=%0h not in {00, FF}\n", i, pkt.m_mask[i]);
|
||||
`stop;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Scenario 2: two arrays with independent size constraints
|
||||
repeat (5) begin
|
||||
ok = ma.randomize() with {
|
||||
m_arr_a.size == m_len_a;
|
||||
m_arr_b.size == m_len_b;
|
||||
};
|
||||
`checkh(ok, 1)
|
||||
`checkh(ma.m_arr_a.size(), ma.m_len_a)
|
||||
`checkh(ma.m_arr_b.size(), ma.m_len_b)
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -8,7 +8,4 @@
|
|||
15 | rand Cls cls;
|
||||
| ^~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Warning-CONSTRAINTIGN: t/t_randomize_method_types_unsup.v:39:41: Unsupported: randomizing this expression, treating as state
|
||||
39 | res = obj.randomize() with { dynarr.size > 2; };
|
||||
| ^~~~
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
Loading…
Reference in New Issue