This commit is contained in:
Ryszard Rozak 2025-07-21 15:40:20 +02:00 committed by GitHub
commit d0d240f8bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 444 additions and 156 deletions

View File

@ -75,6 +75,58 @@ class VerilatedCovImp;
ccontextp->_insertp("hier", name, __VA_ARGS__); \
} while (false)
static inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData,
const IData oldData) {
for (int i = 0; i < width; ++i) *(covp + i) += ((newData ^ oldData) >> i) & 1;
}
static inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const IData newData,
const IData oldData) {
for (int i = 0; i < width; ++i) *(covp + i) += ((newData ^ oldData) >> i) & 1;
}
static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData,
WDataInP oldData) {
for (int i = 0; i < VL_WORDS_I(width); ++i) {
const EData changed = newData[i] ^ oldData[i];
if (changed) {
for (int j = 0; j < width - i * 32; ++j) *(covp + i * 32 + j) += (changed >> j) & 1;
}
}
}
static inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic<uint32_t>* covp,
const IData newData, const IData oldData) VL_MT_SAFE {
for (int i = 0; i < width; ++i) {
if (VL_BITISSET_I((newData ^ oldData), i)) {
(covp + i)->fetch_add(1, std::memory_order_relaxed);
}
}
}
static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic<uint32_t>* covp,
const IData newData, const IData oldData) VL_MT_SAFE {
for (int i = 0; i < width; ++i) {
if (VL_BITISSET_Q((newData ^ oldData), i)) {
(covp + i)->fetch_add(1, std::memory_order_relaxed);
}
}
}
static inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic<uint32_t>* covp,
WDataInP newData, WDataInP oldData) VL_MT_SAFE {
for (int i = 0; i < VL_WORDS_I(width); ++i) {
const EData changed = newData[i] ^ oldData[i];
if (changed) {
for (int j = 0; j < width - i * 32; ++j) {
if (VL_BITISSET_E(changed, j)) {
(covp + i * 32 + j)->fetch_add(1, std::memory_order_relaxed);
}
}
}
}
}
//=============================================================================
// VerilatedCov
/// Per-VerilatedContext coverage data class.

View File

