diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 2adda9d22..22eea94a4 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -127,6 +127,7 @@ class AssertVisitor final : public VNVisitor { // Cleared on netlist // AstNode::user1() -> bool. True if processed // AstAlways::user2p() -> std::vector. Delayed variables via 'm_delayed' + // AstNodeVarRef::user2() -> bool. True if shouldn't be sampled const VNUser1InUse m_user1InUse; const VNUser2InUse m_user2InUse; AstUser2Allocator> 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, std::unordered_map, 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); } } diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 2a722f264..9c946b31e 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -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) { diff --git a/src/V3AssertProp.cpp b/src/V3AssertProp.cpp index 757b65a13..716e78231 100644 --- a/src/V3AssertProp.cpp +++ b/src/V3AssertProp.cpp @@ -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()->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); diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 166824082..4ca004b11 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -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; }