mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 10:03:44 +02:00
V3Timing: pre-clear destructive event state before dynamic waits (#7340)
When lowering dynamic event controls, destructive pre-clear updates were inserted with addHereThisAsNext() on the original node and then that node was replaced. This could leave leaked/orphaned nodes under leak-checking runs. Build an explicit replacement stmt chain instead: [pre-clear stmts] -> trigger loop -> awaitResumption, and replace the original control with the chain head. Keep the loop-only path unchanged when no destructive pre-clear is needed.
This commit is contained in:
+19
-17
@@ -639,15 +639,6 @@ class TimingControlVisitor final : public VNVisitor {
|
||||
return !nodep->isPure();
|
||||
});
|
||||
}
|
||||
// Returns true if the given trigger expression needs a destructive post update after trigger
|
||||
// evaluation. Currently this only applies to named events.
|
||||
bool destructivePostUpdate(AstNode* const exprp) const {
|
||||
return exprp->exists([](const AstNode* const nodep) {
|
||||
const AstNodeDType* const dtypep = nodep->dtypep();
|
||||
const AstBasicDType* const basicp = dtypep ? dtypep->skipRefp()->basicp() : nullptr;
|
||||
return basicp && basicp->isEvent();
|
||||
});
|
||||
}
|
||||
// Creates a trigger scheduler variable
|
||||
AstVarScope* getCreateTriggerSchedulerp(AstSenTree* const sentreep) {
|
||||
if (!sentreep->user1p()) {
|
||||
@@ -1085,7 +1076,6 @@ class TimingControlVisitor final : public VNVisitor {
|
||||
= createTemp(flp, m_dynTrigNames.get(nodep), nodep->findBitDType(), nodep);
|
||||
auto* const initp = new AstAssign{flp, new AstVarRef{flp, trigvscp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitFalse{}}};
|
||||
nodep->addHereThisAsNext(initp);
|
||||
// Await the eval step with the dynamic trigger scheduler. First, create the method
|
||||
// call
|
||||
auto* const evalMethodp = new AstCMethodHard{
|
||||
@@ -1105,10 +1095,10 @@ class TimingControlVisitor final : public VNVisitor {
|
||||
// Get the SenExprBuilder results
|
||||
const SenExprBuilder::Results senResults = m_senExprBuilderp->getAndClearResults();
|
||||
localizeVars(m_procp, senResults.m_vars);
|
||||
// Put all and inits before the trigger eval loop
|
||||
for (AstNodeStmt* const stmtp : senResults.m_inits) {
|
||||
nodep->addHereThisAsNext(stmtp);
|
||||
}
|
||||
// If post updates are destructive (e.g. clearFired on events), perform a
|
||||
// conservative pre-clear once before entering the wait loop so stale state from a
|
||||
// previous wait does not cause an immediate false-positive trigger.
|
||||
const bool hasDestructivePostUpdates = !senResults.m_destructivePostUpdates.empty();
|
||||
// Create the trigger eval loop, which will await the evaluation step and check the
|
||||
// trigger
|
||||
AstNodeExpr* const condp
|
||||
@@ -1129,7 +1119,7 @@ class TimingControlVisitor final : public VNVisitor {
|
||||
loopp->addStmtsp(anyTriggeredMethodp->makeStmt());
|
||||
// If the post update is destructive (e.g. event vars are cleared), create an await for
|
||||
// the post update step
|
||||
if (destructivePostUpdate(sentreep)) {
|
||||
if (hasDestructivePostUpdates) {
|
||||
AstCAwait* const awaitPostUpdatep = awaitEvalp->cloneTree(false);
|
||||
VN_AS(awaitPostUpdatep->exprp(), CMethodHard)->method(VCMethod::SCHED_POST_UPDATE);
|
||||
loopp->addStmtsp(awaitPostUpdatep);
|
||||
@@ -1140,8 +1130,20 @@ class TimingControlVisitor final : public VNVisitor {
|
||||
AstCAwait* const awaitResumep = awaitEvalp->cloneTree(false);
|
||||
VN_AS(awaitResumep->exprp(), CMethodHard)->method(VCMethod::SCHED_RESUMPTION);
|
||||
AstNode::addNext<AstNodeStmt, AstNodeStmt>(loopp, awaitResumep);
|
||||
// Replace the event control with the loop
|
||||
nodep->replaceWith(loopp);
|
||||
// Replace the event control with one explicit stmt chain:
|
||||
// init -> [inits] -> [optional destructive pre-clears] -> loop -> awaitResumption
|
||||
AstNodeStmt* chainp = nullptr;
|
||||
chainp = AstNode::addNextNull(chainp, initp);
|
||||
for (AstNodeStmt* const stmtp : senResults.m_inits) {
|
||||
chainp = AstNode::addNextNull(chainp, stmtp);
|
||||
}
|
||||
if (hasDestructivePostUpdates) {
|
||||
for (AstNodeStmt* const stmtp : senResults.m_destructivePostUpdates) {
|
||||
chainp = AstNode::addNextNull(chainp, stmtp->cloneTree(false));
|
||||
}
|
||||
}
|
||||
chainp = AstNode::addNextNull(chainp, loopp);
|
||||
nodep->replaceWith(chainp);
|
||||
} else {
|
||||
auto* const sentreep = m_finder.getSenTree(nodep->sentreep());
|
||||
nodep->sentreep()->unlinkFrBack()->deleteTree();
|
||||
|
||||
Reference in New Issue
Block a user