@ -399,6 +399,52 @@ public:
ASTGEN_MEMBERS_AstNodeCase;
int instrCount() const override { return INSTR_COUNT_BRANCH; }
};
class AstNodeCoverDecl VL_NOT_FINAL : public AstNodeStmt {
// Coverage analysis point declaration
//
// [After V3CoverageJoin] Duplicate declaration to get data from instead
// @astgen ptr := m_dataDeclp : Optional[AstNodeCoverDecl]
string m_page; // Coverage point's page tag
string m_text; // Coverage point's text
string m_hier; // Coverage point's hierarchy
int m_binNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment
public:
AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment)
: AstNodeStmt(t, fl)
, m_page{page}
, m_text{comment} {}
ASTGEN_MEMBERS_AstNodeCoverDecl;
const char* broken() const override {
if (m_dataDeclp
&& (m_dataDeclp == this || m_dataDeclp->m_dataDeclp)) { // Avoid O(n^2) accessing
v3fatalSrc("dataDeclp should point to real data, not be a list: " << cvtToHex(this));
}
return nullptr;
}
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
int binNum() const { return m_binNum; }
void binNum(int flag) { m_binNum = flag; }
virtual int size() const = 0;
const string& comment() const { return m_text; } // text to insert in code
const string& page() const { return m_page; }
const string& hier() const { return m_hier; }
void hier(const string& flag) { m_hier = flag; }
void comment(const string& flag) { m_text = flag; }
bool sameNode(const AstNode* samep) const override {
const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl);
return (fileline() == asamep->fileline() && hier() == asamep->hier()
&& comment() == asamep->comment() && page() == asamep->page());
}
bool isPredictOptimizable() const override { return false; }
void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; }
// dataDecl nullptr means "use this one", but often you want "this" to
// indicate to get data from here
AstNodeCoverDecl* dataDeclNullp() const { return m_dataDeclp; }
AstNodeCoverDecl* dataDeclThisp() { return dataDeclNullp() ? dataDeclNullp() : this; }
};
class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt {
// Cover or Assert
// Parents: {statement list}
@ -2978,64 +3024,15 @@ public:
return true; // SPECIAL: We don't process code after breaks
}
};
class AstCoverDecl final : public AstNodeStmt {
// Coverage analysis point declaration
//
// [After V3CoverageJoin] Duplicate declaration to get data from instead
// @astgen ptr := m_dataDeclp : Optional[AstCoverDecl]
string m_page;
string m_text;
string m_hier;
string m_linescov;
int m_offset; // Offset column numbers to uniq-ify IFs
int m_binNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment
public:
AstCoverDecl(FileLine* fl, const string& page, const string& comment, const string& linescov,
int offset)
: ASTGEN_SUPER_CoverDecl(fl)
, m_page{page}
, m_text{comment}
, m_linescov{linescov}
, m_offset{offset} {}
ASTGEN_MEMBERS_AstCoverDecl;
const char* broken() const override {
if (m_dataDeclp
&& (m_dataDeclp == this || m_dataDeclp->m_dataDeclp)) { // Avoid O(n^2) accessing
v3fatalSrc("dataDeclp should point to real data, not be a list: " << cvtToHex(this));
}
return nullptr;
}
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
int binNum() const { return m_binNum; }
void binNum(int flag) { m_binNum = flag; }
int offset() const { return m_offset; }
const string& comment() const { return m_text; } // text to insert in code
const string& linescov() const { return m_linescov; }
const string& page() const { return m_page; }
const string& hier() const { return m_hier; }
void hier(const string& flag) { m_hier = flag; }
void comment(const string& flag) { m_text = flag; }
bool sameNode(const AstNode* samep) const override {
const AstCoverDecl* const asamep = VN_DBG_AS(samep, CoverDecl);
return (fileline() == asamep->fileline() && linescov() == asamep->linescov()
&& hier() == asamep->hier() && comment() == asamep->comment());
}
bool isPredictOptimizable() const override { return false; }
void dataDeclp(AstCoverDecl* nodep) { m_dataDeclp = nodep; }
// dataDecl nullptr means "use this one", but often you want "this" to
// indicate to get data from here
AstCoverDecl* dataDeclNullp() const { return m_dataDeclp; }
AstCoverDecl* dataDeclThisp() { return dataDeclNullp() ? dataDeclNullp() : this; }
};
class AstCoverInc final : public AstNodeStmt {
// Coverage analysis point; increment coverage count
// @astgen op1 := toggleExprp : Optional[AstNodeExpr] // [After V3Clock]
// @astgen op2 := toggleCovExprp : Optional[AstNodeExpr] // [After V3Clock]
// These are expressions to which the node corresponds. Used only in toggle coverage
//
// @astgen ptr := m_declp : AstCoverDecl // [After V3CoverageJoin] Declaration
// @astgen ptr := m_declp : AstNodeCoverDecl // [After V3CoverageJoin] Declaration
public:
AstCoverInc(FileLine* fl, AstCoverDecl* declp)
AstCoverInc(FileLine* fl, AstNodeCoverDecl* declp)
: ASTGEN_SUPER_CoverInc(fl)
, m_declp{declp} {}
ASTGEN_MEMBERS_AstCoverInc;
@ -3049,7 +3046,7 @@ public:
bool isPredictOptimizable() const override { return false; }
bool isOutputter() override { return true; }
bool isPure() override { return false; }
AstCoverDecl* declp() const { return m_declp; } // Where defined
AstNodeCoverDecl* declp() const { return m_declp; } // Where defined
};
class AstCoverToggle final : public AstNodeStmt {
// Toggle analysis of given signal
@ -3912,6 +3909,47 @@ public:
: ASTGEN_SUPER_GenCase(fl, exprp, itemsp) {}
ASTGEN_MEMBERS_AstGenCase;
};
class AstCoverOtherDecl final : public AstNodeCoverDecl {
// Coverage analysis point declaration
// Used for other than toggle types of coverage
string m_linescov;
int m_offset; // Offset column numbers to uniq-ify IFs
public:
AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment,
const string& linescov, int offset)
: ASTGEN_SUPER_CoverOtherDecl(fl, page, comment)
, m_linescov{linescov}
, m_offset{offset} {}
ASTGEN_MEMBERS_AstCoverOtherDecl;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
int offset() const { return m_offset; }
int size() const override { return 1; }
const string& linescov() const { return m_linescov; }
bool sameNode(const AstNode* samep) const override {
const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl);
return AstNodeCoverDecl::sameNode(samep) && linescov() == asamep->linescov();
}
};
class AstCoverToggleDecl final : public AstNodeCoverDecl {
// Coverage analysis point declaration
// Used for toggle coverage
const VNumRange m_range; // Packed array range covering each toggle bit
public:
AstCoverToggleDecl(FileLine* fl, const string& page, const string& comment,
const VNumRange& range)
: ASTGEN_SUPER_CoverToggleDecl(fl, page, comment)
, m_range{range} {}
ASTGEN_MEMBERS_AstCoverToggleDecl;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
int size() const override { return m_range.elements(); }
const VNumRange& range() const { return m_range; }
bool sameNode(const AstNode* samep) const override {
const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl);
return AstNodeCoverDecl::sameNode(samep) && range() == asamep->range();
}
};
// === AstNodeCoverOrAssert ===
class AstAssert final : public AstNodeCoverOrAssert {

View File

@ -2870,10 +2870,9 @@ void AstBegin::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, needProcess);
dumpJsonGen(str);
}
void AstCoverDecl::dump(std::ostream& str) const {
void AstNodeCoverDecl::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (!page().empty()) str << " page=" << page();
if (!linescov().empty()) str << " lc=" << linescov();
if (this->dataDeclNullp()) {
static bool s_recursing = false;
str << " -> ";
@ -2888,12 +2887,30 @@ void AstCoverDecl::dump(std::ostream& str) const {
if (binNum()) str << " bin" << std::dec << binNum();
}
}
void AstCoverDecl::dumpJson(std::ostream& str) const {
void AstNodeCoverDecl::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, page);
dumpJsonStrFunc(str, linescov);
dumpJsonNumFunc(str, binNum);
dumpJsonGen(str);
}
void AstCoverOtherDecl::dump(std::ostream& str) const {
this->AstNodeCoverDecl::dump(str);
if (!linescov().empty()) str << " lc=" << linescov();
}
void AstCoverOtherDecl::dumpJson(std::ostream& str) const {
this->AstNodeCoverDecl::dumpJson(str);
dumpJsonStrFunc(str, linescov);
}
void AstCoverToggleDecl::dump(std::ostream& str) const {
this->AstNodeCoverDecl::dump(str);
if (range().ranged()) str << " range=[" << range().left() << ":" << range().right() << "]";
}
void AstCoverToggleDecl::dumpJson(std::ostream& str) const {
this->AstNodeCoverDecl::dumpJson(str);
if (range().ranged()) {
dumpJsonStr(str, "range",
std::to_string(range().left()) + ":" + std::to_string(range().right()));
}
}
void AstCoverInc::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
str << " -> ";

