Refactor loops in V3Active ; add three tests to vlt_all, so they run with threading

Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
This commit is contained in:
Matthew Ballance 2026-03-14 19:41:49 +00:00
parent 1ec09f9d2d
commit a30e729852
4 changed files with 41 additions and 124 deletions

View File

@ -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<const AstClass*, AstCFunc*>
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<const AstClass*, AstSenTree*>
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);
}

View File

@ -9,7 +9,7 @@
import vltest_bootstrap
test.scenarios('vlt')
test.scenarios('vlt_all')
test.compile(verilator_flags2=['--coverage'])

View File

@ -9,7 +9,7 @@
import vltest_bootstrap
test.scenarios('vlt')
test.scenarios('vlt_all')
test.compile(verilator_flags2=['--coverage'])

View File

@ -10,7 +10,7 @@
import vltest_bootstrap
test.scenarios('vlt')
test.scenarios('vlt_all')
test.compile(verilator_flags2=['--coverage'])