Fix handling of rand fields not referenced in constraints (#5305)

Signed-off-by: Ryszard Rozak <rrozak@antmicro.com>
This commit is contained in:
Ryszard Rozak 2024-07-26 11:46:30 +02:00 committed by GitHub
parent 0d43c14b93
commit b9e1d55262
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 93 additions and 37 deletions

View File

@ -743,6 +743,51 @@ class RandomizeVisitor final : public VNVisitor {
return clearp->makeStmt();
}
void addBasicRandomizeBody(AstFunc* const basicRandomizep, AstClass* const nodep) {
FileLine* const fl = nodep->fileline();
AstVar* const basicFvarp = VN_AS(basicRandomizep->fvarp(), Var);
AstVarRef* const basicFvarRefp = new AstVarRef{fl, basicFvarp, VAccess::WRITE};
AstConst* const beginBasicValp = new AstConst{fl, AstConst::WidthedValue{}, 32, 1};
basicRandomizep->addStmtsp(new AstAssign{fl, basicFvarRefp, beginBasicValp});
nodep->foreachMember([&](AstClass* classp, AstVar* memberVarp) {
if (!memberVarp->isRand() || memberVarp->user3()) return;
const AstNodeDType* const dtypep = memberVarp->dtypep()->skipRefp();
if (VN_IS(dtypep, BasicDType) || VN_IS(dtypep, StructDType)) {
AstVar* const randcVarp = newRandcVarsp(memberVarp);
AstVarRef* const refp = new AstVarRef{fl, classp, memberVarp, VAccess::WRITE};
AstNodeStmt* const stmtp = newRandStmtsp(fl, refp, randcVarp);
basicRandomizep->addStmtsp(stmtp);
} else if (const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType)) {
if (classRefp->classp() == nodep) {
memberVarp->v3warn(E_UNSUPPORTED,
"Unsupported: random member variable with the "
"type of the containing class");
return;
}
AstFunc* const memberFuncp
= V3Randomize::newRandomizeFunc(m_memberMap, classRefp->classp());
AstMethodCall* const callp
= new AstMethodCall{fl, new AstVarRef{fl, classp, memberVarp, VAccess::WRITE},
"randomize", nullptr};
callp->taskp(memberFuncp);
callp->dtypeFrom(memberFuncp);
AstVarRef* const basicFvarRefReadp = basicFvarRefp->cloneTree(false);
basicFvarRefReadp->access(VAccess::READ);
AstIf* const assignIfNotNullp = new AstIf{
fl,
new AstNeq{fl, new AstVarRef{fl, classp, memberVarp, VAccess::READ},
new AstConst{fl, AstConst::Null{}}},
new AstAssign{fl, basicFvarRefp->cloneTree(false),
new AstAnd{fl, basicFvarRefReadp, callp}}};
basicRandomizep->addStmtsp(assignIfNotNullp);
} else {
memberVarp->v3warn(E_UNSUPPORTED, "Unsupported: random member variable with type "
<< memberVarp->dtypep()->prettyDTypeNameQ());
}
});
}
// VISITORS
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
@ -815,42 +860,17 @@ class RandomizeVisitor final : public VNVisitor {
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(nodep, "new"), NodeFTask);
UASSERT_OBJ(newp, nodep, "No new() in class");
nodep->foreachMember([&](AstClass* classp, AstVar* memberVarp) {
if (!memberVarp->isRand() || memberVarp->user3()) return;
const AstNodeDType* const dtypep = memberVarp->dtypep()->skipRefp();
if (VN_IS(dtypep, BasicDType) || VN_IS(dtypep, StructDType)) {
AstVar* const randcVarp = newRandcVarsp(memberVarp);
AstVarRef* const refp = new AstVarRef{fl, classp, memberVarp, VAccess::WRITE};
AstNodeStmt* const stmtp = newRandStmtsp(fl, refp, randcVarp);
randomizep->addStmtsp(stmtp);
} else if (const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType)) {
if (classRefp->classp() == nodep) {
memberVarp->v3warn(E_UNSUPPORTED,
"Unsupported: random member variable with the "
"type of the containing class");
return;
}
AstFunc* const memberFuncp
= V3Randomize::newRandomizeFunc(m_memberMap, classRefp->classp());
AstMethodCall* const callp
= new AstMethodCall{fl, new AstVarRef{fl, classp, memberVarp, VAccess::WRITE},
"randomize", nullptr};
callp->taskp(memberFuncp);
callp->dtypeFrom(memberFuncp);
AstVarRef* fvarRefReadp = fvarRefp->cloneTree(false);
fvarRefReadp->access(VAccess::READ);
AstIf* const assignIfNotNullp = new AstIf{
fl,
new AstNeq{fl, new AstVarRef{fl, classp, memberVarp, VAccess::READ},
new AstConst{fl, AstConst::Null{}}},
new AstAssign{fl, fvarRefp->cloneTree(false),
new AstAnd{fl, fvarRefReadp, callp}}};
randomizep->addStmtsp(assignIfNotNullp);
} else {
memberVarp->v3warn(E_UNSUPPORTED, "Unsupported: random member variable with type "
<< memberVarp->dtypep()->prettyDTypeNameQ());
}
});
AstFunc* const basicRandomizep
= V3Randomize::newRandomizeFunc(m_memberMap, nodep, "__Vbasic_randomize");
addBasicRandomizeBody(basicRandomizep, nodep);
AstFuncRef* const basicRandomizeCallp = new AstFuncRef{fl, "__Vbasic_randomize", nullptr};
basicRandomizeCallp->taskp(basicRandomizep);
basicRandomizeCallp->dtypep(basicRandomizep->dtypep());
AstVarRef* const fvarRefReadp = fvarRefp->cloneTree(false);
fvarRefReadp->access(VAccess::READ);
randomizep->addStmtsp(new AstAssign{fl, fvarRefp->cloneTree(false),
new AstAnd{fl, fvarRefReadp, basicRandomizeCallp}});
addPrePostCall(nodep, randomizep, "post_randomize");
nodep->user1(false);
}
@ -978,6 +998,13 @@ class RandomizeVisitor final : public VNVisitor {
randomizeFuncp->addStmtsp(localGenp);
AstFunc* const basicRandomizeFuncp
= V3Randomize::newRandomizeFunc(m_memberMap, classp, "__Vbasic_randomize");
AstFuncRef* const basicRandomizeFuncCallp
= new AstFuncRef{nodep->fileline(), "__Vbasic_randomize", nullptr};
basicRandomizeFuncCallp->taskp(basicRandomizeFuncp);
basicRandomizeFuncCallp->dtypep(basicRandomizeFuncp->dtypep());
// Copy (derive) class constraints if present
if (classGenp) {
AstTask* const constrSetupFuncp = getCreateConstraintSetupFunc(classp);
@ -1004,7 +1031,7 @@ class RandomizeVisitor final : public VNVisitor {
randomizeFuncp->addStmtsp(new AstAssign{
nodep->fileline(),
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var), VAccess::WRITE},
solverCallp});
new AstAnd{nodep->fileline(), basicRandomizeFuncCallp, solverCallp}});
// Replace the node with a call to that function
nodep->name(randomizeFuncp->name());