View File

@ -289,7 +289,7 @@ class BeginVisitor final : public VNVisitor {
}
iterateChildren(nodep);
}
void visit(AstCoverDecl* nodep) override {
void visit(AstNodeCoverDecl* nodep) override {
// Don't need to fix path in coverage statements, they're not under
// any BEGINs, but V3Coverage adds them all under the module itself.
iterateChildren(nodep);

View File

@ -161,10 +161,10 @@ class CCtorsVisitor final : public VNVisitor {
if (v3Global.opt.coverage()) {
V3CCtorsBuilder configure_coverage{nodep, "_configure_coverage", VCtorType::COVERAGE};
for (AstNode* np = nodep->stmtsp(); np; np = np->nextp()) {
if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) {
if (AstNodeCoverDecl* const coverp = VN_CAST(np, NodeCoverDecl)) {
// ... else we don't have a static VlSym to be able to coverage insert
UASSERT_OBJ(!VN_IS(nodep, Class), coverp,
"CoverDecl should be in class's package, not class itself");
"NodeCoverDecl should be in class's package, not class itself");
np = coverp->backp();
configure_coverage.add(coverp->unlinkFrBack());
}

View File

@ -175,7 +175,7 @@ class ClassVisitor final : public VNVisitor {
// m_toScopeMoves.emplace_back(nodep, m_classScopep);
//}
}
void visit(AstCoverDecl* nodep) override {
void visit(AstNodeCoverDecl* nodep) override {
// Need to declare coverage in package, where we have access to symbol table
iterateChildren(nodep);
if (m_classPackagep) m_classPackagep->addStmtsp(nodep->unlinkFrBack());

View File

@ -100,18 +100,20 @@ class ClockVisitor final : public VNVisitor {
// if (debug()) nodep->dumpTree("- ct: ");
// COVERTOGGLE(INC, ORIG, CHANGE) ->
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
AstNode* const incp = nodep->incp()->unlinkFrBack();
AstCoverInc* const incp = nodep->incp()->unlinkFrBack();
AstNodeExpr* const origp = nodep->origp()->unlinkFrBack();
AstNodeExpr* const changeWrp = nodep->changep()->unlinkFrBack();
AstNodeExpr* const changeRdp = ConvertWriteRefsToRead::main(changeWrp->cloneTree(false));
AstNodeExpr* comparedp = nullptr;
incp->toggleExprp(origp->cloneTree(false));
incp->toggleCovExprp(changeRdp->cloneTree(false));
// Xor will optimize better than Eq, when CoverToggle has bit selects,
// but can only use Xor with non-opaque types
if (const AstBasicDType* const bdtypep
= VN_CAST(origp->dtypep()->skipRefp(), BasicDType)) {
if (!bdtypep->isOpaque()) comparedp = new AstXor{nodep->fileline(), origp, changeRdp};
}
if (!comparedp) comparedp = AstEq::newTyped(nodep->fileline(), origp, changeRdp);
UASSERT_OBJ(comparedp, nodep, "Toggle coverage of non-opaque type variable");
AstIf* const newp = new AstIf{nodep->fileline(), comparedp, incp};
// We could add another IF to detect posedges, and only increment if so.
// It's another whole branch though versus a potential memory miss.

View File

@ -180,22 +180,8 @@ class CoverageVisitor final : public VNVisitor {
return nullptr;
}
AstCoverInc* newCoverInc(FileLine* fl, const string& hier, const string& page_prefix,
const string& comment, const string& linescov, int offset,
AstCoverInc* newCoverInc(FileLine* fl, AstNodeCoverDecl* const declp,
const string& trace_var_name) {
// We could use the basename of the filename to the page, but seems
// better for code from an include file to be listed under the
// module using it rather than the include file.
// Note the module name could have parameters appended, we'll consider this
// a feature as it allows for each parameterized block to be counted separately.
// Someday the user might be allowed to specify a different page suffix
const string page = page_prefix + "/" + m_modp->prettyName();
AstCoverDecl* const declp = new AstCoverDecl{fl, page, comment, linescov, offset};
declp->hier(hier);
m_modp->addStmtsp(declp);
UINFO(9, "new " << declp);
AstCoverInc* const incp = new AstCoverInc{fl, declp};
if (!trace_var_name.empty()
&& v3Global.opt.traceCoverage()
@ -317,9 +303,12 @@ class CoverageVisitor final : public VNVisitor {
iterateAndNextNull(nodep->stmtsp());
if (m_state.lineCoverageOn(nodep)) {
lineTrack(nodep);
AstCoverOtherDecl* const declp
= new AstCoverOtherDecl{nodep->fileline(), "v_line/" + m_modp->prettyName(),
"block", linesCov(m_state, nodep), 0};
m_modp->addStmtsp(declp);
AstNode* const newp
= newCoverInc(nodep->fileline(), "", "v_line", "block", linesCov(m_state, nodep),
0, traceNameForLine(nodep, "block"));
= newCoverInc(nodep->fileline(), declp, traceNameForLine(nodep, "block"));
insertProcStatement(nodep, newp);
}
}
@ -355,9 +344,12 @@ class CoverageVisitor final : public VNVisitor {
iterateChildren(nodep);
if (m_state.lineCoverageOn(nodep)) {
lineTrack(nodep);
AstCoverOtherDecl* const declp
= new AstCoverOtherDecl{nodep->fileline(), "v_line/" + m_modp->prettyName(),
"block", linesCov(m_state, nodep), 0};
m_modp->addStmtsp(declp);
AstNode* const newp
= newCoverInc(nodep->fileline(), "", "v_line", "block", linesCov(m_state, nodep),
0, traceNameForLine(nodep, "block"));
= newCoverInc(nodep->fileline(), declp, traceNameForLine(nodep, "block"));
insertProcStatement(nodep, newp);
}
}
@ -403,35 +395,23 @@ class CoverageVisitor final : public VNVisitor {
}
}
void toggleVarBottom(const ToggleEnt& above, const AstVar* varp) {
void toggleVarBottom(const ToggleEnt& above, const AstVar* varp, const VNumRange& range) {
const std::string hierPrefix
= (m_beginHier != "") ? AstNode::prettyName(m_beginHier) + "." : "";
AstCoverToggleDecl* const declp
= new AstCoverToggleDecl{varp->fileline(), "v_toggle/" + m_modp->prettyName(),
hierPrefix + varp->name() + above.m_comment, range};
m_modp->addStmtsp(declp);
AstCoverToggle* const newp = new AstCoverToggle{
varp->fileline(),
newCoverInc(varp->fileline(), "", "v_toggle",
hierPrefix + varp->name() + above.m_comment, "", 0, ""),
varp->fileline(), newCoverInc(varp->fileline(), declp, ""),
above.m_varRefp->cloneTree(false), above.m_chgRefp->cloneTree(false)};
m_modp->addStmtsp(newp);
}
void toggleVarRecurse(const AstNodeDType* const dtypep, const int depth, // per-iteration
const ToggleEnt& above, const AstVar* const varp) { // Constant
if (const AstBasicDType* const bdtypep = VN_CAST(dtypep, BasicDType)) {
if (bdtypep->isRanged()) {
for (int index_docs = bdtypep->lo(); index_docs < bdtypep->hi() + 1;
++index_docs) {
const int index_code = index_docs - bdtypep->lo();
ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]",
new AstSel{varp->fileline(),
above.m_varRefp->cloneTree(false), index_code, 1},
new AstSel{varp->fileline(),
above.m_chgRefp->cloneTree(false), index_code, 1}};
toggleVarBottom(newent, varp);
newent.cleanup();
}
} else {
toggleVarBottom(above, varp);
}
if (const AstBasicDType* const basicp = VN_CAST(dtypep, BasicDType)) {
toggleVarBottom(above, varp, basicp->nrange());
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
const int index_code = index_docs - adtypep->lo();
@ -544,20 +524,26 @@ class CoverageVisitor final : public VNVisitor {
iterate(nodep->thenp());
lineTrack(nodep);
AstNodeExpr* const thenp = nodep->thenp()->unlinkFrBack();
nodep->thenp(new AstExprStmt{thenp->fileline(),
newCoverInc(nodep->fileline(), "", "v_branch",
"cond_then", linesCov(m_state, nodep), 0,
traceNameForLine(nodep, "cond_then")),
thenp});
AstCoverOtherDecl* const thenDeclp
= new AstCoverOtherDecl{thenp->fileline(), "v_branch/" + m_modp->prettyName(),
"cond_then", linesCov(m_state, nodep), 0};
m_modp->addStmtsp(thenDeclp);
nodep->thenp(new AstExprStmt{
thenp->fileline(),
newCoverInc(nodep->fileline(), thenDeclp, traceNameForLine(nodep, "cond_then")),
thenp});
m_state = lastState;
createHandle(nodep);
iterate(nodep->elsep());
AstNodeExpr* const elsep = nodep->elsep()->unlinkFrBack();
nodep->elsep(new AstExprStmt{elsep->fileline(),
newCoverInc(nodep->fileline(), "", "v_branch",
"cond_else", linesCov(m_state, nodep), 1,
traceNameForLine(nodep, "cond_else")),
elsep});
AstCoverOtherDecl* const elseDeclp
= new AstCoverOtherDecl{thenp->fileline(), "v_branch/" + m_modp->prettyName(),
"cond_else", linesCov(m_state, nodep), 1};
m_modp->addStmtsp(elseDeclp);
nodep->elsep(new AstExprStmt{
elsep->fileline(),
newCoverInc(nodep->fileline(), elseDeclp, traceNameForLine(nodep, "cond_else")),
elsep});
m_state = lastState;
} else {
@ -614,22 +600,32 @@ class CoverageVisitor final : public VNVisitor {
// Normal if. Linecov shows what's inside the if (not condition that is
// always executed)
UINFO(4, " COVER-branch: " << nodep);
nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_branch", "if",
linesCov(ifState, nodep), 0,
traceNameForLine(nodep, "if")));
AstCoverOtherDecl* const ifDeclp
= new AstCoverOtherDecl{nodep->fileline(), "v_branch/" + m_modp->prettyName(),
"if", linesCov(ifState, nodep), 0};
m_modp->addStmtsp(ifDeclp);
nodep->addThensp(
newCoverInc(nodep->fileline(), ifDeclp, traceNameForLine(nodep, "if")));
// The else has a column offset of 1 to uniquify it relative to the if
// As "if" and "else" are more than one character wide, this won't overlap
// another token
nodep->addElsesp(newCoverInc(nodep->fileline(), "", "v_branch", "else",
linesCov(elseState, nodep), 1,
traceNameForLine(nodep, "else")));
AstCoverOtherDecl* const elseDeclp
= new AstCoverOtherDecl{nodep->fileline(), "v_branch/" + m_modp->prettyName(),
"else", linesCov(elseState, nodep), 1};
m_modp->addStmtsp(elseDeclp);
nodep->addElsesp(
newCoverInc(nodep->fileline(), elseDeclp, traceNameForLine(nodep, "else")));
}
// If/else attributes to each block as non-branch coverage
else if (first_elsif || cont_elsif) {
UINFO(4, " COVER-elsif: " << nodep);
if (ifState.lineCoverageOn(nodep)) {
nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_line", "elsif",
linesCov(ifState, nodep), 0,
AstCoverOtherDecl* const elsifDeclp = new AstCoverOtherDecl{
nodep->fileline(), "v_line/" + m_modp->prettyName(), "elsif",
linesCov(ifState, nodep), 0};
m_modp->addStmtsp(elsifDeclp);
nodep->addThensp(newCoverInc(nodep->fileline(), elsifDeclp,
traceNameForLine(nodep, "elsif")));
}
// and we don't insert the else as the child if-else will do so
@ -637,14 +633,21 @@ class CoverageVisitor final : public VNVisitor {
// Cover as separate blocks (not a branch as is not two-legged)
if (ifState.lineCoverageOn(nodep)) {
UINFO(4, " COVER-half-if: " << nodep);
nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_line", "if",
linesCov(ifState, nodep), 0,
traceNameForLine(nodep, "if")));
AstCoverOtherDecl* const ifDeclp = new AstCoverOtherDecl{
nodep->fileline(), "v_line/" + m_modp->prettyName(), "if",
linesCov(ifState, nodep), 0};
m_modp->addStmtsp(ifDeclp);
nodep->addThensp(
newCoverInc(nodep->fileline(), ifDeclp, traceNameForLine(nodep, "if")));
}
if (elseState.lineCoverageOn(nodep)) {
UINFO(4, " COVER-half-el: " << nodep);
nodep->addElsesp(newCoverInc(nodep->fileline(), "", "v_line", "else",
linesCov(elseState, nodep), 1,
AstCoverOtherDecl* const elseDeclp = new AstCoverOtherDecl{
nodep->fileline(), "v_line/" + m_modp->prettyName(), "else",
linesCov(elseState, nodep), 1};
m_modp->addStmtsp(elseDeclp);
nodep->addElsesp(newCoverInc(nodep->fileline(), elseDeclp,
traceNameForLine(nodep, "else")));
}
}
@ -666,9 +669,12 @@ class CoverageVisitor final : public VNVisitor {
if (m_state.lineCoverageOn(nodep)) { // if the case body didn't disable it
lineTrack(nodep);
UINFO(4, " COVER: " << nodep);
nodep->addStmtsp(newCoverInc(nodep->fileline(), "", "v_line", "case",
linesCov(m_state, nodep), 0,
traceNameForLine(nodep, "case")));
AstCoverOtherDecl* const declp
= new AstCoverOtherDecl{nodep->fileline(), "v_line/" + m_modp->prettyName(),
"case", linesCov(m_state, nodep), 0};
m_modp->addStmtsp(declp);
nodep->addStmtsp(
newCoverInc(nodep->fileline(), declp, traceNameForLine(nodep, "case")));
}
}
}
@ -681,9 +687,13 @@ class CoverageVisitor final : public VNVisitor {
if (!nodep->coverincsp() && v3Global.opt.coverageUser()) {
// Note the name may be overridden by V3Assert processing
lineTrack(nodep);
nodep->addCoverincsp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover",
linesCov(m_state, nodep), 0,
m_beginHier + "_vlCoverageUserTrace"));
AstCoverOtherDecl* const declp
= new AstCoverOtherDecl{nodep->fileline(), "v_user/" + m_modp->prettyName(),
"cover", linesCov(m_state, nodep), 0};
declp->hier(m_beginHier);
m_modp->addStmtsp(declp);
nodep->addCoverincsp(
newCoverInc(nodep->fileline(), declp, m_beginHier + "_vlCoverageUserTrace"));
}
}
void visit(AstStop* nodep) override {
@ -755,8 +765,10 @@ class CoverageVisitor final : public VNVisitor {
}
comment += ") => ";
comment += (m_objective ? '1' : '0');
AstNode* const newp
= newCoverInc(fl, "", "v_expr", comment, "", 0, traceNameForLine(nodep, name));
AstCoverOtherDecl* const declp = new AstCoverOtherDecl{
nodep->fileline(), "v_expr/" + m_modp->prettyName(), comment, "", 0};
m_modp->addStmtsp(declp);
AstNode* const newp = newCoverInc(fl, declp, traceNameForLine(nodep, name));
UASSERT_OBJ(condp, nodep, "No terms in expression coverage branch");
AstIf* const ifp = new AstIf{fl, condp, newp, nullptr};
ifp->user2(true);

View File

@ -75,7 +75,7 @@ class CoverageJoinVisitor final : public VNVisitor {
// The CoverDecl the duplicate pointed to now needs to point to the
// original's data. I.e. the duplicate will get the coverage number
// from the non-duplicate
AstCoverDecl* const datadeclp = nodep->incp()->declp()->dataDeclThisp();
AstNodeCoverDecl* const datadeclp = nodep->incp()->declp()->dataDeclThisp();
removep->incp()->declp()->dataDeclp(datadeclp);
UINFO(8, " new " << removep->incp()->declp());
// Mark the found node as a duplicate of the first node

View File

@ -661,7 +661,7 @@ public:
}
iterateChildrenConst(nodep);
}
void visit(AstCoverDecl* nodep) override {
void visit(AstCoverOtherDecl* nodep) override {
putns(nodep, "vlSelf->__vlCoverInsert("); // As Declared in emitCoverageDecl
puts("&(vlSymsp->__Vcoverage[");
puts(cvtToStr(nodep->dataDeclThisp()->binNum()));
@ -689,15 +689,67 @@ public:
putsQuoted(nodep->linescov());
puts(");\n");
}
void visit(AstCoverToggleDecl* nodep) override {
putns(nodep, "vlSelf->__vlCoverToggleInsert("); // As Declared in emitCoverageDecl
puts(cvtToStr(nodep->range().right()));
puts(", ");
puts(cvtToStr(nodep->range().left()));
puts(", ");
puts(cvtToStr(nodep->range().ranged()));
puts(", ");
puts("&(vlSymsp->__Vcoverage[");
puts(cvtToStr(nodep->dataDeclThisp()->binNum()));
puts("])");
// If this isn't the first instantiation of this module under this
// design, don't really count the bucket, and rely on verilator_cov to
// aggregate counts. This is because Verilator combines all
// hierarchies itself, and if verilator_cov also did it, you'd end up
// with (number-of-instant) times too many counts in this bin.
puts(", first"); // Enable, passed from __Vconfigure parameter
puts(", ");
putsQuoted(protect(nodep->fileline()->filename()));
puts(", ");
puts(cvtToStr(nodep->fileline()->lineno()));
puts(", ");
puts(cvtToStr(nodep->fileline()->firstColumn()));
puts(", ");
putsQuoted((!nodep->hier().empty() ? "." : "")
+ protectWordsIf(nodep->hier(), nodep->protect()));
puts(", ");
putsQuoted(protectWordsIf(nodep->page(), nodep->protect()));
puts(", ");
putsQuoted(protectWordsIf(nodep->comment(), nodep->protect()));
puts(");\n");
}
void visit(AstCoverInc* nodep) override {
if (v3Global.opt.threads() > 1) {
putns(nodep, "vlSymsp->__Vcoverage[");
puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum()));
puts("].fetch_add(1, std::memory_order_relaxed);\n");
if (nodep->declp()->size() == 1) {
if (v3Global.opt.threads() > 1) {
putns(nodep, "vlSymsp->__Vcoverage[");
puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum()));
puts("].fetch_add(1, std::memory_order_relaxed);\n");
} else {
putns(nodep, "++(vlSymsp->__Vcoverage[");
puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum()));
puts("]);\n");
}
} else {
putns(nodep, "++(vlSymsp->__Vcoverage[");
puts("VL_COV_TOGGLE_CHG_");
if (v3Global.opt.threads() > 1) {
puts("MT_");
} else {
puts("ST_");
}
emitIQW(nodep->toggleExprp());
puts("(");
puts(cvtToStr(nodep->declp()->size()));
puts(", ");
puts("vlSymsp->__Vcoverage + ");
puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum()));
puts("]);\n");
puts(", ");
iterateConst(nodep->toggleExprp());
puts(", ");
iterateConst(nodep->toggleCovExprp());
puts(");\n");
}
}
void visit(AstDisableFork* nodep) override { putns(nodep, "vlProcess->disableFork();\n"); }

View File

@ -181,6 +181,14 @@ class EmitCHeader final : public EmitCConstInit {
"linescovp);\n");
}
if (v3Global.opt.coverageToggle() && !VN_IS(modp, Class)) {
decorateFirst(first, section);
puts("void __vlCoverToggleInsert(int begin, int end, bool ranged, ");
puts(v3Global.opt.threads() > 1 ? "std::atomic<uint32_t>" : "uint32_t");
puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n");
puts("const char* hierp, const char* pagep, const char* commentp);\n");
}
if (v3Global.opt.savable()) {
decorateFirst(first, section);
puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n");

View File

@ -103,7 +103,7 @@ class EmitCGatherDependencies final : VNVisitorConst {
addSelfDependency(nodep->selfPointer(), nodep->varp());
iterateChildrenConst(nodep);
}
void visit(AstCoverDecl* nodep) override {
void visit(AstNodeCoverDecl* nodep) override {
addSymsDependency();
iterateChildrenConst(nodep);
}
@ -307,17 +307,16 @@ class EmitCImp final : EmitCFunc {
splitSizeInc(10);
}
void emitCoverageImp() {
// Rather than putting out VL_COVER_INSERT calls directly, we do it via this
// function. This gets around gcc slowness constructing all of the template
// arguments.
if (v3Global.opt.coverage()) {
puts("\n// Coverage\n");
// Rather than putting out VL_COVER_INSERT calls directly, we do it via this
// function. This gets around gcc slowness constructing all of the template
// arguments.
puts("void " + prefixNameProtect(m_modp) + "::__vlCoverInsert(");
puts(v3Global.opt.threads() > 1 ? "std::atomic<uint32_t>" : "uint32_t");
puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n");
puts("const char* hierp, const char* pagep, const char* commentp, const char* "
"linescovp) "
"{\n");
"linescovp) {\n");
if (v3Global.opt.threads() > 1) {
puts("assert(sizeof(uint32_t) == sizeof(std::atomic<uint32_t>));\n");
puts("uint32_t* count32p = reinterpret_cast<uint32_t*>(countp);\n");
@ -343,6 +342,48 @@ class EmitCImp final : EmitCFunc {
puts("}\n");
splitSizeInc(10);
}
if (v3Global.opt.coverageToggle()) {
puts("\n// Toggle Coverage\n");
puts("void " + prefixNameProtect(m_modp) + "::__vlCoverToggleInsert(");
puts("int begin, int end, bool ranged, ");
puts(v3Global.opt.threads() > 1 ? "std::atomic<uint32_t>" : "uint32_t");
puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n");
puts("const char* hierp, const char* pagep, const char* commentp) {\n");
if (v3Global.opt.threads() > 1) {
puts("assert(sizeof(uint32_t) == sizeof(std::atomic<uint32_t>));\n");
}
puts("int step = (end >= begin) ? 1 : -1;\n");
// range is inclusive
puts("for (int i = begin; i != end + step; i += step) {\n");
if (v3Global.opt.threads() > 1) {
puts("uint32_t* count32p = reinterpret_cast<uint32_t*>(countp);\n");
} else {
puts("uint32_t* count32p = countp;\n");
}
// static doesn't need save-restore as is constant
puts("static uint32_t fake_zero_count = 0;\n");
puts("std::string fullhier = std::string{VerilatedModule::name()} + hierp;\n");
puts("if (!fullhier.empty() && fullhier[0] == '.') fullhier = fullhier.substr(1);\n");
puts("std::string commentWithIndex = commentp;\n");
puts("if (ranged) commentWithIndex += '[' + std::to_string(i) + ']';\n");
// Used for second++ instantiation of identical bin
puts("if (!enable) count32p = &fake_zero_count;\n");
puts("*count32p = 0;\n");
puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), VerilatedModule::name(), "
"count32p,");
puts(" \"filename\",filenamep,");
puts(" \"lineno\",lineno,");
puts(" \"column\",column,\n");
puts("\"hier\",fullhier,");
puts(" \"page\",pagep,");
puts(" \"comment\",commentWithIndex.c_str(),");
puts(" \"\", \"\");\n"); // linescov argument, but in toggle coverage it is always
// empty
puts("++countp;\n");
puts("}\n");
puts("}\n");
splitSizeInc(10);
}
}
void emitDestructorImp(const AstNodeModule* modp) {
puts("\n");

View File

@ -376,10 +376,11 @@ class EmitCSyms final : EmitCBaseVisitorConst {
iterateChildrenConst(nodep);
m_statVarScopeBytes += nodep->varp()->dtypep()->widthTotalBytes();
}
void visit(AstCoverDecl* nodep) override {
void visit(AstNodeCoverDecl* nodep) override {
// Assign numbers to all bins, so we know how big of an array to use
if (!nodep->dataDeclNullp()) { // else duplicate we don't need code for
nodep->binNum(m_coverBins++);
nodep->binNum(m_coverBins);
m_coverBins += nodep->size();
}
}
void visit(AstCFunc* nodep) override {

View File

@ -231,7 +231,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
putbs("continue");
if (!m_suppressSemi) puts(";\n");
}
void visit(AstCoverDecl*) override {} // N/A
void visit(AstNodeCoverDecl*) override {} // N/A
void visit(AstCoverInc*) override {} // N/A
void visit(AstCoverToggle*) override {} // N/A

View File

@ -459,7 +459,7 @@ class InlineRelinkVisitor final : public VNVisitor {
if (afterp) nodep->addScopeEntrp(afterp);
iterateChildren(nodep);
}
void visit(AstCoverDecl* nodep) override {
void visit(AstNodeCoverDecl* nodep) override {
// Fix path in coverage statements
nodep->hier(VString::dot(m_cellp->prettyName(), ".", nodep->hier()));
iterateChildren(nodep);

View File

@ -545,7 +545,7 @@ class UndrivenVisitor final : public VNVisitorConst {
void visit(AstPrimitive*) override {}
// Coverage artifacts etc shouldn't count as a sink
void visit(AstCoverDecl*) override {}
void visit(AstNodeCoverDecl*) override {}
void visit(AstCoverInc*) override {}
void visit(AstCoverToggle*) override {}
void visit(AstTraceDecl*) override {}

View File

@ -45,6 +45,15 @@
} str_queue_t;
str_queue_t str_queue;
typedef struct packed {
// verilator lint_off ASCRANGE
bit [3:5] x;
// verilator lint_on ASCRANGE
bit [0:0] y;
} str_bit_t;
%000002 str_bit_t str_bit;
%000002 str_bit_t [5:2] str_bit_arr;
alpha a1 (/*AUTOINST*/
// Outputs
.toggle_up (toggle_up),
@ -97,10 +106,16 @@
if (cyc == 3) begin
str_queue.q.push_back(1);
toggle <= '1;
str_bit.x <= '1;
str_bit.y <= '1;
str_bit_arr[4].x <= '1;
end
if (cyc == 4) begin
if (str_queue.q.size() != 1) $stop;
toggle <= '0;
str_bit.x[3] <= 0;
str_bit.y[0] <= 0;
str_bit_arr[4].x[3] <= 0;
end
else if (cyc == 10) begin
$write("*-* All Finished *-*\n");

View File

@ -21,7 +21,7 @@ test.inline_checks()
test.file_grep_not(test.obj_dir + "/coverage.dat", "largeish")
if test.vlt_all:
test.file_grep(test.stats, r'Coverage, Toggle points joined\s+(\d+)', 27)
test.file_grep(test.stats, r'Coverage, Toggle points joined\s+(\d+)', 13)
test.run(cmd=[
os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage",

View File

@ -44,6 +44,15 @@ module t (/*AUTOARG*/
} str_queue_t;
str_queue_t str_queue;
typedef struct packed {
// verilator lint_off ASCRANGE
bit [3:5] x;
// verilator lint_on ASCRANGE
bit [0:0] y;
} str_bit_t;
str_bit_t str_bit;
str_bit_t [5:2] str_bit_arr;
alpha a1 (/*AUTOINST*/
// Outputs
.toggle_up (toggle_up),
@ -96,10 +105,16 @@ module t (/*AUTOARG*/
if (cyc == 3) begin
str_queue.q.push_back(1);
toggle <= '1;
str_bit.x <= '1;
str_bit.y <= '1;
str_bit_arr[4].x <= '1;
end
if (cyc == 4) begin
if (str_queue.q.size() != 1) $stop;
toggle <= '0;
str_bit.x[3] <= 0;
str_bit.y[0] <= 0;
str_bit_arr[4].x[3] <= 0;
end
else if (cyc == 10) begin
$write("*-* All Finished *-*\n");

View File

@ -62,6 +62,35 @@
} str_queue_t;
str_queue_t str_queue;
typedef struct packed {
// verilator lint_off ASCRANGE
bit [3:5] x;
// verilator lint_on ASCRANGE
bit [0:0] y;
} str_bit_t;
%000002 str_bit_t str_bit;
-000002 point: comment=str_bit.x[3] hier=top.t
-000001 point: comment=str_bit.x[4] hier=top.t
-000001 point: comment=str_bit.x[5] hier=top.t
-000002 point: comment=str_bit.y[0] hier=top.t
%000002 str_bit_t [5:2] str_bit_arr;
-000000 point: comment=str_bit_arr[2].x[3] hier=top.t
-000000 point: comment=str_bit_arr[2].x[4] hier=top.t
-000000 point: comment=str_bit_arr[2].x[5] hier=top.t
-000000 point: comment=str_bit_arr[2].y[0] hier=top.t
-000000 point: comment=str_bit_arr[3].x[3] hier=top.t
-000000 point: comment=str_bit_arr[3].x[4] hier=top.t
-000000 point: comment=str_bit_arr[3].x[5] hier=top.t
-000000 point: comment=str_bit_arr[3].y[0] hier=top.t
-000002 point: comment=str_bit_arr[4].x[3] hier=top.t
-000001 point: comment=str_bit_arr[4].x[4] hier=top.t
-000001 point: comment=str_bit_arr[4].x[5] hier=top.t
-000000 point: comment=str_bit_arr[4].y[0] hier=top.t
-000000 point: comment=str_bit_arr[5].x[3] hier=top.t
-000000 point: comment=str_bit_arr[5].x[4] hier=top.t
-000000 point: comment=str_bit_arr[5].x[5] hier=top.t
-000000 point: comment=str_bit_arr[5].y[0] hier=top.t
alpha a1 (/*AUTOINST*/
// Outputs
.toggle_up (toggle_up),
@ -138,10 +167,16 @@
if (cyc == 3) begin
str_queue.q.push_back(1);
toggle <= '1;
str_bit.x <= '1;
str_bit.y <= '1;
str_bit_arr[4].x <= '1;
end
if (cyc == 4) begin
if (str_queue.q.size() != 1) $stop;
toggle <= '0;
str_bit.x[3] <= 0;
str_bit.y[0] <= 0;
str_bit_arr[4].x[3] <= 0;
end
else if (cyc == 10) begin
$write("*-* All Finished *-*\n");

View File

@ -16,6 +16,6 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", "--coverage", "--
test.execute()
if test.vlt:
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 620)
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 478)
test.passes()