Skip to content

Commit e25c66d

Browse files
authored
Support complex std::randomize patterns (verilator#6736) (verilator#6737)
1 parent b9b6eb6 commit e25c66d

16 files changed

+272
-197
lines changed

src/V3AstNodeOther.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1878,6 +1878,7 @@ class AstVar final : public AstNode {
18781878
bool m_ignoreSchedWrite : 1; // Ignore writes in scheduling (for special optimizations)
18791879
bool m_dfgMultidriven : 1; // Singal is multidriven, used by DFG to avoid repeat processing
18801880
bool m_globalConstrained : 1; // Global constraint per IEEE 1800-2023 18.5.8
1881+
bool m_isStdRandomizeArg : 1; // Argument variable created for std::randomize (__Varg*)
18811882
void init() {
18821883
m_ansi = false;
18831884
m_declTyped = false;
@@ -1929,6 +1930,7 @@ class AstVar final : public AstNode {
19291930
m_ignoreSchedWrite = false;
19301931
m_dfgMultidriven = false;
19311932
m_globalConstrained = false;
1933+
m_isStdRandomizeArg = false;
19321934
}
19331935

19341936
public:
@@ -2096,6 +2098,8 @@ class AstVar final : public AstNode {
20962098
void setDfgMultidriven() { m_dfgMultidriven = true; }
20972099
void globalConstrained(bool flag) { m_globalConstrained = flag; }
20982100
bool globalConstrained() const { return m_globalConstrained; }
2101+
bool isStdRandomizeArg() const { return m_isStdRandomizeArg; }
2102+
void setStdRandomizeArg() { m_isStdRandomizeArg = true; }
20992103
// METHODS
21002104
void name(const string& name) override { m_name = name; }
21012105
void tag(const string& text) override { m_tag = text; }

src/V3AstNodes.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2784,6 +2784,7 @@ void AstVar::dump(std::ostream& str) const {
27842784
if (isDpiOpenArray()) str << " [DPIOPENA]";
27852785
if (ignorePostWrite()) str << " [IGNPWR]";
27862786
if (ignoreSchedWrite()) str << " [IGNWR]";
2787+
if (isStdRandomizeArg()) str << " [STDRANDARG]";
27872788
if (!lifetime().isNone()) str << " [" << lifetime().ascii() << "] ";
27882789
str << " " << varType();
27892790
}
@@ -2805,6 +2806,7 @@ void AstVar::dumpJson(std::ostream& str) const {
28052806
dumpJsonBoolFunc(str, isDpiOpenArray);
28062807
dumpJsonBoolFunc(str, isFuncReturn);
28072808
dumpJsonBoolFunc(str, isFuncLocal);
2809+
dumpJsonBoolFunc(str, isStdRandomizeArg);
28082810
dumpJsonStr(str, "lifetime", lifetime().ascii());
28092811
dumpJsonStr(str, "varType", varType().ascii());
28102812
if (dtypep()) dumpJsonStr(str, "dtypeName", dtypep()->name());

src/V3Randomize.cpp

Lines changed: 83 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ class RandomizeMarkVisitor final : public VNVisitor {
175175
if (varrefp->varp() == varp) return true;
176176
} else if (const AstMemberSel* const memberselp = VN_CAST(exprp, MemberSel)) {
177177
if (memberselp->varp() == varp) return true;
178+
} else if (const AstArraySel* const arrselp = VN_CAST(exprp, ArraySel)) {
179+
if (VN_AS(arrselp->fromp(), VarRef)->varp() == varp) return true;
178180
}
179181
}
180182
return false;
@@ -535,14 +537,19 @@ class RandomizeMarkVisitor final : public VNVisitor {
535537
AstNodeExpr* exprp = argp->exprp();
536538
while (exprp) {
537539
AstVar* randVarp = nullptr;
540+
AstVarRef* varrefp = nullptr;
538541
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
539542
randVarp = memberSelp->varp();
540543
exprp = memberSelp->fromp();
544+
} else if ((varrefp = VN_CAST(exprp, VarRef))) {
545+
randVarp = varrefp->varp();
546+
varrefp->user1(true);
547+
exprp = nullptr;
541548
} else {
542-
AstVarRef* varrefp = nullptr;
543-
varrefp = VN_AS(exprp, VarRef);
549+
varrefp = VN_AS(VN_CAST(exprp, ArraySel)->fromp(), VarRef);
544550
randVarp = varrefp->varp();
545551
varrefp->user1(true);
552+
varrefp->access(VAccess::READWRITE);
546553
exprp = nullptr;
547554
}
548555
UASSERT_OBJ(randVarp, nodep, "No rand variable found");
@@ -674,7 +681,7 @@ class RandomizeMarkVisitor final : public VNVisitor {
674681

675682
void visit(AstNodeExpr* nodep) override {
676683
iterateChildrenConst(nodep);
677-
if (!m_constraintExprGenp) return;
684+
if (!m_constraintExprGenp && !m_inStdWith) return;
678685
nodep->user1((nodep->op1p() && nodep->op1p()->user1())
679686
|| (nodep->op2p() && nodep->op2p()->user1())
680687
|| (nodep->op3p() && nodep->op3p()->user1())
@@ -1425,6 +1432,7 @@ class CaptureVisitor final : public VNVisitor {
14251432
AstVar* getVar(AstVar* const varp) const {
14261433
const auto it = m_varCloneMap.find(varp);
14271434
if (it == m_varCloneMap.end()) return nullptr;
1435+
if (it->second->isStdRandomizeArg()) return nullptr;
14281436
return it->second;
14291437
}
14301438

@@ -1495,6 +1503,7 @@ class CaptureVisitor final : public VNVisitor {
14951503
if (m_ignore.count(nodep)) return;
14961504
m_ignore.emplace(nodep);
14971505
UASSERT_OBJ(nodep->varp(), nodep, "Variable unlinked");
1506+
if (nodep->varp()->isStdRandomizeArg()) return;
14981507
CaptureMode capMode = getVarRefCaptureMode(nodep);
14991508
if (mode(capMode) == CaptureMode::CAP_NO) return;
15001509
if (mode(capMode) == CaptureMode::CAP_VALUE) captureRefByValue(nodep, capMode);
@@ -1633,6 +1642,33 @@ class RandomizeVisitor final : public VNVisitor {
16331642
std::set<std::string> m_writtenVars; // Track write_var calls per class to avoid duplicates
16341643

16351644
// METHODS
1645+
// Check if two nodes are semantically equivalent (not pointer equality):
1646+
static bool isSimilarNode(const AstNodeExpr* withExpr, const AstNodeExpr* argExpr) {
1647+
// VarRef: compare variable pointers
1648+
if (VN_IS(argExpr, VarRef) && VN_IS(withExpr, VarRef)) {
1649+
return VN_AS(withExpr, VarRef)->varp() == VN_AS(argExpr, VarRef)->varp();
1650+
}
1651+
// MemberSel: compare object and member (obj.y)
1652+
if (VN_IS(argExpr, MemberSel) && VN_IS(withExpr, MemberSel)) {
1653+
const AstMemberSel* const withMSp = VN_AS(withExpr, MemberSel);
1654+
const AstMemberSel* const argMSp = VN_AS(argExpr, MemberSel);
1655+
if (withMSp->varp() != argMSp->varp()) return false;
1656+
// Recursively compare the base object expression
1657+
return isSimilarNode(withMSp->fromp(), argMSp->fromp());
1658+
}
1659+
// ArraySel: compare array base and index (arr[i])
1660+
if (VN_IS(argExpr, ArraySel) && VN_IS(withExpr, ArraySel)) {
1661+
const AstArraySel* const withASp = VN_AS(withExpr, ArraySel);
1662+
const AstArraySel* const argASp = VN_AS(argExpr, ArraySel);
1663+
// Index must be Sel type, extract VarRef using fromp()
1664+
if (!VN_IS(withASp->bitp(), Sel) || !VN_IS(argASp->bitp(), Sel)) return false;
1665+
const AstNodeExpr* const withIdxp = VN_AS(withASp->bitp(), Sel)->fromp();
1666+
const AstNodeExpr* const argIdxp = VN_AS(argASp->bitp(), Sel)->fromp();
1667+
return isSimilarNode(withASp->fromp(), argASp->fromp())
1668+
&& isSimilarNode(withIdxp, argIdxp);
1669+
}
1670+
return false;
1671+
}
16361672
void createRandomGenerator(AstClass* const classp) {
16371673
if (classp->user3p()) return;
16381674
if (classp->extendsp()) {
@@ -2605,45 +2641,35 @@ class RandomizeVisitor final : public VNVisitor {
26052641
new AstConst{nodep->fileline(), AstConst::WidthedValue{}, 32, 1}});
26062642
std::unique_ptr<CaptureVisitor> withCapturep;
26072643
int argn = 0;
2644+
AstWith* withp = nullptr;
26082645
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
2609-
AstArg* const argp = VN_CAST(pinp, Arg);
2610-
AstWith* const withp = VN_CAST(pinp, With);
2611-
if (withp) {
2612-
FileLine* const fl = nodep->fileline();
2613-
withCapturep
2614-
= std::make_unique<CaptureVisitor>(withp->exprp(), m_modp, nullptr);
2615-
withCapturep->addFunctionArguments(randomizeFuncp);
2616-
// Clear old constraints and variables for std::randomize with clause
2617-
if (stdrand) {
2618-
randomizeFuncp->addStmtsp(
2619-
implementConstraintsClearAll(randomizeFuncp->fileline(), stdrand));
2620-
}
2621-
AstNode* const capturedTreep = withp->exprp()->unlinkFrBackWithNext();
2622-
randomizeFuncp->addStmtsp(capturedTreep);
2623-
{
2624-
ConstraintExprVisitor{m_memberMap, capturedTreep, randomizeFuncp,
2625-
stdrand, nullptr, m_writtenVars};
2626-
}
2627-
AstCExpr* const solverCallp = new AstCExpr{fl};
2628-
solverCallp->dtypeSetBit();
2629-
solverCallp->add(new AstVarRef{fl, stdrand, VAccess::READWRITE});
2630-
solverCallp->add(".next()");
2631-
AstVar* const fvarp = VN_AS(randomizeFuncp->fvarp(), Var);
2632-
AstVarRef* const retvalReadp = new AstVarRef{fl, fvarp, VAccess::READ};
2633-
AstNodeExpr* const andExprp = new AstAnd{fl, retvalReadp, solverCallp};
2634-
AstVarRef* const retvalWritep = new AstVarRef{fl, fvarp, VAccess::WRITE};
2635-
randomizeFuncp->addStmtsp(new AstAssign{fl, retvalWritep, andExprp});
2636-
}
2646+
if ((withp = VN_CAST(pinp, With))) break;
2647+
}
2648+
for (const AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
2649+
const AstArg* const argp = VN_CAST(pinp, Arg);
26372650
if (!argp) continue;
26382651
AstNodeExpr* exprp = argp->exprp();
2639-
26402652
AstCMethodHard* const basicMethodp = new AstCMethodHard{
26412653
nodep->fileline(),
26422654
new AstVarRef{nodep->fileline(), stdrand, VAccess::READWRITE},
26432655
VCMethod::RANDOMIZER_BASIC_STD_RANDOMIZATION};
26442656
AstVar* const refvarp
26452657
= new AstVar{exprp->fileline(), VVarType::MEMBER,
26462658
"__Varg"s + std::to_string(++argn), exprp->dtypep()};
2659+
refvarp->setStdRandomizeArg();
2660+
// Replace argument occurrences in 'with' clause with __Varg* reference.
2661+
if (withp) {
2662+
withp->foreach([&](AstNodeExpr* exp) {
2663+
if (isSimilarNode(exp, exprp)) {
2664+
AstVarRef* const replaceVar
2665+
= new AstVarRef{exprp->fileline(), refvarp, VAccess::READWRITE};
2666+
exp->replaceWith(replaceVar);
2667+
replaceVar->user1(exp->user1());
2668+
replaceVar->varp()->user2p(m_modp);
2669+
VL_DO_DANGLING(pushDeletep(exp), exp);
2670+
}
2671+
});
2672+
}
26472673
refvarp->direction(VDirection::REF);
26482674
refvarp->funcLocal(true);
26492675
refvarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
@@ -2666,6 +2692,31 @@ class RandomizeVisitor final : public VNVisitor {
26662692
VN_AS(randomizeFuncp->fvarp(), Var), VAccess::READ},
26672693
basicMethodp}});
26682694
}
2695+
if (withp) {
2696+
FileLine* const fl = nodep->fileline();
2697+
withCapturep = std::make_unique<CaptureVisitor>(withp->exprp(), m_modp, nullptr);
2698+
withCapturep->addFunctionArguments(randomizeFuncp);
2699+
// Clear old constraints and variables for std::randomize with clause
2700+
if (stdrand) {
2701+
randomizeFuncp->addStmtsp(
2702+
implementConstraintsClearAll(randomizeFuncp->fileline(), stdrand));
2703+
}
2704+
AstNode* const capturedTreep = withp->exprp()->unlinkFrBackWithNext();
2705+
randomizeFuncp->addStmtsp(capturedTreep);
2706+
{
2707+
ConstraintExprVisitor{m_memberMap, capturedTreep, randomizeFuncp,
2708+
stdrand, nullptr, m_writtenVars};
2709+
}
2710+
AstCExpr* const solverCallp = new AstCExpr{fl};
2711+
solverCallp->dtypeSetBit();
2712+
solverCallp->add(new AstVarRef{fl, stdrand, VAccess::READWRITE});
2713+
solverCallp->add(".next()");
2714+
AstVar* const fvarp = VN_AS(randomizeFuncp->fvarp(), Var);
2715+
AstNodeExpr* const andExprp
2716+
= new AstAnd{fl, new AstVarRef{fl, fvarp, VAccess::READ}, solverCallp};
2717+
randomizeFuncp->addStmtsp(
2718+
new AstAssign{fl, new AstVarRef{fl, fvarp, VAccess::WRITE}, andExprp});
2719+
}
26692720
// Remove With nodes from pins as they have been processed
26702721
for (AstNode* pinp = nodep->pinsp(); pinp;) {
26712722
AstNode* const nextp = pinp->nextp();

0 commit comments

Comments
 (0)