2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Netlist (top level) functions
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2020-03-21 16:24:24 +01:00
|
|
|
// Copyright 2003-2020 by Wilson Snyder. This program is free software; you
|
|
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// COVERAGE TRANSFORMATIONS:
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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.
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Coverage.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <map>
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Coverage state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
|
|
class CoverageVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// TYPES
|
2020-05-31 15:05:02 +02:00
|
|
|
typedef std::map<string, int> VarNameMap;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2009-11-05 04:31:53 +01:00
|
|
|
struct ToggleEnt {
|
2020-04-14 04:51:35 +02:00
|
|
|
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
|
2019-05-19 22:13:13 +02:00
|
|
|
ToggleEnt(const string& comment, AstNode* vp, AstNode* cp)
|
2020-04-14 04:51:35 +02:00
|
|
|
: m_comment(comment)
|
|
|
|
|
, m_varRefp(vp)
|
|
|
|
|
, m_chgRefp(cp) {}
|
2019-05-19 22:13:13 +02:00
|
|
|
~ToggleEnt() {}
|
|
|
|
|
void cleanup() {
|
2020-01-18 16:29:49 +01:00
|
|
|
VL_DO_CLEAR(m_varRefp->deleteTree(), m_varRefp = NULL);
|
|
|
|
|
VL_DO_CLEAR(m_chgRefp->deleteTree(), m_chgRefp = NULL);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-11-05 04:31:53 +01:00
|
|
|
};
|
|
|
|
|
|
2014-03-29 16:04:13 +01:00
|
|
|
// NODE STATE
|
|
|
|
|
// Entire netlist:
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstIf::user1() -> bool. True indicates ifelse processed
|
2020-04-14 04:51:35 +02:00
|
|
|
AstUser1InUse m_inuser1;
|
2014-03-29 16:04:13 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// STATE
|
2020-04-14 04:51:35 +02:00
|
|
|
bool m_checkBlock; // Should this block get covered?
|
|
|
|
|
AstNodeModule* m_modp; // Current module to add statement to
|
|
|
|
|
bool m_inToggleOff; // In function/task etc
|
|
|
|
|
bool m_inModOff; // In module with no coverage
|
2020-05-31 15:05:02 +02:00
|
|
|
VarNameMap m_varnames; // Uniquification of inserted variable names
|
2020-04-14 04:51:35 +02:00
|
|
|
string m_beginHier; // AstBegin hier name for user coverage points
|
2008-06-10 03:25:10 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// METHODS
|
2018-05-14 12:50:47 +02:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2008-12-12 21:34:02 +01:00
|
|
|
const char* varIgnoreToggle(AstVar* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Return true if this shouldn't be traced
|
|
|
|
|
// See also similar rule in V3TraceDecl::varIgnoreTrace
|
2020-04-14 04:51:35 +02:00
|
|
|
if (!nodep->isToggleCoverable()) return "Not relevant signal type";
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!v3Global.opt.coverageUnderscore()) {
|
|
|
|
|
string prettyName = nodep->prettyName();
|
2020-04-14 04:51:35 +02:00
|
|
|
if (prettyName[0] == '_') return "Leading underscore";
|
|
|
|
|
if (prettyName.find("._") != string::npos) return "Inlined leading underscore";
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
if ((nodep->width() * nodep->dtypep()->arrayUnpackedElements()) > 256) {
|
2019-05-19 22:13:13 +02:00
|
|
|
return "Wide bus/array > 256 bits";
|
|
|
|
|
}
|
|
|
|
|
// We allow this, though tracing doesn't
|
|
|
|
|
// if (nodep->arrayp(1)) return "Unsupported: Multi-dimensional array";
|
|
|
|
|
return NULL;
|
2008-12-12 21:34:02 +01:00
|
|
|
}
|
|
|
|
|
|
2020-04-14 04:51:35 +02:00
|
|
|
AstCoverInc* newCoverInc(FileLine* fl, const string& hier, const string& page_prefix,
|
|
|
|
|
const string& comment, const string& trace_var_name) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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.)
|
2020-04-14 04:51:35 +02:00
|
|
|
string key = fl->filename() + "\001" + cvtToStr(fl->lineno()) + "\001" + hier + "\001"
|
|
|
|
|
+ page_prefix + "\001" + comment;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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();
|
2008-12-12 22:04:56 +01:00
|
|
|
|
2020-05-31 15:05:02 +02:00
|
|
|
AstCoverDecl* declp = new AstCoverDecl(fl, page, comment);
|
2019-05-19 22:13:13 +02:00
|
|
|
declp->hier(hier);
|
|
|
|
|
m_modp->addStmtp(declp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-10-27 14:27:18 +01:00
|
|
|
AstCoverInc* incp = new AstCoverInc(fl, declp);
|
|
|
|
|
if (!trace_var_name.empty() && v3Global.opt.traceCoverage()) {
|
2020-04-14 04:51:35 +02:00
|
|
|
AstVar* varp = new AstVar(incp->fileline(), AstVarType::MODULETEMP, trace_var_name,
|
2019-11-02 01:09:57 +01:00
|
|
|
incp->findUInt32DType());
|
2019-10-27 14:27:18 +01:00
|
|
|
varp->trace(true);
|
|
|
|
|
varp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
|
|
|
|
|
m_modp->addStmtp(varp);
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(5, "New coverage trace: " << varp << endl);
|
|
|
|
|
AstAssign* assp = new AstAssign(
|
|
|
|
|
incp->fileline(), new AstVarRef(incp->fileline(), varp, true),
|
|
|
|
|
new AstAdd(incp->fileline(), new AstVarRef(incp->fileline(), varp, false),
|
2019-10-27 14:27:18 +01:00
|
|
|
new AstConst(incp->fileline(), AstConst::WidthedValue(), 32, 1)));
|
|
|
|
|
incp->addNext(assp);
|
|
|
|
|
}
|
|
|
|
|
return incp;
|
|
|
|
|
}
|
|
|
|
|
string traceNameForLine(AstNode* nodep, const string& type) {
|
2020-05-31 15:05:02 +02:00
|
|
|
string name = "vlCoverageLineTrace_" + nodep->fileline()->filebasenameNoExt() + "__"
|
|
|
|
|
+ cvtToStr(nodep->fileline()->lineno()) + "_" + type;
|
|
|
|
|
VarNameMap::iterator it = m_varnames.find(name);
|
|
|
|
|
if (it == m_varnames.end()) {
|
|
|
|
|
m_varnames.insert(make_pair(name, 1));
|
|
|
|
|
} else {
|
|
|
|
|
int suffix = (it->second)++;
|
|
|
|
|
name += "_" + cvtToStr(suffix);
|
|
|
|
|
}
|
|
|
|
|
return name;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2008-12-10 23:10:03 +01:00
|
|
|
// VISITORS - BOTH
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
2020-01-20 19:27:27 +01:00
|
|
|
AstNodeModule* origModp = m_modp;
|
|
|
|
|
bool origInModOff = m_inModOff;
|
|
|
|
|
{
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
m_inModOff = nodep->isTop(); // Ignore coverage on top module; it's a shell we created
|
2020-05-31 15:05:02 +02:00
|
|
|
if (!origModp) m_varnames.clear();
|
2020-01-20 19:27:27 +01:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
m_modp = origModp;
|
|
|
|
|
m_inModOff = origInModOff;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2008-12-10 23:10:03 +01:00
|
|
|
|
2008-12-12 21:34:02 +01:00
|
|
|
// VISITORS - TOGGLE COVERAGE
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstNodeFTask* nodep) VL_OVERRIDE {
|
2019-05-19 22:13:13 +02:00
|
|
|
bool oldtog = m_inToggleOff;
|
|
|
|
|
{
|
|
|
|
|
m_inToggleOff = true;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
m_inToggleOff = oldtog;
|
2008-12-12 21:34:02 +01:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstVar* nodep) VL_OVERRIDE {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2020-04-14 04:51:35 +02:00
|
|
|
if (m_modp && !m_inModOff && !m_inToggleOff && nodep->fileline()->coverageOn()
|
|
|
|
|
&& v3Global.opt.coverageToggle()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
const char* disablep = varIgnoreToggle(nodep);
|
|
|
|
|
if (disablep) {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " Disable Toggle: " << disablep << " " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " Toggle: " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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.
|
2008-12-12 21:34:02 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Add signal to hold the old value
|
2020-04-14 04:51:35 +02:00
|
|
|
string newvarname = string("__Vtogcov__") + nodep->shortName();
|
|
|
|
|
AstVar* chgVarp
|
|
|
|
|
= new AstVar(nodep->fileline(), AstVarType::MODULETEMP, newvarname, nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
|
|
|
|
|
m_modp->addStmtp(chgVarp);
|
2008-12-12 21:34:02 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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.
|
2009-11-05 04:31:53 +01:00
|
|
|
|
2020-04-14 04:51:35 +02:00
|
|
|
ToggleEnt newvec(string(""), new AstVarRef(nodep->fileline(), nodep, false),
|
|
|
|
|
new AstVarRef(nodep->fileline(), chgVarp, true));
|
|
|
|
|
toggleVarRecurse(nodep->dtypeSkipRefp(), 0, newvec, nodep, chgVarp);
|
2019-05-19 22:13:13 +02:00
|
|
|
newvec.cleanup();
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-12-12 21:34:02 +01:00
|
|
|
}
|
|
|
|
|
|
2018-10-15 00:39:33 +02:00
|
|
|
void toggleVarBottom(const ToggleEnt& above, const AstVar* varp) {
|
2020-04-14 04:51:35 +02:00
|
|
|
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));
|
2019-05-19 22:13:13 +02:00
|
|
|
m_modp->addStmtp(newp);
|
2009-11-05 04:31:53 +01:00
|
|
|
}
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
void toggleVarRecurse(AstNodeDType* dtypep, int depth, // per-iteration
|
2020-04-14 04:51:35 +02:00
|
|
|
const ToggleEnt& above, AstVar* varp, AstVar* chgVarp) { // Constant
|
2018-02-02 03:32:58 +01:00
|
|
|
if (const AstBasicDType* bdtypep = VN_CAST(dtypep, BasicDType)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (bdtypep->isRanged()) {
|
2020-04-14 04:51:35 +02:00
|
|
|
for (int index_docs = bdtypep->lsb(); index_docs < bdtypep->msb() + 1;
|
|
|
|
|
index_docs++) {
|
2019-05-19 22:13:13 +02:00
|
|
|
int index_code = index_docs - bdtypep->lsb();
|
2020-04-14 04:51:35 +02:00
|
|
|
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));
|
2018-10-15 00:39:33 +02:00
|
|
|
toggleVarBottom(newent, varp);
|
2019-05-19 22:13:13 +02:00
|
|
|
newent.cleanup();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2018-10-15 00:39:33 +02:00
|
|
|
toggleVarBottom(above, varp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
} else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
|
|
|
|
for (int index_docs = adtypep->lsb(); index_docs <= adtypep->msb(); ++index_docs) {
|
2019-05-19 22:13:13 +02:00
|
|
|
int index_code = index_docs - adtypep->lsb();
|
2020-04-14 04:51:35 +02:00
|
|
|
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->subDTypep()->skipRefp(), depth + 1, newent, varp,
|
|
|
|
|
chgVarp);
|
2019-05-19 22:13:13 +02:00
|
|
|
newent.cleanup();
|
|
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
} else if (AstPackArrayDType* adtypep = VN_CAST(dtypep, PackArrayDType)) {
|
|
|
|
|
for (int index_docs = adtypep->lsb(); index_docs <= adtypep->msb(); ++index_docs) {
|
2019-05-19 22:13:13 +02:00
|
|
|
AstNodeDType* subtypep = adtypep->subDTypep()->skipRefp();
|
|
|
|
|
int index_code = index_docs - adtypep->lsb();
|
2020-04-14 04:51:35 +02:00
|
|
|
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
|
|
|
|
|
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
|
|
|
|
|
index_code * subtypep->width(), subtypep->width()),
|
|
|
|
|
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
|
|
|
|
|
index_code * subtypep->width(), subtypep->width()));
|
|
|
|
|
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth + 1, newent, varp,
|
|
|
|
|
chgVarp);
|
2019-05-19 22:13:13 +02:00
|
|
|
newent.cleanup();
|
|
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
} else if (AstStructDType* adtypep = VN_CAST(dtypep, StructDType)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// For now it's packed, so similar to array
|
2020-04-14 04:51:35 +02:00
|
|
|
for (AstMemberDType* itemp = adtypep->membersp(); itemp;
|
|
|
|
|
itemp = VN_CAST(itemp->nextp(), MemberDType)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
|
|
|
|
|
int index_code = itemp->lsb();
|
2020-04-14 04:51:35 +02:00
|
|
|
ToggleEnt newent(above.m_comment + string(".") + itemp->name(),
|
|
|
|
|
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
|
|
|
|
|
index_code, subtypep->width()),
|
|
|
|
|
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
|
|
|
|
|
index_code, subtypep->width()));
|
|
|
|
|
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
|
2019-05-19 22:13:13 +02:00
|
|
|
newent.cleanup();
|
|
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
} else if (AstUnionDType* adtypep = VN_CAST(dtypep, UnionDType)) {
|
2019-09-09 13:50:21 +02:00
|
|
|
// Arbitrarily handle only the first member of the union
|
2019-05-19 22:13:13 +02:00
|
|
|
if (AstMemberDType* itemp = adtypep->membersp()) {
|
|
|
|
|
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
|
2020-04-14 04:51:35 +02:00
|
|
|
ToggleEnt newent(above.m_comment + string(".") + itemp->name(),
|
|
|
|
|
above.m_varRefp->cloneTree(true),
|
|
|
|
|
above.m_chgRefp->cloneTree(true));
|
|
|
|
|
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
|
2019-05-19 22:13:13 +02:00
|
|
|
newent.cleanup();
|
|
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
} else {
|
2019-05-19 22:13:13 +02:00
|
|
|
dtypep->v3fatalSrc("Unexpected node data type in toggle coverage generation: "
|
2020-04-14 04:51:35 +02:00
|
|
|
<< dtypep->prettyTypeName());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2008-12-12 21:34:02 +01:00
|
|
|
}
|
|
|
|
|
|
2008-12-10 23:10:03 +01:00
|
|
|
// VISITORS - LINE COVERAGE
|
2020-04-14 04:51:35 +02:00
|
|
|
virtual void
|
|
|
|
|
visit(AstIf* nodep) VL_OVERRIDE { // Note not AstNodeIf; other types don't get covered
|
|
|
|
|
UINFO(4, " IF: " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_checkBlock) {
|
|
|
|
|
// An else-if. When we iterate the if, use "elsif" marking
|
2020-04-14 04:51:35 +02:00
|
|
|
bool elsif = (VN_IS(nodep->elsesp(), If) && !VN_CAST(nodep->elsesp(), If)->nextp());
|
2018-02-02 03:32:58 +01:00
|
|
|
if (elsif) VN_CAST(nodep->elsesp(), If)->user1(true);
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->ifsp());
|
2020-04-14 04:51:35 +02:00
|
|
|
if (m_checkBlock && !m_inModOff && nodep->fileline()->coverageOn()
|
|
|
|
|
&& v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it
|
|
|
|
|
UINFO(4, " COVER: " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->user1()) {
|
2019-10-27 14:27:18 +01:00
|
|
|
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "elsif",
|
|
|
|
|
traceNameForLine(nodep, "elsif")));
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2019-10-27 14:27:18 +01:00
|
|
|
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "if",
|
|
|
|
|
traceNameForLine(nodep, "if")));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Don't do empty else's, only empty if/case's
|
|
|
|
|
if (nodep->elsesp()) {
|
|
|
|
|
m_checkBlock = true;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->elsesp());
|
2020-04-14 04:51:35 +02:00
|
|
|
if (m_checkBlock && !m_inModOff && nodep->fileline()->coverageOn()
|
|
|
|
|
&& v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it
|
|
|
|
|
UINFO(4, " COVER: " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!elsif) { // elsif done inside if()
|
2020-04-14 04:51:35 +02:00
|
|
|
nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(), "", "v_line",
|
|
|
|
|
"else", traceNameForLine(nodep, "else")));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_checkBlock = true; // Reset as a child may have cleared it
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstCaseItem* nodep) VL_OVERRIDE {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " CASEI: " << nodep << endl);
|
|
|
|
|
if (m_checkBlock && !m_inModOff && nodep->fileline()->coverageOn()
|
|
|
|
|
&& v3Global.opt.coverageLine()) {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->bodysp());
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_checkBlock) { // if the case body didn't disable it
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " COVER: " << nodep << endl);
|
2019-10-27 14:27:18 +01:00
|
|
|
nodep->addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case",
|
|
|
|
|
traceNameForLine(nodep, "case")));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
m_checkBlock = true; // Reset as a child may have cleared it
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstCover* nodep) VL_OVERRIDE {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " COVER: " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_checkBlock = true; // Always do cover blocks, even if there's a $stop
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!nodep->coverincp()) {
|
|
|
|
|
// Note the name may be overridden by V3Assert processing
|
2019-10-27 14:27:18 +01:00
|
|
|
nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover",
|
2020-04-14 04:51:35 +02:00
|
|
|
m_beginHier + "_vlCoverageUserTrace"));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
m_checkBlock = true; // Reset as a child may have cleared it
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstStop* nodep) VL_OVERRIDE {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " STOP: " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_checkBlock = false;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstPragma* nodep) VL_OVERRIDE {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) {
|
|
|
|
|
// Skip all NEXT nodes under this block, and skip this if/case branch
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(4, " OFF: " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_checkBlock = false;
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2018-05-11 02:55:37 +02:00
|
|
|
if (m_checkBlock) iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2008-11-05 16:23:03 +01:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstBegin* nodep) VL_OVERRIDE {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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;
|
2020-04-14 04:51:35 +02:00
|
|
|
if (nodep->name() != "") {
|
|
|
|
|
m_beginHier = m_beginHier + (m_beginHier != "" ? "." : "") + nodep->name();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
m_beginHier = oldHier;
|
|
|
|
|
m_inToggleOff = oldtog;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2008-12-10 23:10:03 +01:00
|
|
|
|
|
|
|
|
// VISITORS - BOTH
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstNode* nodep) VL_OVERRIDE {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_checkBlock) {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_checkBlock = true; // Reset as a child may have cleared it
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2015-10-04 04:33:06 +02:00
|
|
|
explicit CoverageVisitor(AstNetlist* rootp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Operate on all modules
|
|
|
|
|
m_checkBlock = true;
|
2018-06-15 00:59:24 +02:00
|
|
|
m_modp = NULL;
|
2019-05-19 22:13:13 +02:00
|
|
|
m_beginHier = "";
|
|
|
|
|
m_inToggleOff = false;
|
|
|
|
|
m_inModOff = true;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(rootp);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
virtual ~CoverageVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Coverage class functions
|
|
|
|
|
|
|
|
|
|
void V3Coverage::coverage(AstNetlist* rootp) {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
|
|
|
|
{ CoverageVisitor visitor(rootp); } // Destruct before checking
|
2017-09-18 04:52:57 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("coverage", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|