mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 10:03:44 +02:00
Add covergroup runtime registry (#8134)
This commit is contained in:
+208
-123
@@ -170,8 +170,8 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
std::map<AstVar*, CoverpointBins> m_cpBins; // Runtime coverpoint -> binsof index ranges
|
||||
std::set<AstCoverCross*>
|
||||
m_droppedCrosses; // Crosses with a bare-variable item: drop (COVERIGN)
|
||||
std::map<int, AstCDType*> m_vlCoverpointTypes; // hit-list bound K -> "VlCoverpointT<K>" type
|
||||
AstCDType* m_vlCoverCrossDTypep = nullptr; // Shared "VlCoverCross" C++ member type
|
||||
std::map<uint32_t, AstCoverpointDType*> m_cpDTypes; // Hit-list bound -> interned dtype
|
||||
AstVar* m_cgInstVarp = nullptr; // __Vcg_inst handle member of the current covergroup
|
||||
|
||||
VMemberMap m_memberMap; // Member names cached for fast lookup
|
||||
|
||||
@@ -187,6 +187,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
m_cpVarMap.clear();
|
||||
m_cpBins.clear();
|
||||
m_droppedCrosses.clear();
|
||||
m_cgInstVarp = nullptr;
|
||||
|
||||
// Scan every cross item to record the coverpoints it references (the cross dimensions)
|
||||
// and to flag any cross naming a bare variable -- a would-be implicit coverpoint, which
|
||||
@@ -204,6 +205,10 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
// The instance node owns this instance's coverpoint/cross runtimes, so it must exist
|
||||
// before any of them is created. Emitted first, ahead of both generate loops.
|
||||
generateInstanceAttach();
|
||||
|
||||
// For each coverpoint, generate sampling code
|
||||
for (AstCoverpoint* cpp : m_coverpoints) generateCoverpointCode(cpp);
|
||||
|
||||
@@ -537,7 +542,6 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
const string varName = "__Vprev_" + coverpointp->name();
|
||||
AstVar* prevVarp
|
||||
= new AstVar{coverpointp->fileline(), VVarType::MEMBER, varName, exprp->dtypep()};
|
||||
prevVarp->isStatic(false);
|
||||
m_covergroupp->addMembersp(prevVarp);
|
||||
|
||||
UINFO(4, " Created previous value variable: " << varName);
|
||||
@@ -562,7 +566,6 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
// Use 8-bit integer for state position (sequences rarely > 255 items)
|
||||
AstVar* stateVarp
|
||||
= new AstVar{binp->fileline(), VVarType::MEMBER, varName, VFlagLogicPacked{}, 8};
|
||||
stateVarp->isStatic(false);
|
||||
m_covergroupp->addMembersp(stateVarp);
|
||||
|
||||
UINFO(4, " Created sequence state variable: " << varName);
|
||||
@@ -644,17 +647,62 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get (or create) the "VlCoverpointT<K>" member type for hit-list bound K.
|
||||
AstCDType* vlCoverpointType(FileLine* fl, int hitBound) {
|
||||
const auto it = m_vlCoverpointTypes.find(hitBound);
|
||||
if (it != m_vlCoverpointTypes.end()) return it->second;
|
||||
AstCDType* const typep
|
||||
= new AstCDType{fl, "VlCoverpointT<" + std::to_string(hitBound) + ">"};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(typep);
|
||||
m_vlCoverpointTypes.emplace(hitBound, typep);
|
||||
// The interned AstBasicDType for one of the covergroup runtime keywords.
|
||||
static AstBasicDType* basicDType(FileLine* fl, VBasicDTypeKwd kwd) {
|
||||
return v3Global.rootp()->typeTablep()->findBasicDType(fl, kwd);
|
||||
}
|
||||
|
||||
// Get (or create) the coverpoint dtype for a hit-list bound, interned so each distinct bound
|
||||
// yields one node.
|
||||
AstCoverpointDType* coverpointDType(FileLine* fl, uint32_t hitBound) {
|
||||
AstCoverpointDType*& typep = m_cpDTypes[hitBound];
|
||||
if (!typep) {
|
||||
typep = new AstCoverpointDType{fl, hitBound};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(typep);
|
||||
}
|
||||
return typep;
|
||||
}
|
||||
|
||||
// Emit the covergroup's instance handle member and the constructor statement that creates
|
||||
// its node in the per-context coverage registry. Runs before any coverpoint or cross is
|
||||
// generated, so their runtimes can be added to the node as they are created.
|
||||
void generateInstanceAttach() {
|
||||
FileLine* const fl = m_covergroupp->fileline();
|
||||
// V3LinkParse synthesizes a 'new' for every covergroup; the item generators below already
|
||||
// rely on that, and this attach runs even for a covergroup with no coverpoints at all.
|
||||
UASSERT_OBJ(m_constructorp, m_covergroupp, "Covergroup missing synthesized constructor");
|
||||
m_cgInstVarp = new AstVar{fl, VVarType::MEMBER, "__Vcg_inst",
|
||||
basicDType(fl, VBasicDTypeKwd::COVERGROUP_INSTHANDLE)};
|
||||
m_covergroupp->addMembersp(m_cgInstVarp);
|
||||
|
||||
// The type node is keyed by the covergroup type name -- the same string that keys this
|
||||
// covergroup's coverage-database hierarchy, so it is exactly as unique. Obfuscated the
|
||||
// same way, so --protect-ids exposes no new identifier.
|
||||
const std::string typeName
|
||||
= VIdProtect::protectWordsIf(m_covergroupp->name(), v3Global.opt.protectIds());
|
||||
m_constructorp->addStmtsp(
|
||||
itemCall(fl, m_cgInstVarp, VCMethod::COVERGROUP_ATTACH,
|
||||
{ctext(fl, "vlSymsp->_vm_contextp__->covergroupRegistryp()"
|
||||
"->newCovergroupInst("
|
||||
+ quoted(typeName) + ")")},
|
||||
/*usePtr=*/false)
|
||||
->makeStmt());
|
||||
}
|
||||
|
||||
// Emit 'this->__Vcp_x = this->__Vcg_inst.p()->addCoverpoint<K>();' (or addCross), which
|
||||
// creates the item runtime in the instance node and borrows a pointer to it.
|
||||
AstAssign* makeItemCreate(FileLine* fl, AstVar* itemVarp, VCMethod method) {
|
||||
// '__Vcg_inst.p()' -- a value handle, so '.' not '->'
|
||||
AstCMethodHard* const instp
|
||||
= new AstCMethodHard{fl, memberRef(fl, m_cgInstVarp), VCMethod::COVERGROUP_INST_P};
|
||||
instp->usePtr(false);
|
||||
instp->dtypeSetVoid(); // Opaque receiver; only ever the 'fromp' of the call below
|
||||
AstCMethodHard* const createp = new AstCMethodHard{fl, instp, method};
|
||||
createp->usePtr(true);
|
||||
createp->dtypep(itemVarp->dtypep());
|
||||
return new AstAssign{fl, memberRef(fl, itemVarp, VAccess::WRITE), createp};
|
||||
}
|
||||
|
||||
// Constant bounds of one rangesp() element (an InsideRange or a single Const). Each bound is
|
||||
// the raw AST node -- an AstConst or an AstUnbounded ('$') -- with the const/unbounded view
|
||||
// derived on demand, so there is one source of truth per bound. After a successful
|
||||
@@ -790,12 +838,42 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
}
|
||||
|
||||
// A 'this->m_member' reference for embedding in an AstCStmt
|
||||
AstVarRef* memberRef(FileLine* fl, AstVar* varp) {
|
||||
AstVarRef* const refp = new AstVarRef{fl, varp, VAccess::READ};
|
||||
AstVarRef* memberRef(FileLine* fl, AstVar* varp, VAccess access = VAccess::READ) {
|
||||
AstVarRef* const refp = new AstVarRef{fl, varp, access};
|
||||
refp->selfPointer(VSelfPointerText{VSelfPointerText::This{}});
|
||||
return refp;
|
||||
}
|
||||
|
||||
// A 'this->m_member-><method>(args...)' call on one member. usePtr is false only for
|
||||
// __Vcg_inst, which is a value handle; the item members are borrowed pointers into the
|
||||
// instance node. Numeric arguments are AstConst; the rest are C++ text that has no AST
|
||||
// form (see ctext).
|
||||
AstCMethodHard* itemCall(FileLine* fl, AstVar* varp, VCMethod method,
|
||||
const std::vector<AstNodeExpr*>& args = {}, bool usePtr = true) {
|
||||
AstCMethodHard* const callp = new AstCMethodHard{fl, memberRef(fl, varp), method};
|
||||
for (AstNodeExpr* const argp : args) callp->addPinsp(argp);
|
||||
callp->usePtr(usePtr);
|
||||
callp->dtypeSetVoid();
|
||||
return callp;
|
||||
}
|
||||
|
||||
// An unsigned integer argument.
|
||||
static AstConst* cnum(FileLine* fl, uint32_t value) { return new AstConst{fl, value}; }
|
||||
|
||||
// A literal C++ argument with no AST equivalent: a 'const char*' string literal (an SV
|
||||
// string AstConst emits '"..."s', a std::string temporary the runtime cannot borrow), a
|
||||
// VlCovBinKind enum token, or a '__V' temporary declared by the enclosing AstCStmt.
|
||||
static AstCExpr* ctext(FileLine* fl, const std::string& text) {
|
||||
return new AstCExpr{fl, text};
|
||||
}
|
||||
|
||||
// A C++ string literal. Escapes control characters as the emitter does elsewhere -- bin
|
||||
// names and filenames reach the generated code verbatim when --protect-ids is off, and an
|
||||
// SV escaped identifier may hold a quote or backslash.
|
||||
static std::string quoted(const std::string& text) {
|
||||
return "\"" + V3OutFormatter::quoteNameControls(text) + "\"";
|
||||
}
|
||||
|
||||
// Individual equality targets of an array bin (bins b[] = {values/ranges}), in order.
|
||||
// An open-ended bound ('$', AstUnbounded) resolves to the coverpoint domain: '[lo:$]'
|
||||
// covers [lo:maxVal] and '[$:hi]' covers [0:hi]. One target is produced per value; a
|
||||
@@ -855,32 +933,30 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
return values;
|
||||
}
|
||||
|
||||
// Emit a 'this->m_cp.addSingleNamer/addArrayNamer(...)' statement for one bin
|
||||
AstCStmt* makeNamer(AstVar* cpVarp, AstCoverBin* binp, int count) {
|
||||
// Emit a 'this->m_cp->addSingleNamer/addArrayNamer(...)' statement for one bin
|
||||
AstNodeStmt* makeNamer(AstVar* cpVarp, AstCoverBin* binp, int count) {
|
||||
FileLine* const fl = binp->fileline();
|
||||
CoverpointBins& bins = m_cpBins.at(cpVarp);
|
||||
const uint32_t normalCount
|
||||
= binp->binsType().binIsNormal() ? static_cast<uint32_t>(count < 0 ? 1 : count) : 0;
|
||||
bins.spans.emplace(binp->name(), std::make_pair(bins.total, normalCount));
|
||||
bins.total += normalCount;
|
||||
AstCStmt* const cs = new AstCStmt{fl};
|
||||
cs->add(memberRef(fl, cpVarp));
|
||||
// Under --protect-ids the filename and bin name flow into the coverage database
|
||||
// verbatim, so obfuscate them exactly as line/toggle coverage points are (whole-
|
||||
// unit filename, per-word bin name). A no-op when --protect-ids is off.
|
||||
const bool prot = v3Global.opt.protectIds();
|
||||
const std::string loc = "\"" + VIdProtect::protectIf(fl->filename(), prot) + "\", "
|
||||
+ std::to_string(fl->lineno()) + ", "
|
||||
+ std::to_string(fl->firstColumn()) + ");";
|
||||
const std::string binName = VIdProtect::protectWordsIf(binp->name(), prot);
|
||||
if (count < 0) { // single bin
|
||||
cs->add(".addSingleNamer(" + std::string{binp->binsType().binSetEnum()} + ", \""
|
||||
+ binName + "\", " + loc);
|
||||
} else { // value array bin
|
||||
cs->add(".addArrayNamer(" + std::string{binp->binsType().binSetEnum()} + ", "
|
||||
+ std::to_string(count) + ", \"" + binName + "\", " + loc);
|
||||
}
|
||||
return cs;
|
||||
const bool single = count < 0;
|
||||
std::vector<AstNodeExpr*> args{ctext(fl, binp->binsType().binSetEnum())};
|
||||
if (!single) args.push_back(cnum(fl, static_cast<uint32_t>(count))); // value array bin
|
||||
args.push_back(ctext(fl, quoted(VIdProtect::protectWordsIf(binp->name(), prot))));
|
||||
args.push_back(ctext(fl, quoted(VIdProtect::protectIf(fl->filename(), prot))));
|
||||
args.push_back(cnum(fl, static_cast<uint32_t>(fl->lineno())));
|
||||
args.push_back(cnum(fl, static_cast<uint32_t>(fl->firstColumn())));
|
||||
return itemCall(fl, cpVarp,
|
||||
single ? VCMethod::COVERGROUP_ADD_SINGLE_NAMER
|
||||
: VCMethod::COVERGROUP_ADD_ARRAY_NAMER,
|
||||
args)
|
||||
->makeStmt();
|
||||
}
|
||||
|
||||
// Emit 'if (iff && cond) m_cp.incrementBin(idx);' (or recordHit, + illegal action) in sample()
|
||||
@@ -894,11 +970,11 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
// Emit 'this->m_cp.incrementBin(idx);' (Normal) or '.recordHit(idx);'
|
||||
// (ignore/illegal/default).
|
||||
AstNodeStmt* makeRuntimeBinHit(FileLine* fl, const ConvBinTarget& tgt) {
|
||||
AstCStmt* const cs = new AstCStmt{fl};
|
||||
cs->add(memberRef(fl, tgt.cpVarp));
|
||||
cs->add((tgt.isNormal ? ".incrementBin(" : ".recordHit(") + std::to_string(tgt.idx)
|
||||
+ ");");
|
||||
return cs;
|
||||
return itemCall(fl, tgt.cpVarp,
|
||||
tgt.isNormal ? VCMethod::COVERGROUP_INCREMENT_BIN
|
||||
: VCMethod::COVERGROUP_RECORD_HIT,
|
||||
{cnum(fl, static_cast<uint32_t>(tgt.idx))})
|
||||
->makeStmt();
|
||||
}
|
||||
|
||||
void emitConvHitIf(AstCoverpoint* coverpointp, AstCoverBin* binp, AstVar* cpVarp, int idx,
|
||||
@@ -949,26 +1025,25 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
const int hitBound = computeHitListBound(coverpointp, exprp, crossFed);
|
||||
UINFO(6, " Hit-list bound (max bin overlap) = " << hitBound);
|
||||
AstVar* const cpVarp = new AstVar{fl, VVarType::MEMBER, "__Vcp_" + coverpointp->name(),
|
||||
vlCoverpointType(fl, hitBound)};
|
||||
cpVarp->isStatic(false);
|
||||
coverpointDType(fl, static_cast<uint32_t>(hitBound))};
|
||||
m_covergroupp->addMembersp(cpVarp);
|
||||
m_cpVars.push_back(cpVarp);
|
||||
m_cpVarMap[coverpointp->name()] = cpVarp;
|
||||
m_cpBins.emplace(cpVarp, CoverpointBins{});
|
||||
// Create the runtime in the instance node first; everything below configures it.
|
||||
m_constructorp->addStmtsp(makeItemCreate(fl, cpVarp, VCMethod::COVERGROUP_ADD_COVERPOINT));
|
||||
|
||||
// A cross reads this coverpoint's hit list, so clear it at the start of the
|
||||
// coverpoint's sample() contribution (before any incrementBin appends to it).
|
||||
if (crossFed) {
|
||||
AstCStmt* const clrp = new AstCStmt{fl};
|
||||
clrp->add(memberRef(fl, cpVarp));
|
||||
clrp->add(".clearHitList();");
|
||||
UASSERT_OBJ(m_sampleFuncp, coverpointp, "sample() CFunc not set for clearHitList");
|
||||
m_sampleFuncp->addStmtsp(clrp);
|
||||
m_sampleFuncp->addStmtsp(
|
||||
itemCall(fl, cpVarp, VCMethod::COVERGROUP_CLEAR_HIT_LIST)->makeStmt());
|
||||
}
|
||||
|
||||
// Walk bins (non-default, then default), assigning sequential indices that match the
|
||||
// namer append order; emit sample increments and collect namer statements.
|
||||
std::vector<AstCStmt*> namerStmts;
|
||||
std::vector<AstNodeStmt*> namerStmts;
|
||||
std::vector<AstCoverBin*> defaultBins;
|
||||
int idx = 0;
|
||||
for (AstNode* binp = coverpointp->binsp(); binp; binp = binp->nextp()) {
|
||||
@@ -1037,19 +1112,19 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
const bool prot = v3Global.opt.protectIds();
|
||||
const std::string hier
|
||||
= VIdProtect::protectWordsIf(m_covergroupp->name() + "." + coverpointp->name(), prot);
|
||||
AstCStmt* const initp = new AstCStmt{fl};
|
||||
initp->add(memberRef(fl, cpVarp));
|
||||
initp->add(".init(\"" + hier + "\", " + std::to_string(atLeastValue) + ", "
|
||||
+ std::to_string(idx) + ");");
|
||||
m_constructorp->addStmtsp(initp);
|
||||
for (AstCStmt* const ns : namerStmts) m_constructorp->addStmtsp(ns);
|
||||
m_constructorp->addStmtsp(
|
||||
itemCall(fl, cpVarp, VCMethod::COVERGROUP_INIT,
|
||||
{ctext(fl, quoted(hier)), cnum(fl, static_cast<uint32_t>(atLeastValue)),
|
||||
cnum(fl, static_cast<uint32_t>(idx))})
|
||||
->makeStmt());
|
||||
for (AstNodeStmt* const ns : namerStmts) m_constructorp->addStmtsp(ns);
|
||||
if (v3Global.opt.coverage()) {
|
||||
const std::string page
|
||||
= VIdProtect::protectIf("v_covergroup/" + m_covergroupp->name(), prot);
|
||||
AstCStmt* const regp = new AstCStmt{fl};
|
||||
regp->add(memberRef(fl, cpVarp));
|
||||
regp->add(".registerBins(vlSymsp->_vm_contextp__->coveragep(), \"" + page + "\");");
|
||||
m_constructorp->addStmtsp(regp);
|
||||
m_constructorp->addStmtsp(itemCall(fl, cpVarp, VCMethod::COVERGROUP_REGISTER_BINS,
|
||||
{ctext(fl, "vlSymsp->_vm_contextp__->coveragep()"),
|
||||
ctext(fl, quoted(page))})
|
||||
->makeStmt());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1321,34 +1396,53 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
// Append a "{ VlCoverpoint* __Vcx_cps[] = {&cp0, &cp1, ...}; <member>.<call> }" statement.
|
||||
AstCStmt* makeCrossCpsCall(FileLine* fl, const std::vector<AstVar*>& cpVars, AstVar* cxVarp,
|
||||
const std::string& callText,
|
||||
const std::vector<AstCoverCrossBin*>& bins = {}) {
|
||||
// "{ double __Vc = 0.0; double __Vt = 0.0; <item>->coverageParts(__Vc, __Vt);
|
||||
// __Vcov += __Vc; __Vtot += __Vt; }" -- one item's contribution to get_coverage().
|
||||
// The out-param temporaries make this a block, so only the call itself is a node.
|
||||
AstCStmt* makeCoveragePartsBlock(FileLine* fl, AstVar* itemVarp) {
|
||||
AstCStmt* const cs = new AstCStmt{fl};
|
||||
cs->add("{ ");
|
||||
if (!bins.empty()) {
|
||||
cs->add("const bool __Vcx_iffs[] = {");
|
||||
bool first = true;
|
||||
for (const AstCoverCrossBin* const binp : bins) {
|
||||
if (!first) cs->add(", ");
|
||||
first = false;
|
||||
cs->add(binp->iffp() ? binp->iffp()->cloneTree(false)
|
||||
: new AstConst{fl, AstConst::BitTrue{}});
|
||||
}
|
||||
cs->add("}; ");
|
||||
}
|
||||
cs->add("VlCoverpoint* __Vcx_cps[] = {");
|
||||
bool first = true;
|
||||
for (AstVar* const cpVarp : cpVars) {
|
||||
cs->add(first ? "&" : ", &");
|
||||
first = false;
|
||||
cs->add(memberRef(fl, cpVarp));
|
||||
cs->add("{ double __Vc = 0.0; double __Vt = 0.0; ");
|
||||
cs->add(itemCall(fl, itemVarp, VCMethod::COVERGROUP_COVERAGE_PARTS,
|
||||
{ctext(fl, "__Vc"), ctext(fl, "__Vt")}));
|
||||
cs->add("; __Vcov += __Vc; __Vtot += __Vt; }");
|
||||
return cs;
|
||||
}
|
||||
|
||||
// Append a "{ VlCoverpoint* __Vcx_cps[] = {cp0, cp1, ...}; <call> }" statement. The brace
|
||||
// and the temporary array stay literal text -- a CMethodHard is one call, not a block --
|
||||
// but callp itself carries the member, method and '->'. Construction only: init() copies
|
||||
// the array into the cross, so sample() reads it from there and needs no array at all.
|
||||
AstCStmt* makeCrossCpsCall(FileLine* fl, const std::vector<AstVar*>& cpVars,
|
||||
AstCMethodHard* callp) {
|
||||
AstCStmt* const cs = new AstCStmt{fl};
|
||||
cs->add("{ VlCoverpoint* __Vcx_cps[] = {");
|
||||
for (size_t d = 0; d < cpVars.size(); ++d) {
|
||||
if (d != 0) cs->add(", ");
|
||||
cs->add(memberRef(fl, cpVars[d]));
|
||||
}
|
||||
cs->add("}; ");
|
||||
cs->add(memberRef(fl, cxVarp));
|
||||
cs->add(callText);
|
||||
cs->add(" }");
|
||||
cs->add(callp);
|
||||
cs->add("; }");
|
||||
return cs;
|
||||
}
|
||||
|
||||
// Append a "{ const bool __Vcx_iffs[] = {<iff>, ...}; <call> }" statement, one entry per
|
||||
// explicit cross bin in declaration order (true where the bin has no iff). As above, the
|
||||
// temporary array is literal text because a CMethodHard is one call, not a block.
|
||||
AstCStmt* makeCrossIffsCall(FileLine* fl, const std::vector<AstCoverCrossBin*>& bins,
|
||||
AstCMethodHard* callp) {
|
||||
AstCStmt* const cs = new AstCStmt{fl};
|
||||
cs->add("{ const bool __Vcx_iffs[] = {");
|
||||
bool first = true;
|
||||
for (const AstCoverCrossBin* const binp : bins) {
|
||||
if (!first) cs->add(", ");
|
||||
first = false;
|
||||
cs->add(binp->iffp() ? binp->iffp()->cloneTree(false)
|
||||
: new AstConst{fl, AstConst::BitTrue{}});
|
||||
}
|
||||
cs->add("}; ");
|
||||
cs->add(callp);
|
||||
cs->add("; }");
|
||||
return cs;
|
||||
}
|
||||
|
||||
@@ -1393,24 +1487,20 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
if (!count) continue;
|
||||
FileLine* const fl = binp->fileline();
|
||||
const bool prot = v3Global.opt.protectIds();
|
||||
const std::string name = V3OutFormatter::quoteNameControls(
|
||||
VIdProtect::protectWordsIf(binp->name(), prot));
|
||||
const std::string file
|
||||
= V3OutFormatter::quoteNameControls(VIdProtect::protectIf(fl->filename(), prot));
|
||||
AstCStmt* const addp = new AstCStmt{fl};
|
||||
addp->add(memberRef(fl, cxVarp));
|
||||
addp->add(".addBin(" + std::to_string(dim) + ", " + std::to_string(first) + ", "
|
||||
+ std::to_string(count) + ", \"" + name + "\", \"" + file + "\", "
|
||||
+ std::to_string(fl->lineno()) + ", " + std::to_string(fl->firstColumn())
|
||||
+ ");");
|
||||
m_constructorp->addStmtsp(addp);
|
||||
m_constructorp->addStmtsp(
|
||||
itemCall(fl, cxVarp, VCMethod::COVERGROUP_ADD_BIN,
|
||||
{cnum(fl, dim), cnum(fl, first), cnum(fl, count),
|
||||
ctext(fl, quoted(VIdProtect::protectWordsIf(binp->name(), prot))),
|
||||
ctext(fl, quoted(VIdProtect::protectIf(fl->filename(), prot))),
|
||||
cnum(fl, static_cast<uint32_t>(fl->lineno())),
|
||||
cnum(fl, static_cast<uint32_t>(fl->firstColumn()))})
|
||||
->makeStmt());
|
||||
bins.push_back(binp);
|
||||
}
|
||||
if (!bins.empty()) {
|
||||
AstCStmt* const finishp = new AstCStmt{crossp->fileline()};
|
||||
finishp->add(memberRef(crossp->fileline(), cxVarp));
|
||||
finishp->add(".finalizeBins();");
|
||||
m_constructorp->addStmtsp(finishp);
|
||||
m_constructorp->addStmtsp(
|
||||
itemCall(crossp->fileline(), cxVarp, VCMethod::COVERGROUP_FINALIZE_BINS)
|
||||
->makeStmt());
|
||||
}
|
||||
return bins;
|
||||
}
|
||||
@@ -1443,42 +1533,47 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
}
|
||||
const int dims = static_cast<int>(cpVars.size());
|
||||
|
||||
if (!m_vlCoverCrossDTypep) {
|
||||
m_vlCoverCrossDTypep = new AstCDType{fl, "VlCoverCross"};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(m_vlCoverCrossDTypep);
|
||||
}
|
||||
AstVar* const cxVarp
|
||||
= new AstVar{fl, VVarType::MEMBER, "__Vcx_" + crossp->name(), m_vlCoverCrossDTypep};
|
||||
cxVarp->isStatic(false);
|
||||
AstVar* const cxVarp = new AstVar{fl, VVarType::MEMBER, "__Vcx_" + crossp->name(),
|
||||
basicDType(fl, VBasicDTypeKwd::COVERGROUP_CROSS)};
|
||||
m_covergroupp->addMembersp(cxVarp);
|
||||
m_crossVars.push_back(cxVarp);
|
||||
m_constructorp->addStmtsp(makeItemCreate(fl, cxVarp, VCMethod::COVERGROUP_ADD_CROSS));
|
||||
|
||||
// Constructor: init (after the coverpoints, which generate earlier) then registration.
|
||||
// Obfuscate the hierarchy/filename/page under --protect-ids as for coverpoints above.
|
||||
const bool prot = v3Global.opt.protectIds();
|
||||
const std::string hier
|
||||
= VIdProtect::protectWordsIf(m_covergroupp->name() + "." + crossp->name(), prot);
|
||||
const std::string initCall
|
||||
= ".init(\"" + hier + "\", " + std::to_string(dims) + ", __Vcx_cps, \""
|
||||
+ VIdProtect::protectIf(fl->filename(), prot) + "\", " + std::to_string(fl->lineno())
|
||||
+ ", " + std::to_string(fl->firstColumn()) + ");";
|
||||
m_constructorp->addStmtsp(makeCrossCpsCall(fl, cpVars, cxVarp, initCall));
|
||||
m_constructorp->addStmtsp(makeCrossCpsCall(
|
||||
fl, cpVars,
|
||||
itemCall(fl, cxVarp, VCMethod::COVERGROUP_INIT,
|
||||
{ctext(fl, quoted(hier)), cnum(fl, static_cast<uint32_t>(dims)),
|
||||
ctext(fl, "__Vcx_cps"),
|
||||
ctext(fl, quoted(VIdProtect::protectIf(fl->filename(), prot))),
|
||||
cnum(fl, static_cast<uint32_t>(fl->lineno())),
|
||||
cnum(fl, static_cast<uint32_t>(fl->firstColumn()))})));
|
||||
const std::vector<AstCoverCrossBin*> bins
|
||||
= generateCrossBins(crossp, cxVarp, cpVars, dimensions);
|
||||
if (v3Global.opt.coverage()) {
|
||||
const std::string page
|
||||
= VIdProtect::protectIf("v_covergroup/" + m_covergroupp->name(), prot);
|
||||
AstCStmt* const regp = new AstCStmt{fl};
|
||||
regp->add(memberRef(fl, cxVarp));
|
||||
regp->add(".registerBins(vlSymsp->_vm_contextp__->coveragep(), \"" + page + "\");");
|
||||
m_constructorp->addStmtsp(regp);
|
||||
m_constructorp->addStmtsp(itemCall(fl, cxVarp, VCMethod::COVERGROUP_REGISTER_BINS,
|
||||
{ctext(fl, "vlSymsp->_vm_contextp__->coveragep()"),
|
||||
ctext(fl, quoted(page))})
|
||||
->makeStmt());
|
||||
}
|
||||
|
||||
// sample(): after all coverpoints have sampled (cross loop runs after coverpoint loop).
|
||||
UASSERT_OBJ(m_sampleFuncp, crossp, "sample() CFunc not set for cross");
|
||||
AstNodeStmt* const samplep = makeCrossCpsCall(
|
||||
fl, cpVars, cxVarp,
|
||||
bins.empty() ? ".sample(__Vcx_cps);" : ".sample(__Vcx_cps, __Vcx_iffs);", bins);
|
||||
// The cross reads its coverpoints from its own m_cps, so sample() needs no cps array;
|
||||
// per-bin iff guards still need a temporary array, hence the block form.
|
||||
AstNodeStmt* const samplep
|
||||
= bins.empty() ? static_cast<AstNodeStmt*>(
|
||||
itemCall(fl, cxVarp, VCMethod::COVERGROUP_SAMPLE)->makeStmt())
|
||||
: static_cast<AstNodeStmt*>(makeCrossIffsCall(
|
||||
fl, bins,
|
||||
itemCall(fl, cxVarp, VCMethod::COVERGROUP_SAMPLE_IFFS,
|
||||
{ctext(fl, "__Vcx_iffs")})));
|
||||
if (AstNodeExpr* const iffp = crossp->iffp()) {
|
||||
m_sampleFuncp->addStmtsp(new AstIf{fl, iffp->cloneTree(false), samplep});
|
||||
} else {
|
||||
@@ -1693,19 +1788,11 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
headp->add("double __Vcov = 0.0; double __Vtot = 0.0;");
|
||||
funcp->addStmtsp(headp);
|
||||
for (AstVar* const cpVarp : m_cpVars) {
|
||||
AstCStmt* const cs = new AstCStmt{fl};
|
||||
cs->add("{ double __Vc = 0.0; double __Vt = 0.0; ");
|
||||
cs->add(memberRef(fl, cpVarp));
|
||||
cs->add(".coverageParts(__Vc, __Vt); __Vcov += __Vc; __Vtot += __Vt; }");
|
||||
funcp->addStmtsp(cs);
|
||||
funcp->addStmtsp(makeCoveragePartsBlock(fl, cpVarp));
|
||||
}
|
||||
// Crosses contribute the same covered/total ratio as their per-tuple bins.
|
||||
for (AstVar* const cxVarp : m_crossVars) {
|
||||
AstCStmt* const cs = new AstCStmt{fl};
|
||||
cs->add("{ double __Vc = 0.0; double __Vt = 0.0; ");
|
||||
cs->add(memberRef(fl, cxVarp));
|
||||
cs->add(".coverageParts(__Vc, __Vt); __Vcov += __Vc; __Vtot += __Vt; }");
|
||||
funcp->addStmtsp(cs);
|
||||
funcp->addStmtsp(makeCoveragePartsBlock(fl, cxVarp));
|
||||
}
|
||||
AstCStmt* const retp = new AstCStmt{fl};
|
||||
retp->add(new AstVarRef{fl, returnVarp, VAccess::WRITE});
|
||||
@@ -1936,7 +2023,6 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
= trigger.memberVarp ? trigger.memberVarp->dtypep() : trigger.baseVarp->dtypep();
|
||||
AstVar* const prevVarp = new AstVar{trigger.eventFl, VVarType::MEMBER,
|
||||
eventPrevName(trigger, triggerIndex), dtypep};
|
||||
prevVarp->isStatic(false);
|
||||
m_enclosingClassp->addMembersp(prevVarp);
|
||||
trigger.prevVarp = prevVarp;
|
||||
for (AstNodeAssign* const asgnp : assignps) {
|
||||
@@ -2102,7 +2188,6 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
v3Global.rootp()->typeTablep()->addTypesp(enclDTypep);
|
||||
AstVar* const handleVarp
|
||||
= new AstVar{fl, VVarType::MEMBER, "__Vcg_enclosingp", enclDTypep};
|
||||
handleVarp->isStatic(false);
|
||||
m_covergroupp->addMembersp(handleVarp);
|
||||
|
||||
// Route each enclosing-member reference through the back-pointer: 'm' -> 'h.m'.
|
||||
|
||||
Reference in New Issue
Block a user