diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 19f4d07ee..666a3ec22 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -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; diff --git a/src/V3Covergroup.cpp b/src/V3Covergroup.cpp index 054f27c34..b1890e001 100644 --- a/src/V3Covergroup.cpp +++ b/src/V3Covergroup.cpp @@ -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; diff --git a/src/verilog.y b/src/verilog.y index 6d6efea6b..db145ea8e 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -7339,8 +7339,29 @@ cross_itemList: // IEEE: part of list_of_cross_items ; cross_item: // ==IEEE: cross_item - id/*cover_point_identifier*/ - { $$ = new AstCoverpointRef{$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: // ==IEEE: cross_body diff --git a/test_regress/t/t_covergroup_cross.out b/test_regress/t/t_covergroup_cross.out index a7311da80..abb035e5e 100644 --- a/test_regress/t/t_covergroup_cross.out +++ b/test_regress/t/t_covergroup_cross.out @@ -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 diff --git a/test_regress/t/t_covergroup_cross.v b/test_regress/t/t_covergroup_cross.v index e86f179a4..f0857bc01 100644 --- a/test_regress/t/t_covergroup_cross.v +++ b/test_regress/t/t_covergroup_cross.v @@ -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" diff --git a/test_regress/t/t_covergroup_cross_opt_unsup.out b/test_regress/t/t_covergroup_cross_opt_unsup.out index d07db1513..e1e2c5621 100644 --- a/test_regress/t/t_covergroup_cross_opt_unsup.out +++ b/test_regress/t/t_covergroup_cross_opt_unsup.out @@ -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 diff --git a/test_regress/t/t_covergroup_cross_opt_unsup.v b/test_regress/t/t_covergroup_cross_opt_unsup.v index bb63a86be..720fbad6c 100644 --- a/test_regress/t/t_covergroup_cross_opt_unsup.v +++ b/test_regress/t/t_covergroup_cross_opt_unsup.v @@ -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 diff --git a/test_regress/t/t_vlcov_covergroup.annotate.out b/test_regress/t/t_vlcov_covergroup.annotate.out index b8616ad2d..16b86a66e 100644 --- a/test_regress/t/t_vlcov_covergroup.annotate.out +++ b/test_regress/t/t_vlcov_covergroup.annotate.out @@ -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