View File

@ -4,6 +4,20 @@
// any use, without warranty, 2024 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
`define check_rand(cl, field) \
begin \
longint prev_result; \
int ok = 0; \
for (int i = 0; i < 10; i++) begin \
longint result; \
void'(cl.randomize()); \
result = longint'(field); \
if (i > 0 && result != prev_result) ok = 1; \
prev_result = result; \
end \
if (ok != 1) $stop; \
end
class Boo;
function new();
boo = 6;
@ -73,6 +87,15 @@ function automatic int return_2();
return 2;
endfunction
class Cls;
rand int a;
rand int b;
endclass
class Cls2 extends Cls;
rand int c;
endclass
module mwith();
submodule sub1();
submodule sub2();
@ -87,6 +110,8 @@ module mwith();
Baz baz = new;
Baz2 baz2 = new;
Bar bar = new;
Cls2 cls2 = new;
Cls cls = cls2;
$display("foo.x = %d", foo.x);
$display("-----------------");
@ -106,6 +131,10 @@ module mwith();
$display("Failed to randomize foo with inline constraints");
end
if (cls.randomize() with { b == 1;} != 1) $stop;
if (cls.b != 1) $stop;
`check_rand(cls2, cls2.a);
`check_rand(cls2, cls2.c);
// Check capture of a static variable
if (foo.randomize() with { a > sub1.sub_var; } != 1) $stop;
// Check reference to a function