From 7d2b6bd9218110621e67fa26c7cf54885f3b695e Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Mon, 4 Aug 2025 14:29:56 +0200 Subject: [PATCH] Internals: Optimize updates of Vtogcov signals. No functional change intended. (#6110) --- include/verilated_cov.h | 54 +++++++ src/V3AstNodeOther.h | 148 +++++++++++------- src/V3AstNodes.cpp | 25 ++- src/V3Begin.cpp | 2 +- src/V3CCtors.cpp | 4 +- src/V3Class.cpp | 2 +- src/V3Clock.cpp | 4 +- src/V3Coverage.cpp | 152 ++++++++++--------- src/V3CoverageJoin.cpp | 2 +- src/V3EmitCFunc.h | 66 +++++++- src/V3EmitCHeaders.cpp | 8 + src/V3EmitCImp.cpp | 53 ++++++- src/V3EmitCSyms.cpp | 5 +- src/V3EmitV.cpp | 2 +- src/V3Inline.cpp | 2 +- src/V3Undriven.cpp | 2 +- test_regress/t/t_cover_line_wide_ternary.out | 6 +- test_regress/t/t_cover_toggle.out | 15 ++ test_regress/t/t_cover_toggle.py | 2 +- test_regress/t/t_cover_toggle.v | 15 ++ test_regress/t/t_cover_toggle__points.out | 35 +++++ test_regress/t/t_opt_const_cov.py | 2 +- 22 files changed, 448 insertions(+), 158 deletions(-) diff --git a/include/verilated_cov.h b/include/verilated_cov.h index 0d27f76ac..31d4fac0f 100644 --- a/include/verilated_cov.h +++ b/include/verilated_cov.h @@ -75,6 +75,60 @@ class VerilatedCovImp; ccontextp->_insertp("hier", name, __VA_ARGS__); \ } while (false) +static inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData, + const IData oldData) { + for (int i = 0; i < width; ++i) *(covp + i) += ((newData ^ oldData) >> i) & 1; +} + +static inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const IData newData, + const IData oldData) { + for (int i = 0; i < width; ++i) *(covp + i) += ((newData ^ oldData) >> i) & 1; +} + +static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData, + WDataInP oldData) { + for (int i = 0; i < VL_WORDS_I(width); ++i) { + const EData changed = newData[i] ^ oldData[i]; + if (changed) { + for (int j = 0; j < width - i * VL_EDATASIZE; ++j) { + *(covp + i * VL_EDATASIZE + j) += (changed >> j) & 1; + } + } + } +} + +static inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic* covp, + const IData newData, const IData oldData) VL_MT_SAFE { + for (int i = 0; i < width; ++i) { + if (VL_BITISSET_I((newData ^ oldData), i)) { + (covp + i)->fetch_add(1, std::memory_order_relaxed); + } + } +} + +static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic* covp, + const IData newData, const IData oldData) VL_MT_SAFE { + for (int i = 0; i < width; ++i) { + if (VL_BITISSET_Q((newData ^ oldData), i)) { + (covp + i)->fetch_add(1, std::memory_order_relaxed); + } + } +} + +static inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic* covp, + WDataInP newData, WDataInP oldData) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(width); ++i) { + const EData changed = newData[i] ^ oldData[i]; + if (changed) { + for (int j = 0; j < width - i * VL_EDATASIZE; ++j) { + if (VL_BITISSET_E(changed, j)) { + (covp + i * VL_EDATASIZE + j)->fetch_add(1, std::memory_order_relaxed); + } + } + } + } +} + //============================================================================= // VerilatedCov /// Per-VerilatedContext coverage data class. diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 16dcfb6fb..f18b9eb9b 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -399,6 +399,52 @@ public: ASTGEN_MEMBERS_AstNodeCase; int instrCount() const override { return INSTR_COUNT_BRANCH; } }; +class AstNodeCoverDecl VL_NOT_FINAL : public AstNodeStmt { + // Coverage analysis point declaration + // + // [After V3CoverageJoin] Duplicate declaration to get data from instead + // @astgen ptr := m_dataDeclp : Optional[AstNodeCoverDecl] + string m_page; // Coverage point's page tag + string m_text; // Coverage point's text + string m_hier; // Coverage point's hierarchy + int m_binNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment +public: + AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment) + : AstNodeStmt(t, fl) + , m_page{page} + , m_text{comment} {} + ASTGEN_MEMBERS_AstNodeCoverDecl; + const char* broken() const override { + if (m_dataDeclp + && (m_dataDeclp == this || m_dataDeclp->m_dataDeclp)) { // Avoid O(n^2) accessing + v3fatalSrc("dataDeclp should point to real data, not be a list: " << cvtToHex(this)); + } + return nullptr; + } + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } + bool maybePointedTo() const override VL_MT_SAFE { return true; } + int binNum() const { return m_binNum; } + void binNum(int flag) { m_binNum = flag; } + virtual int size() const = 0; + const string& comment() const { return m_text; } // text to insert in code + const string& page() const { return m_page; } + const string& hier() const { return m_hier; } + void hier(const string& flag) { m_hier = flag; } + void comment(const string& flag) { m_text = flag; } + bool sameNode(const AstNode* samep) const override { + const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl); + return (fileline() == asamep->fileline() && hier() == asamep->hier() + && comment() == asamep->comment() && page() == asamep->page()); + } + bool isPredictOptimizable() const override { return false; } + void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; } + // dataDecl nullptr means "use this one", but often you want "this" to + // indicate to get data from here + AstNodeCoverDecl* dataDeclNullp() const { return m_dataDeclp; } + AstNodeCoverDecl* dataDeclThisp() { return dataDeclNullp() ? dataDeclNullp() : this; } +}; class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt { // Cover or Assert // Parents: {statement list} @@ -2994,64 +3040,15 @@ public: return true; // SPECIAL: We don't process code after breaks } }; -class AstCoverDecl final : public AstNodeStmt { - // Coverage analysis point declaration - // - // [After V3CoverageJoin] Duplicate declaration to get data from instead - // @astgen ptr := m_dataDeclp : Optional[AstCoverDecl] - string m_page; - string m_text; - string m_hier; - string m_linescov; - int m_offset; // Offset column numbers to uniq-ify IFs - int m_binNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment -public: - AstCoverDecl(FileLine* fl, const string& page, const string& comment, const string& linescov, - int offset) - : ASTGEN_SUPER_CoverDecl(fl) - , m_page{page} - , m_text{comment} - , m_linescov{linescov} - , m_offset{offset} {} - ASTGEN_MEMBERS_AstCoverDecl; - const char* broken() const override { - if (m_dataDeclp - && (m_dataDeclp == this || m_dataDeclp->m_dataDeclp)) { // Avoid O(n^2) accessing - v3fatalSrc("dataDeclp should point to real data, not be a list: " << cvtToHex(this)); - } - return nullptr; - } - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; } - bool maybePointedTo() const override VL_MT_SAFE { return true; } - int binNum() const { return m_binNum; } - void binNum(int flag) { m_binNum = flag; } - int offset() const { return m_offset; } - const string& comment() const { return m_text; } // text to insert in code - const string& linescov() const { return m_linescov; } - const string& page() const { return m_page; } - const string& hier() const { return m_hier; } - void hier(const string& flag) { m_hier = flag; } - void comment(const string& flag) { m_text = flag; } - bool sameNode(const AstNode* samep) const override { - const AstCoverDecl* const asamep = VN_DBG_AS(samep, CoverDecl); - return (fileline() == asamep->fileline() && linescov() == asamep->linescov() - && hier() == asamep->hier() && comment() == asamep->comment()); - } - bool isPredictOptimizable() const override { return false; } - void dataDeclp(AstCoverDecl* nodep) { m_dataDeclp = nodep; } - // dataDecl nullptr means "use this one", but often you want "this" to - // indicate to get data from here - AstCoverDecl* dataDeclNullp() const { return m_dataDeclp; } - AstCoverDecl* dataDeclThisp() { return dataDeclNullp() ? dataDeclNullp() : this; } -}; class AstCoverInc final : public AstNodeStmt { // Coverage analysis point; increment coverage count + // @astgen op1 := toggleExprp : Optional[AstNodeExpr] // [After V3Clock] + // @astgen op2 := toggleCovExprp : Optional[AstNodeExpr] // [After V3Clock] + // These are expressions to which the node corresponds. Used only in toggle coverage // - // @astgen ptr := m_declp : AstCoverDecl // [After V3CoverageJoin] Declaration + // @astgen ptr := m_declp : AstNodeCoverDecl // [After V3CoverageJoin] Declaration public: - AstCoverInc(FileLine* fl, AstCoverDecl* declp) + AstCoverInc(FileLine* fl, AstNodeCoverDecl* declp) : ASTGEN_SUPER_CoverInc(fl) , m_declp{declp} {} ASTGEN_MEMBERS_AstCoverInc; @@ -3065,7 +3062,7 @@ public: bool isPredictOptimizable() const override { return false; } bool isOutputter() override { return true; } bool isPure() override { return false; } - AstCoverDecl* declp() const { return m_declp; } // Where defined + AstNodeCoverDecl* declp() const { return m_declp; } // Where defined }; class AstCoverToggle final : public AstNodeStmt { // Toggle analysis of given signal @@ -3895,6 +3892,47 @@ public: : ASTGEN_SUPER_GenCase(fl, exprp, itemsp) {} ASTGEN_MEMBERS_AstGenCase; }; +class AstCoverOtherDecl final : public AstNodeCoverDecl { + // Coverage analysis point declaration + // Used for other than toggle types of coverage + string m_linescov; + int m_offset; // Offset column numbers to uniq-ify IFs +public: + AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment, + const string& linescov, int offset) + : ASTGEN_SUPER_CoverOtherDecl(fl, page, comment) + , m_linescov{linescov} + , m_offset{offset} {} + ASTGEN_MEMBERS_AstCoverOtherDecl; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int offset() const { return m_offset; } + int size() const override { return 1; } + const string& linescov() const { return m_linescov; } + bool sameNode(const AstNode* samep) const override { + const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl); + return AstNodeCoverDecl::sameNode(samep) && linescov() == asamep->linescov(); + } +}; +class AstCoverToggleDecl final : public AstNodeCoverDecl { + // Coverage analysis point declaration + // Used for toggle coverage + const VNumRange m_range; // Packed array range covering each toggle bit +public: + AstCoverToggleDecl(FileLine* fl, const string& page, const string& comment, + const VNumRange& range) + : ASTGEN_SUPER_CoverToggleDecl(fl, page, comment) + , m_range{range} {} + ASTGEN_MEMBERS_AstCoverToggleDecl; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + int size() const override { return m_range.elements(); } + const VNumRange& range() const { return m_range; } + bool sameNode(const AstNode* samep) const override { + const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl); + return AstNodeCoverDecl::sameNode(samep) && range() == asamep->range(); + } +}; // === AstNodeCoverOrAssert === class AstAssert final : public AstNodeCoverOrAssert { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index a8b488648..660c6a6c2 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2922,10 +2922,9 @@ void AstBegin::dumpJson(std::ostream& str) const { dumpJsonBoolFunc(str, needProcess); dumpJsonGen(str); } -void AstCoverDecl::dump(std::ostream& str) const { +void AstNodeCoverDecl::dump(std::ostream& str) const { this->AstNodeStmt::dump(str); if (!page().empty()) str << " page=" << page(); - if (!linescov().empty()) str << " lc=" << linescov(); if (this->dataDeclNullp()) { static bool s_recursing = false; str << " -> "; @@ -2940,12 +2939,30 @@ void AstCoverDecl::dump(std::ostream& str) const { if (binNum()) str << " bin" << std::dec << binNum(); } } -void AstCoverDecl::dumpJson(std::ostream& str) const { +void AstNodeCoverDecl::dumpJson(std::ostream& str) const { dumpJsonStrFunc(str, page); - dumpJsonStrFunc(str, linescov); dumpJsonNumFunc(str, binNum); dumpJsonGen(str); } +void AstCoverOtherDecl::dump(std::ostream& str) const { + this->AstNodeCoverDecl::dump(str); + if (!linescov().empty()) str << " lc=" << linescov(); +} +void AstCoverOtherDecl::dumpJson(std::ostream& str) const { + this->AstNodeCoverDecl::dumpJson(str); + dumpJsonStrFunc(str, linescov); +} +void AstCoverToggleDecl::dump(std::ostream& str) const { + this->AstNodeCoverDecl::dump(str); + if (range().ranged()) str << " range=[" << range().left() << ":" << range().right() << "]"; +} +void AstCoverToggleDecl::dumpJson(std::ostream& str) const { + this->AstNodeCoverDecl::dumpJson(str); + if (range().ranged()) { + dumpJsonStr(str, "range", + std::to_string(range().left()) + ":" + std::to_string(range().right())); + } +} void AstCoverInc::dump(std::ostream& str) const { this->AstNodeStmt::dump(str); str << " -> "; diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 2a9733bf1..cd6cd7fe9 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -289,7 +289,7 @@ class BeginVisitor final : public VNVisitor { } iterateChildren(nodep); } - void visit(AstCoverDecl* nodep) override { + void visit(AstNodeCoverDecl* nodep) override { // Don't need to fix path in coverage statements, they're not under // any BEGINs, but V3Coverage adds them all under the module itself. iterateChildren(nodep); diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index b90f868c1..319ca6e87 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -161,10 +161,10 @@ class CCtorsVisitor final : public VNVisitor { if (v3Global.opt.coverage()) { V3CCtorsBuilder configure_coverage{nodep, "_configure_coverage", VCtorType::COVERAGE}; for (AstNode* np = nodep->stmtsp(); np; np = np->nextp()) { - if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) { + if (AstNodeCoverDecl* const coverp = VN_CAST(np, NodeCoverDecl)) { // ... else we don't have a static VlSym to be able to coverage insert UASSERT_OBJ(!VN_IS(nodep, Class), coverp, - "CoverDecl should be in class's package, not class itself"); + "NodeCoverDecl should be in class's package, not class itself"); np = coverp->backp(); configure_coverage.add(coverp->unlinkFrBack()); } diff --git a/src/V3Class.cpp b/src/V3Class.cpp index d0d6de7c2..6cd5d9b1e 100644 --- a/src/V3Class.cpp +++ b/src/V3Class.cpp @@ -175,7 +175,7 @@ class ClassVisitor final : public VNVisitor { // m_toScopeMoves.emplace_back(nodep, m_classScopep); //} } - void visit(AstCoverDecl* nodep) override { + void visit(AstNodeCoverDecl* nodep) override { // Need to declare coverage in package, where we have access to symbol table iterateChildren(nodep); if (m_classPackagep) m_classPackagep->addStmtsp(nodep->unlinkFrBack()); diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index 7dc65f903..ac7fa5ff8 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -100,11 +100,13 @@ class ClockVisitor final : public VNVisitor { // UINFOTREE(1, nodep, "", "ct"); // COVERTOGGLE(INC, ORIG, CHANGE) -> // IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; } - AstNode* const incp = nodep->incp()->unlinkFrBack(); + AstCoverInc* const incp = nodep->incp()->unlinkFrBack(); AstNodeExpr* const origp = nodep->origp()->unlinkFrBack(); AstNodeExpr* const changeWrp = nodep->changep()->unlinkFrBack(); AstNodeExpr* const changeRdp = ConvertWriteRefsToRead::main(changeWrp->cloneTree(false)); AstNodeExpr* comparedp = nullptr; + incp->toggleExprp(origp->cloneTree(false)); + incp->toggleCovExprp(changeRdp->cloneTree(false)); // Xor will optimize better than Eq, when CoverToggle has bit selects, // but can only use Xor with non-opaque types if (const AstBasicDType* const bdtypep diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 72adcf0a0..2c142ac0a 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -180,22 +180,8 @@ class CoverageVisitor final : public VNVisitor { return nullptr; } - AstCoverInc* newCoverInc(FileLine* fl, const string& hier, const string& page_prefix, - const string& comment, const string& linescov, int offset, + AstCoverInc* newCoverInc(FileLine* fl, AstNodeCoverDecl* const declp, const string& trace_var_name) { - // We could use the basename of the filename to the page, but seems - // better for code from an include file to be listed under the - // module using it rather than the include file. - // Note the module name could have parameters appended, we'll consider this - // a feature as it allows for each parameterized block to be counted separately. - // Someday the user might be allowed to specify a different page suffix - const string page = page_prefix + "/" + m_modp->prettyName(); - - AstCoverDecl* const declp = new AstCoverDecl{fl, page, comment, linescov, offset}; - declp->hier(hier); - m_modp->addStmtsp(declp); - UINFO(9, "new " << declp); - AstCoverInc* const incp = new AstCoverInc{fl, declp}; if (!trace_var_name.empty() && v3Global.opt.traceCoverage() @@ -316,9 +302,12 @@ class CoverageVisitor final : public VNVisitor { iterateAndNextNull(nodep->stmtsp()); if (m_state.lineCoverageOn(nodep)) { lineTrack(nodep); + AstCoverOtherDecl* const declp + = new AstCoverOtherDecl{nodep->fileline(), "v_line/" + m_modp->prettyName(), + "block", linesCov(m_state, nodep), 0}; + m_modp->addStmtsp(declp); AstNode* const newp - = newCoverInc(nodep->fileline(), "", "v_line", "block", linesCov(m_state, nodep), - 0, traceNameForLine(nodep, "block")); + = newCoverInc(nodep->fileline(), declp, traceNameForLine(nodep, "block")); insertProcStatement(nodep, newp); } } @@ -354,9 +343,12 @@ class CoverageVisitor final : public VNVisitor { iterateChildren(nodep); if (m_state.lineCoverageOn(nodep)) { lineTrack(nodep); + AstCoverOtherDecl* const declp + = new AstCoverOtherDecl{nodep->fileline(), "v_line/" + m_modp->prettyName(), + "block", linesCov(m_state, nodep), 0}; + m_modp->addStmtsp(declp); AstNode* const newp - = newCoverInc(nodep->fileline(), "", "v_line", "block", linesCov(m_state, nodep), - 0, traceNameForLine(nodep, "block")); + = newCoverInc(nodep->fileline(), declp, traceNameForLine(nodep, "block")); insertProcStatement(nodep, newp); } } @@ -402,35 +394,23 @@ class CoverageVisitor final : public VNVisitor { } } - void toggleVarBottom(const ToggleEnt& above, const AstVar* varp) { + void toggleVarBottom(const ToggleEnt& above, const AstVar* varp, const VNumRange& range) { const std::string hierPrefix = (m_beginHier != "") ? AstNode::prettyName(m_beginHier) + "." : ""; + AstCoverToggleDecl* const declp + = new AstCoverToggleDecl{varp->fileline(), "v_toggle/" + m_modp->prettyName(), + hierPrefix + varp->name() + above.m_comment, range}; + m_modp->addStmtsp(declp); AstCoverToggle* const newp = new AstCoverToggle{ - varp->fileline(), - newCoverInc(varp->fileline(), "", "v_toggle", - hierPrefix + varp->name() + above.m_comment, "", 0, ""), + varp->fileline(), newCoverInc(varp->fileline(), declp, ""), above.m_varRefp->cloneTree(false), above.m_chgRefp->cloneTree(false)}; m_modp->addStmtsp(newp); } void toggleVarRecurse(const AstNodeDType* const dtypep, const int depth, // per-iteration const ToggleEnt& above, const AstVar* const varp) { // Constant - if (const AstBasicDType* const bdtypep = VN_CAST(dtypep, BasicDType)) { - if (bdtypep->isRanged()) { - for (int index_docs = bdtypep->lo(); index_docs < bdtypep->hi() + 1; - ++index_docs) { - const int index_code = index_docs - bdtypep->lo(); - ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]", - new AstSel{varp->fileline(), - above.m_varRefp->cloneTree(false), index_code, 1}, - new AstSel{varp->fileline(), - above.m_chgRefp->cloneTree(false), index_code, 1}}; - toggleVarBottom(newent, varp); - newent.cleanup(); - } - } else { - toggleVarBottom(above, varp); - } + if (const AstBasicDType* const basicp = VN_CAST(dtypep, BasicDType)) { + toggleVarBottom(above, varp, basicp->nrange()); } else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) { for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) { const int index_code = index_docs - adtypep->lo(); @@ -543,20 +523,26 @@ class CoverageVisitor final : public VNVisitor { iterate(nodep->thenp()); lineTrack(nodep); AstNodeExpr* const thenp = nodep->thenp()->unlinkFrBack(); - nodep->thenp(new AstExprStmt{thenp->fileline(), - newCoverInc(nodep->fileline(), "", "v_branch", - "cond_then", linesCov(m_state, nodep), 0, - traceNameForLine(nodep, "cond_then")), - thenp}); + AstCoverOtherDecl* const thenDeclp + = new AstCoverOtherDecl{thenp->fileline(), "v_branch/" + m_modp->prettyName(), + "cond_then", linesCov(m_state, nodep), 0}; + m_modp->addStmtsp(thenDeclp); + nodep->thenp(new AstExprStmt{ + thenp->fileline(), + newCoverInc(nodep->fileline(), thenDeclp, traceNameForLine(nodep, "cond_then")), + thenp}); m_state = lastState; createHandle(nodep); iterate(nodep->elsep()); AstNodeExpr* const elsep = nodep->elsep()->unlinkFrBack(); - nodep->elsep(new AstExprStmt{elsep->fileline(), - newCoverInc(nodep->fileline(), "", "v_branch", - "cond_else", linesCov(m_state, nodep), 1, - traceNameForLine(nodep, "cond_else")), - elsep}); + AstCoverOtherDecl* const elseDeclp + = new AstCoverOtherDecl{thenp->fileline(), "v_branch/" + m_modp->prettyName(), + "cond_else", linesCov(m_state, nodep), 1}; + m_modp->addStmtsp(elseDeclp); + nodep->elsep(new AstExprStmt{ + elsep->fileline(), + newCoverInc(nodep->fileline(), elseDeclp, traceNameForLine(nodep, "cond_else")), + elsep}); m_state = lastState; } else { @@ -613,22 +599,32 @@ class CoverageVisitor final : public VNVisitor { // Normal if. Linecov shows what's inside the if (not condition that is // always executed) UINFO(4, " COVER-branch: " << nodep); - nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_branch", "if", - linesCov(ifState, nodep), 0, - traceNameForLine(nodep, "if"))); + AstCoverOtherDecl* const ifDeclp + = new AstCoverOtherDecl{nodep->fileline(), "v_branch/" + m_modp->prettyName(), + "if", linesCov(ifState, nodep), 0}; + m_modp->addStmtsp(ifDeclp); + nodep->addThensp( + newCoverInc(nodep->fileline(), ifDeclp, traceNameForLine(nodep, "if"))); // The else has a column offset of 1 to uniquify it relative to the if // As "if" and "else" are more than one character wide, this won't overlap // another token - nodep->addElsesp(newCoverInc(nodep->fileline(), "", "v_branch", "else", - linesCov(elseState, nodep), 1, - traceNameForLine(nodep, "else"))); + AstCoverOtherDecl* const elseDeclp + = new AstCoverOtherDecl{nodep->fileline(), "v_branch/" + m_modp->prettyName(), + "else", linesCov(elseState, nodep), 1}; + m_modp->addStmtsp(elseDeclp); + nodep->addElsesp( + newCoverInc(nodep->fileline(), elseDeclp, traceNameForLine(nodep, "else"))); } // If/else attributes to each block as non-branch coverage else if (first_elsif || cont_elsif) { UINFO(4, " COVER-elsif: " << nodep); if (ifState.lineCoverageOn(nodep)) { - nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_line", "elsif", - linesCov(ifState, nodep), 0, + AstCoverOtherDecl* const elsifDeclp = new AstCoverOtherDecl{ + nodep->fileline(), "v_line/" + m_modp->prettyName(), "elsif", + linesCov(ifState, nodep), 0}; + m_modp->addStmtsp(elsifDeclp); + + nodep->addThensp(newCoverInc(nodep->fileline(), elsifDeclp, traceNameForLine(nodep, "elsif"))); } // and we don't insert the else as the child if-else will do so @@ -636,14 +632,21 @@ class CoverageVisitor final : public VNVisitor { // Cover as separate blocks (not a branch as is not two-legged) if (ifState.lineCoverageOn(nodep)) { UINFO(4, " COVER-half-if: " << nodep); - nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_line", "if", - linesCov(ifState, nodep), 0, - traceNameForLine(nodep, "if"))); + AstCoverOtherDecl* const ifDeclp = new AstCoverOtherDecl{ + nodep->fileline(), "v_line/" + m_modp->prettyName(), "if", + linesCov(ifState, nodep), 0}; + m_modp->addStmtsp(ifDeclp); + nodep->addThensp( + newCoverInc(nodep->fileline(), ifDeclp, traceNameForLine(nodep, "if"))); } if (elseState.lineCoverageOn(nodep)) { UINFO(4, " COVER-half-el: " << nodep); - nodep->addElsesp(newCoverInc(nodep->fileline(), "", "v_line", "else", - linesCov(elseState, nodep), 1, + AstCoverOtherDecl* const elseDeclp = new AstCoverOtherDecl{ + nodep->fileline(), "v_line/" + m_modp->prettyName(), "else", + linesCov(elseState, nodep), 1}; + m_modp->addStmtsp(elseDeclp); + + nodep->addElsesp(newCoverInc(nodep->fileline(), elseDeclp, traceNameForLine(nodep, "else"))); } } @@ -665,9 +668,12 @@ class CoverageVisitor final : public VNVisitor { if (m_state.lineCoverageOn(nodep)) { // if the case body didn't disable it lineTrack(nodep); UINFO(4, " COVER: " << nodep); - nodep->addStmtsp(newCoverInc(nodep->fileline(), "", "v_line", "case", - linesCov(m_state, nodep), 0, - traceNameForLine(nodep, "case"))); + AstCoverOtherDecl* const declp + = new AstCoverOtherDecl{nodep->fileline(), "v_line/" + m_modp->prettyName(), + "case", linesCov(m_state, nodep), 0}; + m_modp->addStmtsp(declp); + nodep->addStmtsp( + newCoverInc(nodep->fileline(), declp, traceNameForLine(nodep, "case"))); } } } @@ -680,9 +686,13 @@ class CoverageVisitor final : public VNVisitor { if (!nodep->coverincsp() && v3Global.opt.coverageUser()) { // Note the name may be overridden by V3Assert processing lineTrack(nodep); - nodep->addCoverincsp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover", - linesCov(m_state, nodep), 0, - m_beginHier + "_vlCoverageUserTrace")); + AstCoverOtherDecl* const declp + = new AstCoverOtherDecl{nodep->fileline(), "v_user/" + m_modp->prettyName(), + "cover", linesCov(m_state, nodep), 0}; + declp->hier(m_beginHier); + m_modp->addStmtsp(declp); + nodep->addCoverincsp( + newCoverInc(nodep->fileline(), declp, m_beginHier + "_vlCoverageUserTrace")); } } void visit(AstStop* nodep) override { @@ -754,8 +764,10 @@ class CoverageVisitor final : public VNVisitor { } comment += ") => "; comment += (m_objective ? '1' : '0'); - AstNode* const newp - = newCoverInc(fl, "", "v_expr", comment, "", 0, traceNameForLine(nodep, name)); + AstCoverOtherDecl* const declp = new AstCoverOtherDecl{ + nodep->fileline(), "v_expr/" + m_modp->prettyName(), comment, "", 0}; + m_modp->addStmtsp(declp); + AstNode* const newp = newCoverInc(fl, declp, traceNameForLine(nodep, name)); UASSERT_OBJ(condp, nodep, "No terms in expression coverage branch"); AstIf* const ifp = new AstIf{fl, condp, newp, nullptr}; ifp->user2(true); diff --git a/src/V3CoverageJoin.cpp b/src/V3CoverageJoin.cpp index bcdf767cb..39d73bd10 100644 --- a/src/V3CoverageJoin.cpp +++ b/src/V3CoverageJoin.cpp @@ -75,7 +75,7 @@ class CoverageJoinVisitor final : public VNVisitor { // The CoverDecl the duplicate pointed to now needs to point to the // original's data. I.e. the duplicate will get the coverage number // from the non-duplicate - AstCoverDecl* const datadeclp = nodep->incp()->declp()->dataDeclThisp(); + AstNodeCoverDecl* const datadeclp = nodep->incp()->declp()->dataDeclThisp(); removep->incp()->declp()->dataDeclp(datadeclp); UINFO(8, " new " << removep->incp()->declp()); // Mark the found node as a duplicate of the first node diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index e489ae07c..9172013b5 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -697,7 +697,7 @@ public: } iterateChildrenConst(nodep); } - void visit(AstCoverDecl* nodep) override { + void visit(AstCoverOtherDecl* nodep) override { putns(nodep, "vlSelf->__vlCoverInsert("); // As Declared in emitCoverageDecl puts("&(vlSymsp->__Vcoverage["); puts(cvtToStr(nodep->dataDeclThisp()->binNum())); @@ -725,15 +725,67 @@ public: putsQuoted(nodep->linescov()); puts(");\n"); } + void visit(AstCoverToggleDecl* nodep) override { + putns(nodep, "vlSelf->__vlCoverToggleInsert("); // As Declared in emitCoverageDecl + puts(cvtToStr(nodep->range().right())); + puts(", "); + puts(cvtToStr(nodep->range().left())); + puts(", "); + puts(cvtToStr(nodep->range().ranged())); + puts(", "); + puts("&(vlSymsp->__Vcoverage["); + puts(cvtToStr(nodep->dataDeclThisp()->binNum())); + puts("])"); + // If this isn't the first instantiation of this module under this + // design, don't really count the bucket, and rely on verilator_cov to + // aggregate counts. This is because Verilator combines all + // hierarchies itself, and if verilator_cov also did it, you'd end up + // with (number-of-instant) times too many counts in this bin. + puts(", first"); // Enable, passed from __Vconfigure parameter + puts(", "); + putsQuoted(protect(nodep->fileline()->filename())); + puts(", "); + puts(cvtToStr(nodep->fileline()->lineno())); + puts(", "); + puts(cvtToStr(nodep->fileline()->firstColumn())); + puts(", "); + putsQuoted((!nodep->hier().empty() ? "." : "") + + protectWordsIf(nodep->hier(), nodep->protect())); + puts(", "); + putsQuoted(protectWordsIf(nodep->page(), nodep->protect())); + puts(", "); + putsQuoted(protectWordsIf(nodep->comment(), nodep->protect())); + puts(");\n"); + } void visit(AstCoverInc* nodep) override { - if (v3Global.opt.threads() > 1) { - putns(nodep, "vlSymsp->__Vcoverage["); - puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); - puts("].fetch_add(1, std::memory_order_relaxed);\n"); + if (nodep->declp()->size() == 1) { + if (v3Global.opt.threads() > 1) { + putns(nodep, "vlSymsp->__Vcoverage["); + puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); + puts("].fetch_add(1, std::memory_order_relaxed);\n"); + } else { + putns(nodep, "++(vlSymsp->__Vcoverage["); + puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); + puts("]);\n"); + } } else { - putns(nodep, "++(vlSymsp->__Vcoverage["); + puts("VL_COV_TOGGLE_CHG_"); + if (v3Global.opt.threads() > 1) { + puts("MT_"); + } else { + puts("ST_"); + } + emitIQW(nodep->toggleExprp()); + puts("("); + puts(cvtToStr(nodep->declp()->size())); + puts(", "); + puts("vlSymsp->__Vcoverage + "); puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); - puts("]);\n"); + puts(", "); + iterateConst(nodep->toggleExprp()); + puts(", "); + iterateConst(nodep->toggleCovExprp()); + puts(");\n"); } } void visit(AstDisableFork* nodep) override { putns(nodep, "vlProcess->disableFork();\n"); } diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index fd23589fd..ce420095f 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -181,6 +181,14 @@ class EmitCHeader final : public EmitCConstInit { "linescovp);\n"); } + if (v3Global.opt.coverageToggle() && !VN_IS(modp, Class)) { + decorateFirst(first, section); + puts("void __vlCoverToggleInsert(int begin, int end, bool ranged, "); + puts(v3Global.opt.threads() > 1 ? "std::atomic" : "uint32_t"); + puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); + puts("const char* hierp, const char* pagep, const char* commentp);\n"); + } + if (v3Global.opt.savable()) { decorateFirst(first, section); puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n"); diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index f8e0990bf..e2b9e0196 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -103,7 +103,7 @@ class EmitCGatherDependencies final : VNVisitorConst { addSelfDependency(nodep->selfPointer(), nodep->varp()); iterateChildrenConst(nodep); } - void visit(AstCoverDecl* nodep) override { + void visit(AstNodeCoverDecl* nodep) override { addSymsDependency(); iterateChildrenConst(nodep); } @@ -307,17 +307,16 @@ class EmitCImp final : EmitCFunc { splitSizeInc(10); } void emitCoverageImp() { + // Rather than putting out VL_COVER_INSERT calls directly, we do it via this + // function. This gets around gcc slowness constructing all of the template + // arguments. if (v3Global.opt.coverage()) { puts("\n// Coverage\n"); - // Rather than putting out VL_COVER_INSERT calls directly, we do it via this - // function. This gets around gcc slowness constructing all of the template - // arguments. puts("void " + prefixNameProtect(m_modp) + "::__vlCoverInsert("); puts(v3Global.opt.threads() > 1 ? "std::atomic" : "uint32_t"); puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); puts("const char* hierp, const char* pagep, const char* commentp, const char* " - "linescovp) " - "{\n"); + "linescovp) {\n"); if (v3Global.opt.threads() > 1) { puts("assert(sizeof(uint32_t) == sizeof(std::atomic));\n"); puts("uint32_t* count32p = reinterpret_cast(countp);\n"); @@ -343,6 +342,48 @@ class EmitCImp final : EmitCFunc { puts("}\n"); splitSizeInc(10); } + if (v3Global.opt.coverageToggle()) { + puts("\n// Toggle Coverage\n"); + puts("void " + prefixNameProtect(m_modp) + "::__vlCoverToggleInsert("); + puts("int begin, int end, bool ranged, "); + puts(v3Global.opt.threads() > 1 ? "std::atomic" : "uint32_t"); + puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n"); + puts("const char* hierp, const char* pagep, const char* commentp) {\n"); + if (v3Global.opt.threads() > 1) { + puts("assert(sizeof(uint32_t) == sizeof(std::atomic));\n"); + } + puts("int step = (end >= begin) ? 1 : -1;\n"); + // range is inclusive + puts("for (int i = begin; i != end + step; i += step) {\n"); + if (v3Global.opt.threads() > 1) { + puts("uint32_t* count32p = reinterpret_cast(countp);\n"); + } else { + puts("uint32_t* count32p = countp;\n"); + } + // static doesn't need save-restore as is constant + puts("static uint32_t fake_zero_count = 0;\n"); + puts("std::string fullhier = std::string{VerilatedModule::name()} + hierp;\n"); + puts("if (!fullhier.empty() && fullhier[0] == '.') fullhier = fullhier.substr(1);\n"); + puts("std::string commentWithIndex = commentp;\n"); + puts("if (ranged) commentWithIndex += '[' + std::to_string(i) + ']';\n"); + // Used for second++ instantiation of identical bin + puts("if (!enable) count32p = &fake_zero_count;\n"); + puts("*count32p = 0;\n"); + puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), VerilatedModule::name(), " + "count32p,"); + puts(" \"filename\",filenamep,"); + puts(" \"lineno\",lineno,"); + puts(" \"column\",column,\n"); + puts("\"hier\",fullhier,"); + puts(" \"page\",pagep,"); + puts(" \"comment\",commentWithIndex.c_str(),"); + puts(" \"\", \"\");\n"); // linescov argument, but in toggle coverage it is always + // empty + puts("++countp;\n"); + puts("}\n"); + puts("}\n"); + splitSizeInc(10); + } } void emitDestructorImp(const AstNodeModule* modp) { puts("\n"); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index c81020b64..7ad0a7f23 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -376,10 +376,11 @@ class EmitCSyms final : EmitCBaseVisitorConst { iterateChildrenConst(nodep); m_statVarScopeBytes += nodep->varp()->dtypep()->widthTotalBytes(); } - void visit(AstCoverDecl* nodep) override { + void visit(AstNodeCoverDecl* nodep) override { // Assign numbers to all bins, so we know how big of an array to use if (!nodep->dataDeclNullp()) { // else duplicate we don't need code for - nodep->binNum(m_coverBins++); + nodep->binNum(m_coverBins); + m_coverBins += nodep->size(); } } void visit(AstCFunc* nodep) override { diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index e9121517b..468417706 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -234,7 +234,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { putbs("continue"); if (!m_suppressSemi) puts(";\n"); } - void visit(AstCoverDecl*) override {} // N/A + void visit(AstNodeCoverDecl*) override {} // N/A void visit(AstCoverInc*) override {} // N/A void visit(AstCoverToggle*) override {} // N/A diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index d998bb922..82db5844d 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -458,7 +458,7 @@ class InlineRelinkVisitor final : public VNVisitor { if (afterp) nodep->addScopeEntrp(afterp); iterateChildren(nodep); } - void visit(AstCoverDecl* nodep) override { + void visit(AstNodeCoverDecl* nodep) override { // Fix path in coverage statements nodep->hier(VString::dot(m_cellp->prettyName(), ".", nodep->hier())); iterateChildren(nodep); diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 7f1d5b21c..d1abee768 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -545,7 +545,7 @@ class UndrivenVisitor final : public VNVisitorConst { void visit(AstPrimitive*) override {} // Coverage artifacts etc shouldn't count as a sink - void visit(AstCoverDecl*) override {} + void visit(AstNodeCoverDecl*) override {} void visit(AstCoverInc*) override {} void visit(AstCoverToggle*) override {} void visit(AstTraceDecl*) override {} diff --git a/test_regress/t/t_cover_line_wide_ternary.out b/test_regress/t/t_cover_line_wide_ternary.out index 71e8134ec..d9b631b41 100644 --- a/test_regress/t/t_cover_line_wide_ternary.out +++ b/test_regress/t/t_cover_line_wide_ternary.out @@ -41,9 +41,9 @@ -000009 point: comment=block hier=top.t %000009 cyc[0] ? -000009 point: comment=cond_then hier=top.t --000009 point: comment=cond_else hier=top.t %000009 {intfs[1].foo, intfs[1].bar, intfs[1].baz} : -000009 point: comment=cond_then hier=top.t +-000009 point: comment=cond_else hier=top.t %000009 {intfs[0].foo, intfs[0].bar, intfs[0].baz}; -000009 point: comment=cond_else hier=top.t end @@ -54,9 +54,9 @@ +000010 point: comment=block hier=top.t 000010 cyc[0] ? +000010 point: comment=cond_then hier=top.t -+000010 point: comment=cond_else hier=top.t 000010 {intfs[1].foo, intfs[1].bar, intfs[1].baz} : +000010 point: comment=cond_then hier=top.t ++000010 point: comment=cond_else hier=top.t 000010 {intfs[0].foo, intfs[0].bar, intfs[0].baz}; +000010 point: comment=cond_else hier=top.t end @@ -65,9 +65,9 @@ {intf_sel_assign.foo, intf_sel_assign.bar, intf_sel_assign.baz} = 000010 cyc[0] ? +000010 point: comment=cond_then hier=top.t -+000010 point: comment=cond_else hier=top.t 000010 {intfs[1].foo, intfs[1].bar, intfs[1].baz} : +000010 point: comment=cond_then hier=top.t ++000010 point: comment=cond_else hier=top.t 000010 {intfs[0].foo, intfs[0].bar, intfs[0].baz}; +000010 point: comment=cond_else hier=top.t diff --git a/test_regress/t/t_cover_toggle.out b/test_regress/t/t_cover_toggle.out index 29a7dc0a1..cb27dbf59 100644 --- a/test_regress/t/t_cover_toggle.out +++ b/test_regress/t/t_cover_toggle.out @@ -49,6 +49,15 @@ } str_queue_t; str_queue_t str_queue; + typedef struct packed { + // verilator lint_off ASCRANGE + bit [3:5] x; + // verilator lint_on ASCRANGE + bit [0:0] y; + } str_bit_t; +%000002 str_bit_t str_bit; +%000002 str_bit_t [5:2] str_bit_arr; + assign strl.a = clk; alpha a1 (/*AUTOINST*/ @@ -108,10 +117,16 @@ if (cyc == 3) begin str_queue.q.push_back(1); toggle <= '1; + str_bit.x <= '1; + str_bit.y <= '1; + str_bit_arr[4].x <= '1; end if (cyc == 4) begin if (str_queue.q.size() != 1) $stop; toggle <= '0; + str_bit.x[3] <= 0; + str_bit.y[0] <= 0; + str_bit_arr[4].x[3] <= 0; end else if (cyc == 10) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_cover_toggle.py b/test_regress/t/t_cover_toggle.py index 45bf2c21c..6f37d0c84 100755 --- a/test_regress/t/t_cover_toggle.py +++ b/test_regress/t/t_cover_toggle.py @@ -21,7 +21,7 @@ test.inline_checks() test.file_grep_not(test.obj_dir + "/coverage.dat", "largeish") if test.vlt_all: - test.file_grep(test.stats, r'Coverage, Toggle points joined\s+(\d+)', 27) + test.file_grep(test.stats, r'Coverage, Toggle points joined\s+(\d+)', 13) test.run(cmd=[ os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", diff --git a/test_regress/t/t_cover_toggle.v b/test_regress/t/t_cover_toggle.v index e41aa437a..456df1d08 100644 --- a/test_regress/t/t_cover_toggle.v +++ b/test_regress/t/t_cover_toggle.v @@ -48,6 +48,15 @@ module t (/*AUTOARG*/ } str_queue_t; str_queue_t str_queue; + typedef struct packed { + // verilator lint_off ASCRANGE + bit [3:5] x; + // verilator lint_on ASCRANGE + bit [0:0] y; + } str_bit_t; + str_bit_t str_bit; + str_bit_t [5:2] str_bit_arr; + assign strl.a = clk; alpha a1 (/*AUTOINST*/ @@ -107,10 +116,16 @@ module t (/*AUTOARG*/ if (cyc == 3) begin str_queue.q.push_back(1); toggle <= '1; + str_bit.x <= '1; + str_bit.y <= '1; + str_bit_arr[4].x <= '1; end if (cyc == 4) begin if (str_queue.q.size() != 1) $stop; toggle <= '0; + str_bit.x[3] <= 0; + str_bit.y[0] <= 0; + str_bit_arr[4].x[3] <= 0; end else if (cyc == 10) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_cover_toggle__points.out b/test_regress/t/t_cover_toggle__points.out index 144f94d9f..6dc8fc51d 100644 --- a/test_regress/t/t_cover_toggle__points.out +++ b/test_regress/t/t_cover_toggle__points.out @@ -67,6 +67,35 @@ } str_queue_t; str_queue_t str_queue; + typedef struct packed { + // verilator lint_off ASCRANGE + bit [3:5] x; + // verilator lint_on ASCRANGE + bit [0:0] y; + } str_bit_t; +%000002 str_bit_t str_bit; +-000002 point: comment=str_bit.x[3] hier=top.t +-000001 point: comment=str_bit.x[4] hier=top.t +-000001 point: comment=str_bit.x[5] hier=top.t +-000002 point: comment=str_bit.y[0] hier=top.t +%000002 str_bit_t [5:2] str_bit_arr; +-000000 point: comment=str_bit_arr[2].x[3] hier=top.t +-000000 point: comment=str_bit_arr[2].x[4] hier=top.t +-000000 point: comment=str_bit_arr[2].x[5] hier=top.t +-000000 point: comment=str_bit_arr[2].y[0] hier=top.t +-000000 point: comment=str_bit_arr[3].x[3] hier=top.t +-000000 point: comment=str_bit_arr[3].x[4] hier=top.t +-000000 point: comment=str_bit_arr[3].x[5] hier=top.t +-000000 point: comment=str_bit_arr[3].y[0] hier=top.t +-000002 point: comment=str_bit_arr[4].x[3] hier=top.t +-000001 point: comment=str_bit_arr[4].x[4] hier=top.t +-000001 point: comment=str_bit_arr[4].x[5] hier=top.t +-000000 point: comment=str_bit_arr[4].y[0] hier=top.t +-000000 point: comment=str_bit_arr[5].x[3] hier=top.t +-000000 point: comment=str_bit_arr[5].x[4] hier=top.t +-000000 point: comment=str_bit_arr[5].x[5] hier=top.t +-000000 point: comment=str_bit_arr[5].y[0] hier=top.t + assign strl.a = clk; alpha a1 (/*AUTOINST*/ @@ -150,10 +179,16 @@ if (cyc == 3) begin str_queue.q.push_back(1); toggle <= '1; + str_bit.x <= '1; + str_bit.y <= '1; + str_bit_arr[4].x <= '1; end if (cyc == 4) begin if (str_queue.q.size() != 1) $stop; toggle <= '0; + str_bit.x[3] <= 0; + str_bit.y[0] <= 0; + str_bit_arr[4].x[3] <= 0; end else if (cyc == 10) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_opt_const_cov.py b/test_regress/t/t_opt_const_cov.py index 862db88cc..e2403b28b 100755 --- a/test_regress/t/t_opt_const_cov.py +++ b/test_regress/t/t_opt_const_cov.py @@ -16,6 +16,6 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", "--coverage", "-- test.execute() if test.vlt: - test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 620) + test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 478) test.passes()