Support hierarchical reference cross members (#7749) (#7820)

This commit is contained in:
Matthew Ballance 2026-06-24 04:47:36 -07:00 committed by GitHub
parent 995534d3ed
commit d456384d39
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 92 additions and 12 deletions

View File

@ -1185,12 +1185,21 @@ public:
};
class AstCoverpointRef final : public AstNode {
// Reference to a coverpoint used in a cross
const string m_name; // coverpoint name
// @astgen op1 := exprp : Optional[AstNodeExpr] // Non-standard: hierarchical/dotted
// // reference (implicit coverpoint), e.g.
// // 'cross a.b'; nullptr for a plain coverpoint
// // name. An AstDot at parse time, resolved
// // by the time V3Covergroup runs.
const string m_name; // coverpoint name; empty when exprp() carries a hierarchical reference
public:
AstCoverpointRef(FileLine* fl, const string& name)
: ASTGEN_SUPER_CoverpointRef(fl)
, m_name{name} {}
AstCoverpointRef(FileLine* fl, AstNodeExpr* exprp)
: ASTGEN_SUPER_CoverpointRef(fl) {
this->exprp(exprp);
}
ASTGEN_MEMBERS_AstCoverpointRef;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;

View File

@ -98,7 +98,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
for (AstCoverCross* crossp : m_coverCrosses) {
for (AstNode* itemp = crossp->itemsp(); itemp; itemp = itemp->nextp()) {
if (const AstCoverpointRef* const refp = VN_CAST(itemp, CoverpointRef))
m_crossedCpNames.insert(refp->name());
if (!refp->exprp()) m_crossedCpNames.insert(refp->name());
}
}
@ -1295,6 +1295,18 @@ class FunctionalCoverageVisitor final : public VNVisitor {
while (itemp) {
AstNode* const nextp = itemp->nextp();
AstCoverpointRef* const refp = VN_AS(itemp, CoverpointRef);
if (refp->exprp()) {
// Non-standard hierarchical/dotted cross item (e.g. 'cross a.b'): an implicit
// coverpoint over the referenced expression (carried in refp->exprp()). The
// grammar already warned NONSTD; implicit coverpoints are not yet implemented, so
// generate no sampling code for this cross. When support is added the implicit
// coverpoint should be synthesized upstream (V3LinkParse) as a real AstCoverpoint
// so it flows through the normal coverpoint path - by here coverpoint lowering has
// already run.
refp->v3warn(COVERIGN,
"Unsupported: cross of hierarchical reference (implicit coverpoint)");
return;
}
// Find the referenced coverpoint via name map (O(log n) vs O(n) linear scan)
const auto it = m_coverpointMap.find(refp->name());
AstCoverpoint* const foundCpp = (it != m_coverpointMap.end()) ? it->second : nullptr;

View File

@ -7339,8 +7339,29 @@ cross_itemList<nodep>: // IEEE: part of list_of_cross_items
;
cross_item<nodep>: // ==IEEE: cross_item
id/*cover_point_identifier*/
{ $$ = new AstCoverpointRef{$<fl>1, *$1}; }
// // IEEE: cover_point_identifier | variable_identifier - both are a
// // simple identifier. We parse idDotted (a plain hierarchical
// // reference a.b.c, with no bit/array selects) to also accept the
// // non-standard dotted form (e.g. 'cross a.b') that several
// // simulators support; the common simple-identifier case is detected
// // and handled exactly as before.
idDotted
{
if (AstParseRef* const refp = VN_CAST($1, ParseRef)) {
// Standard: simple cover_point_identifier / variable_identifier
$$ = new AstCoverpointRef{refp->fileline(), refp->name()};
VL_DO_DANGLING(refp->deleteTree(), refp);
} else {
// Verilator extension beyond strict IEEE (cross_item is a simple
// identifier): some tools accept a hierarchical/dotted reference.
// Carry the reference expression (still an AstDot here) out of the
// parser unchanged; later stages resolve and, eventually, implement
// it as an implicit coverpoint.
$1->v3warn(NONSTD, "Non-standard hierarchical reference as a coverage "
"cross item (an implicit coverpoint)");
$$ = new AstCoverpointRef{$1->fileline(), $1};
}
}
;
cross_body<nodep>: // ==IEEE: cross_body

View File

@ -109,10 +109,10 @@ cg_range.cp_addr.hi_range: 2
cg_range.cp_addr.lo_range: 2
cg_range.cp_cmd.read: 2
cg_range.cp_cmd.write: 2
cg_unnamed_cross.__cross7.a0_x_read [cross]: 1
cg_unnamed_cross.__cross7.a0_x_write [cross]: 0
cg_unnamed_cross.__cross7.a1_x_read [cross]: 0
cg_unnamed_cross.__cross7.a1_x_write [cross]: 1
cg_unnamed_cross.__cross8.a0_x_read [cross]: 1
cg_unnamed_cross.__cross8.a0_x_write [cross]: 0
cg_unnamed_cross.__cross8.a1_x_read [cross]: 0
cg_unnamed_cross.__cross8.a1_x_write [cross]: 1
cg_unnamed_cross.cp_a.a0: 1
cg_unnamed_cross.cp_a.a1: 1
cg_unnamed_cross.cp_c.read: 1

View File

@ -17,6 +17,9 @@ module t;
logic mode;
logic parity;
typedef struct packed {logic m_p; logic h_mode;} cfg_t;
cfg_t s_cfg = '0;
// 2-way cross
covergroup cg2;
cp_addr: coverpoint addr {bins addr0 = {0}; bins addr1 = {1};}
@ -90,6 +93,12 @@ module t;
addr_cmd_unsup: cross cp_addr, cp_cmd{
option.per_instance = 1; // unsupported for cross - expect COVERIGN warning
}
// Non-standard hierarchical reference as a cross item (an implicit coverpoint):
// accepted with NONSTD, but implicit coverpoints are unsupported so the whole
// cross is dropped (COVERIGN, suppressed here) - it contributes no bins.
/* verilator lint_off NONSTD */
cross_hier: cross cp_addr, s_cfg.m_p;
/* verilator lint_on NONSTD */
endgroup
// Covergroup with an unnamed cross - the cross is reported under the default name "cross"

View File

@ -1,3 +1,8 @@
%Warning-NONSTD: t/t_covergroup_cross_opt_unsup.v:19:34: Non-standard hierarchical reference as a coverage cross item (an implicit coverpoint)
19 | cross_hier: cross cp_a, s_cfg.m_p;
| ^
... For warning description see https://verilator.org/warn/NONSTD?v=latest
... Use "/* verilator lint_off NONSTD */" and lint_on around source to disable this message.
%Warning-COVERIGN: t/t_covergroup_cross_opt_unsup.v:13:7: Ignoring unsupported coverage cross option: 'per_instance'
13 | option.per_instance = 1;
| ^~~~~~
@ -7,4 +12,8 @@
: ... note: In instance 't'
15 | cross_implicit: cross cp_a, var_x;
| ^~~~~
%Warning-COVERIGN: t/t_covergroup_cross_opt_unsup.v:19:34: Unsupported: cross of hierarchical reference (implicit coverpoint)
: ... note: In instance 't'
19 | cross_hier: cross cp_a, s_cfg.m_p;
| ^
%Error: Exiting due to

View File

@ -13,7 +13,13 @@ module t;
option.per_instance = 1; // unsupported for cross; triggers COVERIGN
}
cross_implicit: cross cp_a, var_x;
// Non-standard hierarchical/dotted cross item: can only be a data reference
// (implicit coverpoint), never a coverpoint. Accepted with a NONSTD warning;
// implicit coverpoints are unsupported so the cross is dropped (COVERIGN).
cross_hier: cross cp_a, s_cfg.m_p;
endgroup
typedef struct packed {logic m_p; logic h_mode;} cfg_t;
cfg_t s_cfg = '0;
logic var_x = 1'b0;
cg cg_i = new;
initial begin

View File

@ -28,6 +28,14 @@
-000001 point: type=toggle comment=parity:0->1 hier=top.t
-000000 point: type=toggle comment=parity:1->0 hier=top.t
typedef struct packed {logic m_p; logic h_mode;} cfg_t;
%000001 cfg_t s_cfg = '0;
-000001 point: type=line comment=block hier=top.t
-000000 point: type=toggle comment=s_cfg.h_mode:0->1 hier=top.t
-000000 point: type=toggle comment=s_cfg.h_mode:1->0 hier=top.t
-000000 point: type=toggle comment=s_cfg.m_p:0->1 hier=top.t
-000000 point: type=toggle comment=s_cfg.m_p:1->0 hier=top.t
// 2-way cross
covergroup cg2;
%000002 cp_addr: coverpoint addr {bins addr0 = {0}; bins addr1 = {1};}
@ -257,6 +265,12 @@
// cross: [addr1, write]
option.per_instance = 1; // unsupported for cross - expect COVERIGN warning
}
// Non-standard hierarchical reference as a cross item (an implicit coverpoint):
// accepted with NONSTD, but implicit coverpoints are unsupported so the whole
// cross is dropped (COVERIGN, suppressed here) - it contributes no bins.
/* verilator lint_off NONSTD */
cross_hier: cross cp_addr, s_cfg.m_p;
/* verilator lint_on NONSTD */
endgroup
// Covergroup with an unnamed cross - the cross is reported under the default name "cross"
@ -268,13 +282,13 @@
-000001 point: type=covergroup comment= hier=cg_unnamed_cross.cp_c.read
-000001 point: type=covergroup comment= hier=cg_unnamed_cross.cp_c.write
%000001 cross cp_a, cp_c; // no label: reported under the default cross name
-000001 point: type=covergroup comment= hier=cg_unnamed_cross.__cross7.a0_x_read
-000001 point: type=covergroup comment= hier=cg_unnamed_cross.__cross8.a0_x_read
// cross: [a0, read]
-000000 point: type=covergroup comment= hier=cg_unnamed_cross.__cross7.a0_x_write
-000000 point: type=covergroup comment= hier=cg_unnamed_cross.__cross8.a0_x_write
// cross: [a0, write]
-000000 point: type=covergroup comment= hier=cg_unnamed_cross.__cross7.a1_x_read
-000000 point: type=covergroup comment= hier=cg_unnamed_cross.__cross8.a1_x_read
// cross: [a1, read]
-000001 point: type=covergroup comment= hier=cg_unnamed_cross.__cross7.a1_x_write
-000001 point: type=covergroup comment= hier=cg_unnamed_cross.__cross8.a1_x_write
// cross: [a1, write]
endgroup