// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Removal of SAMPLED // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2003-2025 by Wilson Snyder. This program is free software; you // can redistribute it and/or modify it under the terms of either the GNU // Lesser General Public License Version 3 or the Perl Artistic License // Version 2.0. // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* // V3Sampled's Transformations: // // Top Scope: // Replace each variable reference under SAMPLED with a new variable. // Remove SAMPLED. // //************************************************************************* #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT #include "V3Sampled.h" VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### // Clock state, as a visitor of each AstNode class SampledVisitor final : public VNVisitor { // NODE STATE // AstVarScope::user1() -> AstVarScope*. The VarScope that stores sampled value // AstVarRef::user1() -> bool. Whether already converted const VNUser1InUse m_user1InUse; // STATE - for current visit position (use VL_RESTORER) AstScope* m_scopep = nullptr; // Current scope bool m_inSampled = false; // True inside a sampled expression // METHODS AstVarScope* createSampledVar(AstVarScope* vscp) { if (vscp->user1p()) return VN_AS(vscp->user1p(), VarScope); const AstVar* const varp = vscp->varp(); const string newvarname = "__Vsampled_" + vscp->scopep()->nameDotless() + "__" + varp->name(); FileLine* const flp = vscp->fileline(); AstVar* const newvarp = new AstVar{flp, VVarType::MODULETEMP, newvarname, varp->dtypep()}; m_scopep->modp()->addStmtsp(newvarp); AstVarScope* const newvscp = new AstVarScope{flp, m_scopep, newvarp}; newvarp->direction(VDirection::INPUT); // Inform V3Sched that it will be driven later newvarp->primaryIO(true); vscp->user1p(newvscp); m_scopep->addVarsp(newvscp); // At the top of _eval, assign them (use valuep here as temporary storage during V3Sched) newvarp->valuep(new AstVarRef{flp, vscp, VAccess::READ}); UINFO(4, "New Sampled: " << newvscp); return newvscp; } // VISITORS void visit(AstScope* nodep) override { VL_RESTORER(m_scopep); m_scopep = nodep; iterateChildren(nodep); } void visit(AstSampled* nodep) override { VL_RESTORER(m_inSampled); m_inSampled = true; iterateChildren(nodep); nodep->replaceWith(nodep->exprp()->unlinkFrBack()); VL_DO_DANGLING(pushDeletep(nodep), nodep); } void visit(AstVarRef* nodep) override { iterateChildren(nodep); if (m_inSampled && !nodep->user1SetOnce()) { UASSERT_OBJ(nodep->access().isReadOnly(), nodep, "Should have failed in V3Access"); AstVarScope* const varscp = nodep->varScopep(); AstVarScope* const lastscp = createSampledVar(varscp); AstNode* const newp = new AstVarRef{nodep->fileline(), lastscp, VAccess::READ}; newp->user1SetOnce(); // Don't sample this one nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } } //-------------------- void visit(AstNode* nodep) override { iterateChildren(nodep); } public: // CONSTRUCTORS explicit SampledVisitor(AstNetlist* netlistp) { iterate(netlistp); } ~SampledVisitor() override = default; }; //###################################################################### // Sampled class functions void V3Sampled::sampledAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); { SampledVisitor{nodep}; } // Destruct before checking V3Global::dumpCheckGlobalTree("sampled", 0, dumpTreeEitherLevel() >= 3); }