diff --git a/src/V3Active.cpp b/src/V3Active.cpp index c97f9f45a..b1629b4b8 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -627,59 +627,45 @@ public: class CovergroupSamplingVisitor final : public VNVisitor { // STATE ActiveNamer m_namer; // Reuse active naming infrastructure - AstScope* m_scopep = nullptr; // Current scope bool m_inFirstPass = true; // First pass collects CFuncs, second pass adds sampling std::unordered_map m_covergroupSampleFuncs; // Class -> sample CFunc - - // Helper to get the clocking event from a covergroup class - AstSenTree* getCovergroupEvent(AstClass* classp) { - // The AstCovergroup (holding the SenTree) was left in membersp by V3Covergroup - for (AstNode* memberp = classp->membersp(); memberp; memberp = memberp->nextp()) { - if (AstCovergroup* const cgp = VN_CAST(memberp, Covergroup)) { - if (cgp->eventp()) return cgp->eventp(); - } - } - return nullptr; - } + std::unordered_map + m_covergroupEvents; // Class -> sampling event (if any) // VISITORS void visit(AstScope* nodep) override { - m_scopep = nodep; m_namer.main(nodep); // Initialize active naming for this scope - // First pass: collect sample CFuncs from covergroup class scopes + // First pass: collect sample CFuncs and sampling events from covergroup class scopes if (m_inFirstPass) { - // Check if this is a covergroup class scope (contains sample CFunc) - for (AstNode* itemp = m_scopep->blocksp(); itemp; itemp = itemp->nextp()) { - if (AstCFunc* const cfuncp = VN_CAST(itemp, CFunc)) { - if (cfuncp->name().find("sample") != string::npos) { - // This is a covergroup class scope - find the class and store the CFunc - // The scope name is like "TOP.t__03a__03acg", extract class name - string scopeName = nodep->name(); - size_t dotPos = scopeName.find('.'); - if (dotPos != string::npos) { - string className = scopeName.substr(dotPos + 1); - // Search netlist for the matching covergroup class - for (AstNode* modp = v3Global.rootp()->modulesp(); modp; - modp = modp->nextp()) { - if (AstClass* const classp = VN_CAST(modp, Class)) { - if (classp->isCovergroup() && classp->name() == className) { - m_covergroupSampleFuncs[classp] = cfuncp; - cfuncp->isCovergroupSample(true); - break; - } - } - } + AstClass* const classp = VN_CAST(nodep->modp(), Class); + if (classp && classp->isCovergroup()) { + for (AstNode* itemp = nodep->blocksp(); itemp; itemp = itemp->nextp()) { + if (AstCFunc* const cfuncp = VN_CAST(itemp, CFunc)) { + if (cfuncp->name().find("sample") != string::npos) { + m_covergroupSampleFuncs[classp] = cfuncp; + cfuncp->isCovergroupSample(true); + break; } + } + } + for (AstNode* memberp = classp->membersp(); memberp;) { + AstNode* const nextp = memberp->nextp(); + if (AstCovergroup* const cgp = VN_CAST(memberp, Covergroup)) { + // Unlink eventp from cgp so it survives cgp's deletion, + // then take ownership in the map for use during the second pass. + if (cgp->eventp()) m_covergroupEvents[classp] = cgp->eventp()->unlinkFrBack(); + cgp->unlinkFrBack(); + VL_DO_DANGLING(cgp->deleteTree(), cgp); break; } + memberp = nextp; } } } iterateChildren(nodep); - m_scopep = nullptr; } void visit(AstVarScope* nodep) override { @@ -701,50 +687,17 @@ class CovergroupSamplingVisitor final : public VNVisitor { if (!classp || !classp->isCovergroup()) return; // Check if this covergroup has an automatic sampling event - AstSenTree* const eventp = getCovergroupEvent(classp); - if (!eventp) return; // No automatic sampling for this covergroup + const auto evtIt = m_covergroupEvents.find(classp); + if (evtIt == m_covergroupEvents.end()) return; // No automatic sampling for this covergroup + AstSenTree* const eventp = evtIt->second; - // Get the sample CFunc - we need to find it in the class scope - // The class scope name is like "TOP.t__03a__03acg" for class "t__03a__03acg" - const string classScopeName = string("TOP.") + classp->name(); - - AstCFunc* sampleCFuncp = nullptr; - // Search through all scopes to find the class scope and its sample CFunc - for (AstNode* scopeNode = m_scopep; scopeNode; scopeNode = scopeNode->backp()) { - if (AstNetlist* netlistp = VN_CAST(scopeNode, Netlist)) { - // Found netlist, search its modules for scopes - for (AstNode* modp = netlistp->modulesp(); modp; modp = modp->nextp()) { - if (AstScope* scopep = VN_CAST(modp, Scope)) { - if (scopep->name() == classScopeName) { - // Found the class scope, now find the sample CFunc - for (AstNode* itemp = scopep->blocksp(); itemp; - itemp = itemp->nextp()) { - if (AstCFunc* cfuncp = VN_CAST(itemp, CFunc)) { - if (cfuncp->name().find("sample") != string::npos) { - sampleCFuncp = cfuncp; - break; - } - } - } - break; - } - } - } - break; - } - } - - if (!sampleCFuncp) { - // Fallback: try the cached version - auto it = m_covergroupSampleFuncs.find(classp); - if (it != m_covergroupSampleFuncs.end()) { sampleCFuncp = it->second; } - } - - if (!sampleCFuncp) { + // Get the sample CFunc from the map populated during the first pass + const auto it = m_covergroupSampleFuncs.find(classp); + if (it == m_covergroupSampleFuncs.end()) { UINFO(4, "Could not find sample() CFunc for covergroup " << classp->name() << endl); - return; // CFunc not found + return; } - UASSERT_OBJ(sampleCFuncp, nodep, "Sample CFunc is null for covergroup"); + AstCFunc* const sampleCFuncp = it->second; // Create a VarRef to the covergroup instance for the method call FileLine* const fl = nodep->fileline(); @@ -761,35 +714,11 @@ class CovergroupSamplingVisitor final : public VNVisitor { // Set argTypes to "vlSymsp" so the emit code will pass it automatically cmethodCallp->argTypes("vlSymsp"); - // Clone the sensitivity for this active block - // Each VarRef in the sensitivity needs to be updated for the current scope + // Clone the sensitivity for this active block. + // V3Scope has already resolved all VarRefs in eventp, so the clone + // inherits correct varScopep values with no fixup needed. AstSenTree* senTreep = eventp->cloneTree(false); - // Fix up VarRefs in the cloned sensitivity - they need varScopep set - senTreep->foreach([this](AstVarRef* refp) { - if (!refp->varScopep() && refp->varp()) { - // Find the VarScope for this Var in the current scope - AstVarScope* vscp = nullptr; - for (AstNode* itemp = m_scopep->varsp(); itemp; itemp = itemp->nextp()) { - if (AstVarScope* const vsp = VN_CAST(itemp, VarScope)) { - if (vsp->varp() == refp->varp()) { - vscp = vsp; - break; - } - } - } - if (vscp) { - refp->varScopep(vscp); - UINFO(4, "Fixed VarRef in SenTree: " << refp->varp()->name() << " -> " - << vscp->name() << endl); - } else { - refp->v3fatalSrc("Could not find VarScope for clock signal '" - << refp->varp()->name() << "' in scope " << m_scopep->name() - << " when creating covergroup sampling active"); - } - } - }); - // Get or create the AstActive node for this sensitivity // senTreep is a template used by getActive() which clones it into the AstActive; // delete it afterwards as it is not added to the AST directly. @@ -814,7 +743,7 @@ public: UINFO(4, "CovergroupSamplingVisitor: Starting" << endl); - // First pass: collect sample CFuncs from covergroup class scopes + // First pass: collect sample CFuncs and sampling events; delete AstCovergroup holders m_inFirstPass = true; iterate(nodep); @@ -822,6 +751,10 @@ public: m_inFirstPass = false; iterate(nodep); + // Release the owned AstSenTree nodes that were unlinked from AstCovergroup during + // the first pass; they are no longer needed after all clones have been made. + for (auto& [classp, evtp] : m_covergroupEvents) VL_DO_DANGLING(evtp->deleteTree(), evtp); + UINFO(4, "CovergroupSamplingVisitor: Complete" << endl); } ~CovergroupSamplingVisitor() override = default; @@ -834,21 +767,5 @@ void V3Active::activeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); { ActiveVisitor{nodep}; } // Destruct before checking { CovergroupSamplingVisitor{nodep}; } // Add automatic covergroup sampling - // Delete AstCovergroup nodes (event holders) left in covergroup classes by - // V3CoverageFunctional. They were kept in the AST to avoid orphaned SenTree nodes; - // now that V3Active has consumed them we can delete them. - for (AstNode* modp = nodep->modulesp(); modp; modp = modp->nextp()) { - if (AstClass* const classp = VN_CAST(modp, Class)) { - if (!classp->isCovergroup()) continue; - for (AstNode* memberp = classp->membersp(); memberp;) { - AstNode* const nextp = memberp->nextp(); - if (AstCovergroup* const cgp = VN_CAST(memberp, Covergroup)) { - cgp->unlinkFrBack(); - VL_DO_DANGLING(cgp->deleteTree(), cgp); - } - memberp = nextp; - } - } - } V3Global::dumpCheckGlobalTree("active", 0, dumpTreeEitherLevel() >= 3); } diff --git a/test_regress/t/t_covergroup_auto_bins.py b/test_regress/t/t_covergroup_auto_bins.py index 10b6f7cd5..ceec4c59e 100755 --- a/test_regress/t/t_covergroup_auto_bins.py +++ b/test_regress/t/t_covergroup_auto_bins.py @@ -9,7 +9,7 @@ import vltest_bootstrap -test.scenarios('vlt') +test.scenarios('vlt_all') test.compile(verilator_flags2=['--coverage']) diff --git a/test_regress/t/t_covergroup_clocked_sample.py b/test_regress/t/t_covergroup_clocked_sample.py index 10b6f7cd5..ceec4c59e 100755 --- a/test_regress/t/t_covergroup_clocked_sample.py +++ b/test_regress/t/t_covergroup_clocked_sample.py @@ -9,7 +9,7 @@ import vltest_bootstrap -test.scenarios('vlt') +test.scenarios('vlt_all') test.compile(verilator_flags2=['--coverage']) diff --git a/test_regress/t/t_covergroup_cross_simple.py b/test_regress/t/t_covergroup_cross_simple.py index f8f6ee53a..ce66dd04d 100755 --- a/test_regress/t/t_covergroup_cross_simple.py +++ b/test_regress/t/t_covergroup_cross_simple.py @@ -10,7 +10,7 @@ import vltest_bootstrap -test.scenarios('vlt') +test.scenarios('vlt_all') test.compile(verilator_flags2=['--coverage'])