mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
This commit is contained in:
@@ -39,26 +39,34 @@ VLCOVGEN_ITEM("'name':'per_instance','short':'P', 'group':1, 'default':0, 'd
|
||||
VLCOVGEN_ITEM("'name':'thresh', 'short':'s', 'group':1, 'default':None, 'descr':'Number of hits to consider covered (aka at_least)'")
|
||||
VLCOVGEN_ITEM("'name':'type', 'short':'t', 'group':1, 'default':'', 'descr':'Type of coverage (block, line, fsm, etc)'")
|
||||
// Bin attributes
|
||||
VLCOVGEN_ITEM("'name':'cross_bins', 'short':'Cb', 'group':0, 'default':'', 'descr':'Comma-separated per-dimension bin names for cross coverage points'")
|
||||
VLCOVGEN_ITEM("'name':'bin', 'short':'B', 'group':0, 'default':'', 'descr':'Bin name for covergroup coverage points'")
|
||||
VLCOVGEN_ITEM("'name':'bin_type', 'short':'Bt', 'group':0, 'default':'', 'descr':'Kind of a covergroup bin that is not coverable: ignore, illegal, or default'")
|
||||
VLCOVGEN_ITEM("'name':'comment', 'short':'o', 'group':0, 'default':'', 'descr':'Textual description for the item'")
|
||||
VLCOVGEN_ITEM("'name':'cross', 'short':'C', 'group':0, 'default':0, 'descr':'True for cross coverage points'")
|
||||
VLCOVGEN_ITEM("'name':'cross_bins', 'short':'Cb', 'group':0, 'default':'', 'descr':'Comma-separated per-dimension bin names for cross coverage points'")
|
||||
VLCOVGEN_ITEM("'name':'fsm_from', 'short':'Ff', 'group':0, 'default':'', 'descr':'FSM source state name for structured FSM coverage points'")
|
||||
VLCOVGEN_ITEM("'name':'fsm_tag', 'short':'Fg', 'group':0, 'default':'', 'descr':'FSM point tag such as reset, reset_include, or default'")
|
||||
VLCOVGEN_ITEM("'name':'fsm_to', 'short':'Ft', 'group':0, 'default':'', 'descr':'FSM destination state name for structured FSM coverage points'")
|
||||
VLCOVGEN_ITEM("'name':'fsm_var', 'short':'Fv', 'group':0, 'default':'', 'descr':'FSM state variable name for structured FSM coverage points'")
|
||||
VLCOVGEN_ITEM("'name':'group_weight','short':'Gw', 'group':0, 'default':None, 'descr':'For totaling covergroups, type_option.weight of the covergroup of this item'")
|
||||
VLCOVGEN_ITEM("'name':'hier', 'short':'h', 'group':0, 'default':'', 'descr':'Hierarchy path name for the item'")
|
||||
VLCOVGEN_ITEM("'name':'lineno', 'short':'l', 'group':0, 'default':0, 'descr':'Line number for the item'")
|
||||
VLCOVGEN_ITEM("'name':'weight', 'short':'w', 'group':0, 'default':None, 'descr':'For totaling items, weight of this item'")
|
||||
// clang-format on
|
||||
|
||||
// VLCOVGEN_CIK_AUTO_EDIT_BEGIN
|
||||
#define VL_CIK_BIN "B"
|
||||
#define VL_CIK_BIN_TYPE "Bt"
|
||||
#define VL_CIK_COLUMN "n"
|
||||
#define VL_CIK_COMMENT "o"
|
||||
#define VL_CIK_CROSS "C"
|
||||
#define VL_CIK_CROSS_BINS "Cb"
|
||||
#define VL_CIK_FILENAME "f"
|
||||
#define VL_CIK_FSM_FROM "Ff"
|
||||
#define VL_CIK_FSM_TAG "Fg"
|
||||
#define VL_CIK_FSM_TO "Ft"
|
||||
#define VL_CIK_FSM_VAR "Fv"
|
||||
#define VL_CIK_GROUP_WEIGHT "Gw"
|
||||
#define VL_CIK_HIER "h"
|
||||
#define VL_CIK_LINENO "l"
|
||||
#define VL_CIK_LINESCOV "S"
|
||||
@@ -77,14 +85,18 @@ public:
|
||||
// Return the short key code for a given a long coverage key
|
||||
static std::string shortKey(const std::string& key) VL_PURE {
|
||||
// VLCOVGEN_SHORT_AUTO_EDIT_BEGIN
|
||||
if (key == "bin") return VL_CIK_BIN;
|
||||
if (key == "bin_type") return VL_CIK_BIN_TYPE;
|
||||
if (key == "column") return VL_CIK_COLUMN;
|
||||
if (key == "comment") return VL_CIK_COMMENT;
|
||||
if (key == "cross") return VL_CIK_CROSS;
|
||||
if (key == "cross_bins") return VL_CIK_CROSS_BINS;
|
||||
if (key == "filename") return VL_CIK_FILENAME;
|
||||
if (key == "fsm_from") return VL_CIK_FSM_FROM;
|
||||
if (key == "fsm_tag") return VL_CIK_FSM_TAG;
|
||||
if (key == "fsm_to") return VL_CIK_FSM_TO;
|
||||
if (key == "fsm_var") return VL_CIK_FSM_VAR;
|
||||
if (key == "group_weight") return VL_CIK_GROUP_WEIGHT;
|
||||
if (key == "hier") return VL_CIK_HIER;
|
||||
if (key == "lineno") return VL_CIK_LINENO;
|
||||
if (key == "linescov") return VL_CIK_LINESCOV;
|
||||
|
||||
@@ -1126,7 +1126,20 @@ uint32_t VlCoverpoint::binCount() const {
|
||||
std::string VlCoverpoint::binName(uint32_t i) const { return declaredBinName(reportedBin(i)); }
|
||||
|
||||
#if VM_COVERAGE
|
||||
void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* page) {
|
||||
// Key of the coverage computation (IEEE 1800-2023 19.11), or "", which leaves the key out, for
|
||||
// the default value 1, so that the records of the common case keep their names
|
||||
static const char* _vl_cov_score_key(const char* keyp, uint32_t value) VL_PURE {
|
||||
return value == 1 ? "" : keyp;
|
||||
}
|
||||
|
||||
void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* page,
|
||||
uint32_t itemWeight, uint32_t groupWeight) {
|
||||
const std::string threshStr = std::to_string(m_atLeast);
|
||||
const std::string weightStr = std::to_string(itemWeight);
|
||||
const std::string groupWeightStr = std::to_string(groupWeight);
|
||||
const char* const threshKeyp = _vl_cov_score_key("thresh", m_atLeast);
|
||||
const char* const weightKeyp = _vl_cov_score_key("weight", itemWeight);
|
||||
const char* const groupWeightKeyp = _vl_cov_score_key("group_weight", groupWeight);
|
||||
for (uint32_t reported = 0; reported < binCount(); ++reported) {
|
||||
const uint32_t i = reportedBin(reported);
|
||||
const VlCovNamer& nm = namerFor(i);
|
||||
@@ -1135,18 +1148,16 @@ void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* pa
|
||||
const std::string full = m_hier + "." + binp;
|
||||
const std::string lineStr = std::to_string(nm.line());
|
||||
const std::string colStr = std::to_string(nm.col());
|
||||
if (kind == VlCovBinKind::KIND_NORMAL) {
|
||||
VL_COVER_INSERT(covcontextp, full.c_str(), &m_counts[i], "page", page, "filename",
|
||||
nm.file(), "lineno", lineStr.c_str(), "column", colStr.c_str(), "bin",
|
||||
binp.c_str());
|
||||
} else {
|
||||
const char* const binType = kind == VlCovBinKind::KIND_IGNORE ? "ignore"
|
||||
: kind == VlCovBinKind::KIND_ILLEGAL ? "illegal"
|
||||
: "default";
|
||||
VL_COVER_INSERT(covcontextp, full.c_str(), &m_counts[i], "page", page, "filename",
|
||||
nm.file(), "lineno", lineStr.c_str(), "column", colStr.c_str(), "bin",
|
||||
binp.c_str(), "bin_type", binType);
|
||||
}
|
||||
// An empty key leaves out the bin type of a Normal bin
|
||||
const char* const binType = kind == VlCovBinKind::KIND_NORMAL ? ""
|
||||
: kind == VlCovBinKind::KIND_IGNORE ? "ignore"
|
||||
: kind == VlCovBinKind::KIND_ILLEGAL ? "illegal"
|
||||
: "default";
|
||||
VL_COVER_INSERT(covcontextp, full.c_str(), &m_counts[i], "page", page, "filename",
|
||||
nm.file(), "lineno", lineStr.c_str(), "column", colStr.c_str(), "bin",
|
||||
binp.c_str(), binType[0] ? "bin_type" : "", binType, threshKeyp,
|
||||
threshStr.c_str(), weightKeyp, weightStr.c_str(), groupWeightKeyp,
|
||||
groupWeightStr.c_str());
|
||||
}
|
||||
}
|
||||
#endif // VM_COVERAGE
|
||||
@@ -1422,9 +1433,15 @@ std::string VlCoverCross::autoBinName(uint32_t flat) const {
|
||||
}
|
||||
|
||||
#if VM_COVERAGE
|
||||
void VlCoverCross::registerBins(VerilatedCovContext* covcontextp, const char* page) {
|
||||
void VlCoverCross::registerBins(VerilatedCovContext* covcontextp, const char* page,
|
||||
uint32_t itemWeight, uint32_t groupWeight) {
|
||||
const std::string lineStr = std::to_string(m_line);
|
||||
const std::string colStr = std::to_string(m_col);
|
||||
// A cross bin is covered once hit, which needs no option.at_least
|
||||
const std::string weightStr = std::to_string(itemWeight);
|
||||
const std::string groupWeightStr = std::to_string(groupWeight);
|
||||
const char* const weightKeyp = _vl_cov_score_key("weight", itemWeight);
|
||||
const char* const groupWeightKeyp = _vl_cov_score_key("group_weight", groupWeight);
|
||||
const uint32_t explicitCount
|
||||
= hasExplicitBins() ? static_cast<uint32_t>(m_explicitp->bins.size()) : 0;
|
||||
// Use the same indexed names for registration and the runtime read interface.
|
||||
@@ -1435,18 +1452,15 @@ void VlCoverCross::registerBins(VerilatedCovContext* covcontextp, const char* pa
|
||||
Bin& userBin = m_explicitp->bins[i];
|
||||
const std::string binLineStr = std::to_string(userBin.line);
|
||||
const std::string binColStr = std::to_string(userBin.col);
|
||||
if (userBin.kind == VlCovBinKind::KIND_NORMAL) {
|
||||
VL_COVER_INSERT(covcontextp, full.c_str(), &userBin.count, "page", page,
|
||||
"filename", userBin.filep, "lineno", binLineStr.c_str(), "column",
|
||||
binColStr.c_str(), "bin", bin.c_str(), "cross", "1");
|
||||
} else {
|
||||
const char* const binType
|
||||
= userBin.kind == VlCovBinKind::KIND_IGNORE ? "ignore" : "illegal";
|
||||
VL_COVER_INSERT(covcontextp, full.c_str(), &userBin.count, "page", page,
|
||||
"filename", userBin.filep, "lineno", binLineStr.c_str(), "column",
|
||||
binColStr.c_str(), "bin", bin.c_str(), "cross", "1", "bin_type",
|
||||
binType);
|
||||
}
|
||||
// An empty key leaves out the bin type of a Normal bin
|
||||
const char* const binType = userBin.kind == VlCovBinKind::KIND_NORMAL ? ""
|
||||
: userBin.kind == VlCovBinKind::KIND_IGNORE ? "ignore"
|
||||
: "illegal";
|
||||
VL_COVER_INSERT(covcontextp, full.c_str(), &userBin.count, "page", page, "filename",
|
||||
userBin.filep, "lineno", binLineStr.c_str(), "column",
|
||||
binColStr.c_str(), "bin", bin.c_str(), "cross", "1",
|
||||
binType[0] ? "bin_type" : "", binType, weightKeyp, weightStr.c_str(),
|
||||
groupWeightKeyp, groupWeightStr.c_str());
|
||||
continue;
|
||||
}
|
||||
const uint32_t flat = autoIndex(i - explicitCount);
|
||||
@@ -1460,7 +1474,8 @@ void VlCoverCross::registerBins(VerilatedCovContext* covcontextp, const char* pa
|
||||
}
|
||||
VL_COVER_INSERT(covcontextp, full.c_str(), &m_flatCountsp[flat], "page", page, "filename",
|
||||
m_file, "lineno", lineStr.c_str(), "column", colStr.c_str(), "bin",
|
||||
bin.c_str(), "cross", "1", "cross_bins", crossBins.c_str());
|
||||
bin.c_str(), "cross", "1", "cross_bins", crossBins.c_str(), weightKeyp,
|
||||
weightStr.c_str(), groupWeightKeyp, groupWeightStr.c_str());
|
||||
}
|
||||
}
|
||||
#endif // VM_COVERAGE
|
||||
|
||||
@@ -165,7 +165,12 @@ public:
|
||||
int line, int col) {
|
||||
addNamer(set, count, VlCovBinNaming::Numbered, name, file, line, col);
|
||||
}
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
/// Register the bins in the coverage database, with what verilator_coverage needs to
|
||||
/// compute coverage (IEEE 1800-2023 19.11): option.at_least, and the weights of the
|
||||
/// coverpoint, itemWeight, and of its covergroup, groupWeight. The weights are those of
|
||||
/// every instance, as the database merges the instances.
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page, uint32_t itemWeight,
|
||||
uint32_t groupWeight);
|
||||
|
||||
/// Configure construction-time value metadata for exclusions and cross selections.
|
||||
void valueType(uint32_t bits, bool isSigned);
|
||||
@@ -433,7 +438,9 @@ public:
|
||||
const char* filep, int line, int col);
|
||||
/// Retain only automatic cross bins not selected by any explicit bin.
|
||||
virtual void finalizeBins();
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page);
|
||||
/// Register the bins in the coverage database; see VlCoverpoint::registerBins().
|
||||
void registerBins(VerilatedCovContext* covcontextp, const char* page, uint32_t itemWeight,
|
||||
uint32_t groupWeight);
|
||||
|
||||
// ---- hot path (from generated sample(), after all coverpoints sampled) ----
|
||||
/// Sample automatic and explicit bins, optionally applying per-bin iff guards.
|
||||
|
||||
+1
-1
@@ -886,7 +886,7 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
|
||||
macro(COVERGROUP_INST_P, "p", PURE, "") \
|
||||
macro(COVERGROUP_LEND_WEIGHT, "lendWeight", false, "rr") \
|
||||
macro(COVERGROUP_RECORD_HIT, "recordHit", false, "r") \
|
||||
macro(COVERGROUP_REGISTER_BINS, "registerBins", false, "rr") \
|
||||
macro(COVERGROUP_REGISTER_BINS, "registerBins", false, "rrrr") \
|
||||
macro(COVERGROUP_SAMPLE, "sample", false, "") \
|
||||
macro(COVERGROUP_SAMPLE_IFFS, "sample", false, "r") \
|
||||
macro(COVERGROUP_SELECT_ALL, "selectAll", false, "") \
|
||||
|
||||
+35
-5
@@ -247,6 +247,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
std::map<std::string, AstCoverpoint*> m_coverpointMap; // Name -> coverpoint for fast lookup
|
||||
std::vector<AstCoverCross*> m_coverCrosses; // Cross coverage items in current covergroup
|
||||
std::vector<AstCgOptionAssign*> m_cgOptions; // Covergroup-level weights, before lowering
|
||||
uint32_t m_cgTypeWeight = 1; // The covergroup's type_option.weight, a constant
|
||||
|
||||
struct EmbeddedEventTrigger final {
|
||||
FileLine* eventFl; // Clocking-event source location
|
||||
@@ -342,6 +343,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
UASSERT_OBJ(optp->optType() == VCoverOptionType::WEIGHT, optp,
|
||||
"Unexpected covergroup option reaching V3Covergroup");
|
||||
FileLine* const fl = optp->fileline();
|
||||
// V3Width left type_option.weight a non-negative constant
|
||||
if (optp->typeOption()) m_cgTypeWeight = VN_AS(optp->valuep(), Const)->toUInt();
|
||||
AstAssign* const assignp = new AstAssign{
|
||||
fl, newWeightSel(fl, optionVar(optp->typeOption()), VAccess::WRITE),
|
||||
optp->valuep()->unlinkFrBack()};
|
||||
@@ -356,6 +359,27 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
m_cgOptions.clear();
|
||||
}
|
||||
|
||||
// The weight of an item in the coverage database, which merges the instances: its
|
||||
// option.weight if a constant, and so of every instance; else its type_option.weight, the
|
||||
// weight of type coverage merged over the instances (IEEE 1800-2023 19.7.1)
|
||||
static uint32_t itemDatabaseWeight(AstNode* optionsp) {
|
||||
const AstNodeExpr* weightp = nullptr; // The option.weight in effect
|
||||
uint32_t typeWeight = 1;
|
||||
for (AstNode* nodep = optionsp; nodep; nodep = nodep->nextp()) {
|
||||
const AstCoverOption* const optp = VN_AS(nodep, CoverOption);
|
||||
if (!(optp->optType() == VCoverOptionType::WEIGHT)) continue;
|
||||
// V3Width left type_option.weight a non-negative constant
|
||||
if (optp->typeOption()) {
|
||||
typeWeight = VN_AS(optp->valuep(), Const)->toUInt();
|
||||
} else {
|
||||
weightp = optp->valuep();
|
||||
}
|
||||
}
|
||||
if (!weightp) return 1;
|
||||
if (const AstConst* const constp = VN_CAST(weightp, Const)) return constp->toUInt();
|
||||
return typeWeight;
|
||||
}
|
||||
|
||||
// Configure an item's option.weight, its weight in instance coverage (IEEE 1800-2023
|
||||
// 19.11). type_option.weight only weighs type coverage merged over the instances, which
|
||||
// type_option.merge_instances would select; without that, it has no effect.
|
||||
@@ -385,6 +409,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
m_excludedVars.clear();
|
||||
m_droppedCrosses.clear();
|
||||
m_cgInstVarp = nullptr;
|
||||
m_cgTypeWeight = 1;
|
||||
|
||||
lowerCovergroupOptions();
|
||||
|
||||
@@ -2375,10 +2400,13 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
if (v3Global.opt.coverage()) {
|
||||
const std::string page
|
||||
= VIdProtect::protectIf("v_covergroup/" + m_covergroupp->name(), prot);
|
||||
m_constructorp->addStmtsp(itemCall(fl, cpVarp, VCMethod::COVERGROUP_REGISTER_BINS,
|
||||
{ctext(fl, "vlSymsp->_vm_contextp__->coveragep()"),
|
||||
ctext(fl, quoted(page))})
|
||||
->makeStmt());
|
||||
m_constructorp->addStmtsp(
|
||||
itemCall(fl, cpVarp, VCMethod::COVERGROUP_REGISTER_BINS,
|
||||
{ctext(fl, "vlSymsp->_vm_contextp__->coveragep()"),
|
||||
ctext(fl, quoted(page)),
|
||||
cnum(fl, itemDatabaseWeight(coverpointp->optionsp())),
|
||||
cnum(fl, m_cgTypeWeight)})
|
||||
->makeStmt());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3678,7 +3706,9 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
= VIdProtect::protectIf("v_covergroup/" + m_covergroupp->name(), prot);
|
||||
m_constructorp->addStmtsp(itemCall(fl, cxVarp, VCMethod::COVERGROUP_REGISTER_BINS,
|
||||
{ctext(fl, "vlSymsp->_vm_contextp__->coveragep()"),
|
||||
ctext(fl, quoted(page))})
|
||||
ctext(fl, quoted(page)),
|
||||
cnum(fl, itemDatabaseWeight(crossp->optionsp())),
|
||||
cnum(fl, m_cgTypeWeight)})
|
||||
->makeStmt());
|
||||
}
|
||||
|
||||
|
||||
+8
-4
@@ -66,11 +66,15 @@ public:
|
||||
string hier() const { return keyExtract(VL_CIK_HIER, m_name.c_str()); }
|
||||
string page() const { return keyExtract("page", m_name.c_str()); }
|
||||
string type() const { return typeExtract(m_name.c_str()); }
|
||||
// Covergroup-specific key accessors (long keys, no short-key alias)
|
||||
string bin() const { return keyExtract("bin", m_name.c_str()); }
|
||||
string binType() const { return keyExtract("bin_type", m_name.c_str()); }
|
||||
bool isCross() const { return !keyExtract("cross", m_name.c_str()).empty(); }
|
||||
// Covergroup-specific key accessors
|
||||
string bin() const { return keyExtract(VL_CIK_BIN, m_name.c_str()); }
|
||||
string binType() const { return keyExtract(VL_CIK_BIN_TYPE, m_name.c_str()); }
|
||||
bool isCross() const { return !keyExtract(VL_CIK_CROSS, m_name.c_str()).empty(); }
|
||||
string crossBins() const { return keyExtract(VL_CIK_CROSS_BINS, m_name.c_str()); }
|
||||
// The covergroup's type_option.weight; "" for the default 1
|
||||
string groupWeight() const { return keyExtract(VL_CIK_GROUP_WEIGHT, m_name.c_str()); }
|
||||
// The coverpoint's or cross's option.weight; "" for the default 1
|
||||
string weight() const { return keyExtract(VL_CIK_WEIGHT, m_name.c_str()); }
|
||||
string thresh() const {
|
||||
// string as maybe ""
|
||||
return keyExtract(VL_CIK_THRESH, m_name.c_str());
|
||||
|
||||
+283
-20
@@ -41,11 +41,42 @@ namespace {
|
||||
// fields already exposed by VlcPoint; they do not affect coverage point
|
||||
// identity, merging, or .dat writing.
|
||||
|
||||
// Map coverage type to (covered points, total points).
|
||||
using TypeTally = std::map<std::string, std::pair<uint64_t, uint64_t>>;
|
||||
// Coverage of a report row: its covered and total points, and for covergroups, their
|
||||
// coverage, which weights coverpoints and crosses rather than counting bins
|
||||
class Tally final {
|
||||
// MEMBERS
|
||||
uint64_t m_hit = 0; // Covered points
|
||||
uint64_t m_total = 0; // Points
|
||||
bool m_scored = false; // Coverage is m_score, not the ratio of the points
|
||||
double m_score = 0.0; // Percent coverage, if m_scored
|
||||
|
||||
public:
|
||||
// ACCESSORS
|
||||
uint64_t hit() const { return m_hit; }
|
||||
uint64_t total() const { return m_total; }
|
||||
bool scored() const { return m_scored; }
|
||||
double score() const { return m_score; }
|
||||
void score(double value) {
|
||||
m_scored = true;
|
||||
m_score = value;
|
||||
}
|
||||
// METHODS
|
||||
void addPoint(bool covered) {
|
||||
if (covered) ++m_hit;
|
||||
++m_total;
|
||||
}
|
||||
void addPoints(const Tally& other) {
|
||||
m_hit += other.m_hit;
|
||||
m_total += other.m_total;
|
||||
}
|
||||
};
|
||||
|
||||
// Map coverage type to its tally.
|
||||
using TypeTally = std::map<std::string, Tally>;
|
||||
|
||||
static constexpr const char* const s_orderedTypes[]
|
||||
= {"line", "toggle", "branch", "expr", "fsm_state", "fsm_arc"};
|
||||
static constexpr const char* const s_covergroupType = "covergroup";
|
||||
static constexpr size_t s_summaryIndent = 2;
|
||||
static constexpr size_t s_reportRowIndent = 4;
|
||||
|
||||
@@ -96,41 +127,243 @@ string duName(const VlcPoint& point) {
|
||||
}
|
||||
|
||||
void tallyPoint(TypeTally& tally, const string& type, uint64_t count) {
|
||||
std::pair<uint64_t, uint64_t>& entry = tally[type];
|
||||
if (count > 0) ++entry.first;
|
||||
++entry.second;
|
||||
tally[type].addPoint(count > 0);
|
||||
}
|
||||
|
||||
// Return percentage as a string, handling 100%, and protecting from div zero.
|
||||
string pctString(uint64_t hit, uint64_t total) {
|
||||
double value = 0.0;
|
||||
if (total) value = 100.0 * static_cast<double>(hit) / static_cast<double>(total);
|
||||
if (hit < total && value > 99.9) value = 99.9; // Presumes precision 1
|
||||
// Keep the percentage calculation in one place so flat summaries and hierarchy
|
||||
// reports cannot drift in zero-total handling.
|
||||
double pct(uint64_t hit, uint64_t total) {
|
||||
return total ? (100.0 * static_cast<double>(hit) / static_cast<double>(total)) : 0.0;
|
||||
}
|
||||
|
||||
// Return a percentage as a string, handling 100%: coverage not complete shows below it.
|
||||
string pctValueString(double value, bool complete) {
|
||||
if (!complete && value > 99.9) value = 99.9; // Presumes precision 1
|
||||
std::stringstream os;
|
||||
os << std::fixed << std::setprecision(1) // 1 matters, see above
|
||||
<< value << "%";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
// Return percentage as a string, handling 100%, and protecting from div zero.
|
||||
string pctString(uint64_t hit, uint64_t total) {
|
||||
return pctValueString(pct(hit, total), hit >= total);
|
||||
}
|
||||
|
||||
// Covergroup coverage (IEEE 1800-2023 19.11). An item, a coverpoint or cross, covers the
|
||||
// ratio of its bins that reached option.at_least, excluding ignore, illegal and default bins;
|
||||
// a covergroup, the average of its items, weighted by their weight in the records (a constant
|
||||
// option.weight, else type_option.weight); and the covergroups together, their average
|
||||
// weighted by type_option.weight. The coverage database merges the instances of a covergroup
|
||||
// type, so this is the coverage of the merged instances, which is what get_coverage() returns
|
||||
// for a single instance.
|
||||
|
||||
// A coverable bin of a coverpoint or cross, of all its records
|
||||
class CgBin final {
|
||||
// MEMBERS
|
||||
string m_name; // Name of the bin
|
||||
uint64_t m_count = 0; // Hits, of all the records
|
||||
uint64_t m_atLeast = 0; // option.at_least, the largest of the records
|
||||
|
||||
public:
|
||||
// ACCESSORS
|
||||
const string& name() const { return m_name; }
|
||||
bool covered() const { return m_count >= m_atLeast; }
|
||||
// METHODS
|
||||
void addRecord(const string& name, uint64_t count, uint64_t atLeast) {
|
||||
m_name = name;
|
||||
m_count += count;
|
||||
m_atLeast = std::max(m_atLeast, atLeast);
|
||||
}
|
||||
};
|
||||
|
||||
// A coverpoint or cross
|
||||
class CgItem final {
|
||||
// MEMBERS
|
||||
uint64_t m_weight = 0; // The weight of the records, the largest
|
||||
std::map<string, CgBin> m_bins; // Coverable bins, by binIdentity()
|
||||
|
||||
public:
|
||||
// ACCESSORS
|
||||
uint64_t weight() const { return m_weight; }
|
||||
const std::map<string, CgBin>& bins() const { return m_bins; }
|
||||
// METHODS
|
||||
void addRecord(uint64_t weight) { m_weight = std::max(m_weight, weight); }
|
||||
CgBin& findNewBin(const string& identity) { return m_bins[identity]; }
|
||||
};
|
||||
|
||||
// A covergroup
|
||||
class CgGroup final {
|
||||
// MEMBERS
|
||||
string m_hier; // Hierarchy above the items
|
||||
uint64_t m_weight = 0; // type_option.weight, the largest of the records
|
||||
std::map<string, CgItem> m_items; // By hierarchy
|
||||
|
||||
public:
|
||||
// ACCESSORS
|
||||
const string& hier() const { return m_hier; }
|
||||
uint64_t weight() const { return m_weight; }
|
||||
const std::map<string, CgItem>& items() const { return m_items; }
|
||||
// METHODS
|
||||
void addRecord(const string& hier, uint64_t weight) {
|
||||
m_hier = hier;
|
||||
m_weight = std::max(m_weight, weight);
|
||||
}
|
||||
CgItem& findNewItem(const string& hier) { return m_items[hier]; }
|
||||
};
|
||||
using CgGroups = std::map<string, CgGroup>; // By page
|
||||
|
||||
// A number of a record, or 1 if the record leaves it out
|
||||
uint64_t keyNumber(const string& value) {
|
||||
return value.empty() ? 1 : std::strtoull(value.c_str(), nullptr, 10);
|
||||
}
|
||||
|
||||
// If a field of a record's name, '\001<key>\002<value>', has a key of the coverage computation
|
||||
bool isScoreField(const string& field) {
|
||||
for (const char* const keyp : {VL_CIK_THRESH, VL_CIK_WEIGHT, VL_CIK_GROUP_WEIGHT}) {
|
||||
const string prefix = string{"\001"} + keyp + "\002";
|
||||
if (field.compare(0, prefix.size(), prefix) == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The name of a bin's record without the keys of the coverage computation, which the records of
|
||||
// a bin may differ in, so identifying the bin. Its name does not: covergroups of distinct scopes
|
||||
// may share a name, as may cross bins, of the names of their coverpoints' bins joined.
|
||||
string binIdentity(const string& recordName) {
|
||||
string identity;
|
||||
string::size_type start = 0;
|
||||
while (start < recordName.size()) {
|
||||
string::size_type end = recordName.find('\001', start + 1);
|
||||
if (end == string::npos) end = recordName.size();
|
||||
const string field = recordName.substr(start, end - start);
|
||||
if (!isScoreField(field)) identity += field;
|
||||
start = end;
|
||||
}
|
||||
return identity;
|
||||
}
|
||||
|
||||
bool isCovergroup(const VlcPoint& point) { return point.type() == s_covergroupType; }
|
||||
|
||||
CgGroups covergroups(VlcPoints& points) {
|
||||
CgGroups groups;
|
||||
for (const VlcPoints::ByName::value_type& i : points) {
|
||||
const VlcPoint& pt = points.pointNumber(i.second);
|
||||
if (!isCovergroup(pt)) continue;
|
||||
// Named '<covergroup>.<item>.<bin>', where only the bin, of its own key, may have dots
|
||||
const string hier = pt.hier();
|
||||
string bin = pt.bin();
|
||||
if (bin.empty() || bin.size() >= hier.size()) bin = hier.substr(hier.rfind('.') + 1);
|
||||
const string itemHier
|
||||
= hier.substr(0, hier.size() - std::min(hier.size(), bin.size() + 1));
|
||||
CgGroup& group = groups[pt.page()];
|
||||
group.addRecord(itemHier.substr(0, itemHier.rfind('.')), keyNumber(pt.groupWeight()));
|
||||
CgItem& item = group.findNewItem(itemHier);
|
||||
item.addRecord(keyNumber(pt.weight()));
|
||||
if (!pt.binType().empty()) continue; // Not coverable
|
||||
item.findNewBin(binIdentity(pt.name())).addRecord(bin, pt.count(), keyNumber(pt.thresh()));
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
Tally itemTally(const CgItem& item) {
|
||||
Tally tally;
|
||||
for (const std::pair<const string, CgBin>& bin : item.bins()) {
|
||||
tally.addPoint(bin.second.covered());
|
||||
}
|
||||
// Without bins, 0.0, or 100.0 if of zero weight (IEEE 1800-2023 19.11.1)
|
||||
tally.score(tally.total() ? pct(tally.hit(), tally.total()) : item.weight() ? 0.0 : 100.0);
|
||||
return tally;
|
||||
}
|
||||
|
||||
// The tally of a covergroup; contributesp, if its items have weight and bins, so that it counts
|
||||
// in the coverage of covergroups together
|
||||
Tally groupTally(const CgGroup& group, bool* contributesp = nullptr) {
|
||||
Tally tally;
|
||||
double weighted = 0.0;
|
||||
double weights = 0.0;
|
||||
for (const std::pair<const string, CgItem>& it : group.items()) {
|
||||
const Tally item = itemTally(it.second);
|
||||
tally.addPoints(item);
|
||||
if (!item.total()) continue; // Excluded from both sums
|
||||
weighted += static_cast<double>(it.second.weight()) * item.score();
|
||||
weights += static_cast<double>(it.second.weight());
|
||||
}
|
||||
if (contributesp) *contributesp = weights != 0.0;
|
||||
// Without items of weight and bins, 0.0, or 100.0 if of zero weight
|
||||
tally.score(weights != 0.0 ? weighted / weights : group.weight() ? 0.0 : 100.0);
|
||||
return tally;
|
||||
}
|
||||
|
||||
Tally groupsTally(const std::vector<const CgGroup*>& groups) {
|
||||
Tally tally;
|
||||
double weighted = 0.0;
|
||||
double weights = 0.0;
|
||||
bool anyWeight = false;
|
||||
for (const CgGroup* const groupp : groups) {
|
||||
bool contributes = false;
|
||||
const Tally group = groupTally(*groupp, &contributes);
|
||||
tally.addPoints(group);
|
||||
if (groupp->weight()) anyWeight = true;
|
||||
if (!contributes) continue;
|
||||
weighted += static_cast<double>(groupp->weight()) * group.score();
|
||||
weights += static_cast<double>(groupp->weight());
|
||||
}
|
||||
// Without covergroups of weight that contribute, 0.0, or 100.0 if all have zero weight
|
||||
tally.score(weights != 0.0 ? weighted / weights : anyWeight ? 0.0 : 100.0);
|
||||
return tally;
|
||||
}
|
||||
|
||||
// The tallies of covergroups, of their coverpoints and crosses, and of the coverable bins of
|
||||
// those, by name; a covergroup of a dotted name also tallies in each node of its name
|
||||
std::map<string, Tally> covergroupTallies(const CgGroups& groups) {
|
||||
std::map<string, Tally> tallies;
|
||||
std::map<string, std::vector<const CgGroup*>> nodeGroups;
|
||||
for (const CgGroups::value_type& it : groups) {
|
||||
const CgGroup& group = it.second;
|
||||
string path;
|
||||
for (const string& part : splitHier(group.hier())) {
|
||||
path = path.empty() ? part : path + "." + part;
|
||||
nodeGroups[path].push_back(&group);
|
||||
}
|
||||
for (const std::pair<const string, CgItem>& item : group.items()) {
|
||||
tallies[item.first] = itemTally(item.second);
|
||||
for (const std::pair<const string, CgBin>& bin : item.second.bins()) {
|
||||
// Of the bins of the name
|
||||
tallies[item.first + "." + bin.second.name()].addPoint(bin.second.covered());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const std::pair<const string, std::vector<const CgGroup*>>& it : nodeGroups) {
|
||||
tallies[it.first]
|
||||
= it.second.size() == 1 ? groupTally(*it.second.front()) : groupsTally(it.second);
|
||||
}
|
||||
return tallies;
|
||||
}
|
||||
|
||||
// Shared row formatter. The callers choose which rows to print; this only keeps
|
||||
// the text layout identical between the flat and hierarchy reports.
|
||||
void printIndent(size_t indent) {
|
||||
for (size_t i = 0; i < indent; ++i) std::cout << ' ';
|
||||
}
|
||||
|
||||
void printTallyRow(const string& type, uint64_t hit, uint64_t total, size_t indent,
|
||||
size_t typeWidth, size_t countWidth) {
|
||||
void printTallyRow(const string& type, const Tally& tally, size_t indent, size_t typeWidth,
|
||||
size_t countWidth) {
|
||||
printIndent(indent);
|
||||
// A score is complete at 100%, which it may reach with bins of no weight uncovered
|
||||
const string percent = tally.scored() ? pctValueString(tally.score(), tally.score() >= 100.0)
|
||||
: pctString(tally.hit(), tally.total());
|
||||
// Right-align percentages to the width of "100.0%", so that rows line up
|
||||
std::cout << std::left << std::setw(typeWidth) << type << " : " << std::right << std::fixed
|
||||
<< pctString(hit, total) << " (" << std::setw(countWidth) << hit << "/"
|
||||
<< std::setw(countWidth) << total << ")\n";
|
||||
<< std::setw(6) << percent << " (" << std::setw(countWidth) << tally.hit() << "/"
|
||||
<< std::setw(countWidth) << tally.total() << ")\n";
|
||||
}
|
||||
|
||||
size_t countWidth(const TypeTally& tally) {
|
||||
size_t width = cvtToStr(0).size();
|
||||
for (TypeTally::const_iterator it = tally.begin(); it != tally.end(); ++it) {
|
||||
width = std::max(width, cvtToStr(it->second.first).size());
|
||||
width = std::max(width, cvtToStr(it->second.second).size());
|
||||
width = std::max(width, cvtToStr(it->second.hit()).size());
|
||||
width = std::max(width, cvtToStr(it->second.total()).size());
|
||||
}
|
||||
return width;
|
||||
}
|
||||
@@ -157,19 +390,35 @@ void printTypeTally(const TypeTally& tally, size_t indent, bool includeMissingOr
|
||||
const string type = typep;
|
||||
const TypeTally::const_iterator it = tally.find(type);
|
||||
if (it != tally.end()) {
|
||||
printTallyRow(type, it->second.first, it->second.second, indent, typWidth, cntWidth);
|
||||
printTallyRow(type, it->second, indent, typWidth, cntWidth);
|
||||
} else if (includeMissingOrdered) {
|
||||
printTallyRow(type, 0, 0, indent, typWidth, cntWidth);
|
||||
printTallyRow(type, Tally{}, indent, typWidth, cntWidth);
|
||||
}
|
||||
}
|
||||
for (TypeTally::const_iterator it = tally.begin(); it != tally.end(); ++it) {
|
||||
if (!isOrderedType(it->first)) {
|
||||
printTallyRow(it->first, it->second.first, it->second.second, indent, typWidth,
|
||||
cntWidth);
|
||||
printTallyRow(it->first, it->second, indent, typWidth, cntWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print covergroups, coverpoints, crosses, and bins one per line, so that searching for a name
|
||||
// shows its coverage
|
||||
void printCovergroupTallies(const std::map<string, Tally>& tallies, int levels) {
|
||||
std::map<string, Tally> shown;
|
||||
size_t nameWidth = 0;
|
||||
for (const std::pair<const string, Tally>& it : tallies) {
|
||||
if (levels >= 0 && static_cast<int>(splitHier(it.first).size()) > levels + 1) continue;
|
||||
shown.insert(it);
|
||||
nameWidth = std::max(nameWidth, it.first.size());
|
||||
}
|
||||
const size_t cntWidth = countWidth(shown);
|
||||
std::cout << "Covergroup Coverage Summary:\n";
|
||||
for (const std::pair<const string, Tally>& it : shown) {
|
||||
printTallyRow(it.first, it.second, s_summaryIndent, nameWidth, cntWidth);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void VlcTop::readCoverage(const string& filename, bool nonfatal) {
|
||||
@@ -520,8 +769,15 @@ void VlcTop::printTypeSummary() {
|
||||
TypeTally tally;
|
||||
for (VlcPoints::ByName::value_type& i : m_points) {
|
||||
const VlcPoint& pt = m_points.pointNumber(i.second);
|
||||
if (isCovergroup(pt)) continue; // Tallied below, by covergroup
|
||||
tallyPoint(tally, displayType(pt), pt.count());
|
||||
}
|
||||
const CgGroups groups = covergroups(m_points);
|
||||
if (!groups.empty()) {
|
||||
std::vector<const CgGroup*> groupps;
|
||||
for (const CgGroups::value_type& it : groups) groupps.push_back(&it.second);
|
||||
tally[s_covergroupType] = groupsTally(groupps);
|
||||
}
|
||||
if (tally.empty()) return;
|
||||
std::cout << "Coverage Summary:\n";
|
||||
// Keep the legacy summary behavior of showing standard coverage types even
|
||||
@@ -536,6 +792,7 @@ void VlcTop::printHierarchyReport() {
|
||||
bool hasCollapsedHier = false;
|
||||
for (VlcPoints::ByName::value_type& i : m_points) {
|
||||
const VlcPoint& pt = m_points.pointNumber(i.second);
|
||||
if (isCovergroup(pt)) continue; // Reported below, by covergroup
|
||||
const string hier = reportHier(pt);
|
||||
if (hier.empty()) continue;
|
||||
hasHier = true;
|
||||
@@ -549,7 +806,12 @@ void VlcTop::printHierarchyReport() {
|
||||
}
|
||||
tallyPoint(duTallies[duName(pt)], type, pt.count());
|
||||
}
|
||||
const std::map<string, Tally> cgTallies = covergroupTallies(covergroups(m_points));
|
||||
|
||||
if (!hasHier && !cgTallies.empty()) {
|
||||
printCovergroupTallies(cgTallies, opt.reportLevels());
|
||||
return;
|
||||
}
|
||||
if (!hasHier) {
|
||||
std::cout << "%Warning: --report hierarchy input has no hierarchy fields; "
|
||||
<< "printing flat summary instead.\n";
|
||||
@@ -582,4 +844,5 @@ void VlcTop::printHierarchyReport() {
|
||||
// types only, but in the same stable order as the flat summary.
|
||||
printTypeTally(it->second, s_reportRowIndent, false);
|
||||
}
|
||||
if (!cgTallies.empty()) printCovergroupTallies(cgTallies, levels);
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ def covergroup_coverage_report(test, outfile=None):
|
||||
if not h_m:
|
||||
continue
|
||||
hier = h_m.group(1)
|
||||
bt_m = re.search(r'\x01bin_type\x02([^\x01]+)', entry)
|
||||
cross_m = re.search(r'\x01cross\x021', entry)
|
||||
bt_m = re.search(r'\x01Bt\x02([^\x01]+)', entry)
|
||||
cross_m = re.search(r'\x01C\x021', entry)
|
||||
annotations = []
|
||||
if bt_m:
|
||||
annotations.append(bt_m.group(1))
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
$ verilator_coverage --report hierarchy --levels 3 coverage.dat
|
||||
Hierarchy Coverage Summary:
|
||||
t
|
||||
fsm_state : 70.0% ( 7/10)
|
||||
fsm_arc : 58.3% ( 7/12)
|
||||
fsm_state : 70.0% ( 7/10)
|
||||
fsm_arc : 58.3% ( 7/12)
|
||||
t.u1
|
||||
fsm_state : 80.0% (4/5)
|
||||
fsm_arc : 66.7% (4/6)
|
||||
fsm_state : 80.0% (4/5)
|
||||
fsm_arc : 66.7% (4/6)
|
||||
t.u2
|
||||
fsm_state : 60.0% (3/5)
|
||||
fsm_arc : 50.0% (3/6)
|
||||
fsm_state : 60.0% (3/5)
|
||||
fsm_arc : 50.0% (3/6)
|
||||
Design Unit Coverage Summary:
|
||||
fsm
|
||||
fsm_state : 70.0% ( 7/10)
|
||||
fsm_arc : 58.3% ( 7/12)
|
||||
fsm_state : 70.0% ( 7/10)
|
||||
fsm_arc : 58.3% ( 7/12)
|
||||
|
||||
@@ -16,9 +16,9 @@ coverage_covergroup_common.run(test)
|
||||
|
||||
# Verify coverage.dat format contains covergroup entries (replaces t_covergroup_database)
|
||||
test.file_grep(test.coverage_filename, r'covergroup')
|
||||
test.file_grep(test.coverage_filename, r'bin.{0,2}low')
|
||||
test.file_grep(test.coverage_filename, r'bin.{0,2}high')
|
||||
test.file_grep(test.coverage_filename, r'\x01B\x02low')
|
||||
test.file_grep(test.coverage_filename, r'\x01B\x02high')
|
||||
test.file_grep(test.coverage_filename, r'cg_db\.cp\.low')
|
||||
test.file_grep(test.coverage_filename, r'cg_db\.cp\.high')
|
||||
test.file_grep(test.coverage_filename, r'.*bin.{0,2}low.*\' [1-9]')
|
||||
test.file_grep(test.coverage_filename, r'.*bin.{0,2}high.*\' [1-9]')
|
||||
test.file_grep(test.coverage_filename, r'.*\x01B\x02low.*\' [1-9]')
|
||||
test.file_grep(test.coverage_filename, r'.*\x01B\x02high.*\' [1-9]')
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
$ verilator_coverage --report summary,hierarchy coverage.dat
|
||||
Coverage Summary:
|
||||
line : 0.0% ( 0/ 0)
|
||||
toggle : 0.0% ( 0/ 0)
|
||||
branch : 0.0% ( 0/ 0)
|
||||
expr : 0.0% ( 0/ 0)
|
||||
fsm_state : 0.0% ( 0/ 0)
|
||||
fsm_arc : 0.0% ( 0/ 0)
|
||||
covergroup : 67.6% (31/51)
|
||||
Covergroup Coverage Summary:
|
||||
__vlAnonCG_twin : 50.0% ( 2/ 4)
|
||||
__vlAnonCG_twin.cp : 50.0% ( 2/ 4)
|
||||
__vlAnonCG_twin.cp.hit : 50.0% ( 1/ 2)
|
||||
__vlAnonCG_twin.cp.miss : 50.0% ( 1/ 2)
|
||||
collide : 91.7% ( 7/ 8)
|
||||
collide.p : 100.0% ( 2/ 2)
|
||||
collide.p.a : 100.0% ( 1/ 1)
|
||||
collide.p.a_x_b : 100.0% ( 1/ 1)
|
||||
collide.q : 100.0% ( 2/ 2)
|
||||
collide.q.b_x_c : 100.0% ( 1/ 1)
|
||||
collide.q.c : 100.0% ( 1/ 1)
|
||||
collide.x : 75.0% ( 3/ 4)
|
||||
collide.x.a_x_b_x_b_x_c : 100.0% ( 1/ 1)
|
||||
collide.x.a_x_b_x_c : 50.0% ( 1/ 2)
|
||||
collide.x.a_x_c : 100.0% ( 1/ 1)
|
||||
crossed : 30.0% ( 3/10)
|
||||
crossed.cp_a : 50.0% ( 1/ 2)
|
||||
crossed.cp_a.auto_0 : 100.0% ( 1/ 1)
|
||||
crossed.cp_a.auto_1 : 0.0% ( 0/ 1)
|
||||
crossed.cp_b : 25.0% ( 1/ 4)
|
||||
crossed.cp_b.auto_0 : 100.0% ( 1/ 1)
|
||||
crossed.cp_b.auto_1 : 0.0% ( 0/ 1)
|
||||
crossed.cp_b.auto_2 : 0.0% ( 0/ 1)
|
||||
crossed.cp_b.auto_3 : 0.0% ( 0/ 1)
|
||||
crossed.x : 25.0% ( 1/ 4)
|
||||
crossed.x.auto_0_x_auto_0 : 100.0% ( 1/ 1)
|
||||
crossed.x.auto_0_x_auto_1 : 0.0% ( 0/ 1)
|
||||
crossed.x.auto_0_x_auto_2 : 0.0% ( 0/ 1)
|
||||
crossed.x.auto_0_x_auto_3 : 0.0% ( 0/ 1)
|
||||
empty : 0.0% ( 0/ 0)
|
||||
empty.cp : 0.0% ( 0/ 0)
|
||||
empty.unweighted : 100.0% ( 0/ 0)
|
||||
excluded : 100.0% ( 1/ 1)
|
||||
excluded.cp : 100.0% ( 1/ 1)
|
||||
excluded.cp.hit : 100.0% ( 1/ 1)
|
||||
free : 100.0% ( 0/ 0)
|
||||
free.cp : 0.0% ( 0/ 0)
|
||||
heavy : 50.0% ( 1/ 2)
|
||||
heavy.cp : 50.0% ( 1/ 2)
|
||||
heavy.cp.auto_0 : 100.0% ( 1/ 1)
|
||||
heavy.cp.auto_1 : 0.0% ( 0/ 1)
|
||||
instance_weight : 100.0% ( 3/ 4)
|
||||
instance_weight.cp_a : 50.0% ( 1/ 2)
|
||||
instance_weight.cp_a.auto_0 : 100.0% ( 1/ 1)
|
||||
instance_weight.cp_a.auto_1 : 0.0% ( 0/ 1)
|
||||
instance_weight.cp_b : 100.0% ( 2/ 2)
|
||||
instance_weight.cp_b.auto_0 : 100.0% ( 1/ 1)
|
||||
instance_weight.cp_b.auto_1 : 100.0% ( 1/ 1)
|
||||
merged : 100.0% ( 2/ 2)
|
||||
merged.cp : 100.0% ( 2/ 2)
|
||||
merged.cp.auto_0 : 100.0% ( 1/ 1)
|
||||
merged.cp.auto_1 : 100.0% ( 1/ 1)
|
||||
threshold : 50.0% ( 1/ 2)
|
||||
threshold.cp : 50.0% ( 1/ 2)
|
||||
threshold.cp.auto_0 : 100.0% ( 1/ 1)
|
||||
threshold.cp.auto_1 : 0.0% ( 0/ 1)
|
||||
type_weight : 75.0% ( 3/ 4)
|
||||
type_weight.cp_a : 50.0% ( 1/ 2)
|
||||
type_weight.cp_a.auto_0 : 100.0% ( 1/ 1)
|
||||
type_weight.cp_a.auto_1 : 0.0% ( 0/ 1)
|
||||
type_weight.cp_b : 100.0% ( 2/ 2)
|
||||
type_weight.cp_b.auto_0 : 100.0% ( 1/ 1)
|
||||
type_weight.cp_b.auto_1 : 100.0% ( 1/ 1)
|
||||
unequal : 37.5% ( 2/ 6)
|
||||
unequal.cp_a : 50.0% ( 1/ 2)
|
||||
unequal.cp_a.auto_0 : 100.0% ( 1/ 1)
|
||||
unequal.cp_a.auto_1 : 0.0% ( 0/ 1)
|
||||
unequal.cp_b : 25.0% ( 1/ 4)
|
||||
unequal.cp_b.auto_0 : 100.0% ( 1/ 1)
|
||||
unequal.cp_b.auto_1 : 0.0% ( 0/ 1)
|
||||
unequal.cp_b.auto_2 : 0.0% ( 0/ 1)
|
||||
unequal.cp_b.auto_3 : 0.0% ( 0/ 1)
|
||||
varying : 62.5% ( 3/ 4)
|
||||
varying.cp : 50.0% ( 1/ 2)
|
||||
varying.cp.auto_0 : 100.0% ( 1/ 1)
|
||||
varying.cp.auto_1 : 0.0% ( 0/ 1)
|
||||
varying.cq : 100.0% ( 2/ 2)
|
||||
varying.cq.auto_0 : 100.0% ( 1/ 1)
|
||||
varying.cq.auto_1 : 100.0% ( 1/ 1)
|
||||
weighted : 100.0% ( 3/ 4)
|
||||
weighted.cp_a : 50.0% ( 1/ 2)
|
||||
weighted.cp_a.auto_0 : 100.0% ( 1/ 1)
|
||||
weighted.cp_a.auto_1 : 0.0% ( 0/ 1)
|
||||
weighted.cp_b : 100.0% ( 2/ 2)
|
||||
weighted.cp_b.auto_0 : 100.0% ( 1/ 1)
|
||||
weighted.cp_b.auto_1 : 100.0% ( 1/ 1)
|
||||
|
||||
$ verilator_coverage --report summary,hierarchy edge.dat
|
||||
Coverage Summary:
|
||||
line : 0.0% (0/0)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 0.0% (0/0)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
covergroup : 58.3% (3/5)
|
||||
Covergroup Coverage Summary:
|
||||
pkg : 75.0% (2/3)
|
||||
pkg.alpha : 50.0% (1/2)
|
||||
pkg.alpha.cp : 50.0% (1/2)
|
||||
pkg.alpha.cp.b0 : 100.0% (1/1)
|
||||
pkg.alpha.cp.b1 : 0.0% (0/1)
|
||||
pkg.beta : 100.0% (1/1)
|
||||
pkg.beta.cp : 100.0% (1/1)
|
||||
pkg.beta.cp.b0 : 100.0% (1/1)
|
||||
split : 25.0% (1/2)
|
||||
split.cp : 0.0% (0/1)
|
||||
split.cp.b0 : 0.0% (0/1)
|
||||
split.cq : 100.0% (1/1)
|
||||
split.cq.b0 : 100.0% (1/1)
|
||||
|
||||
$ verilator_coverage --report summary zero.dat
|
||||
Coverage Summary:
|
||||
line : 0.0% (0/0)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 0.0% (0/0)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
covergroup : 100.0% (0/1)
|
||||
|
||||
$ verilator_coverage --report summary none.dat
|
||||
Coverage Summary:
|
||||
line : 0.0% (0/0)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 0.0% (0/0)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
covergroup : 0.0% (0/0)
|
||||
|
||||
$ verilator_coverage --report summary near.dat
|
||||
Coverage Summary:
|
||||
line : 0.0% ( 0/ 0)
|
||||
toggle : 0.0% ( 0/ 0)
|
||||
branch : 0.0% ( 0/ 0)
|
||||
expr : 0.0% ( 0/ 0)
|
||||
fsm_state : 0.0% ( 0/ 0)
|
||||
fsm_arc : 0.0% ( 0/ 0)
|
||||
covergroup : 99.9% (2000/2001)
|
||||
|
||||
$ verilator_coverage --report hierarchy mixed.dat
|
||||
Hierarchy Coverage Summary:
|
||||
top
|
||||
line : 100.0% (1/1)
|
||||
toggle : 0.0% (0/1)
|
||||
top.t
|
||||
line : 100.0% (1/1)
|
||||
toggle : 0.0% (0/1)
|
||||
Design Unit Coverage Summary:
|
||||
t
|
||||
line : 100.0% (1/1)
|
||||
toggle : 0.0% (0/1)
|
||||
Covergroup Coverage Summary:
|
||||
cg : 50.0% (1/2)
|
||||
cg.cp : 50.0% (1/2)
|
||||
cg.cp.a : 100.0% (1/1)
|
||||
cg.cp.b : 0.0% (0/1)
|
||||
|
||||
$ verilator_coverage --report hierarchy --levels 1 mixed.dat
|
||||
Hierarchy Coverage Summary:
|
||||
top
|
||||
line : 100.0% (1/1)
|
||||
toggle : 0.0% (0/1)
|
||||
top.t
|
||||
line : 100.0% (1/1)
|
||||
toggle : 0.0% (0/1)
|
||||
Design Unit Coverage Summary:
|
||||
t
|
||||
line : 100.0% (1/1)
|
||||
toggle : 0.0% (0/1)
|
||||
Covergroup Coverage Summary:
|
||||
cg : 50.0% (1/2)
|
||||
cg.cp : 50.0% (1/2)
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
from coverage_common import init_log, run_vlcov, vlcov_run_context
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile(verilator_flags2=['--coverage-user'])
|
||||
|
||||
test.execute()
|
||||
|
||||
log = test.obj_dir + "/vlcov.log"
|
||||
tmp_log = test.obj_dir + "/vlcov.tmp"
|
||||
init_log(log)
|
||||
vlcov = vlcov_run_context(test, log, tmp_log)
|
||||
|
||||
|
||||
def dat_line(fields, count):
|
||||
return "C '" + "".join("\001" + key + "\002" + value
|
||||
for key, value in fields) + "' " + str(count) + "\n"
|
||||
|
||||
|
||||
def write_dat(name, records, points=()):
|
||||
filename = test.obj_dir + "/" + name
|
||||
with open(filename, "w", encoding="utf-8") as fh:
|
||||
fh.write("# SystemC::Coverage-3\n")
|
||||
for group, item, bin_name, count, keys in records:
|
||||
fields = [("t", "covergroup"), ("page", "v_covergroup/" + group), ("f", "t/cg.v"),
|
||||
("l", "1")]
|
||||
fields += keys
|
||||
fields.append(("h", group + "." + item + "." + bin_name))
|
||||
fh.write(dat_line(fields, count))
|
||||
for kind, hier, lineno, count in points:
|
||||
fields = [("t", kind), ("page", "v_" + kind + "/t"), ("f", "t/t.v"), ("l", lineno),
|
||||
("h", hier)]
|
||||
fh.write(dat_line(fields, count))
|
||||
return filename
|
||||
|
||||
|
||||
# Covergroups of dotted names under one node; a record without its bin's name; and records of a
|
||||
# bin with different weights and thresholds, which merge with the largest of those
|
||||
edge_cov = write_dat("edge.dat", [
|
||||
("pkg.alpha", "cp", "b0", 1, [("B", "b0")]),
|
||||
("pkg.alpha", "cp", "b1", 0, [("B", "b1")]),
|
||||
("pkg.beta", "cp", "b0", 1, []),
|
||||
("split", "cp", "b0", 1, [("B", "b0"), ("s", "2"), ("w", "2")]),
|
||||
("split", "cp", "b0", 0, [("B", "b0"), ("w", "3")]),
|
||||
("split", "cq", "b0", 1, [("B", "b0")]),
|
||||
])
|
||||
# Covergroups of zero weight only: 100
|
||||
zero_cov = write_dat("zero.dat", [("idle", "cp", "b0", 0, [("B", "b0"), ("Gw", "0")])])
|
||||
# Covergroups without coverable bins only: 0
|
||||
none_cov = write_dat("none.dat", [("void", "cp", "b0", 0, [("B", "b0"), ("Bt", "ignore")])])
|
||||
# Coverage that is not complete shows below 100%: 99.95 as 99.9
|
||||
near_cov = write_dat("near.dat", [("near", "cp", "b" + str(i), int(i != 0), [("B", "b" + str(i))])
|
||||
for i in range(2001)])
|
||||
# A module shows a row per coverage type under its name; covergroups show one line each
|
||||
mixed_cov = write_dat("mixed.dat", [("cg", "cp", "a", 1, [("B", "a")]),
|
||||
("cg", "cp", "b", 0, [("B", "b")])],
|
||||
points=[("line", "top.t", "10", 1), ("toggle", "top.t", "11", 0)])
|
||||
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report summary,hierarchy coverage.dat",
|
||||
args=["--report", "summary,hierarchy", test.obj_dir + "/coverage.dat"])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report summary,hierarchy edge.dat",
|
||||
args=["--report", "summary,hierarchy", edge_cov])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report summary zero.dat",
|
||||
args=["--report", "summary", zero_cov])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report summary none.dat",
|
||||
args=["--report", "summary", none_cov])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report summary near.dat",
|
||||
args=["--report", "summary", near_cov])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report hierarchy mixed.dat",
|
||||
args=["--report", "hierarchy", mixed_cov])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report hierarchy --levels 1 mixed.dat",
|
||||
args=["--report", "hierarchy", "--levels", "1", mixed_cov])
|
||||
|
||||
test.files_identical(log, test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,208 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Covergroup coverage in verilator_coverage reports is as get_coverage() computes it
|
||||
// (IEEE 1800-2023 19.11), not a ratio of bins
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkr(gotv,expv) do if ((gotv) > (expv) + 0.001 || (gotv) < (expv) - 0.001) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
// Ignore, illegal and default bins are not coverable: 100
|
||||
covergroup excluded with function sample (int value);
|
||||
cp: coverpoint value {
|
||||
bins hit = {0};
|
||||
ignore_bins ignored = {1};
|
||||
illegal_bins illegal = {2};
|
||||
bins other = default;
|
||||
}
|
||||
endgroup
|
||||
// The average of the coverpoints, 50 and 25: 37.5
|
||||
covergroup unequal with function sample (bit a, bit [1:0] b);
|
||||
cp_a: coverpoint a;
|
||||
cp_b: coverpoint b;
|
||||
endgroup
|
||||
// A coverpoint of zero weight does not count: 100
|
||||
covergroup weighted with function sample (bit a, bit b);
|
||||
cp_a: coverpoint a {
|
||||
option.weight = 0;
|
||||
type_option.weight = 0;
|
||||
}
|
||||
cp_b: coverpoint b;
|
||||
endgroup
|
||||
// option.weight weighs the coverpoints, as in get_coverage(): 100, then 75
|
||||
covergroup instance_weight with function sample (bit a, bit b);
|
||||
cp_a: coverpoint a {
|
||||
option.weight = 0;
|
||||
}
|
||||
cp_b: coverpoint b;
|
||||
endgroup
|
||||
covergroup type_weight with function sample (bit a, bit b);
|
||||
cp_a: coverpoint a {
|
||||
type_option.weight = 0;
|
||||
}
|
||||
cp_b: coverpoint b;
|
||||
endgroup
|
||||
// A bin is covered once hit option.at_least times: 50
|
||||
covergroup threshold with function sample (bit a);
|
||||
cp: coverpoint a {
|
||||
option.at_least = 2;
|
||||
}
|
||||
endgroup
|
||||
// A cross is an item, of its weight, whose ignored bins do not count: 50, 25, and 25 thrice:
|
||||
// 30
|
||||
covergroup crossed with function sample (bit a, bit [1:0] b);
|
||||
cp_a: coverpoint a;
|
||||
cp_b: coverpoint b;
|
||||
x: cross cp_a, cp_b{
|
||||
option.weight = 3;
|
||||
ignore_bins ignored = binsof (cp_a) intersect {1};
|
||||
}
|
||||
endgroup
|
||||
// Bins of a name are distinct bins: the cross bins of 'a' and 'b_x_c', and of 'a_x_b' and
|
||||
// 'c', are both named a_x_b_x_c: 100, 100 and 75: 91.67
|
||||
covergroup collide with function sample (bit u, bit v);
|
||||
p: coverpoint u {
|
||||
bins a = {0};
|
||||
bins a_x_b = {1};
|
||||
}
|
||||
q: coverpoint v {
|
||||
bins c = {0};
|
||||
bins b_x_c = {1};
|
||||
}
|
||||
x: cross p, q;
|
||||
endgroup
|
||||
// Weighs as its type_option.weight, a coverpoint whose option.weight is not a constant, and
|
||||
// so may differ in the instances the coverage database merges: 50 three times, and 100: 62.5,
|
||||
// where get_coverage() averages the instances: 50
|
||||
covergroup varying(int w) with function sample (bit v, bit u);
|
||||
cp: coverpoint v {
|
||||
option.weight = w;
|
||||
type_option.weight = 3;
|
||||
}
|
||||
cq: coverpoint u;
|
||||
endgroup
|
||||
// Without coverable bins: 0, so not counted in the coverage of the covergroups, and a
|
||||
// coverpoint of zero weight without them, 100
|
||||
covergroup empty with function sample (bit a);
|
||||
cp: coverpoint a {
|
||||
ignore_bins ignored = {[0 : 1]};
|
||||
}
|
||||
unweighted: coverpoint a {
|
||||
option.weight = 0;
|
||||
ignore_bins ignored = {[0 : 1]};
|
||||
}
|
||||
endgroup
|
||||
// Of zero weight, without coverable bins: 100
|
||||
covergroup free with function sample (bit a);
|
||||
type_option.weight = 0;
|
||||
cp: coverpoint a {
|
||||
ignore_bins ignored = {[0 : 1]};
|
||||
}
|
||||
endgroup
|
||||
// Of three times the weight of the others in the coverage of the covergroups: 50
|
||||
covergroup heavy with function sample (bit a);
|
||||
type_option.weight = 3;
|
||||
cp: coverpoint a;
|
||||
endgroup
|
||||
// The coverage database merges the bins of the instances: 100, where get_coverage() averages
|
||||
// the instances: 50
|
||||
covergroup merged with function sample (bit a);
|
||||
cp: coverpoint a;
|
||||
endgroup
|
||||
|
||||
// Covergroups of a name in distinct classes are both named __vlAnonCG_twin, with distinct
|
||||
// bins: 50, and get_coverage() averages their instances: 50
|
||||
class First;
|
||||
bit v;
|
||||
covergroup twin;
|
||||
cp: coverpoint v {
|
||||
bins hit = {0};
|
||||
bins miss = {1};
|
||||
}
|
||||
endgroup
|
||||
function new;
|
||||
twin = new;
|
||||
endfunction
|
||||
endclass
|
||||
class Second;
|
||||
bit v;
|
||||
covergroup twin;
|
||||
cp: coverpoint v {
|
||||
bins hit = {0};
|
||||
bins miss = {1};
|
||||
}
|
||||
endgroup
|
||||
function new;
|
||||
twin = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
excluded excluded_inst = new;
|
||||
unequal unequal_inst = new;
|
||||
weighted weighted_inst = new;
|
||||
instance_weight instance_weight_inst = new;
|
||||
type_weight type_weight_inst = new;
|
||||
threshold threshold_inst = new;
|
||||
crossed crossed_inst = new;
|
||||
empty empty_inst = new;
|
||||
free free_inst = new;
|
||||
heavy heavy_inst = new;
|
||||
merged merged_first = new;
|
||||
merged merged_second = new;
|
||||
collide collide_inst = new;
|
||||
varying varying_none = new(0);
|
||||
varying varying_two = new(2);
|
||||
First first = new;
|
||||
Second second = new;
|
||||
|
||||
initial begin
|
||||
excluded_inst.sample(0);
|
||||
unequal_inst.sample(0, 0);
|
||||
weighted_inst.sample(0, 0);
|
||||
weighted_inst.sample(0, 1);
|
||||
instance_weight_inst.sample(0, 0);
|
||||
instance_weight_inst.sample(0, 1);
|
||||
type_weight_inst.sample(0, 0);
|
||||
type_weight_inst.sample(0, 1);
|
||||
threshold_inst.sample(0);
|
||||
threshold_inst.sample(0);
|
||||
threshold_inst.sample(1);
|
||||
crossed_inst.sample(0, 0);
|
||||
empty_inst.sample(0);
|
||||
free_inst.sample(0);
|
||||
heavy_inst.sample(0);
|
||||
merged_first.sample(0);
|
||||
merged_second.sample(1);
|
||||
collide_inst.sample(0, 0);
|
||||
collide_inst.sample(0, 1);
|
||||
collide_inst.sample(1, 1);
|
||||
varying_none.sample(0, 0);
|
||||
varying_two.sample(0, 1);
|
||||
first.v = 0;
|
||||
first.twin.sample();
|
||||
second.v = 1;
|
||||
second.twin.sample();
|
||||
`checkr(excluded_inst.get_coverage(), 100.0);
|
||||
`checkr(unequal_inst.get_coverage(), 37.5);
|
||||
`checkr(weighted_inst.get_coverage(), 100.0);
|
||||
`checkr(instance_weight_inst.get_coverage(), 100.0);
|
||||
`checkr(type_weight_inst.get_coverage(), 75.0);
|
||||
`checkr(threshold_inst.get_coverage(), 50.0);
|
||||
`checkr(crossed_inst.get_coverage(), 30.0);
|
||||
`checkr(empty_inst.get_coverage(), 0.0);
|
||||
`checkr(free_inst.get_coverage(), 100.0);
|
||||
`checkr(heavy_inst.get_coverage(), 50.0);
|
||||
`checkr(merged_first.get_coverage(), 50.0);
|
||||
`checkr(collide_inst.get_coverage(), 275.0 / 3);
|
||||
`checkr(varying_none.get_coverage(), 50.0);
|
||||
`checkr(first.twin.get_coverage(), 50.0);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -1,30 +1,30 @@
|
||||
$ verilator_coverage --report summary t/t_vlcov_data_e.dat
|
||||
Coverage Summary:
|
||||
line : 88.9% ( 40/ 45)
|
||||
toggle : 33.3% ( 35/105)
|
||||
branch : 78.1% ( 50/ 64)
|
||||
expr : 66.7% ( 8/ 12)
|
||||
fsm_state : 0.0% ( 0/ 0)
|
||||
fsm_arc : 0.0% ( 0/ 0)
|
||||
line : 88.9% ( 40/ 45)
|
||||
toggle : 33.3% ( 35/105)
|
||||
branch : 78.1% ( 50/ 64)
|
||||
expr : 66.7% ( 8/ 12)
|
||||
fsm_state : 0.0% ( 0/ 0)
|
||||
fsm_arc : 0.0% ( 0/ 0)
|
||||
|
||||
$ verilator_coverage t/t_cover_hier.out
|
||||
Coverage Summary:
|
||||
line : 100.0% (4/4)
|
||||
toggle : 0.0% (0/0)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 100.0% (4/4)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
user : 100.0% (2/2)
|
||||
|
||||
$ verilator_coverage --report summary,hier t/t_cover_hier.out
|
||||
Coverage Summary:
|
||||
line : 100.0% (4/4)
|
||||
toggle : 0.0% (0/0)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 100.0% (4/4)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
user : 100.0% (2/2)
|
||||
Hierarchy Coverage Summary:
|
||||
top
|
||||
@@ -58,11 +58,11 @@ Design Unit Coverage Summary:
|
||||
$ verilator_coverage --report hier,summary --levels 0 t/t_cover_hier.out
|
||||
Coverage Summary:
|
||||
line : 100.0% (4/4)
|
||||
toggle : 0.0% (0/0)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 100.0% (4/4)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
user : 100.0% (2/2)
|
||||
Hierarchy Coverage Summary:
|
||||
top
|
||||
@@ -80,11 +80,11 @@ Design Unit Coverage Summary:
|
||||
$ verilator_coverage --report hier,summary --levels -4 t/t_cover_hier.out
|
||||
Coverage Summary:
|
||||
line : 100.0% (4/4)
|
||||
toggle : 0.0% (0/0)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 100.0% (4/4)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
user : 100.0% (2/2)
|
||||
Hierarchy Coverage Summary:
|
||||
top
|
||||
@@ -233,19 +233,19 @@ $ verilator_coverage --report hierarchy collapsed.dat
|
||||
Note: hierarchy report contains collapsed hierarchy paths; it is not precise per-instance coverage.
|
||||
Hierarchy Coverage Summary:
|
||||
tb
|
||||
line : 50.0% (1/2)
|
||||
branch : 50.0% (1/2)
|
||||
line : 50.0% (1/2)
|
||||
branch : 50.0% (1/2)
|
||||
tb.cluster*
|
||||
line : 50.0% (1/2)
|
||||
line : 50.0% (1/2)
|
||||
branch : 100.0% (1/1)
|
||||
tb.cluster*.u_core
|
||||
line : 50.0% (1/2)
|
||||
line : 50.0% (1/2)
|
||||
branch : 100.0% (1/1)
|
||||
tb.cluster?
|
||||
branch : 0.0% (0/1)
|
||||
branch : 0.0% (0/1)
|
||||
tb.cluster?.u_core
|
||||
branch : 0.0% (0/1)
|
||||
branch : 0.0% (0/1)
|
||||
Design Unit Coverage Summary:
|
||||
core
|
||||
line : 50.0% (1/2)
|
||||
branch : 50.0% (1/2)
|
||||
line : 50.0% (1/2)
|
||||
branch : 50.0% (1/2)
|
||||
|
||||
@@ -10,8 +10,8 @@ $ verilator_coverage --report hierarchy missing.dat
|
||||
%Warning: --report hierarchy input has no hierarchy fields; printing flat summary instead.
|
||||
Coverage Summary:
|
||||
line : 100.0% (1/1)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 0.0% (0/0)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 0.0% (0/0)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
|
||||
@@ -1,64 +1,64 @@
|
||||
$ verilator_coverage --report summary coverage.dat
|
||||
Coverage Summary:
|
||||
line : 82.9% (29/35)
|
||||
toggle : 85.4% (82/96)
|
||||
branch : 78.6% (22/28)
|
||||
expr : 57.1% ( 8/14)
|
||||
fsm_state : 91.7% (11/12)
|
||||
fsm_arc : 71.4% (10/14)
|
||||
line : 82.9% (29/35)
|
||||
toggle : 85.4% (82/96)
|
||||
branch : 78.6% (22/28)
|
||||
expr : 57.1% ( 8/14)
|
||||
fsm_state : 91.7% (11/12)
|
||||
fsm_arc : 71.4% (10/14)
|
||||
|
||||
$ verilator_coverage --report hierarchy --levels 3 coverage.dat
|
||||
Hierarchy Coverage Summary:
|
||||
tb
|
||||
line : 82.9% (29/35)
|
||||
toggle : 85.4% (82/96)
|
||||
branch : 78.6% (22/28)
|
||||
expr : 57.1% ( 8/14)
|
||||
fsm_state : 91.7% (11/12)
|
||||
fsm_arc : 71.4% (10/14)
|
||||
line : 82.9% (29/35)
|
||||
toggle : 85.4% (82/96)
|
||||
branch : 78.6% (22/28)
|
||||
expr : 57.1% ( 8/14)
|
||||
fsm_state : 91.7% (11/12)
|
||||
fsm_arc : 71.4% (10/14)
|
||||
tb.cluster_a
|
||||
line : 73.3% (11/15)
|
||||
toggle : 76.1% (35/46)
|
||||
branch : 66.7% ( 8/12)
|
||||
line : 73.3% (11/15)
|
||||
toggle : 76.1% (35/46)
|
||||
branch : 66.7% ( 8/12)
|
||||
expr : 100.0% ( 6/ 6)
|
||||
fsm_state : 83.3% ( 5/ 6)
|
||||
fsm_arc : 57.1% ( 4/ 7)
|
||||
fsm_state : 83.3% ( 5/ 6)
|
||||
fsm_arc : 57.1% ( 4/ 7)
|
||||
tb.cluster_a.u_core
|
||||
line : 66.7% ( 8/12)
|
||||
toggle : 83.3% (20/24)
|
||||
branch : 62.5% ( 5/ 8)
|
||||
fsm_state : 83.3% ( 5/ 6)
|
||||
fsm_arc : 57.1% ( 4/ 7)
|
||||
line : 66.7% ( 8/12)
|
||||
toggle : 83.3% (20/24)
|
||||
branch : 62.5% ( 5/ 8)
|
||||
fsm_state : 83.3% ( 5/ 6)
|
||||
fsm_arc : 57.1% ( 4/ 7)
|
||||
tb.cluster_b
|
||||
line : 86.7% (13/15)
|
||||
toggle : 93.5% (43/46)
|
||||
branch : 83.3% (10/12)
|
||||
expr : 0.0% ( 0/ 6)
|
||||
line : 86.7% (13/15)
|
||||
toggle : 93.5% (43/46)
|
||||
branch : 83.3% (10/12)
|
||||
expr : 0.0% ( 0/ 6)
|
||||
fsm_state : 100.0% ( 6/ 6)
|
||||
fsm_arc : 85.7% ( 6/ 7)
|
||||
fsm_arc : 85.7% ( 6/ 7)
|
||||
tb.cluster_b.u_core
|
||||
line : 83.3% (10/12)
|
||||
line : 83.3% (10/12)
|
||||
toggle : 100.0% (24/24)
|
||||
branch : 87.5% ( 7/ 8)
|
||||
branch : 87.5% ( 7/ 8)
|
||||
fsm_state : 100.0% ( 6/ 6)
|
||||
fsm_arc : 85.7% ( 6/ 7)
|
||||
fsm_arc : 85.7% ( 6/ 7)
|
||||
Design Unit Coverage Summary:
|
||||
cluster
|
||||
line : 100.0% ( 3/ 3)
|
||||
toggle : 68.2% (15/22)
|
||||
branch : 75.0% ( 3/ 4)
|
||||
toggle : 68.2% (15/22)
|
||||
branch : 75.0% ( 3/ 4)
|
||||
expr : 100.0% ( 6/ 6)
|
||||
cluster__M1
|
||||
line : 100.0% ( 3/ 3)
|
||||
toggle : 86.4% (19/22)
|
||||
branch : 75.0% ( 3/ 4)
|
||||
expr : 0.0% ( 0/ 6)
|
||||
toggle : 86.4% (19/22)
|
||||
branch : 75.0% ( 3/ 4)
|
||||
expr : 0.0% ( 0/ 6)
|
||||
fsm_core
|
||||
line : 75.0% (18/24)
|
||||
toggle : 91.7% (44/48)
|
||||
branch : 75.0% (12/16)
|
||||
fsm_state : 91.7% (11/12)
|
||||
fsm_arc : 71.4% (10/14)
|
||||
line : 75.0% (18/24)
|
||||
toggle : 91.7% (44/48)
|
||||
branch : 75.0% (12/16)
|
||||
fsm_state : 91.7% (11/12)
|
||||
fsm_arc : 71.4% (10/14)
|
||||
tb
|
||||
line : 100.0% (5/5)
|
||||
toggle : 100.0% (4/4)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Coverage Summary:
|
||||
line : 0.0% ( 0/ 0)
|
||||
toggle : 0.0% ( 0/ 0)
|
||||
branch : 99.9% (10999/11000)
|
||||
expr : 0.0% ( 0/ 0)
|
||||
fsm_state : 0.0% ( 0/ 0)
|
||||
fsm_arc : 0.0% ( 0/ 0)
|
||||
line : 0.0% ( 0/ 0)
|
||||
toggle : 0.0% ( 0/ 0)
|
||||
branch : 99.9% (10999/11000)
|
||||
expr : 0.0% ( 0/ 0)
|
||||
fsm_state : 0.0% ( 0/ 0)
|
||||
fsm_arc : 0.0% ( 0/ 0)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Coverage Summary:
|
||||
line : 88.9% ( 40/ 45)
|
||||
toggle : 33.3% ( 35/105)
|
||||
branch : 78.1% ( 50/ 64)
|
||||
expr : 66.7% ( 8/ 12)
|
||||
fsm_state : 0.0% ( 0/ 0)
|
||||
fsm_arc : 0.0% ( 0/ 0)
|
||||
line : 88.9% ( 40/ 45)
|
||||
toggle : 33.3% ( 35/105)
|
||||
branch : 78.1% ( 50/ 64)
|
||||
expr : 66.7% ( 8/ 12)
|
||||
fsm_state : 0.0% ( 0/ 0)
|
||||
fsm_arc : 0.0% ( 0/ 0)
|
||||
|
||||
Reference in New Issue
Block a user