Add toggle coverage

This commit is contained in:
Wilson Snyder 2008-12-12 15:34:02 -05:00
parent 6b46da0240
commit 77405ddded
26 changed files with 610 additions and 13 deletions

View File

@ -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.

View File

@ -183,6 +183,7 @@ descriptions in the next sections for more information.
--compiler <compiler-name> 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<var>[=<value>] 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

View File

@ -140,6 +140,7 @@ RAW_OBJS = \
V3Combine.o \
V3Const__gen.o \
V3Coverage.o \
V3CoverageJoin.o \
V3Dead.o \
V3Delayed.o \
V3Depth.o \

View File

@ -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 "<<nodep<<endl);
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstFinal* nodep, AstNUser*) {
// Relink to CFUNC for the final
UINFO(4," FINAL "<<nodep<<endl);

View File

@ -905,7 +905,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append) {
editCountSetLast();
}
void AstNode::v3errorEnd(ostringstream& str) {
void AstNode::v3errorEnd(ostringstream& str) const {
if (this && m_fileline) {
ostringstream nsstr;
nsstr<<str.str();

View File

@ -731,7 +731,7 @@ public:
AstNode* addNextNull(AstNode* newp); // Returns this, adds to end of list, NULL is OK
void addNextHere(AstNode* newp); // Adds after speced node
void replaceWith(AstNode* newp); // Replace current node in tree with new node
void v3errorEnd(ostringstream& str);
void v3errorEnd(ostringstream& str) const;
virtual void dump(ostream& str=cout);
AstNode* unlinkFrBack(AstNRelinker* linkerp=NULL); // Unlink this from whoever points to it.
AstNode* unlinkFrBackWithNext(AstNRelinker* linkerp=NULL); // Unlink this from whoever points to it, keep entire next list with unlinked node

View File

@ -393,6 +393,12 @@ void AstNodeFTask::dump(ostream& str) {
}
void AstCoverDecl::dump(ostream& str) {
this->AstNode::dump(str);
if (this->dataDeclNullp()) {
str<<" -> ";
this->dataDeclNullp()->dump(str);
} else {
if (binNum()) { str<<" bin"<<dec<<binNum(); }
}
}
void AstCoverInc::dump(ostream& str) {
this->AstNode::dump(str);

View File

@ -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}

View File

@ -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);

View File

@ -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: "<<disablep<<" "<<nodep<<endl);
} else {
UINFO(4, " Toggle: "<<nodep<<endl);
// There's several overall ways to approach this
// Treat like tracing, where a end-of-timestamp action sees all changes
// Works ok, but would be quite slow as need to reform vectors before the calls
// Convert to "always @ (posedge signal[#]) coverinc"
// Would mark many signals as clocks, precluding many later optimizations
// Convert to "if (x & !lastx) CoverInc"
// OK, but we couldn't later detect them to schedule where the IFs get called
// Convert to "AstCoverInc(CoverInc...)"
// We'll do this, and make the if(...) coverinc later.
// Compute size of the problem
int dimensions = 0;
for (AstRange* arrayp=nodep->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<int> selects_docs; selects_docs.resize(dimensions);
vector<int> 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<int>& selects_docs, vector<int>& 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_docs<nodep->msb()+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<int>& selects_docs, vector<int>& 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; dim<dimension; dim++) {
// Comments are strings, not symbols, so we don't need __BRA__ __KET__
comment += "["+cvtToStr(selects_docs[dim])+"]";
varRefp = new AstArraySel(nodep->fileline(), 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: "<<nodep<<endl);
@ -154,13 +270,16 @@ private:
// (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
@ -178,6 +297,7 @@ public:
// Operate on all modules
m_checkBlock = true;
m_beginHier = "";
m_inToggleOff = false;
rootp->iterateChildren(*this);
}
virtual ~CoverageVisitor() {}

134
src/V3CoverageJoin.cpp Normal file
View File

@ -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 <cstdio>
#include <cstdarg>
#include <unistd.h>
#include <vector>
#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<AstCoverToggle*> 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 "<<nodep<<" -->> "<<nodep->incp()->declp()<<endl);
UINFO(8," dup "<<removep<<" -->> "<<removep->incp()->declp()<<endl);
// The CoverDecl the duplicate pointed to now needs to point to the original's data
// IE the duplicate will get the coverage number from the non-duplicate
AstCoverDecl* datadeclp = nodep->incp()->declp()->dataDeclThisp();
removep->incp()->declp()->dataDeclp (datadeclp);
UINFO(8," new "<<removep->incp()->declp()<<endl);
// Mark the found node as a duplicate of the first node
// (Not vice-versa as we have the iterator for the found node)
removep->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__<<": "<<endl);
CoverageJoinVisitor visitor (rootp);
}

37
src/V3CoverageJoin.h Normal file
View File

@ -0,0 +1,37 @@
// -*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilator: Coverage modules/signals together
//
// 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.
//
//*************************************************************************
#ifndef _V3COVERAGEJOIN_H_
#define _V3COVERAGEJOIN_H_ 1
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3CoverageJoin {
public:
// CREATORS
static void coverageJoin(AstNetlist* rootp);
};
#endif // Guard

View File

@ -191,7 +191,7 @@ public:
virtual void visit(AstCoverDecl* nodep, AstNUser*) {
puts("__vlCoverInsert("); // As Declared in emitCoverageDecl
puts("&(vlSymsp->__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*) {

View File

@ -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*) {}

View File

@ -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());

View File

@ -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;

View File

@ -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;

View File

@ -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; }

View File

@ -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

View File

@ -150,7 +150,14 @@ private:
virtual void visit(AstAlways* nodep, AstNUser*) {
// Add to list of blocks under this scope
UINFO(4," Move "<<nodep<<endl);
AstAlways* clonep = nodep->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 "<<nodep<<endl);
AstNode* clonep = nodep->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);
}

View File

@ -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<AstVarScope*>::iterator it = m_modTableVscs.begin(); it!=m_modTableVscs.end(); ++it) {
AstVarScope* vsc2p= *it;

View File

@ -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";

View File

@ -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);

View File

@ -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/) {

View File

@ -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;

View File

@ -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