Internals: Clean up sampling. No functional change intended (#7389)

This commit is contained in:
Ryszard Rozak 2026-04-09 14:49:07 +02:00 committed by GitHub
parent 9f9532ff78
commit cf6a2aec19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 63 additions and 85 deletions

View File

@ -127,6 +127,7 @@ class AssertVisitor final : public VNVisitor {
// Cleared on netlist
// AstNode::user1() -> bool. True if processed
// AstAlways::user2p() -> std::vector<AstVar*>. Delayed variables via 'm_delayed'
// AstNodeVarRef::user2() -> bool. True if shouldn't be sampled
const VNUser1InUse m_user1InUse;
const VNUser2InUse m_user2InUse;
AstUser2Allocator<AstAlways, std::vector<AstVar*>> m_delayed;
@ -149,7 +150,6 @@ class AssertVisitor final : public VNVisitor {
bool m_inRestrict = false; // True inside restrict assertion
AstNode* m_passsp = nullptr; // Current pass statement
AstNode* m_failsp = nullptr; // Current fail statement
bool m_underAssert = false; // Visited from assert
// Map from (expression, senTree) to AstAlways that computes delayed values of the expression
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
m_modExpr2Sen2DelayedAlwaysp;
@ -394,8 +394,18 @@ class AssertVisitor final : public VNVisitor {
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
{ AssertDeFutureVisitor{nodep->propp(), m_modp, m_modPastNum++}; }
iterateChildren(nodep);
iterateAndNextNull(nodep->sentreep());
if (AstAssert* const assertp = VN_CAST(nodep, Assert)) {
iterateAndNextNull(assertp->failsp());
} else if (AstAssertIntrinsic* const assertp = VN_CAST(nodep, AssertIntrinsic)) {
iterateAndNextNull(assertp->failsp());
} else if (AstCover* const coverp = VN_CAST(nodep, Cover)) {
iterateAndNextNull(coverp->coverincsp());
} else if (!VN_IS(nodep, Restrict)) {
nodep->v3fatalSrc("Unhandled assert type");
}
iterateAndNextNull(nodep->passsp());
AstSenTree* const sentreep = nodep->sentreep();
if (nodep->immediate()) {
UASSERT_OBJ(!sentreep, nodep, "Immediate assertions don't have sensitivity");
@ -453,10 +463,8 @@ class AssertVisitor final : public VNVisitor {
VL_RESTORER(m_passsp);
VL_RESTORER(m_failsp);
VL_RESTORER(m_underAssert);
m_passsp = passsp;
m_failsp = failsp;
m_underAssert = true;
iterate(nodep->propp());
AstNode* propExprp;
@ -710,7 +718,7 @@ class AssertVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstNodeVarRef* nodep) override {
if (m_inSampled && !nodep->varp()->noSample()) {
if (m_inSampled && !nodep->user2() && !nodep->varp()->isTemp()) {
if (!nodep->access().isReadOnly()) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Write to variable in sampled expression");
@ -731,26 +739,31 @@ class AssertVisitor final : public VNVisitor {
iterateChildren(nodep);
}
void visit(AstPExprClause* nodep) override {
if (m_underAssert) {
if (nodep->pass() && m_passsp) {
// Cover adds COVERINC by AstNode::addNext, thus need to clone next too.
nodep->replaceWith(m_passsp->cloneTree(true));
} else if (!nodep->pass() && m_failsp) {
// Stop may be added, thus need to clone next too.
nodep->replaceWith(m_failsp->cloneTree(true));
} else {
nodep->unlinkFrBack();
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
AstNode* stmtsp = nullptr;
if (nodep->pass() && m_passsp) {
// Cover adds COVERINC by AstNode::addNext, thus need to clone next too.
stmtsp = m_passsp->cloneTree(true);
} else if (!nodep->pass() && m_failsp) {
stmtsp = m_failsp->cloneTree(true);
}
if (stmtsp) {
stmtsp->foreachAndNext([](AstNodeVarRef* const refp) {
// References inside action blocks shouldn't be implicitly sampled
// m_passsp/m_failsp have been already visited once and refs explicitly sampled
// are handled already
refp->user2(1);
});
nodep->replaceWith(stmtsp);
} else {
nodep->unlinkFrBack();
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstPExpr* nodep) override {
if (m_underAssert) {
VL_RESTORER(m_inSampled);
m_inSampled = false;
iterateChildren(nodep);
} else if (m_inRestrict) {
if (m_inRestrict) {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
} else {
iterateChildren(nodep);
}
}

View File

@ -480,7 +480,6 @@ private:
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
cntVarp->noSample(true);
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true};
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
@ -494,11 +493,8 @@ private:
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, throughoutOkp, VAccess::WRITE},
new AstConst{flp, AstConst::BitTrue{}}});
// Check condition at tick 0 (sequence start, before entering loop)
AstSampled* const initSampledp
= new AstSampled{flp, throughoutp->cloneTreePure(false)};
initSampledp->dtypeSetBit();
beginp->addStmtsp(
new AstIf{flp, new AstLogNot{flp, initSampledp},
new AstIf{flp, new AstLogNot{flp, throughoutp->cloneTreePure(false)},
new AstAssign{flp, new AstVarRef{flp, throughoutOkp, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}}});
}
@ -520,10 +516,8 @@ private:
new AstConst{flp, 1}}});
// Check throughout condition at each tick during delay (IEEE 1800-2023 16.9.9)
if (throughoutp) {
AstSampled* const sampledp = new AstSampled{flp, throughoutp};
sampledp->dtypeSetBit();
loopp->addStmtsp(
new AstIf{flp, new AstLogNot{flp, sampledp},
new AstIf{flp, new AstLogNot{flp, throughoutp},
new AstAssign{flp, new AstVarRef{flp, throughoutOkp, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}}});
}
@ -729,7 +723,6 @@ private:
AstVar* const cntVarp = new AstVar{flp, VVarType::MODULETEMP, m_consRepNames.get(""),
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::STATIC_EXPLICIT);
cntVarp->noSample(true);
m_modp->addStmtsp(cntVarp);
AstNodeExpr* const exprClonep = exprp->cloneTreePure(false);
AstNodeExpr* const saturatingIncrp = new AstCond{
@ -853,7 +846,6 @@ private:
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, name + "__counter",
exprp->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
cntVarp->noSample(true);
AstBegin* const beginp = new AstBegin{flp, name + "__block", cntVarp, true};
beginp->addStmtsp(
@ -865,10 +857,9 @@ private:
new AstLt{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
countp->cloneTreePure(false)}});
// if ($sampled(expr)) cnt++
AstSampled* const sampledp = new AstSampled{flp, exprp};
sampledp->dtypeFrom(exprp);
// sampled is applied to whole property expr
loopp->addStmtsp(
new AstIf{flp, sampledp,
new AstIf{flp, exprp,
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
new AstAdd{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
new AstConst{flp, 1}}}});
@ -884,9 +875,7 @@ private:
beginp->addStmtsp(new AstEventControl{
flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr});
}
AstSampled* const sampledRhsp = new AstSampled{flp, rhsp};
sampledRhsp->dtypeFrom(rhsp);
beginp->addStmtsp(new AstIf{flp, sampledRhsp, new AstPExprClause{flp, true},
beginp->addStmtsp(new AstIf{flp, rhsp, new AstPExprClause{flp, true},
new AstPExprClause{flp, false}});
} else {
beginp->addStmtsp(new AstPExprClause{flp, true});
@ -1018,8 +1007,7 @@ private:
// Overlapped implication (|->): check antecedent on same cycle.
// disable iff is applied at the assertion level, not at the
// antecedent gate, matching the existing non-PExpr overlapped path.
condp = new AstSampled{flp, lhsp};
condp->dtypeFrom(lhsp);
condp = lhsp;
} else {
// Non-overlapped implication (|=>): check antecedent from previous cycle
if (m_disablep) {

View File

@ -195,11 +195,6 @@ class AssertPropConsRepVisitor final : public VNVisitor {
bp->addStmtsp(setDone());
return bp;
};
auto sampled = [&](AstNodeExpr* ep) {
AstSampled* const sp = new AstSampled{flp, ep};
sp->dtypeFrom(ep);
return sp;
};
// Loop body: ##1 delay, then branch on count vs min
AstLoop* const loopp = new AstLoop{flp};
@ -217,13 +212,13 @@ class AssertPropConsRepVisitor final : public VNVisitor {
failStmts()});
}
AstIf* const tryNextp = new AstIf{
flp, sampled(nextExprp), passStmts(),
new AstIf{flp, sampled(repExprp->cloneTreePure(false)), continueBlockp, failStmts()}};
flp, nextExprp, passStmts(),
new AstIf{flp, repExprp->cloneTreePure(false), continueBlockp, failStmts()}};
if (r.minN > 0) {
// When cnt < minN: still accumulating -- must see expr to continue
AstIf* const accumulatep
= new AstIf{flp, sampled(repExprp->cloneTreePure(false)), incrCnt(), failStmts()};
= new AstIf{flp, repExprp->cloneTreePure(false), incrCnt(), failStmts()};
loopp->addStmtsp(
new AstIf{flp,
new AstGte{flp, cntRef(VAccess::READ),
@ -247,14 +242,14 @@ class AssertPropConsRepVisitor final : public VNVisitor {
// Zero-repetition path: skip directly to ##1 and check next
AstBegin* const skipBlockp = new AstBegin{flp, "", nullptr, true};
skipBlockp->addStmtsp(delayp->cloneTree(false));
skipBlockp->addStmtsp(new AstIf{flp, sampled(nextExprp->cloneTreePure(false)),
passStmts(), failStmts()});
skipBlockp->addStmtsp(
new AstIf{flp, nextExprp->cloneTreePure(false), passStmts(), failStmts()});
return skipBlockp;
}
return new AstPExprClause{flp, false};
}();
AstIf* const topIfp = new AstIf{flp, sampled(repExprp), entryBlockp, elsep};
AstIf* const topIfp = new AstIf{flp, repExprp, entryBlockp, elsep};
// Wrap everything in a PExpr with cnt and done as locals
AstBegin* const bodyp = new AstBegin{flp, "", cntVarp, true};
@ -466,11 +461,9 @@ class AssertPropLowerVisitor final : public VNVisitor {
auto& exprs = it->second;
// Combine all expressions at this cycle with LogAnd
AstNodeExpr* condp = new AstSampled{flp, exprs[0]};
condp->dtypeSetBit();
AstNodeExpr* condp = exprs[0];
for (size_t i = 1; i < exprs.size(); ++i) {
AstNodeExpr* const rp = new AstSampled{flp, exprs[i]};
rp->dtypeSetBit();
AstNodeExpr* const rp = exprs[i];
condp = new AstLogAnd{flp, condp, rp};
condp->dtypeSetBit();
}
@ -572,23 +565,22 @@ class AssertPropLowerVisitor final : public VNVisitor {
const int brMaxCycle = (entry.branchId == 0) ? br0MaxCycle : br1MaxCycle;
const bool isLast = (cycle == brMaxCycle);
AstNodeExpr* const sampledp = new AstSampled{flp, entry.exprp->cloneTree(false)};
sampledp->dtypeSetBit();
AstNodeExpr* const exprp = entry.exprp->cloneTree(false);
AstNodeExpr* const alivep
= new AstLogNot{flp, new AstVarRef{flp, deadVarp, VAccess::READ}};
alivep->dtypeSetBit();
if (isLast) {
// Last check: alive && passes -> pass
AstNodeExpr* const passCond = new AstLogAnd{flp, alivep, sampledp};
AstNodeExpr* const passCond = new AstLogAnd{flp, alivep, exprp};
passCond->dtypeSetBit();
cycleBlock->addStmtsp(new AstIf{flp, passCond, makePass()});
// alive && fails -> dead
AstNodeExpr* const alive2p
= new AstLogNot{flp, new AstVarRef{flp, deadVarp, VAccess::READ}};
alive2p->dtypeSetBit();
AstNodeExpr* const failCond = new AstLogAnd{
flp, alive2p, new AstLogNot{flp, sampledp->cloneTree(false)}};
AstNodeExpr* const failCond
= new AstLogAnd{flp, alive2p, new AstLogNot{flp, exprp->cloneTree(false)}};
failCond->dtypeSetBit();
cycleBlock->addStmtsp(
new AstIf{flp, failCond,
@ -597,7 +589,7 @@ class AssertPropLowerVisitor final : public VNVisitor {
} else {
// Non-last: alive && fails -> dead
AstNodeExpr* const failCond
= new AstLogAnd{flp, alivep, new AstLogNot{flp, sampledp}};
= new AstLogAnd{flp, alivep, new AstLogNot{flp, exprp}};
failCond->dtypeSetBit();
cycleBlock->addStmtsp(
new AstIf{flp, failCond,
@ -888,10 +880,8 @@ class AssertPropTransformer final {
AstBegin* const passsp = new AstBegin{nodep->fileline(), "", nullptr, true};
AstNode* const failsp = vtxp->outEdges().backp()->top()->as<DfaStmtVertex>()->nodep();
AstSampled* const sampledp
= new AstSampled{nodep->fileline(), VN_AS(vtxp->nodep(), NodeExpr)};
sampledp->dtypeFrom(vtxp->nodep());
AstIf* const ifp = new AstIf{nodep->fileline(), sampledp, passsp, failsp};
AstNodeExpr* const exprp = VN_AS(vtxp->nodep(), NodeExpr);
AstIf* const ifp = new AstIf{nodep->fileline(), exprp, passsp, failsp};
m_current->addStmtsp(ifp);
m_current = passsp;
return processEdge(vtxp->outEdges().frontp());
@ -1101,10 +1091,7 @@ class RangeDelayExpander final : public VNVisitor {
AstNode* makeRangeCheckBody(FileLine* flp, AstVar* stateVarp, AstVar* cntVarp,
AstVar* failVarp, AstNodeExpr* exprp, AstNode* matchActionp,
bool isUnbounded) {
if (isUnbounded) {
return new AstIf{flp, new AstSampled{flp, exprp->cloneTree(false)}, matchActionp,
nullptr};
}
if (isUnbounded) return new AstIf{flp, exprp->cloneTree(false), matchActionp, nullptr};
AstBegin* const timeoutp = new AstBegin{flp, "", nullptr, true};
timeoutp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, failVarp, VAccess::WRITE},
new AstConst{flp, AstConst::BitTrue{}}});
@ -1116,8 +1103,7 @@ class RangeDelayExpander final : public VNVisitor {
AstIf* const failOrRetryp = new AstIf{
flp, new AstEq{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 0}},
timeoutp, decrementp};
return new AstIf{flp, new AstSampled{flp, exprp->cloneTree(false)}, matchActionp,
failOrRetryp};
return new AstIf{flp, exprp->cloneTree(false), matchActionp, failOrRetryp};
}
AstNode* buildFsmBody(FileLine* flp, AstVar* stateVarp, AstVar* cntVarp, AstVar* failVarp,
@ -1186,8 +1172,7 @@ class RangeDelayExpander final : public VNVisitor {
new AstConst{flp, AstConst::BitTrue{}}});
failp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, stateVarp, VAccess::WRITE},
new AstConst{flp, 0}});
AstIf* const bodyp = new AstIf{
flp, new AstSampled{flp, step.exprp->cloneTree(false)}, passp, failp};
AstIf* const bodyp = new AstIf{flp, step.exprp->cloneTree(false), passp, failp};
fsmChainp = chainState(flp, fsmChainp, stateVarp, bounds[i].checkState, bodyp);
}
}
@ -1199,12 +1184,12 @@ class RangeDelayExpander final : public VNVisitor {
// Trigger = antecedent AND/OR first step expression
AstNodeExpr* triggerp = nullptr;
if (antExprp && firstStep.exprp) {
triggerp = new AstAnd{flp, new AstSampled{flp, antExprp->cloneTree(false)},
new AstSampled{flp, firstStep.exprp->cloneTree(false)}};
triggerp
= new AstAnd{flp, antExprp->cloneTree(false), firstStep.exprp->cloneTree(false)};
} else if (antExprp) {
triggerp = new AstSampled{flp, antExprp->cloneTree(false)};
triggerp = antExprp->cloneTree(false);
} else if (firstStep.exprp) {
triggerp = new AstSampled{flp, firstStep.exprp->cloneTree(false)};
triggerp = firstStep.exprp->cloneTree(false);
}
if (firstStep.isUnbounded && firstStep.rangeMin == 0 && steps.size() > 1) {
@ -1215,8 +1200,7 @@ class RangeDelayExpander final : public VNVisitor {
const int checkState = bounds[0].checkState;
const int afterMatch = checkState + 1;
const bool isTail = (steps.size() == 2 && nextStep.delay == 0);
AstNodeExpr* const immCheckp = new AstSampled{flp, nextStep.exprp->cloneTree(false)};
immCheckp->dtypeSetBit();
AstNodeExpr* const immCheckp = nextStep.exprp->cloneTree(false);
AstNode* const immMatchp
= makeOnMatchAction(flp, stateVarp, cntVarp, isTail, afterMatch, nextStep.delay);
AstNode* const toCheckp = makeStateTransition(flp, stateVarp, cntVarp, checkState, 0);
@ -1350,15 +1334,12 @@ class RangeDelayExpander final : public VNVisitor {
AstVar* const stateVarp = new AstVar{flp, VVarType::MODULETEMP, baseName + "__state",
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
stateVarp->lifetime(VLifetime::STATIC_EXPLICIT);
stateVarp->noSample(true);
AstVar* const cntVarp = new AstVar{flp, VVarType::MODULETEMP, baseName + "__cnt",
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::STATIC_EXPLICIT);
cntVarp->noSample(true);
AstVar* const failVarp = new AstVar{flp, VVarType::MODULETEMP, baseName + "__fail",
nodep->findBasicDType(VBasicDTypeKwd::BIT)};
failVarp->lifetime(VLifetime::STATIC_EXPLICIT);
failVarp->noSample(true);
// Build FSM body
AstNode* const fsmBodyp = buildFsmBody(flp, stateVarp, cntVarp, failVarp, steps, antExprp);

View File

@ -1968,7 +1968,6 @@ class AstVar final : public AstNode {
bool m_dfgAllowMultidriveTri : 1; // Allow DFG MULTIDRIVEN warning for intentional tri nets
bool m_globalConstrained : 1; // Global constraint per IEEE 1800-2023 18.5.8
bool m_isStdRandomizeArg : 1; // Argument variable created for std::randomize (__Varg*)
bool m_noSample : 1; // Do not wrap with AstSampled in assertion context
bool m_processQueue : 1; // Process queue variable
void init() {
m_ansi = false;
@ -2027,7 +2026,6 @@ class AstVar final : public AstNode {
m_dfgAllowMultidriveTri = false;
m_globalConstrained = false;
m_isStdRandomizeArg = false;
m_noSample = false;
m_processQueue = false;
}
@ -2176,8 +2174,6 @@ public:
void noReset(bool flag) { m_noReset = flag; }
bool noSubst() const { return m_noSubst; }
void noSubst(bool flag) { m_noSubst = flag; }
bool noSample() const { return m_noSample; }
void noSample(bool flag) { m_noSample = flag; }
bool processQueue() const { return m_processQueue; }
void processQueue(bool flag) { m_processQueue = flag; }
bool sampled() const { return m_sampled; }