//************************************************************************* // DESCRIPTION: Verilator: Netlist (top level) functions // // Code available from: http://www.veripool.org/verilator // // AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli // //************************************************************************* // // Copyright 2003-2009 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. // // 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. // //************************************************************************* // COVERAGE TRANSFORMATIONS: // At each IF/(IF else)/CASEITEM, // If there's no coverage off on the block below it, // or a $stop // Insert a COVERDECL node in the module. // (V3Emit reencodes into per-module numbers for emitting.) // Insert a COVERINC node at the end of the statement list // for that if/else/case. // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include #include #include #include #include "V3Global.h" #include "V3Coverage.h" #include "V3Ast.h" //###################################################################### // Coverage state, as a visitor of each AstNode class CoverageVisitor : public AstNVisitor { private: // TYPES typedef map FileMap; struct ToggleEnt { string m_comment; // Comment for coverage dump AstNode* m_varRefp; // How to get to this element AstNode* m_chgRefp; // How to get to this element ToggleEnt(const string& comment, AstNode* vp, AstNode* cp) : m_comment(comment), m_varRefp(vp), m_chgRefp(cp) {} ~ToggleEnt() {} void cleanup() { m_varRefp->deleteTree(); m_varRefp=NULL; m_chgRefp->deleteTree(); m_chgRefp=NULL; } }; // 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 // METHODS static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } 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& page_prefix, const string& comment) { // For line coverage, we may have multiple if's on one line, so disambiguate if // everything is otherwise identical // (Don't set column otherwise as it may result in making bins not match up with // different types of coverage enabled.) string key = fl->filename()+"\001"+cvtToStr(fl->lineno())+"\001"+hier+"\001"+page_prefix+"\001"+comment; int column = 0; FileMap::iterator it = m_fileps.find(key); if (it == m_fileps.end()) { m_fileps.insert(make_pair(key,column+1)); } else { column = (it->second)++; } // 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 string page = page_prefix + "/" + m_modp->prettyName(); AstCoverDecl* declp = new AstCoverDecl(fl, column, page, comment); declp->hier(hier); m_modp->addStmtp(declp); return new AstCoverInc(fl, declp); } // VISITORS - BOTH virtual void visit(AstModule* nodep, AstNUser*) { m_modp = nodep; m_fileps.clear(); nodep->iterateChildren(*this); 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: "<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. ToggleEnt newvec (string(""), new AstVarRef(nodep->fileline(), nodep, false), new AstVarRef(nodep->fileline(), chgVarp, true)); toggleVarRecurse(nodep->dtypep(), 0, newvec, nodep, chgVarp); newvec.cleanup(); } } } void toggleVarBottom(AstNodeDType* nodep, int depth, // per-iteration const ToggleEnt& above, AstVar* varp, AstVar* chgVarp) { // Constant AstCoverToggle* newp = new AstCoverToggle (varp->fileline(), newCoverInc(varp->fileline(), "", "v_toggle", varp->name()+above.m_comment), above.m_varRefp->cloneTree(true), above.m_chgRefp->cloneTree(true)); m_modp->addStmtp(newp); } void toggleVarRecurse(AstNodeDType* nodep, int depth, // per-iteration const ToggleEnt& above, AstVar* varp, AstVar* chgVarp) { // Constant if (AstBasicDType* bdtypep = nodep->castBasicDType()) { if (bdtypep->rangep()) { for (int index_docs=bdtypep->lsb(); index_docsmsb()+1; index_docs++) { int index_code = index_docs - bdtypep->lsb(); ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]", new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true), index_code, 1), new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true), index_code, 1)); toggleVarBottom(nodep, depth+1, newent, varp, chgVarp); newent.cleanup(); } } else { toggleVarBottom(nodep, depth+1, above, varp, chgVarp); } } else if (AstArrayDType* adtypep = nodep->castArrayDType()) { for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb()+1; ++index_docs) { int index_code = index_docs - adtypep->lsb(); ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]", new AstArraySel(varp->fileline(), above.m_varRefp->cloneTree(true), index_code), new AstArraySel(varp->fileline(), above.m_chgRefp->cloneTree(true), index_code)); toggleVarRecurse(adtypep->dtypep(), depth+1, newent, varp, chgVarp); newent.cleanup(); } } else { nodep->v3fatalSrc("Unexpected node data type in toggle coverage generation: "<prettyTypeName()); } } // VISITORS - LINE COVERAGE virtual void visit(AstIf* nodep, AstNUser*) { // Note not AstNodeIf; other types don't get covered UINFO(4," IF: "<ifsp()->iterateAndNext(*this); if (m_checkBlock && nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it if (!nodep->backp()->castIf() || nodep->backp()->castIf()->elsesp()!=nodep) { // Ignore if else; did earlier UINFO(4," COVER: "<addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "if")); } } // Don't do empty else's, only empty if/case's if (nodep->elsesp()) { m_checkBlock = true; nodep->elsesp()->iterateAndNext(*this); if (m_checkBlock && nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it UINFO(4," COVER: "<elsesp()->castIf()) { nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(), "", "v_line", "elsif")); } else { nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(), "", "v_line", "else")); } } } m_checkBlock = true; // Reset as a child may have cleared it } } virtual void visit(AstCaseItem* nodep, AstNUser*) { UINFO(4," CASEI: "<fileline()->coverageOn() && v3Global.opt.coverageLine()) { nodep->bodysp()->iterateAndNext(*this); if (m_checkBlock) { // if the case body didn't disable it UINFO(4," COVER: "<addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case")); } m_checkBlock = true; // Reset as a child may have cleared it } } virtual void visit(AstPslCover* nodep, AstNUser*) { UINFO(4," PSLCOVER: "<iterateChildren(*this); if (!nodep->coverincp()) { // Note the name may be overridden by V3Assert processing nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover")); } m_checkBlock = true; // Reset as a child may have cleared it } virtual void visit(AstStop* nodep, AstNUser*) { UINFO(4," STOP: "<pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) { // Skip all NEXT nodes under this block, and skip this if/case branch UINFO(4," OFF: "<unlinkFrBack()->deleteTree(); nodep=NULL; } else { if (m_checkBlock) nodep->iterateChildren(*this); } } virtual void visit(AstBegin* nodep, AstNUser*) { // Record the hierarchy of any named begins, so we can apply to user // coverage points. This is because there may be cov points inside // generate blocks; each point should get separate consideration. // (Currently ignored for line coverage, since any generate iteration // covers the code in that line.) string oldHier = m_beginHier; bool oldtog = m_inToggleOff; { m_inToggleOff = true; if (nodep->name()!="") { m_beginHier = m_beginHier + (m_beginHier!=""?".":"") + nodep->name(); } nodep->iterateChildren(*this); } m_beginHier = oldHier; m_inToggleOff = oldtog; } // VISITORS - BOTH virtual void visit(AstNode* nodep, AstNUser*) { // Default: Just iterate if (m_checkBlock) { nodep->iterateChildren(*this); m_checkBlock = true; // Reset as a child may have cleared it } } public: // CONSTUCTORS CoverageVisitor(AstNetlist* rootp) { // Operate on all modules m_checkBlock = true; m_beginHier = ""; m_inToggleOff = false; rootp->iterateChildren(*this); } virtual ~CoverageVisitor() {} }; //###################################################################### // Coverage class functions void V3Coverage::coverage(AstNetlist* rootp) { UINFO(2,__FUNCTION__<<": "<