diff --git a/Changes b/Changes index 8a289e644..e2ad535ea 100644 --- a/Changes +++ b/Changes @@ -5,7 +5,9 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.700*** -** Add /*verilator coverage_on/_off */. +** Add --coverage_toggle for toggle coverage analysis. + +*** Add /*verilator coverage_on/_off */ to bracket coverage regions. *** Optimize two-level shift and and/or trees, +23% on one test. diff --git a/bin/verilator b/bin/verilator index 2cfbc4b30..e7583225f 100755 --- a/bin/verilator +++ b/bin/verilator @@ -183,6 +183,7 @@ descriptions in the next sections for more information. --compiler Tune for specified C++ compiler --coverage Enable all coverage --coverage-line Enable line coverage + --coverage-toggle Enable toggle coverage --coverage-user Enable PSL/SVL user coverage -D[=] Set preprocessor define --debug Enable debugging @@ -296,7 +297,8 @@ functions to avoid error C1061. =item --coverage -Enables all forms of coverage, alias for --coverage-line, --coverage-user. +Enables all forms of coverage, alias for "--coverage-line --coverage-toggle +--coverage-user". =item --coverage-line @@ -318,6 +320,37 @@ Note Verilator may over-count combinatorial (non-clocked) blocks when those blocks receive signals which have had the UNOPTFLAT warning disabled; for most accurate results do not disable this warning when using coverage. +=item --coverage-toggle + +Specifies signal toggle coverage analysis code should be inserted. + +Every bit of every signal in a module has a counter inserted. The counter +will increment on every edge change of the corresponding bit. + +Signals that are part of tasks or begin/end blocks are considered local +variables and are not covered. Signals that begin with underscores, are +integers, or are very wide (>256 bits total storage across all dimensions) +are also not covered. + +Hierarchy is compressed, such that if a module is instantiated multiple +times, coverage will be summed for that bit across ALL instantiations of +that module. + +Verilator makes a minimally-intelligent decision about what clock domain +the signal goes to, and only looks for edges in that clock domain. This +means that edges may be ignored if it is known that the edge could never be +seen by the receiving logic. This algorithm may improve in the future. +The net result is coverage may be lower than what would be seen by looking +at traces, but the coverage is a more accurate representation of the +quality of stimulus into the design. + +There may be edges counted near time zero while the model stabilizes. It's +a good practice to zero all coverage just before releasing reset to prevent +counting such behavior. + +A /*verilator coverage_off/on */ comment pair can be used around signals +that do not need toggle analysis, such as RAMs and register files. + =item --coverage-user Enables user inserted functional coverage. Currently, all functional diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index bdd4e15cc..d0695215d 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -140,6 +140,7 @@ RAW_OBJS = \ V3Combine.o \ V3Const__gen.o \ V3Coverage.o \ + V3CoverageJoin.o \ V3Dead.o \ V3Delayed.o \ V3Depth.o \ diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 28e7e87e4..2421e2c18 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -223,6 +223,13 @@ private: nodep->unlinkFrBack(); wantactivep->addStmtsp(nodep); } + virtual void visit(AstCoverToggle* nodep, AstNUser*) { + // Relink to CACTIVE, unless already under it + UINFO(4," COVERTOGGLE "<fileline()); + nodep->unlinkFrBack(); + wantactivep->addStmtsp(nodep); + } virtual void visit(AstFinal* nodep, AstNUser*) { // Relink to CFUNC for the final UINFO(4," FINAL "<AstNode::dump(str); + if (this->dataDeclNullp()) { + str<<" -> "; + this->dataDeclNullp()->dump(str); + } else { + if (binNum()) { str<<" bin"<AstNode::dump(str); diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 0c9d81cd7..7c76a0603 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -97,6 +97,10 @@ struct AstArraySel : public AstNodeSel { :AstNodeSel(fl, fromp, bitp) { if (fromp) widthSignedFrom(fromp); } + AstArraySel(FileLine* fl, AstNode* fromp, int bit) + :AstNodeSel(fl, fromp, new AstConst(fl,bit)) { + if (fromp) widthSignedFrom(fromp); + } ASTNODE_NODE_FUNCS(ArraySel, ARRAYSEL) virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { V3ERROR_NA; /* How can from be a const? */ } @@ -320,6 +324,10 @@ public: || varType()==AstVarType::REG || varType()==AstVarType::INTEGER); } bool isTemp() const { return (varType()==AstVarType::BLOCKTEMP || varType()==AstVarType::MODULETEMP || varType()==AstVarType::STMTTEMP || varType()==AstVarType::XTEMP); } + bool isToggleCoverable() const { return ((isIO() || isSignal()) + && varType()!=AstVarType::INTEGER + // Wrapper would otherwise duplicate wrapped module's coverage + && !isSc() && !isPrimaryIO()); } bool isStatementTemp() const { return (varType()==AstVarType::STMTTEMP); } bool isMovableToBlock() const { return (varType()==AstVarType::BLOCKTEMP || isFuncLocal()); } bool isPure() const { return (varType()==AstVarType::XTEMP); } @@ -1002,6 +1010,7 @@ struct AstCoverDecl : public AstNodeStmt { // Parents: {statement list} // Children: none private: + AstCoverDecl* m_dataDeclp; // [After V3CoverageJoin] Pointer to duplicate declaration to get data from instead string m_typeText; string m_text; string m_hier; @@ -1012,8 +1021,14 @@ public: : AstNodeStmt(fl) { m_text = comment; m_typeText = type; m_column = column; m_binNum = 0; + m_dataDeclp = NULL; } ASTNODE_NODE_FUNCS(CoverDecl, COVERDECL) + virtual bool broken() const { + if (m_dataDeclp && !m_dataDeclp->brokeExists()) return true; + if (m_dataDeclp && m_dataDeclp->m_dataDeclp) v3fatalSrc("dataDeclp should point to real data, not be a list"); // Avoid O(n^2) accessing + return false; } + virtual void cloneRelink() { if (m_dataDeclp && m_dataDeclp->clonep()) m_dataDeclp = m_dataDeclp->clonep()->castCoverDecl(); } virtual void dump(ostream& str); virtual int instrCount() const { return 1+2*instrCountLd(); } virtual bool maybePointedTo() const { return true; } @@ -1032,6 +1047,10 @@ public: && comment()==samep->castCoverDecl()->comment() && column()==samep->castCoverDecl()->column()); } virtual bool isPredictOptimizable() const { return false; } + void dataDeclp(AstCoverDecl* nodep) { m_dataDeclp=nodep; } + // dataDecl NULL 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; } }; struct AstCoverInc : public AstNodeStmt { @@ -1060,6 +1079,30 @@ public: AstCoverDecl* declp() const { return m_declp; } // Where defined }; +struct AstCoverToggle : public AstNodeStmt { + // Toggle analysis of given signal + // Parents: MODULE + // Children: AstCoverInc, orig var, change det var + AstCoverToggle(FileLine* fl, AstCoverInc* incp, AstNode* origp, AstNode* changep) + : AstNodeStmt(fl) { + setOp1p(incp); + setOp2p(origp); + setOp3p(changep); + } + ASTNODE_NODE_FUNCS(CoverToggle, COVERTOGGLE) + virtual int instrCount() const { return 3+instrCountBranch()+instrCountLd(); } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode*) const { return true; } + virtual bool isGateOptimizable() const { return false; } + virtual bool isPredictOptimizable() const { return true; } + virtual bool isOutputter() const { return false; } // Though the AstCoverInc under this is an outputter + // but isSplittable() true + AstCoverInc* incp() const { return op1p()->castCoverInc(); } + void incp(AstCoverInc* nodep) { setOp1p(nodep); } + AstNode* origp() const { return op2p(); } + AstNode* changep() const { return op3p(); } +}; + struct AstGenCase : public AstNodeCase { // Generate Case statement // Parents: {statement list} diff --git a/src/V3Clock.cpp b/src/V3Clock.cpp index 4fd53c959..1213bd0e3 100644 --- a/src/V3Clock.cpp +++ b/src/V3Clock.cpp @@ -282,6 +282,26 @@ private: } nodep->deleteTree(); nodep = NULL; } + virtual void visit(AstCoverToggle* nodep, AstNUser*) { + //nodep->dumpTree(cout,"ct:"); + //COVERTOGGLE(INC, ORIG, CHANGE) -> + // IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; } + AstNode* incp = nodep->incp()->unlinkFrBack(); + AstNode* origp = nodep->origp()->unlinkFrBack(); + AstNode* changep = nodep->changep()->unlinkFrBack(); + AstIf* newp = new AstIf(nodep->fileline(), + new AstXor(nodep->fileline(), + origp, + changep), + incp, NULL); + // We could add another IF to detect posedges, and only increment if so. + // It's another whole branch though verus a potential memory miss. + // We'll go with the miss. + newp->addIfsp(new AstAssign(nodep->fileline(), + changep->cloneTree(false), + origp->cloneTree(false))); + nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL; + } virtual void visit(AstInitial* nodep, AstNUser*) { AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName()); nodep->replaceWith(cmtp); diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 478048ca9..6e267e8fa 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -50,12 +50,29 @@ private: // STATE bool m_checkBlock; // Should this block get covered? AstModule* m_modp; // Current module to add statement to + bool m_inToggleOff; // In function/task etc FileMap m_fileps; // Column counts for each fileline string m_beginHier; // AstBegin hier name for user coverage points //int debug() { return 9; } // METHODS + const char* varIgnoreToggle(AstVar* nodep) { + // Return true if this shouldn't be traced + // See also similar rule in V3TraceDecl::varIgnoreTrace + string prettyName = nodep->prettyName(); + if (!nodep->isToggleCoverable()) + return "Not relevant signal type"; + if (prettyName.c_str()[0] == '_') + return "Leading underscore"; + if (prettyName.find("._") != string::npos) + return "Inlined leading underscore"; + if ((nodep->width()*nodep->arrayElements()) > 256) return "Wide bus/array > 256 bits"; + // We allow this, though tracing doesn't + // if (nodep->arrayp(1)) return "Unsupported: Multi-dimensional array"; + return NULL; + } + AstCoverInc* newCoverInc(FileLine* fl, const string& hier, const string& type, const string& comment) { int column = 0; @@ -81,6 +98,105 @@ private: m_modp = NULL; } + // VISITORS - TOGGLE COVERAGE + virtual void visit(AstNodeFTask* nodep, AstNUser*) { + bool oldtog = m_inToggleOff; + { + m_inToggleOff = true; + nodep->iterateChildren(*this); + } + m_inToggleOff = oldtog; + } + virtual void visit(AstVar* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (m_modp && !m_inToggleOff + && nodep->fileline()->coverageOn() && v3Global.opt.coverageToggle()) { + const char* disablep = varIgnoreToggle(nodep); + if (disablep) { + UINFO(4, " Disable Toggle: "<arraysp(); arrayp; arrayp = arrayp->nextp()->castRange()) { + dimensions++; + } + + // Add signal to hold the old value + string newvarname = (string)"__Vtogcov__"+nodep->shortName(); + AstVar* chgVarp = new AstVar (nodep->fileline(), AstVarType::MODULETEMP, newvarname, nodep); + m_modp->addStmtp(chgVarp); + + // Create bucket for each dimension * bit. + // This is necessarily an O(n^2) expansion, which is why + // we limit coverage to signals with < 256 bits. + vector selects_docs; selects_docs.resize(dimensions); + vector selects_code; selects_code.resize(dimensions); + toggleVarRecurse(nodep, chgVarp, nodep->arraysp(), + 0, selects_docs, selects_code ); + } + } + } + + void toggleVarRecurse(AstVar* nodep, AstVar* chgVarp, AstRange* arrayp, + int dimension, vector& selects_docs, vector& selects_code) { + if (arrayp) { + for (int index=arrayp->lsbConst(); index<=arrayp->msbConst()+1; index++) { + // Handle the next dimension, if any + selects_docs[dimension] = index; + selects_code[dimension] = index - arrayp->lsbConst(); + toggleVarRecurse(nodep, chgVarp, arrayp->nextp()->castRange(), + dimension+1, selects_docs, selects_code); + } + } else { // No more arraying - just each bit in the width + if (nodep->rangep()) { + for (int bitindex_docs=nodep->lsb(); bitindex_docsmsb()+1; bitindex_docs++) { + toggleVarBottom(nodep, chgVarp, + dimension, selects_docs, selects_code, + true, bitindex_docs); + } + } else { + toggleVarBottom(nodep, chgVarp, + dimension, selects_docs, selects_code, + false, 0); + } + } + } + void toggleVarBottom(AstVar* nodep, AstVar* chgVarp, + int dimension, vector& selects_docs, vector& selects_code, + bool bitsel, int bitindex_docs) { + string comment = nodep->name(); + AstNode* varRefp = new AstVarRef(nodep->fileline(), nodep, false); + AstNode* chgRefp = new AstVarRef(nodep->fileline(), chgVarp, true); + // Now determine the name of, and how to get to the bit of this slice + for (int dim=0; dimfileline(), varRefp, selects_code[dim]); + chgRefp = new AstArraySel(nodep->fileline(), chgRefp, selects_code[dim]); + } + if (bitsel) { + comment += "["+cvtToStr(bitindex_docs)+"]"; + int bitindex_code = bitindex_docs - nodep->lsb(); + varRefp = new AstSel(nodep->fileline(), varRefp, bitindex_code, 1); + chgRefp = new AstSel(nodep->fileline(), chgRefp, bitindex_code, 1); + } + AstCoverToggle* newp = new AstCoverToggle (nodep->fileline(), + newCoverInc(nodep->fileline(), "", "v_toggle", comment), + varRefp, chgRefp); + m_modp->addStmtp(newp); + } + // VISITORS - LINE COVERAGE virtual void visit(AstIf* nodep, AstNUser*) { UINFO(4," IF: "<name()!="") { m_beginHier = m_beginHier + (m_beginHier!=""?".":"") + nodep->name(); } nodep->iterateChildren(*this); } m_beginHier = oldHier; + m_inToggleOff = oldtog; } // VISITORS - BOTH @@ -178,6 +297,7 @@ public: // Operate on all modules m_checkBlock = true; m_beginHier = ""; + m_inToggleOff = false; rootp->iterateChildren(*this); } virtual ~CoverageVisitor() {} diff --git a/src/V3CoverageJoin.cpp b/src/V3CoverageJoin.cpp new file mode 100644 index 000000000..406f234e1 --- /dev/null +++ b/src/V3CoverageJoin.cpp @@ -0,0 +1,134 @@ +//************************************************************************* +// DESCRIPTION: Verilator: Netlist (top level) functions +// +// Code available from: http://www.veripool.org/verilator +// +// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli +// +//************************************************************************* +// +// Copyright 2003-2008 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// General Public License or the Perl Artistic License. +// +// Verilator is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +//************************************************************************* +// COVERAGEJOIN TRANSFORMATIONS: +// If two COVERTOGGLEs have same VARSCOPE, combine them +//************************************************************************* + + +#include "config_build.h" +#include "verilatedos.h" +#include +#include +#include +#include + +#include "V3Global.h" +#include "V3CoverageJoin.h" +#include "V3Hashed.h" +#include "V3Stats.h" + +//###################################################################### +// CoverageJoin state, as a visitor of each AstNode + +class CoverageJoinVisitor : public AstNVisitor { +private: + // NODE STATE + // V3Hashed + // AstCoverToggle->VarRef::user4() // V3Hashed calculation + + //AstUser4InUse In V3Hashed + + // TYPES + typedef vector ToggleList; + + // STATE + ToggleList m_toggleps; // List of of all AstCoverToggle's + + V3Double0 m_statToggleJoins; // Statistic tracking + + //int debug() { return 9; } + + // METHODS + void detectDuplicates() { + UINFO(9,"Finding duplicates\n"); + // Note uses user4 + V3Hashed hashed; // Duplicate code detection + // Hash all of the original signals we toggle cover + for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) { + AstCoverToggle* nodep = *it; + hashed.hashAndInsert(nodep->origp()); + } + // Find if there are any duplicates + for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) { + AstCoverToggle* nodep = *it; + if (nodep->backp()) { // nodep->backp() is null if we already detected it's a duplicate and unlinked it + // Want to choose a base node, and keep finding duplicates that are identical + // This prevents making chains where a->b, then c->d, then b->c, as we'll find a->b, a->c, a->d directly. + while (1) { + V3Hashed::iterator dupit = hashed.findDuplicate(nodep->origp()); + if (dupit == hashed.end()) break; + // + AstNode* duporigp = hashed.iteratorNodep(dupit); + // Note hashed will point to the original variable (what's duplicated), not the covertoggle, + // but we need to get back to the covertoggle which is immediately above, so: + AstCoverToggle* removep = duporigp->backp()->castCoverToggle(); + if (!removep) nodep->v3fatalSrc("CoverageJoin duplicate of wrong type"); + UINFO(8," Orig "<> "<incp()->declp()<> "<incp()->declp()<incp()->declp()->dataDeclThisp(); + removep->incp()->declp()->dataDeclp (datadeclp); + UINFO(8," new "<incp()->declp()<unlinkFrBack(); pushDeletep(removep); removep=NULL; + // Remove node from comparison so don't hit it again + hashed.erase(dupit); + m_statToggleJoins++; + } + } + } + } + + // VISITORS + virtual void visit(AstNetlist* nodep, AstNUser*) { + // Find all Coverage's + nodep->iterateChildren(*this); + // Simplify + detectDuplicates(); + } + virtual void visit(AstCoverToggle* nodep, AstNUser*) { + m_toggleps.push_back(nodep); + nodep->iterateChildren(*this); + } + //-------------------- + virtual void visit(AstNodeMath* nodep, AstNUser*) {} // Accelerate + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + CoverageJoinVisitor(AstNetlist* nodep) { + nodep->accept(*this); + } + virtual ~CoverageJoinVisitor() { + V3Stats::addStat("Coverage, Toggle points joined", m_statToggleJoins); + } +}; + +//###################################################################### +// Coverage class functions + +void V3CoverageJoin::coverageJoin(AstNetlist* rootp) { + UINFO(2,__FUNCTION__<<": "<__Vcoverage["); - puts(cvtToStr(nodep->binNum())); puts("])"); + 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 SystemPerl to // aggregate counts. This is because Verilator combines all @@ -208,7 +208,7 @@ public: } virtual void visit(AstCoverInc* nodep, AstNUser*) { puts("++(vlSymsp->__Vcoverage["); - puts(cvtToStr(nodep->declp()->binNum())); + puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); puts("]);\n"); } virtual void visit(AstCReturn* nodep, AstNUser*) { diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index fb1c0a92b..8b70c8b76 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -93,7 +93,9 @@ class EmitCSyms : EmitCBaseVisitor { } virtual void visit(AstCoverDecl* nodep, AstNUser*) { // Assign numbers to all bins, so we know how big of an array to use - nodep->binNum(m_coverBins++); + if (!nodep->dataDeclNullp()) { // else duplicate we don't need code for + nodep->binNum(m_coverBins++); + } } // NOPs virtual void visit(AstNodeStmt*, AstNUser*) {} diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index d9f7169a2..01e171e24 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -159,6 +159,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { } virtual void visit(AstCoverDecl*, AstNUser*) {} // N/A virtual void visit(AstCoverInc*, AstNUser*) {} // N/A + virtual void visit(AstCoverToggle*, AstNUser*) {} // N/A void visitNodeDisplay(AstNode* nodep, AstNode* filep, const string& text, AstNode* exprsp) { putbs(nodep->verilogKwd()); diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index 5537a9694..320965f5d 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -372,6 +372,9 @@ private: virtual void visit(AstAssignW* nodep, AstNUser*) { iterateNewStmt(nodep, NULL, NULL); } + virtual void visit(AstCoverToggle* nodep, AstNUser*) { + iterateNewStmt(nodep, "CoverToggle", "CoverToggle"); + } virtual void visit(AstTraceInc* nodep, AstNUser*) { bool lastslow = m_inSlow; m_inSlow = true; diff --git a/src/V3Options.cpp b/src/V3Options.cpp index efdede325..596d5a61e 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -607,6 +607,7 @@ void V3Options::parseOptsList(FileLine* fl, int argc, char** argv) { else if ( !strcmp (sw, "-cc") ) { m_outFormatOk = true; m_systemC = false; m_systemPerl = false; } else if ( onoff (sw, "-coverage", flag/*ref*/) ) { coverage(flag); } else if ( onoff (sw, "-coverage-line", flag/*ref*/) ){ m_coverageLine = flag; } + else if ( onoff (sw, "-coverage-toggle", flag/*ref*/) ){ m_coverageToggle = flag; } else if ( onoff (sw, "-coverage-user", flag/*ref*/) ){ m_coverageUser = flag; } else if ( onoff (sw, "-covsp", flag/*ref*/) ) { } // TBD else if ( onoff (sw, "-debug-check", flag/*ref*/) ){ m_debugCheck = flag; } @@ -854,6 +855,7 @@ V3Options::V3Options() { m_autoflush = false; m_coverageLine = false; + m_coverageToggle = false; m_coverageUser = false; m_debugCheck = false; m_dumpTree = false; diff --git a/src/V3Options.h b/src/V3Options.h index 55e7c3411..8c898a954 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -89,6 +89,7 @@ class V3Options { bool m_assert; // main switch: --assert bool m_autoflush; // main switch: --autoflush bool m_coverageLine; // main switch: --coverage-block + bool m_coverageToggle;// main switch: --coverage-toggle bool m_coverageUser; // main switch: --coverage-func bool m_debugCheck; // main switch: --debug-check bool m_dumpTree; // main switch: --dump-tree @@ -159,7 +160,7 @@ class V3Options { void addIncDir(const string& incdir); void addLibExt(const string& libext); void optimize(int level); - void coverage(bool flag) { m_coverageLine = m_coverageUser = flag; } + void coverage(bool flag) { m_coverageLine = m_coverageToggle = m_coverageUser = flag; } bool onoff(const char* sw, const char* arg, bool& flag); static bool wildmatchi(const char* s, const char* p); static string getenvStr(const char* envvar, const char* defaultValue); @@ -188,8 +189,9 @@ class V3Options { bool stats() const { return m_stats; } bool assertOn() const { return m_assert; } // assertOn as "assert" may be defined bool autoflush() const { return m_autoflush; } - bool coverage() const { return m_coverageUser || m_coverageLine; } + bool coverage() const { return m_coverageLine || m_coverageToggle || m_coverageUser; } bool coverageLine() const { return m_coverageLine; } + bool coverageToggle() const { return m_coverageToggle; } bool coverageUser() const { return m_coverageUser; } bool debugCheck() const { return m_debugCheck; } bool dumpTree() const { return m_dumpTree; } diff --git a/src/V3Order.cpp b/src/V3Order.cpp index 4c8e1123a..253a52c83 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -674,6 +674,9 @@ private: iterateNewStmt(nodep); m_inPost = false; } + virtual void visit(AstCoverToggle* nodep, AstNUser*) { + iterateNewStmt(nodep); + } virtual void visit(AstCFunc*, AstNUser*) { // Ignore for now // We should detect what variables are set in the function, and make diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index 73036ddc8..d4307d5ea 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -150,7 +150,14 @@ private: virtual void visit(AstAlways* nodep, AstNUser*) { // Add to list of blocks under this scope UINFO(4," Move "<cloneTree(false); + AstNode* clonep = nodep->cloneTree(false); + m_scopep->addActivep(clonep); + clonep->iterateChildren(*this); // We iterate under the *clone* + } + virtual void visit(AstCoverToggle* nodep, AstNUser*) { + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false); m_scopep->addActivep(clonep); clonep->iterateChildren(*this); // We iterate under the *clone* } @@ -279,6 +286,9 @@ private: virtual void visit(AstAlways* nodep, AstNUser*) { movedDeleteOrIterate(nodep); } + virtual void visit(AstCoverToggle* nodep, AstNUser*) { + movedDeleteOrIterate(nodep); + } virtual void visit(AstNodeFTask* nodep, AstNUser*) { movedDeleteOrIterate(nodep); } diff --git a/src/V3Table.cpp b/src/V3Table.cpp index 87301d5d1..693d80092 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -624,6 +624,7 @@ private: AstVarScope* findDuplicateTable(AstVarScope* vsc1p) { // See if another table we've created is identical, if so use it for both. + // (A more 'modern' way would be to instead use V3Hashed::findDuplicate) AstVar* var1p = vsc1p->varp(); for (deque::iterator it = m_modTableVscs.begin(); it!=m_modTableVscs.end(); ++it) { AstVarScope* vsc2p= *it; diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index eb12edce8..abc885286 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -59,6 +59,7 @@ private: // METHODS const char* varIgnoreTrace(AstVar* nodep) { // Return true if this shouldn't be traced + // See also similar rule in V3Coverage::varIgnoreToggle string prettyName = nodep->prettyName(); if (!nodep->isTrace()) return "Verilator trace_off"; diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 3b467b8c8..507757e33 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -37,6 +37,7 @@ #include "V3Combine.h" #include "V3Const.h" #include "V3Coverage.h" +#include "V3CoverageJoin.h" #include "V3Dead.h" #include "V3Delayed.h" #include "V3Depth.h" @@ -311,6 +312,12 @@ void process () { v3info("Command Line disabled gate optimization with -Og/-O0. This may cause ordering problems."); } + // Combine COVERINCs with duplicate terms + if (v3Global.opt.coverage()) { + V3CoverageJoin::coverageJoin(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("coveragejoin.tree")); + } + // Remove unused vars V3Const::constifyAll(v3Global.rootp()); V3Dead::deadifyAll(v3Global.rootp(), true); diff --git a/test_regress/driver.pl b/test_regress/driver.pl index 6904b8be8..c3e40b36c 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -259,7 +259,7 @@ sub new { $self->{stats} ||= "$self->{obj_dir}/V".$self->{name}."__stats.txt"; $self->{status_filename} ||= "$self->{obj_dir}/V".$self->{name}.".status"; $self->{run_log_filename} ||= "$self->{obj_dir}/vl_sim.log"; - $self->{coverage_filename} ||= "$self->{obj_dir}/V".$self->{name}."_coverage.pl"; + $self->{coverage_filename} ||= "$self->{obj_dir}/vl_coverage.pl"; ($self->{top_filename} = $self->{pl_filename}) =~ s/\.pl$/\.v/; if (!$self->{make_top_shell}) { $self->{top_shell_filename} = $self->{top_filename}; @@ -484,9 +484,10 @@ sub inline_checks { my $fh = IO::File->new("<$self->{top_filename}"); while (defined(my $line = $fh->getline)) { if ($line =~ /CHECK/) { - if ($line =~ /CHECK_COVER *\( *([---0-9]+) *, *"([^"]+)" *, *(\d+) *\)/) { - my $lineno=($. + $1); my $hier=$2; my $count=$3; + if ($line =~ /CHECK_COVER *\( *([---0-9]+) *, *"([^"]+)" *, *("([^"]+)" *,|) *(\d+) *\)/) { + my $lineno=($. + $1); my $hier=$2; my $comment=$4; my $count=$5; my $regexp = "\001l\002".$lineno; + $regexp .= ".*\001o\002".quotemeta($comment) if $comment; $regexp .= ".*\001h\002".quotemeta($hier); $regexp .= ".*' ".$count; if ($contents !~ /$regexp/) { diff --git a/test_regress/t/t_cover_toggle.pl b/test_regress/t/t_cover_toggle.pl new file mode 100755 index 000000000..b3c470c2c --- /dev/null +++ b/test_regress/t/t_cover_toggle.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2008 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + verilator_flags2 => [$Self->{v3}?'--sp --coverage-toggle --stats':''], + ); + +execute ( + check_finished=>1, + ); + +# Read the input .v file and do any CHECK_COVER requests +inline_checks(); + +file_grep ($Self->{stats}, qr/Coverage, Toggle points joined\s+25/i); + +ok(1); +1; diff --git a/test_regress/t/t_cover_toggle.v b/test_regress/t/t_cover_toggle.v new file mode 100644 index 000000000..728f0396f --- /dev/null +++ b/test_regress/t/t_cover_toggle.v @@ -0,0 +1,138 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2008 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + reg toggle; initial toggle=0; + + integer cyc; initial cyc=1; + wire [7:0] cyc_copy = cyc[7:0]; + wire toggle_up; + + alpha a1 (/*AUTOINST*/ + // Outputs + .toggle_up (toggle_up), + // Inputs + .clk (clk), + .toggle (toggle), + .cyc_copy (cyc_copy[7:0])); + alpha a2 (/*AUTOINST*/ + // Outputs + .toggle_up (toggle_up), + // Inputs + .clk (clk), + .toggle (toggle), + .cyc_copy (cyc_copy[7:0])); + + beta b1 (/*AUTOINST*/ + // Inputs + .clk (clk), + .toggle_up (toggle_up)); + + off o1 (/*AUTOINST*/ + // Inputs + .clk (clk), + .toggle (toggle)); + + reg [1:0] memory[121:110]; + + reg [1023:0] largeish; + // CHECK_COVER_MISSING(-1) + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + memory[cyc + 'd100] <= memory[cyc + 'd100] + 2'b1; + toggle <= '0; + if (cyc==3) begin + toggle <= '1; + end + if (cyc==4) begin + toggle <= '0; + end + else if (cyc==10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +module alpha (/*AUTOARG*/ + // Outputs + toggle_up, + // Inputs + clk, toggle, cyc_copy + ); + + // t.a1 and t.a2 collapse to a count of 2 + + input clk; + + input toggle; + // CHECK_COVER(-1,"TOP.v.a*",4) + // 2 edges * (t.a1 and t.a2) + + input [7:0] cyc_copy; + // CHECK_COVER(-1,"TOP.v.a*","cyc_copy[0]",22) + // CHECK_COVER(-2,"TOP.v.a*","cyc_copy[1]",10) + // CHECK_COVER(-3,"TOP.v.a*","cyc_copy[2]",4) + // CHECK_COVER(-4,"TOP.v.a*","cyc_copy[3]",2) + // CHECK_COVER(-5,"TOP.v.a*","cyc_copy[4]",0) + // CHECK_COVER(-6,"TOP.v.a*","cyc_copy[5]",0) + // CHECK_COVER(-7,"TOP.v.a*","cyc_copy[6]",0) + // CHECK_COVER(-8,"TOP.v.a*","cyc_copy[7]",0) + + reg toggle_internal; + // CHECK_COVER(-1,"TOP.v.a*",4) + // 2 edges * (t.a1 and t.a2) + + output reg toggle_up; + // CHECK_COVER(-1,"TOP.v.a*",4) + // 2 edges * (t.a1 and t.a2) + + always @ (posedge clk) begin + toggle_internal <= toggle; + toggle_up <= toggle; + end +endmodule + +module beta (/*AUTOARG*/ + // Inputs + clk, toggle_up + ); + + input clk; + + input toggle_up; + // CHECK_COVER(-1,"TOP.v.b1","toggle_up",2) + + /* verilator public_module */ + + always @ (posedge clk) begin + if (0 && toggle_up) begin end + end +endmodule + +module off (/*AUTOARG*/ + // Inputs + clk, toggle + ); + + // verilator coverage_off + input clk; + // CHECK_COVER_MISSING(-1) + + // verilator coverage_on + input toggle; + // CHECK_COVER(-1,"TOP.v.o1","toggle",2) + +endmodule