From 2886291eba1e0e67f6ade4045d680ed97d1bfa59 Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Fri, 5 Jun 2026 06:35:01 -0700 Subject: [PATCH] Support covergroups, coverpoints, and bins (#784) (#7117) Fixes #784. --- docs/guide/exe_verilator_coverage.rst | 10 +- docs/guide/languages.rst | 19 +- docs/guide/simulating.rst | 30 +- include/verilated_cov_key.h | 3 + src/CMakeLists.txt | 3 + src/Makefile_obj.in | 1 + src/V3Active.cpp | 139 ++ src/V3AstAttr.h | 114 ++ src/V3AstNodeOther.h | 213 ++- src/V3AstNodes.cpp | 51 + src/V3Coverage.cpp | 13 +- src/V3Covergroup.cpp | 1665 +++++++++++++++++ src/V3Covergroup.h | 30 + src/V3EmitV.cpp | 77 + src/V3Global.h | 3 + src/V3LinkParse.cpp | 263 +++ src/V3ParseGrammar.h | 93 +- src/V3Task.cpp | 1 + src/V3Timing.cpp | 24 +- src/V3Width.cpp | 43 +- src/Verilator.cpp | 5 + src/VlcPoint.h | 22 + src/VlcTop.cpp | 4 +- src/verilog.y | 369 ++-- test_regress/t/coverage_covergroup_common.py | 53 + test_regress/t/t_covergroup_args.out | 6 + test_regress/t/t_covergroup_args.py | 6 +- test_regress/t/t_covergroup_args.v | 78 +- test_regress/t/t_covergroup_array_bins.out | 4 + test_regress/t/t_covergroup_array_bins.py | 15 + test_regress/t/t_covergroup_array_bins.v | 94 + test_regress/t/t_covergroup_auto_bin_max.out | 27 + test_regress/t/t_covergroup_auto_bin_max.py | 15 + test_regress/t/t_covergroup_auto_bin_max.v | 115 ++ .../t/t_covergroup_auto_bin_max_bad.out | 7 + .../t/t_covergroup_auto_bin_max_bad.py | 15 + .../t/t_covergroup_auto_bin_max_bad.v | 20 + test_regress/t/t_covergroup_auto_bins.out | 15 + test_regress/t/t_covergroup_auto_bins.py | 15 + test_regress/t/t_covergroup_auto_bins.v | 86 + .../t/t_covergroup_auto_sample_timing.out | 4 + .../t/t_covergroup_auto_sample_timing.py | 18 + test_regress/t/t_covergroup_autobins_bad.out | 76 + ..._class.py => t_covergroup_autobins_bad.py} | 2 +- test_regress/t/t_covergroup_autobins_bad.v | 75 + test_regress/t/t_covergroup_bin_counts.out | 18 + test_regress/t/t_covergroup_bin_counts.py | 24 + test_regress/t/t_covergroup_bin_counts.v | 163 ++ .../t/t_covergroup_clocked_sample.out | 4 + test_regress/t/t_covergroup_clocked_sample.py | 15 + test_regress/t/t_covergroup_clocked_sample.v | 45 + .../t_covergroup_coverpoint_method_unsup.out | 40 + ...> t_covergroup_coverpoint_method_unsup.py} | 0 ...=> t_covergroup_coverpoint_method_unsup.v} | 0 .../t/t_covergroup_coverpoints_unsup.out | 52 - test_regress/t/t_covergroup_cross.out | 108 ++ test_regress/t/t_covergroup_cross.py | 15 + test_regress/t/t_covergroup_cross.v | 264 +++ .../t/t_covergroup_cross_opt_unsup.out | 10 + .../t/t_covergroup_cross_opt_unsup.py | 15 + test_regress/t/t_covergroup_cross_opt_unsup.v | 23 + test_regress/t/t_covergroup_default_bins.out | 36 + test_regress/t/t_covergroup_default_bins.py | 15 + test_regress/t/t_covergroup_default_bins.v | 206 ++ .../t/t_covergroup_embedded_unsup.out | 7 + test_regress/t/t_covergroup_embedded_unsup.py | 16 + test_regress/t/t_covergroup_embedded_unsup.v | 45 + test_regress/t/t_covergroup_empty.out | 2 + test_regress/t/t_covergroup_empty.py | 15 + test_regress/t/t_covergroup_empty.v | 40 + test_regress/t/t_covergroup_extends.v | 39 - .../t/t_covergroup_extends_newfirst.v | 39 - .../t/t_covergroup_func_override_bad.out | 4 +- .../t/t_covergroup_func_override_bad.v | 1 - test_regress/t/t_covergroup_iff.out | 19 + test_regress/t/t_covergroup_iff.py | 15 + test_regress/t/t_covergroup_iff.v | 219 +++ test_regress/t/t_covergroup_ignore_bins.out | 20 + test_regress/t/t_covergroup_ignore_bins.py | 15 + test_regress/t/t_covergroup_ignore_bins.v | 90 + test_regress/t/t_covergroup_illegal_bins.out | 13 + test_regress/t/t_covergroup_illegal_bins.py | 15 + test_regress/t/t_covergroup_illegal_bins.v | 76 + test_regress/t/t_covergroup_in_class.v | 19 - .../t/t_covergroup_in_class_colliding.v | 32 - .../t/t_covergroup_in_class_duplicate_bad.out | 12 +- .../t/t_covergroup_in_class_duplicate_bad.v | 1 - .../t/t_covergroup_in_class_namespace.out | 4 + ....py => t_covergroup_in_class_namespace.py} | 6 +- .../t/t_covergroup_in_class_namespace.v | 45 + .../t/t_covergroup_in_class_with_sample.py | 16 - .../t/t_covergroup_in_class_with_sample.v | 15 - .../t/t_covergroup_member_event_unsup.out | 7 + .../t/t_covergroup_member_event_unsup.py | 16 + .../t/t_covergroup_member_event_unsup.v | 19 + test_regress/t/t_covergroup_method_bad.out | 11 - test_regress/t/t_covergroup_method_bad.py | 18 - test_regress/t/t_covergroup_method_bad.v | 20 - .../t/t_covergroup_negative_ranges.out | 4 + .../t/t_covergroup_negative_ranges.py | 12 + test_regress/t/t_covergroup_negative_ranges.v | 62 + .../t/t_covergroup_new_override_bad.out | 4 +- .../t/t_covergroup_new_override_bad.v | 1 - test_regress/t/t_covergroup_option.out | 18 + test_regress/t/t_covergroup_option.py | 9 +- test_regress/t/t_covergroup_option.v | 60 +- test_regress/t/t_covergroup_option_bad.out | 21 +- test_regress/t/t_covergroup_option_bad.py | 6 +- test_regress/t/t_covergroup_option_bad.v | 49 +- test_regress/t/t_covergroup_option_bad2.out | 12 - test_regress/t/t_covergroup_option_bad2.py | 18 - test_regress/t/t_covergroup_option_bad2.v | 22 - test_regress/t/t_covergroup_option_unsup.out | 6 + ...xtends.py => t_covergroup_option_unsup.py} | 2 +- test_regress/t/t_covergroup_option_unsup.v | 21 + .../t/t_covergroup_static_coverage.out | 5 + .../t/t_covergroup_static_coverage.py | 15 + test_regress/t/t_covergroup_static_coverage.v | 74 + test_regress/t/t_covergroup_trans.out | 9 + test_regress/t/t_covergroup_trans.py | 15 + test_regress/t/t_covergroup_trans.v | 61 + .../t/t_covergroup_trans_errors_bad.out | 15 + ...ng.py => t_covergroup_trans_errors_bad.py} | 2 +- .../t/t_covergroup_trans_errors_bad.v | 22 + test_regress/t/t_covergroup_trans_restart.out | 1 + test_regress/t/t_covergroup_trans_restart.py | 15 + test_regress/t/t_covergroup_trans_restart.v | 54 + .../t/t_covergroup_undef_field_bad.out | 8 + .../t/t_covergroup_undef_field_bad.py | 16 + test_regress/t/t_covergroup_undef_field_bad.v | 24 + test_regress/t/t_covergroup_unsup.out | 514 ++--- test_regress/t/t_covergroup_unsup.py | 4 +- test_regress/t/t_covergroup_unsup.v | 38 +- test_regress/t/t_covergroup_unsup_ign.out | 31 + test_regress/t/t_covergroup_unsup_ign.py | 4 +- test_regress/t/t_covergroup_unsup_ign2.out | 306 +++ test_regress/t/t_covergroup_unsup_ign2.py | 3 +- test_regress/t/t_covergroup_wildcard_bins.out | 5 + test_regress/t/t_covergroup_wildcard_bins.py | 15 + test_regress/t/t_covergroup_wildcard_bins.v | 80 + .../t/t_covergroup_with_function_foo_bad.out | 4 +- .../t/t_covergroup_with_function_foo_bad.v | 1 - .../t/t_covergroup_with_sample_args.py | 16 - .../t/t_covergroup_with_sample_args.v | 17 - .../t_covergroup_with_sample_args_default.py | 16 - .../t/t_covergroup_with_sample_args_default.v | 18 - ...overgroup_with_sample_args_too_few_bad.out | 4 +- ..._covergroup_with_sample_args_too_few_bad.v | 1 - ...vergroup_with_sample_args_too_many_bad.out | 2 +- ...overgroup_with_sample_args_too_many_bad.py | 4 +- ...covergroup_with_sample_args_too_many_bad.v | 6 +- .../t/t_covergroup_with_sample_namedargs.py | 16 - .../t/t_covergroup_with_sample_namedargs.v | 15 - .../t/t_covergroup_with_sample_zeroargs.py | 16 - .../t/t_covergroup_with_sample_zeroargs.v | 15 - test_regress/t/t_debug_emitv.out | 292 ++- test_regress/t/t_debug_emitv.py | 4 + test_regress/t/t_debug_emitv.v | 62 + test_regress/t/t_debug_emitv_addrids.py | 5 +- test_regress/t/t_dist_warn_coverage.py | 2 + test_regress/t/t_dump.v | 10 + .../t/t_vlcov_covergroup.annotate.out | 551 ++++++ test_regress/t/t_vlcov_covergroup.py | 33 + 163 files changed, 7575 insertions(+), 1123 deletions(-) create mode 100644 src/V3Covergroup.cpp create mode 100644 src/V3Covergroup.h create mode 100644 test_regress/t/coverage_covergroup_common.py create mode 100644 test_regress/t/t_covergroup_args.out create mode 100644 test_regress/t/t_covergroup_array_bins.out create mode 100755 test_regress/t/t_covergroup_array_bins.py create mode 100644 test_regress/t/t_covergroup_array_bins.v create mode 100644 test_regress/t/t_covergroup_auto_bin_max.out create mode 100755 test_regress/t/t_covergroup_auto_bin_max.py create mode 100644 test_regress/t/t_covergroup_auto_bin_max.v create mode 100644 test_regress/t/t_covergroup_auto_bin_max_bad.out create mode 100755 test_regress/t/t_covergroup_auto_bin_max_bad.py create mode 100644 test_regress/t/t_covergroup_auto_bin_max_bad.v create mode 100644 test_regress/t/t_covergroup_auto_bins.out create mode 100755 test_regress/t/t_covergroup_auto_bins.py create mode 100644 test_regress/t/t_covergroup_auto_bins.v create mode 100644 test_regress/t/t_covergroup_auto_sample_timing.out create mode 100755 test_regress/t/t_covergroup_auto_sample_timing.py create mode 100644 test_regress/t/t_covergroup_autobins_bad.out rename test_regress/t/{t_covergroup_in_class.py => t_covergroup_autobins_bad.py} (88%) create mode 100644 test_regress/t/t_covergroup_autobins_bad.v create mode 100644 test_regress/t/t_covergroup_bin_counts.out create mode 100755 test_regress/t/t_covergroup_bin_counts.py create mode 100644 test_regress/t/t_covergroup_bin_counts.v create mode 100644 test_regress/t/t_covergroup_clocked_sample.out create mode 100755 test_regress/t/t_covergroup_clocked_sample.py create mode 100644 test_regress/t/t_covergroup_clocked_sample.v create mode 100644 test_regress/t/t_covergroup_coverpoint_method_unsup.out rename test_regress/t/{t_covergroup_coverpoints_unsup.py => t_covergroup_coverpoint_method_unsup.py} (100%) rename test_regress/t/{t_covergroup_coverpoints_unsup.v => t_covergroup_coverpoint_method_unsup.v} (100%) delete mode 100644 test_regress/t/t_covergroup_coverpoints_unsup.out create mode 100644 test_regress/t/t_covergroup_cross.out create mode 100755 test_regress/t/t_covergroup_cross.py create mode 100644 test_regress/t/t_covergroup_cross.v create mode 100644 test_regress/t/t_covergroup_cross_opt_unsup.out create mode 100755 test_regress/t/t_covergroup_cross_opt_unsup.py create mode 100644 test_regress/t/t_covergroup_cross_opt_unsup.v create mode 100644 test_regress/t/t_covergroup_default_bins.out create mode 100755 test_regress/t/t_covergroup_default_bins.py create mode 100644 test_regress/t/t_covergroup_default_bins.v create mode 100644 test_regress/t/t_covergroup_embedded_unsup.out create mode 100755 test_regress/t/t_covergroup_embedded_unsup.py create mode 100644 test_regress/t/t_covergroup_embedded_unsup.v create mode 100644 test_regress/t/t_covergroup_empty.out create mode 100755 test_regress/t/t_covergroup_empty.py create mode 100644 test_regress/t/t_covergroup_empty.v delete mode 100644 test_regress/t/t_covergroup_extends.v delete mode 100644 test_regress/t/t_covergroup_extends_newfirst.v create mode 100644 test_regress/t/t_covergroup_iff.out create mode 100755 test_regress/t/t_covergroup_iff.py create mode 100644 test_regress/t/t_covergroup_iff.v create mode 100644 test_regress/t/t_covergroup_ignore_bins.out create mode 100755 test_regress/t/t_covergroup_ignore_bins.py create mode 100644 test_regress/t/t_covergroup_ignore_bins.v create mode 100644 test_regress/t/t_covergroup_illegal_bins.out create mode 100755 test_regress/t/t_covergroup_illegal_bins.py create mode 100644 test_regress/t/t_covergroup_illegal_bins.v delete mode 100644 test_regress/t/t_covergroup_in_class.v delete mode 100644 test_regress/t/t_covergroup_in_class_colliding.v create mode 100644 test_regress/t/t_covergroup_in_class_namespace.out rename test_regress/t/{t_covergroup_extends_newfirst.py => t_covergroup_in_class_namespace.py} (85%) create mode 100644 test_regress/t/t_covergroup_in_class_namespace.v delete mode 100755 test_regress/t/t_covergroup_in_class_with_sample.py delete mode 100644 test_regress/t/t_covergroup_in_class_with_sample.v create mode 100644 test_regress/t/t_covergroup_member_event_unsup.out create mode 100755 test_regress/t/t_covergroup_member_event_unsup.py create mode 100644 test_regress/t/t_covergroup_member_event_unsup.v delete mode 100644 test_regress/t/t_covergroup_method_bad.out delete mode 100755 test_regress/t/t_covergroup_method_bad.py delete mode 100644 test_regress/t/t_covergroup_method_bad.v create mode 100644 test_regress/t/t_covergroup_negative_ranges.out create mode 100755 test_regress/t/t_covergroup_negative_ranges.py create mode 100644 test_regress/t/t_covergroup_negative_ranges.v create mode 100644 test_regress/t/t_covergroup_option.out delete mode 100644 test_regress/t/t_covergroup_option_bad2.out delete mode 100755 test_regress/t/t_covergroup_option_bad2.py delete mode 100644 test_regress/t/t_covergroup_option_bad2.v create mode 100644 test_regress/t/t_covergroup_option_unsup.out rename test_regress/t/{t_covergroup_extends.py => t_covergroup_option_unsup.py} (88%) create mode 100644 test_regress/t/t_covergroup_option_unsup.v create mode 100644 test_regress/t/t_covergroup_static_coverage.out create mode 100755 test_regress/t/t_covergroup_static_coverage.py create mode 100644 test_regress/t/t_covergroup_static_coverage.v create mode 100644 test_regress/t/t_covergroup_trans.out create mode 100755 test_regress/t/t_covergroup_trans.py create mode 100644 test_regress/t/t_covergroup_trans.v create mode 100644 test_regress/t/t_covergroup_trans_errors_bad.out rename test_regress/t/{t_covergroup_in_class_colliding.py => t_covergroup_trans_errors_bad.py} (88%) create mode 100644 test_regress/t/t_covergroup_trans_errors_bad.v create mode 100644 test_regress/t/t_covergroup_trans_restart.out create mode 100755 test_regress/t/t_covergroup_trans_restart.py create mode 100644 test_regress/t/t_covergroup_trans_restart.v create mode 100644 test_regress/t/t_covergroup_undef_field_bad.out create mode 100755 test_regress/t/t_covergroup_undef_field_bad.py create mode 100644 test_regress/t/t_covergroup_undef_field_bad.v create mode 100644 test_regress/t/t_covergroup_unsup_ign.out create mode 100644 test_regress/t/t_covergroup_unsup_ign2.out create mode 100644 test_regress/t/t_covergroup_wildcard_bins.out create mode 100755 test_regress/t/t_covergroup_wildcard_bins.py create mode 100644 test_regress/t/t_covergroup_wildcard_bins.v delete mode 100755 test_regress/t/t_covergroup_with_sample_args.py delete mode 100644 test_regress/t/t_covergroup_with_sample_args.v delete mode 100755 test_regress/t/t_covergroup_with_sample_args_default.py delete mode 100644 test_regress/t/t_covergroup_with_sample_args_default.v delete mode 100755 test_regress/t/t_covergroup_with_sample_namedargs.py delete mode 100644 test_regress/t/t_covergroup_with_sample_namedargs.v delete mode 100755 test_regress/t/t_covergroup_with_sample_zeroargs.py delete mode 100644 test_regress/t/t_covergroup_with_sample_zeroargs.v create mode 100644 test_regress/t/t_vlcov_covergroup.annotate.out create mode 100755 test_regress/t/t_vlcov_covergroup.py diff --git a/docs/guide/exe_verilator_coverage.rst b/docs/guide/exe_verilator_coverage.rst index 01c9ee782..87c433c72 100644 --- a/docs/guide/exe_verilator_coverage.rst +++ b/docs/guide/exe_verilator_coverage.rst @@ -133,9 +133,13 @@ verilator_coverage Arguments .. option:: --filter-type Skips records of coverage types that matches with - Possible values are `toggle`, `line`, `branch`, `expr`, `user`, - `fsm_state`, `fsm_arc` and a wildcard with `\*` or `?`. The default - value is `\*`. + Possible values are `toggle`, `line`, `branch`, `expr`, `covergroup`, + `user`, `fsm_state`, `fsm_arc` and a wildcard with `\*` or `?`. The + default value is `\*`. + + The `covergroup` type represents SystemVerilog functional coverage + including covergroups, coverpoints, bins, and cross coverage as defined + in IEEE 1800-2023 Section 19. .. option:: --help diff --git a/docs/guide/languages.rst b/docs/guide/languages.rst index 262a58271..70ded1ce2 100644 --- a/docs/guide/languages.rst +++ b/docs/guide/languages.rst @@ -40,8 +40,10 @@ union, var, void, priority case/if, and unique case/if. It also supports .name and .\* interconnection. -Verilator partially supports concurrent assert and cover statements; see -the enclosed coverage tests for the allowed syntax. +Verilator partially supports concurrent assert and cover statements, as well as +SystemVerilog functional coverage with ``covergroup``, ``coverpoint``, bins, +cross coverage, and transition bins. See :ref:`Functional Coverage` for details. Verilator has limited support for class and related object-oriented constructs. @@ -363,10 +365,15 @@ appropriate width. Assertions ---------- -Verilator is beginning to add support for assertions. Verilator currently -only converts assertions to simple ``if (...) error`` statements, and -coverage statements to increment the line counters described in the -coverage section. +Verilator partially supports assertions and functional coverage. +Verilator currently converts assertions to simple ``if (...) error`` statements, +and simple coverage statements to increment the line counters described in the +:ref:`coverage section`. + +Verilator also partially supports SystemVerilog functional coverage with +``covergroup``, ``coverpoint``, bins, cross coverage, and transition bins. See +:ref:`Functional Coverage` for details on using +covergroups for comprehensive coverage analysis. Verilator does not support SEREs yet. All assertion and coverage statements must be simple expressions that complete in one cycle. diff --git a/docs/guide/simulating.rst b/docs/guide/simulating.rst index 5fb8ab41e..89baf9ccc 100644 --- a/docs/guide/simulating.rst +++ b/docs/guide/simulating.rst @@ -184,7 +184,8 @@ Verilator supports adding code to the Verilated model to support SystemVerilog code coverage. With :vlopt:`--coverage`, Verilator enables all forms of coverage: -- :ref:`User Coverage` +- :ref:`Property Coverage` +- :ref:`Covergroup Coverage` - :ref:`FSM Coverage` - :ref:`Line Coverage` - :ref:`Toggle Coverage` @@ -193,22 +194,34 @@ When a model with coverage is executed, it will create a coverage file for collection and later analysis, see :ref:`Coverage Collection`. -.. _user coverage: +.. _property coverage: -Functional Coverage -------------------- +Property Coverage +----------------- With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will -translate functional coverage points the user has inserted manually in -SystemVerilog code through into the Verilated model. +translate property coverage points the user has inserted manually in +SystemVerilog code into the Verilated model. -For example, the following SystemVerilog statement will add a coverage -point under the coverage name "DefaultClock": +For simple coverage points, use the ``cover property`` construct: .. code-block:: sv DefaultClock: cover property (@(posedge clk) cyc==3); +This adds a coverage point that tracks whether the condition has been observed. + +.. _covergroup coverage: + +Covergroup Coverage +------------------- + +With :vlopt:`--coverage` or :vlopt:`--coverage-user`, Verilator will +translate covergroup coverage points the user has inserted manually in +SystemVerilog code into the Verilated model. Verilator supports +coverpoints with value and transition bins, and cross points. + + .. _fsm coverage: FSM Coverage @@ -301,7 +314,6 @@ Annotated output produced by :command:`verilator_coverage --annotate` will label FSM points with `fsm_state` and `fsm_arc`, and synthetic fallback transitions with `SYNTHETIC DEFAULT ARC`. - .. _line coverage: Line Coverage diff --git a/include/verilated_cov_key.h b/include/verilated_cov_key.h index fd7947aa2..64566d552 100644 --- a/include/verilated_cov_key.h +++ b/include/verilated_cov_key.h @@ -39,6 +39,7 @@ 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':'comment', 'short':'o', 'group':0, 'default':'', 'descr':'Textual description for the item'") 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'") @@ -52,6 +53,7 @@ VLCOVGEN_ITEM("'name':'weight', 'short':'w', 'group':0, 'default':None, 'd // VLCOVGEN_CIK_AUTO_EDIT_BEGIN #define VL_CIK_COLUMN "n" #define VL_CIK_COMMENT "o" +#define VL_CIK_CROSS_BINS "Cb" #define VL_CIK_FILENAME "f" #define VL_CIK_FSM_FROM "Ff" #define VL_CIK_FSM_TAG "Fg" @@ -77,6 +79,7 @@ public: // VLCOVGEN_SHORT_AUTO_EDIT_BEGIN if (key == "column") return VL_CIK_COLUMN; if (key == "comment") return VL_CIK_COMMENT; + 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; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 71f416129..ad2178203 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -70,6 +70,7 @@ set(HEADERS V3Control.h V3Coverage.h V3CoverageJoin.h + V3Covergroup.h V3Dead.h V3DebugBisect.h V3Delayed.h @@ -202,6 +203,7 @@ set(HEADERS V3WidthCommit.h V3WidthRemove.h VlcBucket.h + VlcCovergroup.h VlcOptions.h VlcPoint.h VlcSource.h @@ -237,6 +239,7 @@ set(COMMON_SOURCES V3Const__gen.cpp V3Coverage.cpp V3CoverageJoin.cpp + V3Covergroup.cpp V3Dead.cpp V3Delayed.cpp V3Depth.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 5cf0d2eaa..08c8899fc 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -249,6 +249,7 @@ RAW_OBJS_PCH_ASTNOMT = \ V3Combine.o \ V3Common.o \ V3Coverage.o \ + V3Covergroup.o \ V3CoverageJoin.o \ V3Dead.o \ V3Delayed.o \ diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 956d0d057..995f4fd5c 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -638,11 +638,150 @@ public: ~ActiveVisitor() override = default; }; +//###################################################################### +// Pass 1: collect sample CFuncs and sampling events from covergroup class scopes + +class CovergroupCollectVisitor final : public VNVisitor { + // NODE STATE + // Netlist: + // AstClass::user1p() -> AstCFunc*. The sample() CFunc for this covergroup class + // AstClass::user2p() -> AstSenTree*. Owned sampling event template (if any) + + // STATE + AstClass* m_classp = nullptr; // Current covergroup class context, or nullptr + + // VISITORS + void visit(AstClass* nodep) override { + if (!nodep->isCovergroup()) return; + VL_RESTORER(m_classp); + m_classp = nodep; + iterateChildren(nodep); + } + + void visit(AstScope* nodep) override { iterateChildren(nodep); } + + void visit(AstCFunc* nodep) override { + if (!m_classp) return; + if (nodep->isCovergroupSample()) m_classp->user1p(nodep); + } + + void visit(AstCovergroup* nodep) override { + // V3Covergroup guarantees: only supported-event covergroups survive to V3Active, + // and they are always inside a covergroup class (so m_classp is set). + // Unlink eventp from cgp so it survives cgp's deletion, + // then store it in user2p for use during the second pass. + m_classp->user2p(nodep->eventp()->unlinkFrBack()); + nodep->unlinkFrBack(); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } + + void visit(AstNode* nodep) override { iterateChildren(nodep); } + +public: + // CONSTRUCTORS + explicit CovergroupCollectVisitor(AstNetlist* nodep) { iterate(nodep); } + ~CovergroupCollectVisitor() override = default; +}; + +//###################################################################### +// Pass 2: inject automatic sample() calls for covergroup instances + +class CovergroupInjectVisitor final : public VNVisitor { + // NODE STATE (set by CovergroupCollectVisitor, consumed here) + // AstClass::user1p() -> AstCFunc*. The sample() CFunc for this covergroup class + // AstClass::user2p() -> AstSenTree*. Owned sampling event template (if any) + + // STATE + ActiveNamer m_namer; // Reuse active naming infrastructure + + // VISITORS + void visit(AstScope* nodep) override { + m_namer.main(nodep); // Initialize active naming for this scope + iterateChildren(nodep); + } + + void visit(AstVarScope* nodep) override { + // Get the underlying var + AstVar* const varp = nodep->varp(); + UASSERT_OBJ(varp, nodep, "AstVarScope must have non-null varp"); + + // Check if the variable is of covergroup class type + const AstNodeDType* const dtypep = varp->dtypep(); + UASSERT_OBJ(dtypep, nodep, "AstVar must have non-null dtypep after V3Width"); + + const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType); + if (!classRefp) return; + + AstClass* const classp = classRefp->classp(); + + // Check if this covergroup has an automatic sampling event + AstSenTree* const eventp = VN_CAST(classp->user2p(), SenTree); + if (!eventp) return; // No automatic sampling for this covergroup + + // V3Covergroup guarantees every supported-event covergroup has a registered sample CFunc + AstCFunc* const sampleCFuncp = VN_AS(classp->user1p(), CFunc); + UASSERT_OBJ(sampleCFuncp, nodep, + "No sample() CFunc found for covergroup " << classp->name()); + + // Create a VarRef to the covergroup instance for the method call + FileLine* const fl = nodep->fileline(); + AstVarRef* const varrefp = new AstVarRef{fl, nodep, VAccess::READ}; + + // Create the CMethodCall to sample() + // Note: We don't pass arguments in argsp since vlSymsp is passed via argTypes + AstCMethodCall* const cmethodCallp + = new AstCMethodCall{fl, varrefp, sampleCFuncp, nullptr}; + + cmethodCallp->dtypeSetVoid(); + cmethodCallp->argTypes("vlSymsp"); + + // Clone the sensitivity for this active block. + // V3Scope has already resolved all VarRefs in eventp, so the clone + // inherits correct varScopep values with no fixup needed. + AstSenTree* senTreep = eventp->cloneTree(false); + + // Get or create the AstActive node for this sensitivity + // senTreep is a template used by getActive() which clones it into the AstActive; + // delete it afterwards as it is not added to the AST directly. + AstActive* const activep = m_namer.getActive(fl, senTreep); + VL_DO_DANGLING(pushDeletep(senTreep), senTreep); + + // Wrap the sample() call in an AstAlways so SchedPartition handles it + // via visit(AstNodeProcedure*) like any other clocked always block. + activep->addStmtsp( + new AstAlways{fl, VAlwaysKwd::ALWAYS_FF, nullptr, cmethodCallp->makeStmt()}); + } + + void visit(AstClass* nodep) override { + iterateChildren(nodep); + // Delete the owned sampling event template stored during collection + if (AstSenTree* const eventp = VN_CAST(nodep->user2p(), SenTree)) { + VL_DO_DANGLING(pushDeletep(eventp), eventp); + } + } + + void visit(AstActive*) override {} // Don't iterate into actives + void visit(AstNode* nodep) override { iterateChildren(nodep); } + +public: + // CONSTRUCTORS + explicit CovergroupInjectVisitor(AstNetlist* nodep) { iterate(nodep); } + ~CovergroupInjectVisitor() override = default; +}; + //###################################################################### // Active class functions void V3Active::activeAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); { ActiveVisitor{nodep}; } // Destruct before checking + if (v3Global.useCovergroup()) { + // Add automatic covergroup sampling in two focused passes. + // user1p/user2p on AstClass span both passes; guards must outlive both visitors. + const VNUser1InUse user1InUse; + const VNUser2InUse user2InUse; + CovergroupCollectVisitor{nodep}; // Pass 1: collect CFuncs and events into user#p + CovergroupInjectVisitor{nodep}; // Pass 2: inject sample() calls, delete user2p events + } V3Global::dumpCheckGlobalTree("active", 0, dumpTreeEitherLevel() >= 3); } diff --git a/src/V3AstAttr.h b/src/V3AstAttr.h index 0862caff7..e912bae20 100644 --- a/src/V3AstAttr.h +++ b/src/V3AstAttr.h @@ -1149,6 +1149,120 @@ inline std::ostream& operator<<(std::ostream& os, const VCastable& rhs) { //###################################################################### +class VCoverBinsType final { +public: + enum en : uint8_t { + BINS_ARRAY, // Array of bins with user-speciifed size + BINS_AUTO, // Auto-sized array of bins (eg auto_bin_max) + BINS_DEFAULT, // Default bin + BINS_IGNORE, // Ignore bin + BINS_ILLEGAL, // Illegal bin + BINS_TRANSITION, // Transition bin + BINS_USER, // Single bin with one or more values/ranges + BINS_WILDCARD // Wildcard bin + }; + enum en m_e; + VCoverBinsType() // LCOV_EXCL_START + : m_e{BINS_USER} {} // LCOV_EXCL_STOP + // cppcheck-suppress noExplicitConstructor + constexpr VCoverBinsType(en _e) + : m_e{_e} {} + constexpr operator en() const { return m_e; } // LCOV_EXCL_LINE + const char* ascii() const { + static const char* const names[] + = {"user", "array", "auto", "ignore", "illegal", "default", "wildcard", "transition"}; + return names[m_e]; + } +}; +constexpr bool operator==(const VCoverBinsType& lhs, VCoverBinsType::en rhs) { + return lhs.m_e == rhs; +} + +//###################################################################### + +class VCoverOptionType final { +public: + enum en : uint8_t { + // Shared by option.* and type_option.* + WEIGHT, + GOAL, + AT_LEAST, + AUTO_BIN_MAX, + PER_INSTANCE, + COMMENT, + // option.* only (IEEE 1800-2023 Table 19-1) + NAME, + CROSS_NUM_PRINT_MISSING, + CROSS_RETAIN_AUTO_BINS, + DETECT_OVERLAP, + GET_INST_COVERAGE, + // type_option.* only (IEEE 1800-2023 Table 19-3) + STROBE, + MERGE_INSTANCES, + DISTRIBUTE_FIRST, + REAL_INTERVAL, + // sentinel - should never appear after parse-time validation + UNKNOWN + }; + enum en m_e; + // cppcheck-suppress noExplicitConstructor + constexpr VCoverOptionType(en _e) + : m_e{_e} {} + const char* ascii() const { + static const char* const names[] = {"weight", + "goal", + "at_least", + "auto_bin_max", + "per_instance", + "comment", + "name", + "cross_num_print_missing", + "cross_retain_auto_bins", + "detect_overlap", + "get_inst_coverage", + "strobe", + "merge_instances", + "distribute_first", + "real_interval", + "unknown"}; + return names[m_e]; + } +}; +constexpr bool operator==(const VCoverOptionType& lhs, VCoverOptionType::en rhs) { + return lhs.m_e == rhs; +} + +//###################################################################### + +class VTransRepType final { +public: + enum en : uint8_t { + NONE, // No repetition + CONSEC, // Consecutive repetition [*] + GOTO, // Goto repetition [->] + NONCONS // Nonconsecutive repetition [=] + }; + enum en m_e; + VTransRepType() // LCOV_EXCL_START + : m_e{NONE} {} // LCOV_EXCL_STOP + // cppcheck-suppress noExplicitConstructor + constexpr VTransRepType(en _e) + : m_e{_e} {} + explicit VTransRepType(int _e) // LCOV_EXCL_START + : m_e(static_cast(_e)) {} // LCOV_EXCL_STOP // Need () or GCC 4.8 false warning + constexpr operator en() const { return m_e; } // LCOV_EXCL_LINE + const char* ascii() const { + static const char* const names[] = {"", "[*]", "[->]", "[=]"}; + return names[m_e]; + } + const char* asciiJson() const { + static const char* const names[] = {"\"none\"", "\"consec\"", "\"goto\"", "\"noncons\""}; + return names[m_e]; + } +}; + +//###################################################################### + class VDirection final { public: enum en : uint8_t { NONE, INPUT, OUTPUT, INOUT, REF, CONSTREF }; diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index c34c77988..903130658 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -120,6 +120,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode { bool m_verilogTask : 1; // Declared by user as task (versus internal-made) bool m_virtual : 1; // Virtual method in class bool m_needProcess : 1; // Needs access to VlProcess of the caller + bool m_isCovergroupSample : 1; // Covergroup sample() method VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends) VLifetime m_lifetime; // Default lifetime of local vars VIsCached m_purity; // Pure state @@ -151,7 +152,8 @@ protected: , m_verilogFunction{false} , m_verilogTask{false} , m_virtual{false} - , m_needProcess{false} { + , m_needProcess{false} + , m_isCovergroupSample{false} { addStmtsp(stmtsp); cname(name); // Might be overridden by dpi import/export } @@ -225,6 +227,8 @@ public: void isVirtual(bool flag) { m_virtual = flag; } bool needProcess() const { return m_needProcess; } void setNeedProcess() { m_needProcess = true; } + bool isCovergroupSample() const { return m_isCovergroupSample; } + void isCovergroupSample(bool flag) { m_isCovergroupSample = flag; } void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; } VBaseOverride baseOverride() const { return m_baseOverride; } void lifetime(const VLifetime& flag) { m_lifetime = flag; } @@ -260,6 +264,20 @@ public: string name() const override VL_MT_STABLE { return m_name; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; +class AstNodeFuncCovItem VL_NOT_FINAL : public AstNode { + // Base class for functional coverage items (coverpoints, crosses) +protected: + string m_name; // Item name + +public: + AstNodeFuncCovItem(VNType t, FileLine* fl, const string& name) + : AstNode{t, fl} + , m_name{name} {} + ASTGEN_MEMBERS_AstNodeFuncCovItem; + string name() const override VL_MT_STABLE { return m_name; } + void name(const string& name) override { m_name = name; } + bool maybePointedTo() const override { return true; } +}; class AstNodeGen VL_NOT_FINAL : public AstNode { // Generate construct public: @@ -521,6 +539,7 @@ class AstCFunc final : public AstNode { bool m_recursive : 1; // Recursive or part of recursion bool m_noLife : 1; // Disable V3Life on this function - has multiple calls, and reads Syms // state + bool m_isCovergroupSample : 1; // Automatic covergroup sample() function int m_cost; // Function call cost public: AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "") @@ -551,6 +570,7 @@ public: m_dpiImportWrapper = false; m_recursive = false; m_noLife = false; + m_isCovergroupSample = false; m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost } ASTGEN_MEMBERS_AstCFunc; @@ -626,6 +646,8 @@ public: bool recursive() const { return m_recursive; } void noLife(bool flag) { m_noLife = flag; } bool noLife() const { return m_noLife; } + bool isCovergroupSample() const { return m_isCovergroupSample; } + void isCovergroupSample(bool flag) { m_isCovergroupSample = flag; } void cost(int cost) { m_cost = cost; } // Special methods bool emptyBody() const { @@ -767,13 +789,16 @@ public: class AstCgOptionAssign final : public AstNode { // A covergroup set of option // Parents: CLASS(covergroup) or cross - string m_name; // Option name + const VCoverOptionType m_optType; // Option type + const string m_name; // Original option name (for diagnostics on unknown options) const bool m_typeOption; // type_option vs option // @astgen op1 := valuep : AstNodeExpr public: - AstCgOptionAssign(FileLine* fl, bool typeOption, const string& name, AstNodeExpr* valuep) + AstCgOptionAssign(FileLine* fl, bool typeOption, VCoverOptionType optType, + const string& rawName, AstNodeExpr* valuep) : ASTGEN_SUPER_CgOptionAssign(fl) - , m_name{name} + , m_optType{optType} + , m_name{rawName} , m_typeOption{typeOption} { this->valuep(valuep); } @@ -781,7 +806,8 @@ public: // ACCESSORS void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; - string name() const override VL_MT_STABLE { return m_name; } // * = Bind Target name + string name() const override VL_MT_STABLE { return m_name; } + VCoverOptionType optionType() const { return m_optType; } bool typeOption() const { return m_typeOption; } }; class AstClassExtends final : public AstNode { @@ -1029,6 +1055,145 @@ public: bool isPredictOptimizable() const override { return false; } bool sameNode(const AstNode* /*samep*/) const override { return true; } }; + +class AstCoverBin final : public AstNode { + // Captures data for a coverpoint 'bins' declaration + // @astgen op1 := rangesp : List[AstNode] + // @astgen op2 := iffp : Optional[AstNodeExpr] + // @astgen op3 := arraySizep : Optional[AstNodeExpr] + // @astgen op4 := transp : List[AstCoverTransSet] + const string m_name; // Base name of the bin + const VCoverBinsType m_type; // Bin type (eg AUTO, IGNORE, ILLEGAL) + bool m_isArray = false; // Bin is either an auto-sized array of values or transitions + bool m_isWildcard = false; // Bin uses wildcard matching (independent of ignore/illegal) + +public: + AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal, + bool isWildcard = false) + : ASTGEN_SUPER_CoverBin(fl) + , m_name{name} + , m_type{isIllegal ? VCoverBinsType::BINS_ILLEGAL + : (isIgnore ? VCoverBinsType::BINS_IGNORE + : (isWildcard ? VCoverBinsType::BINS_WILDCARD + : VCoverBinsType::BINS_USER))} + , m_isWildcard{isWildcard} { + addRangesp(rangesp); + } + // Constructor for automatic bins + AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep) + : ASTGEN_SUPER_CoverBin(fl) + , m_name{name} + , m_type{VCoverBinsType::BINS_AUTO} + , m_isArray{true} { + this->arraySizep(arraySizep); + } + // Constructor for default bins (catch-all) + AstCoverBin(FileLine* fl, const string& name, VCoverBinsType type) + : ASTGEN_SUPER_CoverBin(fl) + , m_name{name} + , m_type{type} {} + // Constructor for transition bins + AstCoverBin(FileLine* fl, const string& name, AstCoverTransSet* transp, + VCoverBinsType type = VCoverBinsType::BINS_TRANSITION, bool isArrayBin = false) + : ASTGEN_SUPER_CoverBin(fl) + , m_name{name} + , m_type{type} + , m_isArray{isArrayBin} { + UASSERT(transp, "AstCoverBin transition constructor requires non-null transp"); + addTransp(transp); + } + ASTGEN_MEMBERS_AstCoverBin; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + string name() const override VL_MT_STABLE { return m_name; } + VCoverBinsType binsType() const { return m_type; } + bool isWildcard() const { return m_isWildcard; } + bool isArray() const { return m_isArray; } + void isArray(bool flag) { m_isArray = flag; } +}; +class AstCoverOption final : public AstNode { + // Coverage-option assignment + // @astgen op1 := valuep : AstNodeExpr + const VCoverOptionType m_type; // Option being assigned + +public: + AstCoverOption(FileLine* fl, VCoverOptionType type, AstNodeExpr* valuep) + : ASTGEN_SUPER_CoverOption(fl) + , m_type{type} { + this->valuep(valuep); + } + ASTGEN_MEMBERS_AstCoverOption; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + VCoverOptionType optionType() const { return m_type; } +}; +class AstCoverTransItem final : public AstNode { + // Represents a single transition item: value or value[*N] or value[->N] or value[=N] + // @astgen op1 := valuesp : List[AstNode] + // @astgen op2 := repMinp : Optional[AstNodeExpr] + // @astgen op3 := repMaxp : Optional[AstNodeExpr] + const VTransRepType m_repType; + +public: + AstCoverTransItem(FileLine* fl, AstNode* valuesp, VTransRepType repType = VTransRepType::NONE) + : ASTGEN_SUPER_CoverTransItem(fl) + , m_repType{repType} { + addValuesp(valuesp); + } + ASTGEN_MEMBERS_AstCoverTransItem; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; +}; +class AstCoverTransSet final : public AstNode { + // Represents a transition set: value1 => value2 => value3 + // @astgen op1 := itemsp : List[AstCoverTransItem] +public: + AstCoverTransSet(FileLine* fl, AstCoverTransItem* itemsp) + : ASTGEN_SUPER_CoverTransSet(fl) { + addItemsp(itemsp); + } + ASTGEN_MEMBERS_AstCoverTransSet; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; +}; +class AstCovergroup final : public AstNode { + // Represents a covergroup declaration. V3LinkParse transforms this + // into an AstClass with isCovergroup==true and attaches the clocking + // event as a new AstCovergroup sentinel in class membersp. + // @astgen op1 := argsp : List[AstVar] + // @astgen op2 := membersp : List[AstNode] + // @astgen op3 := eventp : Optional[AstSenTree] + // @astgen op4 := sampleArgsp : List[AstVar] + string m_name; // covergroup name + +public: + AstCovergroup(FileLine* fl, const string& name, AstVar* argsp, AstVar* sampleArgsp, + AstNode* membersp, AstSenTree* eventp) + : ASTGEN_SUPER_Covergroup(fl) + , m_name{name} { + addArgsp(argsp); + addSampleArgsp(sampleArgsp); + addMembersp(membersp); + this->eventp(eventp); + } + ASTGEN_MEMBERS_AstCovergroup; + string name() const override VL_MT_STABLE { return m_name; } + void name(const string& name) override { m_name = name; } + bool maybePointedTo() const override { return true; } +}; +class AstCoverpointRef final : public AstNode { + // Reference to a coverpoint used in a cross + const string m_name; // coverpoint name + +public: + AstCoverpointRef(FileLine* fl, const string& name) + : ASTGEN_SUPER_CoverpointRef(fl) + , m_name{name} {} + ASTGEN_MEMBERS_AstCoverpointRef; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + string name() const override VL_MT_STABLE { return m_name; } +}; class AstDefParam final : public AstNode { // A defparam assignment // Parents: MODULE @@ -2602,6 +2767,39 @@ public: void dump(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override; }; +class AstCoverCross final : public AstNodeFuncCovItem { + // @astgen op1 := itemsp : List[AstCoverpointRef] + // @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only + // @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items; + // // post-LinkParse: empty +public: + AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp) + : ASTGEN_SUPER_CoverCross(fl, name) { + UASSERT(itemsp, "AstCoverCross requires at least one coverpoint reference"); + addItemsp(itemsp); + } + ASTGEN_MEMBERS_AstCoverCross; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; +}; +class AstCoverpoint final : public AstNodeFuncCovItem { + // @astgen op1 := exprp : AstNodeExpr + // @astgen op2 := binsp : List[AstNode] // Parse: mixed AstCoverBin/AstCgOptionAssign; + // post-LinkParse: AstCoverBin only + // @astgen op3 := iffp : Optional[AstNodeExpr] + // @astgen op4 := optionsp : List[AstCoverOption] +public: + AstCoverpoint(FileLine* fl, const string& name, AstNodeExpr* exprp, + AstNodeExpr* iffp = nullptr, AstNode* binsp = nullptr) + : ASTGEN_SUPER_Coverpoint(fl, name) { + this->exprp(exprp); + this->iffp(iffp); + addBinsp(binsp); + } + ASTGEN_MEMBERS_AstCoverpoint; + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; +}; // === AstNodeGen === class AstGenBlock final : public AstNodeGen { @@ -2685,6 +2883,8 @@ class AstClass final : public AstNodeModule { bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class bool m_virtual = false; // Virtual class bool m_printedFrom = false; // This class is printed from i.e. is used as format arg. + // Covergroup options (when m_covergroup is true) + int m_cgAutoBinMax = -1; // option.auto_bin_max value (-1 = not set, use default 64) public: AstClass(FileLine* fl, const string& name, const string& libname) @@ -2714,6 +2914,9 @@ public: void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; } void markPrintedFrom() { m_printedFrom = true; } bool isPrintedFrom() const { return m_printedFrom; } + // Covergroup options accessors + int cgAutoBinMax() const { return m_cgAutoBinMax; } + void cgAutoBinMax(int value) { m_cgAutoBinMax = value; } // Return true if this class is an extension of base class (SLOW) // Accepts nullptrs static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp); diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 3cf464d23..3bd3f66f6 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -3732,3 +3732,54 @@ void AstWith::dumpJson(std::ostream& str) const { } dumpJsonGen(str); } + +//###################################################################### +// Functional coverage dump methods + +void AstCoverpoint::dump(std::ostream& str) const { this->AstNodeFuncCovItem::dump(str); } + +void AstCoverpoint::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); } + +void AstCoverBin::dump(std::ostream& str) const { + this->AstNode::dump(str); + str << " " << m_type.ascii(); + if (m_isArray) str << "[]"; +} + +void AstCoverBin::dumpJson(std::ostream& str) const { + this->AstNode::dumpJson(str); + str << ", \"binsType\": \"" << m_type.ascii() << "\""; + if (m_isArray) str << ", \"isArray\": true"; +} + +void AstCoverTransItem::dump(std::ostream& str) const { + this->AstNode::dump(str); + if (m_repType != VTransRepType::NONE) str << " " << m_repType.ascii(); +} + +void AstCoverTransItem::dumpJson(std::ostream& str) const { + this->AstNode::dumpJson(str); + str << ", \"repType\": " << m_repType.asciiJson(); +} + +void AstCoverTransSet::dump(std::ostream& str) const { this->AstNode::dump(str); } + +void AstCoverTransSet::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); } + +void AstCoverCross::dump(std::ostream& str) const { this->AstNodeFuncCovItem::dump(str); } + +void AstCoverCross::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); } + +void AstCoverOption::dump(std::ostream& str) const { + this->AstNode::dump(str); + str << " " << m_type.ascii(); +} + +void AstCoverOption::dumpJson(std::ostream& str) const { + this->AstNode::dumpJson(str); + str << ", \"optionType\": \"" << m_type.ascii() << "\""; +} + +void AstCoverpointRef::dump(std::ostream& str) const { this->AstNode::dump(str); } + +void AstCoverpointRef::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); } diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index c11330fd1..63cb65b8a 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -288,6 +288,18 @@ class CoverageVisitor final : public VNVisitor { } iterateChildren(nodep); } + void visit(AstClass* nodep) override { + VL_RESTORER(m_modp); + VL_RESTORER(m_state); + VL_RESTORER(m_exprTempNames); + VL_RESTORER(m_funcTemps); + createHandle(nodep); + m_modp = nodep; + // Covergroup declarations are not executable statements; suppress line/expr/toggle + // coverage so declarative elements (covergroup, coverpoint, cross) are not annotated + m_state.m_inModOff = nodep->isCovergroup(); + iterateChildren(nodep); + } void visit(AstAlways* nodep) override { if (nodep->keyword() == VAlwaysKwd::CONT_ASSIGN) { // Handle continuous assigns for expression coverage (but not line coverage) @@ -806,7 +818,6 @@ class CoverageVisitor final : public VNVisitor { pair.first->second = varp; if (m_ftaskp) { varp->funcLocal(true); - varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); m_ftaskp->stmtsp()->addHereThisAsNext(varp); } else { m_modp->stmtsp()->addHereThisAsNext(varp); diff --git a/src/V3Covergroup.cpp b/src/V3Covergroup.cpp new file mode 100644 index 000000000..727b5689c --- /dev/null +++ b/src/V3Covergroup.cpp @@ -0,0 +1,1665 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Functional coverage implementation +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// 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: 2003-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +// FUNCTIONAL COVERAGE TRANSFORMATIONS: +// For each covergroup (AstClass with isCovergroup()): +// For each coverpoint (AstCoverpoint): +// Generate member variable for VerilatedCoverpoint +// Generate initialization in constructor +// Generate sample code in sample() method +// +//************************************************************************* + +#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT + +#include "V3Covergroup.h" + +#include "V3Const.h" +#include "V3MemberMap.h" + +#include + +VL_DEFINE_DEBUG_FUNCTIONS; + +//###################################################################### +// Functional coverage visitor + +class FunctionalCoverageVisitor final : public VNVisitor { + // NODE STATE + // Entire netlist: + // AstCoverpoint::user1p() -> AstVar*. Previous-value variable for transition bins + const VNUser1InUse m_inuser1; + + // STATE + AstClass* m_covergroupp = nullptr; // Current covergroup being processed + AstFunc* m_sampleFuncp = nullptr; // Current sample() function + AstFunc* m_constructorp = nullptr; // Current constructor + std::vector m_coverpoints; // Coverpoints in current covergroup + std::map m_coverpointMap; // Name -> coverpoint for fast lookup + std::vector m_coverCrosses; // Cross coverage items in current covergroup + + // Structure to track bins with their variables and options + struct BinInfo final { + AstCoverBin* binp; + AstVar* varp; + int atLeast; // Minimum hits required for coverage (from option.at_least) + AstCoverpoint* coverpointp; // Associated coverpoint (or nullptr for cross bins) + AstCoverCross* crossp; // Associated cross (or nullptr for coverpoint bins) + string crossBins; // For cross bins: comma-separated individual bin names, in order + BinInfo(AstCoverBin* b, AstVar* v, int al = 1, AstCoverpoint* cp = nullptr, + AstCoverCross* cr = nullptr, const string& cb = "") + : binp{b} + , varp{v} + , atLeast{al} + , coverpointp{cp} + , crossp{cr} + , crossBins{cb} {} + }; + std::vector m_binInfos; // All bins in current covergroup + + VMemberMap m_memberMap; // Member names cached for fast lookup + + // METHODS + void clearBinInfos() { + // Delete pseudo-bins created for cross coverage (they're never inserted into the AST) + for (const BinInfo& bi : m_binInfos) { + if (!bi.coverpointp) pushDeletep(bi.binp); + } + m_binInfos.clear(); + } + + void processCovergroup() { + UINFO(4, "Processing covergroup: " << m_covergroupp->name() << " with " + << m_coverpoints.size() << " coverpoints and " + << m_coverCrosses.size() << " crosses"); + + // Clear bin info for this covergroup (deleting any orphaned cross pseudo-bins) + clearBinInfos(); + + // For each coverpoint, generate sampling code + for (AstCoverpoint* cpp : m_coverpoints) generateCoverpointCode(cpp); + + // For each cross, generate sampling code + for (AstCoverCross* crossp : m_coverCrosses) generateCrossCode(crossp); + + // Generate coverage computation code (even for empty covergroups) + generateCoverageComputationCode(); + + // TODO: Generate instance registry infrastructure for static get_coverage() + // This requires: + // - Static registry members (t_instances, s_mutex) + // - registerInstance() / unregisterInstance() methods + // - Proper C++ emission in EmitC backend + // For now, get_coverage() returns 0.0 (placeholder) + + // Generate coverage database registration if coverage is enabled + if (v3Global.opt.coverage()) generateCoverageRegistration(); + + // Clean up orphaned cross pseudo-bins now that we're done with them + clearBinInfos(); + } + + static constexpr int COVER_BINS_LIMIT + = 1000; // Sanity limit to avoid hangs from e.g. signed underflow + + void expandAutomaticBins(AstCoverpoint* coverpointp, AstNodeExpr* exprp) { + // Find and expand any automatic bins + AstNode* prevBinp = nullptr; + for (AstNode* binp = coverpointp->binsp(); binp;) { + AstCoverBin* const cbinp = VN_AS(binp, CoverBin); + AstNode* const nextBinp = binp->nextp(); + + if (cbinp->binsType() == VCoverBinsType::BINS_AUTO) { + UINFO(4, " Expanding automatic bin: " << cbinp->name()); + + // Get array size - must be a constant + AstNodeExpr* const sizep = cbinp->arraySizep(); + + // Evaluate as constant + const AstConst* constp = VN_CAST(sizep, Const); + if (!constp) { + cbinp->v3error("Automatic bins array size must be a constant"); + binp = nextBinp; + continue; + } + + const int numBins = constp->toSInt(); + if (numBins <= 0) { + cbinp->v3error("Automatic bins array size must be >= 1, got " << numBins); + binp = nextBinp; + continue; + } + if (numBins > COVER_BINS_LIMIT) { + cbinp->v3error("Automatic bins array size of " + << numBins << " exceeds limit of " << COVER_BINS_LIMIT); + binp = nextBinp; + continue; + } + + // Calculate range division + const int width = exprp->width(); + const uint64_t maxVal = (width >= 64) ? UINT64_MAX : ((1ULL << width) - 1); + // For width >= 64: (maxVal+1) would overflow; compute binSize without overflow + const uint64_t binSize + = (width < 64) ? ((maxVal + 1) / numBins) : (UINT64_MAX / numBins + 1); + + UINFO(4, " Width=" << width << " maxVal=" << maxVal << " numBins=" << numBins + << " binSize=" << binSize); + + // Create expanded bins + for (int i = 0; i < numBins; i++) { + const uint64_t lo = static_cast(i) * binSize; + const uint64_t hi = (i == numBins - 1) ? maxVal : ((i + 1) * binSize - 1); + + // Create constants for range (use setQuad to handle values > 32-bit) + V3Number loNum{cbinp->fileline(), width, 0}; + loNum.setQuad(lo); + AstConst* const loConstp = new AstConst{cbinp->fileline(), loNum}; + V3Number hiNum{cbinp->fileline(), width, 0}; + hiNum.setQuad(hi); + AstConst* const hiConstp = new AstConst{cbinp->fileline(), hiNum}; + + // Create InsideRange [lo:hi] + AstInsideRange* const rangep + = new AstInsideRange{cbinp->fileline(), loConstp, hiConstp}; + rangep->dtypeFrom(exprp); // Set dtype from coverpoint expression + + // Create new bin + const string binName = cbinp->name() + "[" + std::to_string(i) + "]"; + AstCoverBin* const newBinp + = new AstCoverBin{cbinp->fileline(), binName, rangep, false, false}; + + // Insert after previous bin + if (prevBinp) { + prevBinp->addNext(newBinp); + } else { + coverpointp->addBinsp(newBinp); + } + prevBinp = newBinp; + } + + // Remove the AUTO bin from the list + VL_DO_DANGLING(pushDeletep(binp->unlinkFrBack()), binp); + } else { + prevBinp = binp; + } + + binp = nextBinp; + } + } + + // Extract all coverpoint option values in a single pass. + // atLeastOut: option.at_least (default 1) + // autoBinMaxOut: option.auto_bin_max (coverpoint overrides covergroup, default 64) + void extractCoverpointOptions(AstCoverpoint* coverpointp, int& atLeastOut, + int& autoBinMaxOut) { + atLeastOut = 1; + autoBinMaxOut = -1; // -1 = not set at coverpoint level + for (AstNode* optionp = coverpointp->optionsp(); optionp; optionp = optionp->nextp()) { + AstCoverOption* const optp = VN_AS(optionp, CoverOption); + AstConst* const constp = VN_CAST(optp->valuep(), Const); + if (!constp) { + optp->valuep()->v3warn(COVERIGN, "Ignoring unsupported: non-constant 'option." + << optp->optionType().ascii() + << "'; using default value"); + continue; + } + if (optp->optionType() == VCoverOptionType::AT_LEAST) { + atLeastOut = constp->toSInt(); + } else { + // V3LinkParse only converts at_least/auto_bin_max coverpoint options into + // AstCoverOption (others are dropped there), so this is the only alternative. + UASSERT_OBJ(optp->optionType() == VCoverOptionType::AUTO_BIN_MAX, optp, + "Unexpected coverpoint option type reaching V3Covergroup"); + autoBinMaxOut = constp->toSInt(); + } + } + // Fall back to covergroup-level auto_bin_max if not set at coverpoint level + if (autoBinMaxOut < 0) { + if (m_covergroupp->cgAutoBinMax() >= 0) { + autoBinMaxOut = m_covergroupp->cgAutoBinMax(); + } else { + autoBinMaxOut = 64; // Default per IEEE 1800-2023 Table 19-1 + } + } + } + + // Extract individual values from a range expression list, used only to carve values + // out of implicit auto-bins. Iterates over all siblings (nextp) in the list, handling + // AstConst (single value) and AstInsideRange ([lo:hi]); an open-ended bound ('$', + // AstUnbounded) resolves to the coverpoint domain min (lower) or max (upper, == maxVal). + void extractValuesFromRange(AstNode* nodep, std::set& values, uint64_t maxVal) { + // Cap enumeration so a '$'-bounded or otherwise huge range cannot blow up memory; + // auto-bins are per-value only for small domains, so a partial set is harmless here. + constexpr size_t maxEnumerate = 1ULL << 16; + for (AstNode* np = nodep; np; np = np->nextp()) { + if (AstConst* constp = VN_CAST(np, Const)) { + if (constp->num().isFourState()) + continue; // wildcard patterns can't be enumerated + values.insert(constp->toUQuad()); + } else if (AstInsideRange* rangep = VN_CAST(np, InsideRange)) { + AstNodeExpr* const lhsp = V3Const::constifyEdit(rangep->lhsp()); + AstNodeExpr* const rhsp = V3Const::constifyEdit(rangep->rhsp()); + const bool loUnbounded = VN_IS(lhsp, Unbounded); + const bool hiUnbounded = VN_IS(rhsp, Unbounded); + AstConst* const loConstp = VN_CAST(lhsp, Const); + AstConst* const hiConstp = VN_CAST(rhsp, Const); + if ((!loConstp && !loUnbounded) || (!hiConstp && !hiUnbounded)) { + rangep->v3error("Non-constant expression in bin range; " + "range bounds must be constants"); + continue; + } + if ((loConstp && loConstp->num().isFourState()) + || (hiConstp && hiConstp->num().isFourState())) + continue; + const uint64_t lo = loUnbounded ? 0 : loConstp->toUQuad(); + const uint64_t hi = hiUnbounded ? maxVal : hiConstp->toUQuad(); + for (uint64_t v = lo; v <= hi; v++) { + if (values.size() >= maxEnumerate) break; + values.insert(v); + } + } else { + np->v3error("Non-constant expression in bin value list; values must be constants"); + } + } + } + + // Single-pass categorization: determine whether any regular (non-ignore/illegal) bins exist + // and collect the set of excluded values from ignore/illegal bins. + void categorizeBins(AstCoverpoint* coverpointp, bool& hasRegularOut, + std::set& excludedOut, uint64_t maxVal) { + hasRegularOut = false; + for (AstNode* binp = coverpointp->binsp(); binp; binp = binp->nextp()) { + AstCoverBin* const cbinp = VN_AS(binp, CoverBin); + const VCoverBinsType btype = cbinp->binsType(); + if (btype == VCoverBinsType::BINS_IGNORE || btype == VCoverBinsType::BINS_ILLEGAL) { + if (AstNode* rangep = cbinp->rangesp()) { + extractValuesFromRange(rangep, excludedOut, maxVal); + } + } else { + hasRegularOut = true; + } + } + } + + // Create implicit automatic bins when coverpoint has no explicit regular bins + void createImplicitAutoBins(AstCoverpoint* coverpointp, AstNodeExpr* exprp, int autoBinMax) { + const int width = exprp->width(); + const uint64_t maxVal = (width >= 64) ? UINT64_MAX : ((1ULL << width) - 1); + + // Single pass: check for regular bins and collect excluded values simultaneously. + // maxVal resolves any '$' (open-ended) bound in ignore_bins/illegal_bins ranges. + bool hasRegular = false; + std::set excluded; + categorizeBins(coverpointp, hasRegular, excluded, maxVal); + + // If already has regular bins, nothing to do + if (hasRegular) return; + + UINFO(4, " Creating implicit automatic bins for coverpoint: " << coverpointp->name()); + + const uint64_t numTotalValues = (width >= 64) ? UINT64_MAX : (1ULL << width); + const uint64_t numValidValues = numTotalValues - excluded.size(); + + // Determine number of bins to create (based on non-excluded values) + int numBins; + if (numValidValues <= static_cast(autoBinMax)) { + // Create one bin per valid value + numBins = numValidValues; + } else { + // Create autoBinMax bins, dividing range + numBins = autoBinMax; + } + + UINFO(4, " Width=" << width << " numTotalValues=" << numTotalValues + << " numValidValues=" << numValidValues << " autoBinMax=" + << autoBinMax << " creating " << numBins << " bins"); + + // Strategy: Create bins for each value (if numValidValues <= autoBinMax) + // or create range bins that avoid excluded values + if (numValidValues <= static_cast(autoBinMax)) { + // Create one bin per valid value + int binCount = 0; + for (uint64_t v = 0; v <= maxVal && binCount < numBins; v++) { + // Skip excluded values + if (excluded.find(v) != excluded.end()) continue; + + // Create single-value bin + AstConst* const valConstp = new AstConst{ + coverpointp->fileline(), V3Number(coverpointp->fileline(), width, v)}; + AstConst* const valConstp2 = new AstConst{ + coverpointp->fileline(), V3Number(coverpointp->fileline(), width, v)}; + + AstInsideRange* const rangep + = new AstInsideRange{coverpointp->fileline(), valConstp, valConstp2}; + rangep->dtypeFrom(exprp); + + const string binName = "auto_" + std::to_string(binCount); + AstCoverBin* const newBinp + = new AstCoverBin{coverpointp->fileline(), binName, rangep, false, false}; + + coverpointp->addBinsp(newBinp); + binCount++; + } + UINFO(4, " Created " << binCount << " single-value automatic bins"); + } else { + // Create range bins (more complex - need to handle excluded values in ranges) + // For simplicity, create bins and let excluded values not match any bin + const uint64_t binSize = (maxVal + 1) / numBins; + + for (int i = 0; i < numBins; i++) { + const uint64_t lo = i * binSize; + const uint64_t hi = (i == numBins - 1) ? maxVal : ((i + 1) * binSize - 1); + + // Create constants for range + AstConst* const loConstp = new AstConst{ + coverpointp->fileline(), V3Number(coverpointp->fileline(), width, lo)}; + AstConst* const hiConstp = new AstConst{ + coverpointp->fileline(), V3Number(coverpointp->fileline(), width, hi)}; + + // Create InsideRange [lo:hi] + AstInsideRange* const rangep + = new AstInsideRange{coverpointp->fileline(), loConstp, hiConstp}; + rangep->dtypeFrom(exprp); + + // Create bin name + const string binName = "auto_" + std::to_string(i); + AstCoverBin* const newBinp + = new AstCoverBin{coverpointp->fileline(), binName, rangep, false, false}; + + // Add to coverpoint + coverpointp->addBinsp(newBinp); + } + + UINFO(4, " Created range-based automatic bins"); + } + } + + // Sanitize generated names to be valid C++ identifiers + static string sanitizeGeneratedName(string name) { + std::replace(name.begin(), name.end(), '[', '_'); + std::replace(name.begin(), name.end(), ']', '_'); + return name; + } + + AstVar* createCoverageCounterVar(FileLine* fl, const string& varName, AstNodeDType* dtypep) { + AstVar* const varp = new AstVar{fl, VVarType::MEMBER, varName, dtypep}; + varp->isStatic(false); + varp->valuep(new AstConst{fl, AstConst::WidthedValue{}, 32, 0}); + m_covergroupp->addMembersp(varp); + return varp; + } + + AstVar* createTrackedCoverpointBinCounter(AstCoverpoint* coverpointp, AstCoverBin* binp, + const string& generatedBinName, int atLeastValue, + const string& logPrefix, + const string& logSuffix = "") { + const string varName = "__Vcov_" + coverpointp->name() + "_" + generatedBinName; + AstVar* const varp + = createCoverageCounterVar(binp->fileline(), varName, binp->findUInt32DType()); + UINFO(4, " " << logPrefix << ": " << varName << logSuffix); + m_binInfos.push_back(BinInfo(binp, varp, atLeastValue, coverpointp)); + return varp; + } + + AstNodeExpr* applyCoverpointIffCondition(AstCoverpoint* coverpointp, FileLine* fl, + AstNodeExpr* condp) { + if (AstNodeExpr* const iffp = coverpointp->iffp()) { + UINFO(6, " Adding iff condition"); + condp = new AstAnd{fl, iffp->cloneTree(false), condp}; + } + return condp; + } + + void addCoverpointBinHitIf(AstCoverpoint* coverpointp, AstCoverBin* binp, AstVar* hitVarp, + AstNodeExpr* condp, const string& illegalErrMsg, + const char* assertMsg) { + AstNode* stmtp = makeBinHitIncrement(binp->fileline(), hitVarp); + if (binp->binsType() == VCoverBinsType::BINS_ILLEGAL) { + stmtp = stmtp->addNext(makeIllegalBinAction(binp->fileline(), illegalErrMsg)); + } + + AstIf* const ifp = new AstIf{ + binp->fileline(), applyCoverpointIffCondition(coverpointp, binp->fileline(), condp), + stmtp, nullptr}; + UASSERT_OBJ(m_sampleFuncp, binp, assertMsg); + m_sampleFuncp->addStmtsp(ifp); + } + + // Create previous value variable for transition tracking + AstVar* createPrevValueVar(AstCoverpoint* coverpointp, AstNodeExpr* exprp) { + // Check if already created + if (AstVar* const prevVarp = VN_CAST(coverpointp->user1p(), Var)) return prevVarp; + + // Create variable to store previous sampled value + 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); + + // Initialize to zero in constructor + AstNodeExpr* const initExprp + = new AstConst{prevVarp->fileline(), AstConst::WidthedValue{}, prevVarp->width(), 0}; + AstNodeStmt* const initStmtp = new AstAssign{ + prevVarp->fileline(), new AstVarRef{prevVarp->fileline(), prevVarp, VAccess::WRITE}, + initExprp}; + m_constructorp->addStmtsp(initStmtp); + + coverpointp->user1p(prevVarp); + return prevVarp; + } + + // Create state position variable for multi-value transition bins + // Tracks position in sequence: 0=not started, 1=seen first item, etc. + AstVar* createSequenceStateVar(AstCoverpoint* coverpointp, AstCoverBin* binp) { + // Create variable to track sequence position + const string varName = "__Vseqpos_" + coverpointp->name() + "_" + binp->name(); + // 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); + + // Initialize to 0 (not started) in constructor + AstNodeStmt* const initStmtp = new AstAssign{ + stateVarp->fileline(), new AstVarRef{stateVarp->fileline(), stateVarp, VAccess::WRITE}, + new AstConst{stateVarp->fileline(), AstConst::WidthedValue{}, 8, 0}}; + m_constructorp->addStmtsp(initStmtp); + + return stateVarp; + } + + void generateCoverpointCode(AstCoverpoint* coverpointp) { + UINFO(4, " Generating code for coverpoint: " << coverpointp->name()); + + // Get the coverpoint expression + AstNodeExpr* const exprp = coverpointp->exprp(); + + // Expand automatic bins before processing + expandAutomaticBins(coverpointp, exprp); + + // Extract all coverpoint options in a single pass + int atLeastValue; + int autoBinMax; + extractCoverpointOptions(coverpointp, atLeastValue, autoBinMax); + UINFO(6, " Coverpoint at_least = " << atLeastValue << " auto_bin_max = " << autoBinMax); + + // Create implicit automatic bins if no regular bins exist + createImplicitAutoBins(coverpointp, exprp, autoBinMax); + + // Generate member variables and matching code for each bin + // Process in two passes: first non-default bins, then default bins + std::vector defaultBins; + bool hasTransition = false; + for (AstNode* binp = coverpointp->binsp(); binp; binp = binp->nextp()) { + AstCoverBin* const cbinp = VN_AS(binp, CoverBin); + + // Defer default bins to second pass + if (cbinp->binsType() == VCoverBinsType::BINS_DEFAULT) { + defaultBins.push_back(cbinp); + continue; + } + + // Handle array bins: create separate bin for each value/transition + if (cbinp->isArray()) { + if (cbinp->transp()) { // transition bin (includes illegal_bins with transitions) + hasTransition = true; + generateTransitionArrayBins(coverpointp, cbinp, exprp, atLeastValue); + } else { + generateArrayBins(coverpointp, cbinp, exprp, atLeastValue); + } + continue; + } + + // Create a member variable to track hits for this bin + // Sanitize bin name to make it a valid C++ identifier + const string binName = sanitizeGeneratedName(cbinp->name()); + AstVar* const varp = createTrackedCoverpointBinCounter( + coverpointp, cbinp, binName, atLeastValue, "Created member variable", + " type=" + string{cbinp->binsType().ascii()}); + + // Note: Coverage database registration happens later via VL_COVER_INSERT + // (see generateCoverageDeclarations() method around line 1164) + // Classes use "v_covergroup/" hier prefix vs modules + + // Generate bin matching code in sample() + // Handle transition bins specially (includes illegal_bins with transition syntax) + if (cbinp->transp()) { + hasTransition = true; + generateTransitionBinMatchCode(coverpointp, cbinp, exprp, varp); + } else { + generateBinMatchCode(coverpointp, cbinp, exprp, varp); + } + } + + // Second pass: Handle default bins + // Default bin matches when value doesn't match any other explicit bin + for (AstCoverBin* defBinp : defaultBins) { + // Create member variable for default bin + const string binName = sanitizeGeneratedName(defBinp->name()); + AstVar* const varp = createTrackedCoverpointBinCounter( + coverpointp, defBinp, binName, atLeastValue, "Created default bin variable"); + + // Generate matching code: if (NOT (bin1 OR bin2 OR ... OR binN)) + generateDefaultBinMatchCode(coverpointp, defBinp, exprp, varp); + } + + // After all bins processed, if coverpoint has transition bins, update previous value + if (hasTransition) { + AstVar* const prevVarp = VN_AS(coverpointp->user1p(), Var); + // Generate: __Vprev_cpname = current_value; + AstNodeStmt* updateStmtp + = new AstAssign{coverpointp->fileline(), + new AstVarRef{prevVarp->fileline(), prevVarp, VAccess::WRITE}, + exprp->cloneTree(false)}; + m_sampleFuncp->addStmtsp(updateStmtp); + UINFO(4, " Added previous value update at end of sample()"); + } + } + + void generateBinMatchCode(AstCoverpoint* coverpointp, AstCoverBin* binp, AstNodeExpr* exprp, + AstVar* hitVarp) { + UINFO(4, " Generating bin match for: " << binp->name()); + + // Build the bin matching condition using the shared function + AstNodeExpr* fullCondp = buildBinCondition(binp, exprp); + + if (!fullCondp) { + // Reachable: e.g. 'ignore_bins ib = default' creates a BINS_IGNORE bin + // with null rangesp. Skipping match code generation is correct in that case. + return; + } + + UINFO(4, " Adding bin match if statement to sample function"); + addCoverpointBinHitIf(coverpointp, binp, hitVarp, fullCondp, + "Illegal bin " + binp->prettyNameQ() + " hit in coverpoint " + + coverpointp->prettyNameQ(), + "sample() CFunc not set when generating bin match code"); + UINFO(4, " Successfully added if statement for bin: " << binp->name()); + } + + // Generate matching code for default bins + // Default bins match when value doesn't match any other explicit bin + void generateDefaultBinMatchCode(AstCoverpoint* coverpointp, AstCoverBin* defBinp, + AstNodeExpr* exprp, AstVar* hitVarp) { + UINFO(4, " Generating default bin match for: " << defBinp->name()); + + // Build OR of all non-default, non-ignore bins + AstNodeExpr* anyBinMatchp = nullptr; + + for (AstNode* binp = coverpointp->binsp(); binp; binp = binp->nextp()) { + AstCoverBin* const cbinp = VN_AS(binp, CoverBin); + + // Skip default, ignore, and illegal bins + if (cbinp->binsType() == VCoverBinsType::BINS_DEFAULT + || cbinp->binsType() == VCoverBinsType::BINS_IGNORE + || cbinp->binsType() == VCoverBinsType::BINS_ILLEGAL) { + continue; + } + + // Build condition for this bin + AstNodeExpr* const binCondp = buildBinCondition(cbinp, exprp); + UASSERT_OBJ(binCondp, cbinp, + "buildBinCondition returned nullptr for non-ignore/non-illegal bin"); + + // OR with previous conditions + if (anyBinMatchp) { + anyBinMatchp = new AstOr{defBinp->fileline(), anyBinMatchp, binCondp}; + } else { + anyBinMatchp = binCondp; + } + } + + // Default matches when NO explicit bin matches + AstNodeExpr* defaultCondp = nullptr; + if (anyBinMatchp) { + // NOT (bin1 OR bin2 OR ... OR binN) + defaultCondp = new AstNot{defBinp->fileline(), anyBinMatchp}; + } else { + // No other bins - default always matches (shouldn't happen in practice) + defaultCondp = new AstConst{defBinp->fileline(), AstConst::BitTrue{}}; + } + + // Apply iff condition if present + if (AstNodeExpr* iffp = coverpointp->iffp()) { + defaultCondp = new AstAnd{defBinp->fileline(), iffp->cloneTree(false), defaultCondp}; + } + + // Create increment statement + AstNode* const stmtp = makeBinHitIncrement(defBinp->fileline(), hitVarp); + + // Create if statement + AstIf* const ifp = new AstIf{defBinp->fileline(), defaultCondp, stmtp, nullptr}; + + UASSERT_OBJ(m_sampleFuncp, defBinp, + "sample() CFunc not set when generating default bin code"); + m_sampleFuncp->addStmtsp(ifp); + UINFO(4, " Successfully added default bin if statement"); + } + + // Generate matching code for transition bins + // Transition bins match sequences like: (val1 => val2 => val3) + void generateTransitionBinMatchCode(AstCoverpoint* coverpointp, AstCoverBin* binp, + AstNodeExpr* exprp, AstVar* hitVarp) { + UINFO(4, " Generating transition bin match for: " << binp->name()); + + // Get the (single) transition set + AstCoverTransSet* const transSetp = binp->transp(); + + // Use the helper function to generate code for this transition + generateSingleTransitionCode(coverpointp, binp, exprp, hitVarp, transSetp); + } + + // Generate state machine code for multi-value transition sequences + // Handles transitions like (1 => 2 => 3 => 4) + void generateMultiValueTransitionCode(AstCoverpoint* coverpointp, AstCoverBin* binp, + AstNodeExpr* exprp, AstVar* hitVarp, + const std::vector& items) { + UINFO(4, " Generating multi-value transition state machine for: " << binp->name()); + UINFO(4, " Sequence length: " << items.size() << " items"); + + // Create state position variable + AstVar* const stateVarp = createSequenceStateVar(coverpointp, binp); + + // Build case statement with N cases (one for each state 0 to N-1) + // State 0: Not started, looking for first item + // State 1 to N-1: In progress, looking for next item + + AstCase* const casep + = new AstCase{binp->fileline(), VCaseType::CT_CASE, + new AstVarRef{stateVarp->fileline(), stateVarp, VAccess::READ}, nullptr}; + + // Generate each case item in the switch statement + for (size_t state = 0; state < items.size(); ++state) { + AstCaseItem* caseItemp = generateTransitionStateCase(coverpointp, binp, exprp, hitVarp, + stateVarp, items, state); + casep->addItemsp(caseItemp); + } + + // Add default case (reset to state 0) to prevent CASEINCOMPLETE warnings, + // since the state variable is wider than the number of valid states. + AstCaseItem* const defaultItemp = new AstCaseItem{ + binp->fileline(), nullptr, + new AstAssign{binp->fileline(), + new AstVarRef{binp->fileline(), stateVarp, VAccess::WRITE}, + new AstConst{binp->fileline(), AstConst::WidthedValue{}, 8, 0}}}; + casep->addItemsp(defaultItemp); + + m_sampleFuncp->addStmtsp(casep); + UINFO(4, " Successfully added multi-value transition state machine"); + } + + // Generate code for a single state in the transition state machine + // Returns the case item for this state + AstCaseItem* generateTransitionStateCase(AstCoverpoint* coverpointp, AstCoverBin* binp, + AstNodeExpr* exprp, AstVar* hitVarp, + AstVar* stateVarp, + const std::vector& items, + size_t state) { + FileLine* const fl = binp->fileline(); + + // Build condition for current value matching expected item at this state + AstNodeExpr* matchCondp = buildTransitionItemCondition(items[state], exprp); + + // Apply iff condition if present + if (AstNodeExpr* iffp = coverpointp->iffp()) { + matchCondp = new AstAnd{fl, iffp->cloneTree(false), matchCondp}; + } + + AstNodeStmt* matchActionp = nullptr; + + if (state == items.size() - 1) { + // Last state: sequence complete! + // Increment bin counter + matchActionp = makeBinHitIncrement(fl, hitVarp); + + // For illegal_bins, add error message + if (binp->binsType() == VCoverBinsType::BINS_ILLEGAL) { + const string errMsg = "Illegal transition bin " + binp->prettyNameQ() + + " hit in coverpoint " + coverpointp->prettyNameQ(); + matchActionp = matchActionp->addNext(makeIllegalBinAction(fl, errMsg)); + } + + // Reset state to 0 + matchActionp = matchActionp->addNext( + new AstAssign{fl, new AstVarRef{fl, stateVarp, VAccess::WRITE}, + new AstConst{fl, AstConst::WidthedValue{}, 8, 0}}); + } else { + // Intermediate state: advance to next state + matchActionp = new AstAssign{ + fl, new AstVarRef{fl, stateVarp, VAccess::WRITE}, + new AstConst{fl, AstConst::WidthedValue{}, 8, static_cast(state + 1)}}; + } + + // Build restart logic: check if current value matches first item + // If so, restart sequence from state 1 (even if we're in middle of sequence) + AstNodeStmt* noMatchActionp = nullptr; + if (state > 0) { + // Check if current value matches first item (restart condition) + AstNodeExpr* restartCondp = buildTransitionItemCondition(items[0], exprp); + + UASSERT_OBJ(restartCondp, items[0], + "buildTransitionItemCondition returned nullptr for restart"); + // Apply iff condition + if (AstNodeExpr* iffp = coverpointp->iffp()) { + restartCondp = new AstAnd{fl, iffp->cloneTree(false), restartCondp}; + } + + // Restart to state 1 + AstNodeStmt* restartActionp + = new AstAssign{fl, new AstVarRef{fl, stateVarp, VAccess::WRITE}, + new AstConst{fl, AstConst::WidthedValue{}, 8, 1}}; + + // Reset to state 0 (else branch) + AstNodeStmt* resetActionp + = new AstAssign{fl, new AstVarRef{fl, stateVarp, VAccess::WRITE}, + new AstConst{fl, AstConst::WidthedValue{}, 8, 0}}; + + noMatchActionp = new AstIf{fl, restartCondp, restartActionp, resetActionp}; + } + // For state 0, no action needed if no match (stay in state 0) + + // Combine into if-else + AstNodeStmt* const stmtp = new AstIf{fl, matchCondp, matchActionp, noMatchActionp}; + + // Create case item for this state value + AstCaseItem* const caseItemp = new AstCaseItem{ + fl, new AstConst{fl, AstConst::WidthedValue{}, 8, static_cast(state)}, + stmtp}; + + return caseItemp; + } + + // Create: $error(msg); $stop; Used when an illegal bin is hit. + AstNodeStmt* makeIllegalBinAction(FileLine* fl, const string& errMsg) { + AstDisplay* const errorp + = new AstDisplay{fl, VDisplayType::DT_ERROR, errMsg, nullptr, nullptr}; + errorp->fmtp()->timeunit(m_covergroupp->timeunit()); + static_cast(errorp)->addNext(new AstStop{fl, true}); + return errorp; + } + + // Create: hitVarp = hitVarp + 1 + AstAssign* makeBinHitIncrement(FileLine* fl, AstVar* hitVarp) { + return new AstAssign{fl, new AstVarRef{fl, hitVarp, VAccess::WRITE}, + new AstAdd{fl, new AstVarRef{fl, hitVarp, VAccess::READ}, + new AstConst{fl, AstConst::WidthedValue{}, 32, 1}}}; + } + + // Clone a constant node, widening to targetWidth if needed (zero-extend). + // Used to ensure comparisons use matching widths after V3Width has run. + static AstConst* widenConst(FileLine* fl, AstConst* constp, int targetWidth) { + if (constp->width() == targetWidth) return constp->cloneTree(false); + V3Number num{fl, targetWidth, 0}; + num.opAssign(constp->num()); + return new AstConst{fl, num}; + } + + // Build a range condition: minp <= exprp <= maxp. + // Uses signed comparisons if exprp is signed; omits trivially-true bounds for unsigned. + // All arguments are non-owning; clones exprp/minp/maxp as needed. + AstNodeExpr* makeRangeCondition(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* minp, + AstNodeExpr* maxp) { + const int exprWidth = exprp->widthMin(); + AstConst* const minConstp = VN_AS(minp, Const); + AstConst* const maxConstp = VN_AS(maxp, Const); + // Widen constants to match expression width so post-V3Width nodes use correct macros + AstConst* const minWidep = widenConst(fl, minConstp, exprWidth); + AstConst* const maxWidep = widenConst(fl, maxConstp, exprWidth); + if (exprp->isSigned()) { + return new AstAnd{fl, new AstGteS{fl, exprp->cloneTree(false), minWidep}, + new AstLteS{fl, exprp->cloneTree(false), maxWidep}}; + } + // Unsigned: skip bounds that are trivially satisfied for the expression width + const bool skipLowerCheck = (minConstp->toUQuad() == 0); + bool skipUpperCheck = false; + if (exprWidth <= 64) { + const uint64_t maxVal + = (exprWidth == 64) ? ~static_cast(0) : ((1ULL << exprWidth) - 1ULL); + skipUpperCheck = (maxConstp->toUQuad() == maxVal); + } + if (skipLowerCheck && skipUpperCheck) { + VL_DO_DANGLING(pushDeletep(minWidep), minWidep); + VL_DO_DANGLING(pushDeletep(maxWidep), maxWidep); + return new AstConst{fl, AstConst::BitTrue{}}; + } else if (skipLowerCheck) { + VL_DO_DANGLING(pushDeletep(minWidep), minWidep); + return new AstLte{fl, exprp->cloneTree(false), maxWidep}; + } else if (skipUpperCheck) { + VL_DO_DANGLING(pushDeletep(maxWidep), maxWidep); + return new AstGte{fl, exprp->cloneTree(false), minWidep}; + } else { + return new AstAnd{fl, new AstGte{fl, exprp->cloneTree(false), minWidep}, + new AstLte{fl, exprp->cloneTree(false), maxWidep}}; + } + } + + // Build a one-sided comparison for an open-ended bin range whose other bound is '$'. + // '$' denotes the coverpoint domain extreme, so {[lo:$]} == (expr >= lo) and + // {[$:hi]} == (expr <= hi). + AstNodeExpr* makeOpenRangeCondition(FileLine* fl, AstNodeExpr* exprp, AstConst* boundp, + bool isLowerBound) { + AstConst* const widep = widenConst(fl, boundp, exprp->widthMin()); + if (isLowerBound) { + if (exprp->isSigned()) return new AstGteS{fl, exprp->cloneTree(false), widep}; + return new AstGte{fl, exprp->cloneTree(false), widep}; + } + if (exprp->isSigned()) return new AstLteS{fl, exprp->cloneTree(false), widep}; + return new AstLte{fl, exprp->cloneTree(false), widep}; + } + + // Build condition for a single transition item. + // Returns expression that checks if exprp matches the item's value/range list. + // Overload for when the expression is a variable read -- creates and manages the VarRef + // internally, so callers don't need to construct a temporary node. + AstNodeExpr* buildTransitionItemCondition(AstCoverTransItem* itemp, AstVar* varp) { + AstNodeExpr* varRefp = new AstVarRef{varp->fileline(), varp, VAccess::READ}; + AstNodeExpr* const condp = buildTransitionItemCondition(itemp, varRefp); + VL_DO_DANGLING(pushDeletep(varRefp), varRefp); + return condp; + } + + // Non-owning: exprp is cloned internally; caller retains ownership of exprp. + AstNodeExpr* buildTransitionItemCondition(AstCoverTransItem* itemp, AstNodeExpr* exprp) { + AstNodeExpr* condp = nullptr; + + for (AstNode* valp = itemp->valuesp(); valp; valp = valp->nextp()) { + AstNodeExpr* singleCondp = nullptr; + + AstConst* const constp = VN_AS(valp, Const); + singleCondp + = new AstEq{constp->fileline(), exprp->cloneTree(false), constp->cloneTree(false)}; + + if (condp) { + condp = new AstOr{itemp->fileline(), condp, singleCondp}; + } else { + condp = singleCondp; + } + } + + return condp; + } + + // Generate multiple bins for array bins + // Array bins create one bin per value in the range list + void generateArrayBins(AstCoverpoint* coverpointp, AstCoverBin* arrayBinp, AstNodeExpr* exprp, + int atLeastValue) { + UINFO(4, " Generating array bins for: " << arrayBinp->name()); + + // Extract all values from the range list + std::vector values; + for (AstNode* rangep = arrayBinp->rangesp(); rangep; rangep = rangep->nextp()) { + if (AstInsideRange* const insideRangep = VN_CAST(rangep, InsideRange)) { + // For InsideRange [min:max], create bins for each value + AstNodeExpr* const minp = V3Const::constifyEdit(insideRangep->lhsp()); + AstNodeExpr* const maxp = V3Const::constifyEdit(insideRangep->rhsp()); + AstConst* const minConstp = VN_CAST(minp, Const); + AstConst* const maxConstp = VN_CAST(maxp, Const); + if (minConstp && maxConstp) { + const int minVal = minConstp->toSInt(); + const int maxVal = maxConstp->toSInt(); + UINFO(6, " Expanding InsideRange [" << minVal << ":" << maxVal << "]"); + for (int val = minVal; val <= maxVal; ++val) { + values.push_back(new AstConst{insideRangep->fileline(), + AstConst::WidthedValue{}, + (int)exprp->width(), (uint32_t)val}); + } + } else { + arrayBinp->v3error("Non-constant expression in array bins range; " + "range bounds must be constants"); + return; + } + } else { + // Single value - should be an expression + values.push_back(VN_AS(rangep->cloneTree(false), NodeExpr)); + } + } + + // Create a separate bin for each value + int index = 0; + for (AstNodeExpr* valuep : values) { + const string sanitizedName = arrayBinp->name() + "_" + std::to_string(index); + AstVar* const varp = createTrackedCoverpointBinCounter( + coverpointp, arrayBinp, sanitizedName, atLeastValue, + "Created array bin [" + std::to_string(index) + "]"); + + // Generate matching code for this specific value + generateArrayBinMatchCode(coverpointp, arrayBinp, exprp, varp, valuep); + + ++index; + } + + UINFO(4, " Generated " << index << " array bins"); + } + + // Generate matching code for a single array bin element + void generateArrayBinMatchCode(AstCoverpoint* coverpointp, AstCoverBin* binp, + AstNodeExpr* exprp, AstVar* hitVarp, AstNodeExpr* valuep) { + // Create condition: expr == value + AstNodeExpr* condp = new AstEq{binp->fileline(), exprp->cloneTree(false), valuep}; + + addCoverpointBinHitIf(coverpointp, binp, hitVarp, condp, + "Illegal bin " + binp->prettyNameQ() + " hit in coverpoint " + + coverpointp->prettyNameQ(), + "sample() CFunc not set when generating array bin code"); + } + + // Generate multiple bins for transition array bins + // Array bins with transitions create one bin per transition sequence + void generateTransitionArrayBins(AstCoverpoint* coverpointp, AstCoverBin* arrayBinp, + AstNodeExpr* exprp, int atLeastValue) { + UINFO(4, " Generating transition array bins for: " << arrayBinp->name()); + + // Extract all transition sets + std::vector transSets; + for (AstNode* transSetp = arrayBinp->transp(); transSetp; transSetp = transSetp->nextp()) + transSets.push_back(VN_AS(transSetp, CoverTransSet)); + + UINFO(4, " Found " << transSets.size() << " transition sets"); + int index = 0; + for (AstCoverTransSet* transSetp : transSets) { + const string sanitizedName = arrayBinp->name() + "_" + std::to_string(index); + AstVar* const varp = createTrackedCoverpointBinCounter( + coverpointp, arrayBinp, sanitizedName, atLeastValue, + "Created transition array bin [" + std::to_string(index) + "]"); + + // Generate matching code for this specific transition + generateSingleTransitionCode(coverpointp, arrayBinp, exprp, varp, transSetp); + + ++index; + } + + UINFO(4, " Generated " << index << " transition array bins"); + } + + // Generate code for a single transition sequence (used by both regular and array bins) + void generateSingleTransitionCode(AstCoverpoint* coverpointp, AstCoverBin* binp, + AstNodeExpr* exprp, AstVar* hitVarp, + AstCoverTransSet* transSetp) { + UINFO(4, " Generating code for transition sequence"); + + // Get or create previous value variable + AstVar* const prevVarp = createPrevValueVar(coverpointp, exprp); + + UASSERT_OBJ( + transSetp, binp, + "Transition bin has no transition set (transp() was checked before calling this)"); + + // Get transition items (the sequence: item1 => item2 => item3) + std::vector items; + for (AstNode* itemp = transSetp->itemsp(); itemp; itemp = itemp->nextp()) + items.push_back(VN_AS(itemp, CoverTransItem)); + + if (items.empty()) { + binp->v3error("Transition set without items"); + return; + } + + if (items.size() == 1) { + // Single item transition not valid (need at least 2 values for =>) + binp->v3error("Transition requires at least two values"); + return; + } else if (items.size() == 2) { + // Simple two-value transition: (val1 => val2) + // Use optimized direct comparison (no state machine needed) + AstNodeExpr* const cond1p = buildTransitionItemCondition(items[0], prevVarp); + AstNodeExpr* const cond2p = buildTransitionItemCondition(items[1], exprp); + + // Combine: prev matches val1 AND current matches val2 + AstNodeExpr* fullCondp = new AstAnd{binp->fileline(), cond1p, cond2p}; + + addCoverpointBinHitIf(coverpointp, binp, hitVarp, fullCondp, + "Illegal transition bin " + binp->prettyNameQ() + + " hit in coverpoint " + coverpointp->prettyNameQ(), + "sample() CFunc not set when generating transition bin code"); + + UINFO(4, " Successfully added 2-value transition if statement"); + } else { + // Multi-value sequence (a => b => c => ...) + // Use state machine to track position in sequence + generateMultiValueTransitionCode(coverpointp, binp, exprp, hitVarp, items); + } + } + + // Recursive helper to generate Cartesian product of cross bins + void generateCrossBinsRecursive(AstCoverCross* crossp, + const std::vector& coverpointRefs, + const std::vector>& allCpBins, + std::vector currentCombination, + size_t dimension) { + if (dimension == allCpBins.size()) { + // Base case: we have a complete combination, generate the cross bin + generateOneCrossBin(crossp, coverpointRefs, currentCombination); + return; + } + + // Recursive case: iterate through bins at current dimension + for (AstCoverBin* binp : allCpBins[dimension]) { + currentCombination.push_back(binp); + generateCrossBinsRecursive(crossp, coverpointRefs, allCpBins, currentCombination, + dimension + 1); + currentCombination.pop_back(); + } + } + + // Generate a single cross bin for a specific combination of bins + void generateOneCrossBin(AstCoverCross* crossp, + const std::vector& coverpointRefs, + const std::vector& bins) { + // Build sanitized name from all bins + string binName; + string varName = "__Vcov_" + crossp->name(); + string crossBins; // Comma-separated individual bin names (one per coverpoint dimension) + + for (size_t i = 0; i < bins.size(); ++i) { + const string sanitized = sanitizeGeneratedName(bins[i]->name()); + + if (i > 0) { + binName += "_x_"; + varName += "_x_"; + crossBins += ","; + } + binName += sanitized; + varName += "_" + sanitized; + crossBins += sanitized; + } + + // Create member variable for this cross bin + AstVar* const varp + = createCoverageCounterVar(crossp->fileline(), varName, bins[0]->findUInt32DType()); + + UINFO(4, " Created cross bin variable: " << varName); + + // Track this for coverage computation + AstCoverBin* const pseudoBinp = new AstCoverBin{ + crossp->fileline(), binName, static_cast(nullptr), false, false}; + m_binInfos.push_back(BinInfo(pseudoBinp, varp, 1, nullptr, crossp, crossBins)); + + // Generate matching code: if (bin1 && bin2 && ... && binN) varName++; + generateNWayCrossBinMatchCode(crossp, coverpointRefs, bins, varp); + } + + // Generate matching code for N-way cross bin + void generateNWayCrossBinMatchCode(AstCoverCross* crossp, + const std::vector& coverpointRefs, + const std::vector& bins, AstVar* hitVarp) { + UINFO(4, " Generating " << bins.size() << "-way cross bin match"); + + // Build combined condition by ANDing all bin conditions + AstNodeExpr* fullCondp = nullptr; + + for (size_t i = 0; i < bins.size(); ++i) { + AstNodeExpr* const exprp = coverpointRefs[i]->exprp(); + AstNodeExpr* const condp = buildBinCondition(bins[i], exprp); + + if (fullCondp) { + fullCondp = new AstAnd{crossp->fileline(), fullCondp, condp}; + } else { + fullCondp = condp; + } + } + + // Generate: if (cond1 && cond2 && ... && condN) { ++varName; } + AstNodeStmt* const incrp = makeBinHitIncrement(crossp->fileline(), hitVarp); + + AstIf* const ifp = new AstIf{crossp->fileline(), fullCondp, incrp}; + m_sampleFuncp->addStmtsp(ifp); + } + + void generateCrossCode(AstCoverCross* crossp) { + UINFO(4, " Generating code for cross: " << crossp->name()); + + // Resolve coverpoint references and build list + std::vector coverpointRefs; + AstNode* itemp = crossp->itemsp(); + while (itemp) { + AstNode* const nextp = itemp->nextp(); + AstCoverpointRef* const refp = VN_AS(itemp, CoverpointRef); + // 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; + + if (!foundCpp) { + // Name not found as an explicit coverpoint - it's a direct variable + // reference (implicit coverpoint), which Verilator does not support. + // Warn and drop the whole cross. + refp->v3warn(COVERIGN, "Unsupported: cross of '" + << refp->prettyName() + << "' which is not a coverpoint (implicit coverpoint)"); + return; + } + + coverpointRefs.push_back(foundCpp); + + // Delete the reference node - it's no longer needed + VL_DO_DANGLING(pushDeletep(refp->unlinkFrBack()), refp); + itemp = nextp; + } // LCOV_EXCL_BR_LINE + + UINFO(4, " Generating " << coverpointRefs.size() << "-way cross"); + + // Collect bins from all coverpoints (excluding ignore/illegal bins) + std::vector> allCpBins; + for (AstCoverpoint* cpp : coverpointRefs) { + std::vector cpBins; + for (AstNode* binp = cpp->binsp(); binp; binp = binp->nextp()) { + AstCoverBin* const cbinp = VN_AS(binp, CoverBin); + if (cbinp->binsType() == VCoverBinsType::BINS_USER) { cpBins.push_back(cbinp); } + } + UINFO(4, " Found " << cpBins.size() << " bins in " << cpp->name()); + allCpBins.push_back(cpBins); + } + + // Generate cross bins using Cartesian product + generateCrossBinsRecursive(crossp, coverpointRefs, allCpBins, {}, 0); + } + + AstNodeExpr* buildBinCondition(AstCoverBin* binp, AstNodeExpr* exprp) { + // Get the range list from the bin + AstNode* const rangep = binp->rangesp(); + if (!rangep) return nullptr; + + // Check if this is a wildcard bin + const bool isWildcard = binp->isWildcard(); + + // Build condition by OR-ing all ranges together + AstNodeExpr* fullCondp = nullptr; + + for (AstNode* currRangep = rangep; currRangep; currRangep = currRangep->nextp()) { + AstNodeExpr* rangeCondp = nullptr; + + if (AstInsideRange* irp = VN_CAST(currRangep, InsideRange)) { + AstNodeExpr* const minExprp = irp->lhsp(); + AstNodeExpr* const maxExprp = irp->rhsp(); + AstConst* const minConstp = VN_CAST(minExprp, Const); + AstConst* const maxConstp = VN_CAST(maxExprp, Const); + const bool loUnbounded = VN_IS(minExprp, Unbounded); + const bool hiUnbounded = VN_IS(maxExprp, Unbounded); + if (loUnbounded || hiUnbounded) { + // Open-ended range: '$' is the coverpoint domain min/max, so the + // range reduces to a single inequality (e.g. {[10:$]} -> expr >= 10). + AstConst* const boundp = hiUnbounded ? minConstp : maxConstp; + if (loUnbounded && hiUnbounded) { + rangeCondp = new AstConst{irp->fileline(), AstConst::BitTrue{}}; + } else if (!boundp) { + irp->v3error("Non-constant expression in bin range; " + "range bounds must be constants"); + return nullptr; + } else if (boundp->num().isFourState()) { + irp->v3error("Four-state (x/z) value in bin range bound; " + "range bounds must be two-state constants"); + return nullptr; + } else { + rangeCondp = makeOpenRangeCondition(irp->fileline(), exprp, boundp, + /*isLowerBound=*/hiUnbounded); + } + } else if (!minConstp || !maxConstp) { + irp->v3error("Non-constant expression in bin range; " + "range bounds must be constants"); + return nullptr; + } else if (minConstp->num().isFourState() || maxConstp->num().isFourState()) { + irp->v3error("Four-state (x/z) value in bin range bound; " + "range bounds must be two-state constants"); + return nullptr; + } else if (minConstp->toUQuad() == maxConstp->toUQuad()) { + // Single value + if (isWildcard) { + rangeCondp = buildWildcardCondition(binp, exprp, minConstp); + } else { + rangeCondp = new AstEq{binp->fileline(), exprp->cloneTree(false), + minExprp->cloneTree(false)}; + } + } else { + rangeCondp = makeRangeCondition(irp->fileline(), exprp, minExprp, maxExprp); + } + } else if (AstConst* constp = VN_CAST(currRangep, Const)) { + if (isWildcard) { + rangeCondp = buildWildcardCondition(binp, exprp, constp); + } else { + rangeCondp = new AstEq{binp->fileline(), exprp->cloneTree(false), + constp->cloneTree(false)}; + } + } else { + currRangep->v3error( + "Non-constant expression in bin range; values must be constants"); + return nullptr; + } + + UASSERT_OBJ(rangeCondp, binp, "rangeCondp is null after building range condition"); + fullCondp + = fullCondp ? new AstOr{binp->fileline(), fullCondp, rangeCondp} : rangeCondp; + } + + return fullCondp; + } + + // Build a wildcard condition: (expr & mask) == (value & mask) + // where mask has 1s for defined bits and 0s for wildcard bits + // Non-owning: exprp is cloned internally; caller retains ownership. + AstNodeExpr* buildWildcardCondition(AstCoverBin* binp, AstNodeExpr* exprp, AstConst* constp) { + FileLine* const fl = binp->fileline(); + + // Extract mask from constant (bits that are not X/Z) + V3Number mask{constp, constp->width()}; + V3Number value{constp, constp->width()}; + + for (int bit = 0; bit < constp->width(); ++bit) { + if (constp->num().bitIs0(bit) || constp->num().bitIs1(bit)) { + mask.setBit(bit, 1); + value.setBit(bit, constp->num().bitIs1(bit) ? 1 : 0); + } else { + mask.setBit(bit, 0); + value.setBit(bit, 0); + } + } + + // Generate: (expr & mask) == (value & mask) + AstConst* const maskConstp = new AstConst{fl, mask}; + AstConst* const valueConstp = new AstConst{fl, value}; + + AstNodeExpr* const exprMasked = new AstAnd{fl, exprp->cloneTree(false), maskConstp}; + AstNodeExpr* const valueMasked = new AstAnd{fl, valueConstp, maskConstp->cloneTree(false)}; + + return new AstEq{fl, exprMasked, valueMasked}; + } + + void generateCoverageComputationCode() { + UINFO(4, " Generating coverage computation code"); + + // Invalidate cache: addMembersp() calls in generateCoverpointCode/generateCrossCode + // have added new members since the last scan, so clear before re-querying. + m_memberMap.clear(); + + // Find get_coverage() and get_inst_coverage() methods + AstFunc* const getCoveragep + = VN_CAST(m_memberMap.findMember(m_covergroupp, "get_coverage"), Func); + AstFunc* const getInstCoveragep + = VN_CAST(m_memberMap.findMember(m_covergroupp, "get_inst_coverage"), Func); + + // Even if there are no bins, we still need to generate the coverage methods + // Empty covergroups should return 100% coverage + if (m_binInfos.empty()) { + UINFO(4, " No bins found, will generate method to return 100%"); + } else { + UINFO(6, " Found " << m_binInfos.size() << " bins for coverage"); + } + + // Generate code for get_inst_coverage() + generateCoverageMethodBody(getInstCoveragep); + + // Generate code for get_coverage() (type-level) + // NOTE: Full type-level coverage requires instance tracking infrastructure + // For now, return 0.0 as a placeholder + AstVar* const coverageReturnVarp = VN_AS(getCoveragep->fvarp(), Var); + // TODO: Implement proper type-level coverage aggregation + // This requires tracking all instances and averaging their coverage + // For now, return 0.0 + getCoveragep->addStmtsp(new AstAssign{ + getCoveragep->fileline(), + new AstVarRef{getCoveragep->fileline(), coverageReturnVarp, VAccess::WRITE}, + new AstConst{getCoveragep->fileline(), AstConst::RealDouble{}, 0.0}}); + UINFO(4, " Added placeholder get_coverage() (returns 0.0)"); + } + + void generateCoverageMethodBody(AstFunc* funcp) { + FileLine* const fl = funcp->fileline(); + + // Count total bins (excluding ignore_bins and illegal_bins) + int totalBins = 0; + for (const BinInfo& bi : m_binInfos) { + UINFO(6, " Bin: " << bi.binp->name() << " type=" << bi.binp->binsType().ascii()); + if (bi.binp->binsType() != VCoverBinsType::BINS_IGNORE + && bi.binp->binsType() != VCoverBinsType::BINS_ILLEGAL) { + totalBins++; + } + } + + UINFO(4, " Total regular bins: " << totalBins << " of " << m_binInfos.size()); + + if (totalBins == 0) { + // No coverage to compute - return 100%. + // Any parser-generated initialization of returnVar is overridden by our assignment. + UINFO(4, " Empty covergroup, returning 100.0"); + AstVar* const returnVarp = VN_AS(funcp->fvarp(), Var); + funcp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, returnVarp, VAccess::WRITE}, + new AstConst{fl, AstConst::RealDouble{}, 100.0}}); + UINFO(4, " Added assignment to return 100.0"); + return; + } + + // Create local variable to count covered bins + AstVar* const coveredCountp + = new AstVar{fl, VVarType::BLOCKTEMP, "__Vcovered_count", funcp->findUInt32DType()}; + coveredCountp->funcLocal(true); + funcp->addStmtsp(coveredCountp); + + // Initialize: covered_count = 0 + funcp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, coveredCountp, VAccess::WRITE}, + new AstConst{fl, AstConst::WidthedValue{}, 32, 0}}); + + // For each regular bin, if count > 0, increment covered_count + for (const BinInfo& bi : m_binInfos) { + // Skip ignore_bins and illegal_bins in coverage calculation + if (bi.binp->binsType() == VCoverBinsType::BINS_IGNORE + || bi.binp->binsType() == VCoverBinsType::BINS_ILLEGAL) { + continue; + } + + // if (bin_count >= at_least) covered_count++; + AstIf* ifp = new AstIf{ + fl, + new AstGte{fl, new AstVarRef{fl, bi.varp, VAccess::READ}, + new AstConst{fl, AstConst::WidthedValue{}, 32, + static_cast(bi.atLeast)}}, + new AstAssign{fl, new AstVarRef{fl, coveredCountp, VAccess::WRITE}, + new AstAdd{fl, new AstVarRef{fl, coveredCountp, VAccess::READ}, + new AstConst{fl, AstConst::WidthedValue{}, 32, 1}}}, + nullptr}; + funcp->addStmtsp(ifp); + } + + // Find the return variable + AstVar* const returnVarp = VN_AS(funcp->fvarp(), Var); + + // Calculate coverage: (covered_count / total_bins) * 100.0 + // return_var = (double)covered_count / (double)total_bins * 100.0 + + // Cast covered_count to real/double + AstNodeExpr* const coveredReal + = new AstIToRD{fl, new AstVarRef{fl, coveredCountp, VAccess::READ}}; + + // Create total bins as a double constant + AstNodeExpr* const totalReal + = new AstConst{fl, AstConst::RealDouble{}, static_cast(totalBins)}; + + // Divide using AstDivD (double division that emits native /) + AstNodeExpr* const divExpr = new AstDivD{fl, coveredReal, totalReal}; + + // Multiply by 100 using AstMulD (double multiplication that emits native *) + AstNodeExpr* const hundredConst = new AstConst{fl, AstConst::RealDouble{}, 100.0}; + AstNodeExpr* const coverageExpr = new AstMulD{fl, hundredConst, divExpr}; + + // Assign to return variable + funcp->addStmtsp( + new AstAssign{fl, new AstVarRef{fl, returnVarp, VAccess::WRITE}, coverageExpr}); + + UINFO(6, " Added coverage computation to " << funcp->name() << " with " << totalBins + << " bins (excluding ignore/illegal)"); + } + + void generateCoverageRegistration() { + // Generate VL_COVER_INSERT calls for each bin in the covergroup + // This registers the bins with the coverage database so they can be reported + + UINFO(4, + " Generating coverage database registration for " << m_binInfos.size() << " bins"); + + if (m_binInfos.empty()) return; + + // For each bin, generate a VL_COVER_INSERT call + // The calls use CCall nodes to invoke VL_COVER_INSERT macro + for (const BinInfo& binInfo : m_binInfos) { + AstVar* const varp = binInfo.varp; + AstCoverBin* const binp = binInfo.binp; + AstCoverpoint* const coverpointp = binInfo.coverpointp; + AstCoverCross* const crossp = binInfo.crossp; + + FileLine* const fl = binp->fileline(); + + // Build hierarchical name: covergroup.coverpoint.bin or covergroup.cross.bin + std::string hierName = m_covergroupp->name(); + const std::string binName = binp->name(); + + if (coverpointp) { + // Coverpoint bin: V3LinkParse guarantees a non-empty name for every + // coverpoint (user label, single-variable name, or synthesized __Vcoverpoint). + UASSERT_OBJ(!coverpointp->name().empty(), coverpointp, + "Coverpoint without a name (should be set in V3LinkParse)"); + hierName += "." + coverpointp->name(); + } else { + // Cross bin: grammar always provides a name (user label or auto "__crossN") + hierName += "." + crossp->name(); + } + hierName += "." + binName; + + // Generate: VL_COVER_INSERT(contextp, hier, &binVar, "page", "v_covergroup/...", ...) + + UINFO(6, " Registering bin: " << hierName << " -> " << varp->name()); + + // Build the coverage insert as a C statement mixing literal text with a proper + // AstVarRef for the bin variable. Using AstVarRef (with selfPointer=This) lets + // V3Name apply __PVT__ mangling and the emitter apply nameProtect(), which also + // handles --protect-ids correctly. The vlSymsp->_vm_contextp__ path is the + // established convention used by the existing __vlCoverInsert helper. + // Use "page" field with v_covergroup prefix so the coverage type is identified + // correctly (consistent with code coverage). + const std::string pageName = "v_covergroup/" + m_covergroupp->name(); + AstCStmt* const cstmtp = new AstCStmt{fl}; + cstmtp->add("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), " + "\"" + + hierName + "\", &("); + AstVarRef* const binVarRefp = new AstVarRef{fl, varp, VAccess::READ}; + binVarRefp->selfPointer(VSelfPointerText{VSelfPointerText::This{}}); + cstmtp->add(binVarRefp); + cstmtp->add("), \"page\", \"" + pageName + + "\", " + "\"filename\", \"" + + fl->filename() + + "\", " + "\"lineno\", \"" + + std::to_string(fl->lineno()) + + "\", " + "\"column\", \"" + + std::to_string(fl->firstColumn()) + "\", "); + const std::string crossSuffix + = crossp ? (", \"cross\", \"1\", \"cross_bins\", \"" + binInfo.crossBins + "\"") + : ""; + if (binp->binsType() == VCoverBinsType::BINS_IGNORE) { + cstmtp->add("\"bin\", \"" + binName + "\", \"bin_type\", \"ignore\"" + crossSuffix + + ");"); + } else if (binp->binsType() == VCoverBinsType::BINS_ILLEGAL) { + cstmtp->add("\"bin\", \"" + binName + "\", \"bin_type\", \"illegal\"" + crossSuffix + + ");"); + } else { + cstmtp->add("\"bin\", \"" + binName + "\"" + crossSuffix + ");"); + } + + // Add to constructor + m_constructorp->addStmtsp(cstmtp); + + UINFO(6, " Added VL_COVER_INSERT call to constructor"); + } + } + + // VISITORS + AstNode* findEnclosingMemberRef(AstClass* cgClassp) { + // An embedded covergroup is lowered into a sibling AstClass that has no handle to + // the enclosing object. A coverpoint/iff/cross expression that references a + // (non-static) member of the enclosing class therefore emits C++ that accesses the + // member as if it were static ("invalid use of non-static data member"). Detect + // such references so the caller can skip lowering with a clean warning instead of + // producing uncompilable code. Returns the first offending node, or nullptr. + // Collect the covergroup class's own member variables (sample/constructor args); + // references to those are legitimate. + std::set ownVars; + for (AstNode* itemp = cgClassp->membersp(); itemp; itemp = itemp->nextp()) { + if (const AstVar* const varp = VN_CAST(itemp, Var)) ownVars.insert(varp); + } + AstNode* offenderp = nullptr; + const auto scan = [&](AstNode* rootp) { + rootp->foreach([&](AstVarRef* refp) { + if (offenderp) return; + const AstVar* const varp = refp->varp(); // Always set post-LinkDot + // A member of another class (the enclosing class) reached with no handle. + // Members of the covergroup class itself (sample/constructor args) are + // legitimate and excluded via ownVars. + if (varp->isClassMember() && !ownVars.count(varp)) offenderp = refp; + }); + }; + for (AstCoverpoint* cpp : m_coverpoints) scan(cpp); + for (AstCoverCross* crossp : m_coverCrosses) scan(crossp); + return offenderp; + } + + void visit(AstClass* nodep) override { + UINFO(9, "Visiting class: " << nodep->name() << " isCovergroup=" << nodep->isCovergroup()); + if (nodep->isCovergroup()) { + VL_RESTORER(m_covergroupp); + VL_RESTORER(m_sampleFuncp); + VL_RESTORER(m_constructorp); + VL_RESTORER(m_coverpoints); + VL_RESTORER(m_coverpointMap); + VL_RESTORER(m_coverCrosses); + m_covergroupp = nodep; + m_sampleFuncp = nullptr; + m_constructorp = nullptr; + m_coverpoints.clear(); + m_coverpointMap.clear(); + m_coverCrosses.clear(); + + // Extract and store the clocking event from AstCovergroup node + // The parser creates this node to preserve the event information + bool hasUnsupportedEvent = false; + for (AstNode* itemp = nodep->membersp(); itemp;) { + AstNode* const nextp = itemp->nextp(); + if (AstCovergroup* const cgp = VN_CAST(itemp, Covergroup)) { + // Store the event in the global map for V3Active to retrieve later + // V3LinkParse only creates this sentinel AstCovergroup node when a clocking + // event exists, so cgp->eventp() is always non-null here. + UASSERT_OBJ(cgp->eventp(), cgp, + "Sentinel AstCovergroup in class must have non-null eventp"); + // Check if the clocking event references a member variable (unsupported) + // Clocking events should be on signals/nets, not class members + bool eventUnsupported = false; + for (AstNode* senp = cgp->eventp()->sensesp(); senp; senp = senp->nextp()) { + AstSenItem* const senItemp = VN_AS(senp, SenItem); + if (AstVarRef* const varrefp // LCOV_EXCL_BR_LINE + = VN_CAST(senItemp->sensp(), VarRef)) { + if (varrefp->varp()->isClassMember()) { + cgp->v3warn(COVERIGN, "Unsupported: 'covergroup' clocking event " + "on member variable"); + eventUnsupported = true; + hasUnsupportedEvent = true; + break; + } + } + } + + if (!eventUnsupported) { + // Leave cgp in the class membersp so the SenTree stays + // linked in the AST. V3Active will find it via membersp, + // use the event, then delete the AstCovergroup itself. + UINFO(4, "Keeping covergroup event node for V3Active: " << nodep->name()); + itemp = nextp; + continue; + } + // Remove the AstCovergroup node - either unsupported event or no event + VL_DO_DANGLING(pushDeletep(cgp->unlinkFrBack()), cgp); + } + itemp = nextp; + } + + // If covergroup has unsupported clocking event, skip processing it + // but still clean up coverpoints so they don't reach downstream passes + if (hasUnsupportedEvent) { + iterateChildren(nodep); + for (AstCoverpoint* cpp : m_coverpoints) { + VL_DO_DANGLING(pushDeletep(cpp->unlinkFrBack()), cpp); + } + for (AstCoverCross* crossp : m_coverCrosses) { + VL_DO_DANGLING(pushDeletep(crossp->unlinkFrBack()), crossp); + } + return; + } + + // Find the sample() method and constructor + m_sampleFuncp = VN_CAST(m_memberMap.findMember(nodep, "sample"), Func); + // V3LinkParse always synthesizes a sample() method for every covergroup, and the + // sampling-code generation below dereferences m_sampleFuncp unconditionally. + UASSERT_OBJ(m_sampleFuncp, nodep, "Covergroup missing synthesized sample() method"); + m_sampleFuncp->isCovergroupSample(true); + m_constructorp = VN_CAST(m_memberMap.findMember(nodep, "new"), Func); + UINFO(9, "Found sample() method: " << (m_sampleFuncp ? "yes" : "no")); + UINFO(9, "Found constructor: " << (m_constructorp ? "yes" : "no")); + + iterateChildren(nodep); + + // Option B safety net for embedded covergroups: if a coverpoint/iff/cross + // references a member of the enclosing class, lowering would emit uncompilable + // C++ (no handle to the enclosing instance). Skip this covergroup with a clean + // warning rather than crashing the C++ compile. (Full support - an enclosing + // back-pointer - is the planned follow-up.) + if (AstNode* const offenderp = findEnclosingMemberRef(nodep)) { + offenderp->v3warn(COVERIGN, + "Unsupported: 'covergroup' coverpoint referencing enclosing " + "class member; ignoring covergroup " + << nodep->prettyNameQ()); + for (AstCoverpoint* cpp : m_coverpoints) { + VL_DO_DANGLING(pushDeletep(cpp->unlinkFrBack()), cpp); + } + for (AstCoverCross* crossp : m_coverCrosses) { + VL_DO_DANGLING(pushDeletep(crossp->unlinkFrBack()), crossp); + } + return; + } + + processCovergroup(); + // Remove lowered coverpoints/crosses from the class - they have been + // fully translated into C++ code and must not reach downstream passes + for (AstCoverpoint* cpp : m_coverpoints) { + VL_DO_DANGLING(pushDeletep(cpp->unlinkFrBack()), cpp); + } + for (AstCoverCross* crossp : m_coverCrosses) { + VL_DO_DANGLING(pushDeletep(crossp->unlinkFrBack()), crossp); + } + } else { + iterateChildren(nodep); + } + } + + void visit(AstCoverpoint* nodep) override { + UINFO(9, "Found coverpoint: " << nodep->name()); + m_coverpoints.push_back(nodep); + m_coverpointMap.emplace(nodep->name(), nodep); + iterateChildren(nodep); + } + + void visit(AstCoverCross* nodep) override { + UINFO(9, "Found cross: " << nodep->name()); + m_coverCrosses.push_back(nodep); + iterateChildren(nodep); + } + + void visit(AstNode* nodep) override { iterateChildren(nodep); } + +public: + // CONSTRUCTORS + explicit FunctionalCoverageVisitor(AstNetlist* nodep) { iterate(nodep); } + ~FunctionalCoverageVisitor() override = default; +}; + +//###################################################################### +// Functional coverage class functions + +void V3Covergroup::covergroup(AstNetlist* nodep) { + UINFO(4, __FUNCTION__ << ": "); + { FunctionalCoverageVisitor{nodep}; } // Destruct before checking + V3Global::dumpCheckGlobalTree("coveragefunc", 0, dumpTreeEitherLevel() >= 3); +} diff --git a/src/V3Covergroup.h b/src/V3Covergroup.h new file mode 100644 index 000000000..949b1484b --- /dev/null +++ b/src/V3Covergroup.h @@ -0,0 +1,30 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Covergroup implementation +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// 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: 2003-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#ifndef VERILATOR_V3COVERGROUP_H_ +#define VERILATOR_V3COVERGROUP_H_ + +#include "V3Ast.h" +#include "V3Error.h" + +//============================================================================ + +class V3Covergroup final { +public: + static void covergroup(AstNetlist* nodep); +}; + +#endif // Guard diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index d06a59bbc..4201a0ad5 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -307,6 +307,72 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { void visit(AstCoverInc*) override {} // N/A void visit(AstCoverToggle*) override {} // N/A + void visit(AstCovergroup* nodep) override { + // AstCovergroup appears as a member inside the lowered AstClass body. + // The outer covergroup/endcovergroup wrapper is already emitted by the + // AstNodeModule visitor (verilogKwd()="covergroup" on AstClass::isCovergroup). + // Here we only emit the clocking event, if any. + if (nodep->eventp()) { + putfs(nodep, ""); + iterateConst(nodep->eventp()); + } + } + void visit(AstCoverpoint* nodep) override { + putfs(nodep, nodep->name() + ": coverpoint "); + iterateAndNextConstNull(nodep->exprp()); + if (nodep->binsp() || nodep->optionsp()) { + puts(" {\n"); + iterateAndNextConstNull(nodep->optionsp()); + iterateAndNextConstNull(nodep->binsp()); + puts("}"); + } + puts(";\n"); + } + void visit(AstCoverBin* nodep) override { + switch (nodep->binsType()) { + case VCoverBinsType::BINS_IGNORE: putfs(nodep, "ignore_bins "); break; + case VCoverBinsType::BINS_ILLEGAL: putfs(nodep, "illegal_bins "); break; + default: putfs(nodep, "bins "); break; + } + puts(nodep->name()); + if (nodep->binsType() == VCoverBinsType::BINS_DEFAULT) { + puts(" = default"); + } else if (nodep->transp()) { + puts(" = "); + for (AstNode* setp = nodep->transp(); setp; setp = setp->nextp()) { + if (setp != nodep->transp()) puts(", "); + iterateConst(setp); + } + } else if (nodep->rangesp()) { // LCOV_EXCL_BR_LINE - false: CoverBin always has + // transp/rangesp/default + puts(" = {"); + for (AstNode* rangep = nodep->rangesp(); rangep; rangep = rangep->nextp()) { + if (rangep != nodep->rangesp()) puts(", "); + iterateConst(rangep); + } + puts("}"); + } + puts(";\n"); + } + void visit(AstCoverpointRef* nodep) override { putfs(nodep, nodep->name()); } + void visit(AstCoverCross* nodep) override { + putfs(nodep, nodep->name() + ": cross "); + for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) { + if (itemp != nodep->itemsp()) puts(", "); + iterateConst(itemp); + } + puts(";\n"); + } + void visit(AstCoverTransSet* nodep) override { + puts("("); + for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) { + if (itemp != nodep->itemsp()) puts(" => "); + iterateConst(itemp); + } + puts(")"); + } + void visit(AstCoverTransItem* nodep) override { iterateChildrenConst(nodep); } + void visit(AstCvtPackString* nodep) override { putfs(nodep, ""); if (AstConst* const lhsConstp = VN_CAST(nodep->lhsp(), Const)) { @@ -745,6 +811,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { iterateAndNextConstNull(nodep->elsep()); puts(")"); } + void visit(AstInsideRange* nodep) override { + puts("["); + iterateAndNextConstNull(nodep->lhsp()); + puts(":"); + iterateAndNextConstNull(nodep->rhsp()); + puts("]"); + } void visit(AstRange* nodep) override { puts("["); if (VN_IS(nodep->leftp(), Const) && VN_IS(nodep->rightp(), Const)) { @@ -942,6 +1015,10 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { puts("\n???? // "s + nodep->prettyTypeName() + " -> UNLINKED\n"); } } + void visit(AstClassRefDType* nodep) override { + UASSERT_OBJ(nodep->classp(), nodep, "AstClassRefDType not linked"); + putfs(nodep, EmitCUtil::prefixNameProtect(nodep->classp())); + } void visit(AstRequireDType* nodep) override { iterateConst(nodep->lhsp()); } void visit(AstModport* nodep) override { puts(nodep->verilogKwd()); diff --git a/src/V3Global.h b/src/V3Global.h index c4af01f87..4825a540a 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -132,6 +132,7 @@ class V3Global final { bool m_hasSystemCSections = false; // Has AstSystemCSection that need to be emitted bool m_useParallelBuild = false; // Use parallel build for model bool m_useRandSequence = false; // Has `randsequence` + bool m_useCovergroup = false; // Has covergroup declarations bool m_useRandomizeMethods = false; // Need to define randomize() class methods bool m_hasPrintedObjects = false; // Design has format args printed with to_string() uint64_t m_currentHierBlockCost = 0; // Total cost of this hier block, used for scheduling @@ -220,6 +221,8 @@ public: void useParallelBuild(bool flag) { m_useParallelBuild = flag; } bool useRandSequence() const { return m_useRandSequence; } void useRandSequence(bool flag) { m_useRandSequence = flag; } + bool useCovergroup() const { return m_useCovergroup; } + void useCovergroup(bool flag) { m_useCovergroup = flag; } bool useRandomizeMethods() const { return m_useRandomizeMethods; } void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; } bool hasPrintedObjects() const { return m_hasPrintedObjects; } diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index c55a5e7c5..e6beb5760 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -64,6 +64,7 @@ class LinkParseVisitor final : public VNVisitor { AstNodeExpr* m_defaultInSkewp = nullptr; // Current default input skew AstNodeExpr* m_defaultOutSkewp = nullptr; // Current default output skew int m_anonUdpId = 0; // Counter for anonymous UDP instances + int m_coverpointNum = 0; // Counter for unnamed coverpoints within current covergroup int m_genblkAbove = 0; // Begin block number of if/case/for above int m_genblkNum = 0; // Begin block number, 0=none seen int m_beginDepth = 0; // How many begin blocks above current node within current AstNodeModule @@ -1149,6 +1150,268 @@ class LinkParseVisitor final : public VNVisitor { iterateChildren(nodep); } + // Append, for each arg in argsp, an INPUT parameter plus a "this. = " + // assignment to funcp. The parameter is a clone of the covergroup member and so shares its + // name; 'this.' on the LHS targets the member, otherwise the same-named local parameter + // shadows it and the assignment self-assigns the parameter, leaving the member unwritten. + // argsp may be null (no args appended). + static void addArgMemberCopies(AstFunc* funcp, AstNode* argsp) { + for (AstNode* argp = argsp; argp; argp = argp->nextp()) { + AstVar* const origVarp = VN_AS(argp, Var); + AstVar* const paramp = origVarp->cloneTree(false); + paramp->funcLocal(true); + paramp->direction(VDirection::INPUT); + funcp->addStmtsp(paramp); + AstNodeExpr* const lhsp = new AstDot{ + origVarp->fileline(), false, new AstParseRef{origVarp->fileline(), "this"}, + new AstParseRef{origVarp->fileline(), origVarp->name()}}; + AstNodeExpr* const rhsp = new AstParseRef{paramp->fileline(), paramp->name()}; + funcp->addStmtsp(new AstAssign{origVarp->fileline(), lhsp, rhsp}); + } + } + + // Create boilerplate covergroup methods on the given AstClass. + // argsp/sampleArgsp are the raw arg lists still owned by the caller; they are iterated + // (cloned) but not deleted here. + static void createCovergroupMethods(AstClass* nodep, AstFunc* newFuncp, AstNode* argsp, + AstNode* sampleArgsp) { + // Hidden static to take unspecified reference argument results + AstVar* const defaultVarp + = new AstVar{nodep->fileline(), VVarType::MEMBER, "__Vint", nodep->findIntDType()}; + defaultVarp->lifetime(VLifetime::STATIC_EXPLICIT); + nodep->addStmtsp(defaultVarp); + + // Handle constructor arguments - add function parameters and assignments + if (argsp) { + UASSERT_OBJ(newFuncp, nodep, + "Covergroup class must have a 'new' constructor function"); + // Save the existing body statements and unlink them, so the arg assignments run + // before the coverage body, then re-append the body. + AstNode* const existingBodyp = newFuncp->stmtsp(); + if (existingBodyp) existingBodyp->unlinkFrBackWithNext(); + addArgMemberCopies(newFuncp, argsp); + if (existingBodyp) newFuncp->addStmtsp(existingBodyp); + } + + // IEEE: option / type_option members allow external access (cg_inst.option.X) + // and require std:: types; std:: is kept because setUsesStdPackage() is called + // at parse time for every covergroup declaration. + { + AstVar* const varp + = new AstVar{nodep->fileline(), VVarType::MEMBER, "option", VFlagChildDType{}, + new AstRefDType{nodep->fileline(), "vl_covergroup_options_t", + new AstClassOrPackageRef{nodep->fileline(), "std", + nullptr, nullptr}, + nullptr}}; + nodep->addMembersp(varp); + } + { + AstVar* const varp + = new AstVar{nodep->fileline(), VVarType::MEMBER, "type_option", VFlagChildDType{}, + new AstRefDType{nodep->fileline(), "vl_covergroup_type_options_t", + new AstClassOrPackageRef{nodep->fileline(), "std", + nullptr, nullptr}, + nullptr}}; + nodep->addMembersp(varp); + } + + // IEEE: function void sample([arguments]) + { + AstFunc* const funcp = new AstFunc{nodep->fileline(), "sample", nullptr, nullptr}; + addArgMemberCopies(funcp, sampleArgsp); + funcp->classMethod(true); + funcp->dtypep(funcp->findVoidDType()); + nodep->addMembersp(funcp); + } + + // IEEE: function void start(), void stop() + for (const string& name : {"start"s, "stop"s}) { + AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr}; + funcp->classMethod(true); + funcp->dtypep(funcp->findVoidDType()); + nodep->addMembersp(funcp); + } + + // IEEE: static function real get_coverage(optional ref int, optional ref int) + // IEEE: function real get_inst_coverage(optional ref int, optional ref int) + for (const string& name : {"get_coverage"s, "get_inst_coverage"s}) { + AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr}; + funcp->fileline()->warnOff(V3ErrorCode::NORETURN, true); + funcp->isStatic(name == "get_coverage"); + funcp->classMethod(true); + funcp->dtypep(funcp->findVoidDType()); + nodep->addMembersp(funcp); + { + AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, name, + nodep->findDoubleDType()}; + varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); + varp->funcLocal(true); + varp->direction(VDirection::OUTPUT); + varp->funcReturn(true); + funcp->fvarp(varp); + } + for (const string& varname : {"covered_bins"s, "total_bins"s}) { + AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, varname, + nodep->findStringDType()}; + varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); + varp->funcLocal(true); + varp->direction(VDirection::INPUT); + varp->valuep(new AstVarRef{nodep->fileline(), defaultVarp, VAccess::READ}); + funcp->addStmtsp(varp); + } + } + + // IEEE: function void set_inst_name(string) + { + AstFunc* const funcp + = new AstFunc{nodep->fileline(), "set_inst_name", nullptr, nullptr}; + funcp->classMethod(true); + funcp->dtypep(funcp->findVoidDType()); + nodep->addMembersp(funcp); + AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, "name", + nodep->findStringDType()}; + varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); + varp->funcLocal(true); + varp->direction(VDirection::INPUT); + funcp->addStmtsp(varp); + } + } + + void visit(AstCovergroup* nodep) override { + // AstCovergroup can only appear inside a module/class/package; never at root level. + UASSERT_OBJ(m_modp, nodep, "AstCovergroup not under module"); + // If we're already inside a covergroup class, this is the sentinel AstCovergroup + // node carrying the clocking event for V3Covergroup - don't re-transform it. + if (VN_IS(m_modp, Class) && VN_AS(m_modp, Class)->isCovergroup()) return; + + // Transform raw parse-time AstCovergroup into a fully-formed AstClass + cleanFileline(nodep); + + const string libname = m_modp->libname(); + AstClass* const cgClassp = new AstClass{nodep->fileline(), nodep->name(), libname}; + cgClassp->isCovergroup(true); + v3Global.useCovergroup(true); + + // Clocking event: unlink before deleteTree, attach as AstCovergroup child on class + if (AstSenTree* const eventp = nodep->eventp()) { + eventp->unlinkFrBack(); + AstCovergroup* const cgNodep = new AstCovergroup{ + nodep->fileline(), nodep->name(), nullptr, nullptr, nullptr, eventp}; + cgClassp->addMembersp(cgNodep); + } + + // Convert constructor args to member variables + for (AstNode* argp = nodep->argsp(); argp; argp = argp->nextp()) { + AstVar* const origVarp = VN_AS(argp, Var); + AstVar* const memberp = origVarp->cloneTree(false); + memberp->varType(VVarType::MEMBER); + memberp->funcLocal(false); + memberp->direction(VDirection::NONE); + cgClassp->addMembersp(memberp); + } + + // Convert sample args to member variables + for (AstNode* argp = nodep->sampleArgsp(); argp; argp = argp->nextp()) { + AstVar* const origVarp = VN_AS(argp, Var); + AstVar* const memberp = origVarp->cloneTree(false); + memberp->varType(VVarType::MEMBER); + memberp->funcLocal(false); + memberp->direction(VDirection::NONE); + cgClassp->addMembersp(memberp); + } + + // Create the constructor; detach membersp (coverage body) and use as its body + AstFunc* const newFuncp = new AstFunc{nodep->fileline(), "new", nullptr, nullptr}; + newFuncp->fileline()->warnOff(V3ErrorCode::NORETURN, true); + newFuncp->classMethod(true); + newFuncp->isConstructor(true); + newFuncp->dtypep(cgClassp->dtypep()); + if (AstNode* const bodyp = nodep->membersp()) { + bodyp->unlinkFrBackWithNext(); + newFuncp->addStmtsp(bodyp); + } + cgClassp->addMembersp(newFuncp); + + // Add all boilerplate covergroup methods (reads argsp/sampleArgsp from nodep) + createCovergroupMethods(cgClassp, newFuncp, nodep->argsp(), nodep->sampleArgsp()); + + // Replace AstCovergroup with AstClass and process the new class normally. + // Reset the unnamed-coverpoint counter so synthesized names are stable and + // independent of unrelated covergroups elsewhere in the file. + VL_RESTORER(m_coverpointNum); + m_coverpointNum = 0; + nodep->replaceWith(cgClassp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + iterate(cgClassp); + } + + void visit(AstCoverpoint* nodep) override { + cleanFileline(nodep); + // Give every coverpoint a guaranteed-unique, deterministic name so all downstream + // consumers (generated bin-variable names, the cross coverpoint map, hierarchical + // report names) see a consistent identifier. Unlabeled coverpoints arrive from the + // grammar with an empty name; left empty, two of them in one covergroup collide on + // the generated "__Vcov__" variable name (e.g. duplicate "__Vcov__auto_0"). + if (nodep->name().empty()) { + // A single-identifier coverpoint expression is the only form that parses to an + // AstParseRef with a usable name here; a dotted/scoped/select/concatenation/call + // expression is either a different node (so the cast yields null) or a name-less + // AstParseRef (e.g. a member-select). Either of those gets a synthesized name. + const AstParseRef* const refp = VN_CAST(nodep->exprp(), ParseRef); + if (refp && !refp->name().empty()) { + // Single-identifier coverpoint: take the variable's name (IEEE 1800-2023 + // 19.5 - an unlabeled coverpoint of a single variable is named for it). + nodep->name(refp->name()); + } else { + // Compound expression (member/part select, concatenation, ...): synthesize + // a unique name. Leading "__V" keeps it out of the user namespace. + nodep->name("__Vcoverpoint" + cvtToStr(m_coverpointNum++)); + } + } + // Re-sort the parse-time mixed bins list (AstCoverBin + AstCgOptionAssign) + // into the typed binsp and optionsp slots. The grammar attaches both node types + // to binsp (op2) as a raw List[AstNode]; now that they are properly parented we + // can iterate and split them without any temporary-parent tricks. + for (AstNode *itemp = nodep->binsp(), *nextp; itemp; itemp = nextp) { + nextp = itemp->nextp(); + if (AstCgOptionAssign* const optp = VN_CAST(itemp, CgOptionAssign)) { + optp->unlinkFrBack(); + if (optp->optionType() == VCoverOptionType::AT_LEAST + || optp->optionType() == VCoverOptionType::AUTO_BIN_MAX) { + nodep->addOptionsp(new AstCoverOption{optp->fileline(), optp->optionType(), + optp->valuep()->cloneTree(false)}); + } else { + optp->v3warn(COVERIGN, + "Ignoring unsupported coverage option: " + optp->prettyNameQ()); + } + VL_DO_DANGLING(optp->deleteTree(), optp); + } + } + iterateChildren(nodep); + } + + void visit(AstCoverCross* nodep) override { + cleanFileline(nodep); + // Distribute the parse-time raw cross_body list (rawBodyp, op3) into the + // typed optionsp slot. The grammar produces AstCgOptionAssign nodes for + // option.* items; convert them to AstCoverOption exactly as visit(AstCoverpoint*) + // does. Other items (functions, unsupported bin selectors) are discarded. + for (AstNode *itemp = nodep->rawBodyp(), *nextp; itemp; itemp = nextp) { + nextp = itemp->nextp(); + itemp->unlinkFrBack(); + AstCgOptionAssign* const optp = VN_AS(itemp, CgOptionAssign); + const VCoverOptionType optType = optp->optionType(); + optp->v3warn(COVERIGN, + "Ignoring unsupported coverage cross option: " + optp->prettyNameQ()); + // Always preserve the option node so V3Coverage can track its source line + // for coverage annotation, even when the option itself is unsupported. + nodep->addOptionsp( + new AstCoverOption{optp->fileline(), optType, optp->valuep()->cloneTree(false)}); + VL_DO_DANGLING(optp->deleteTree(), optp); + } + iterateChildren(nodep); + } + void visit(AstNode* nodep) override { // Default: Just iterate cleanFileline(nodep); diff --git a/src/V3ParseGrammar.h b/src/V3ParseGrammar.h index 42a3db9c4..c081d599a 100644 --- a/src/V3ParseGrammar.h +++ b/src/V3ParseGrammar.h @@ -15,11 +15,13 @@ //************************************************************************* #include "V3Ast.h" +#include "V3Const.h" #include "V3Control.h" #include "V3Global.h" #include "V3ParseImp.h" // Defines YYTYPE; before including bison header #include +#include class V3ParseGrammar final { public: @@ -95,97 +97,6 @@ public: nodep->trace(singletonp()->allTracingOn(fileline)); return nodep; } - void createCoverGroupMethods(AstClass* nodep, AstNode* sampleArgs) { - // Hidden static to take unspecified reference argument results - AstVar* const defaultVarp - = new AstVar{nodep->fileline(), VVarType::MEMBER, "__Vint", nodep->findIntDType()}; - defaultVarp->lifetime(VLifetime::STATIC_EXPLICIT); - nodep->addStmtsp(defaultVarp); - - // IEEE: option - { - v3Global.setUsesStdPackage(); - AstVar* const varp - = new AstVar{nodep->fileline(), VVarType::MEMBER, "option", VFlagChildDType{}, - new AstRefDType{nodep->fileline(), "vl_covergroup_options_t", - new AstClassOrPackageRef{nodep->fileline(), "std", - nullptr, nullptr}, - nullptr}}; - nodep->addMembersp(varp); - } - - // IEEE: type_option - { - v3Global.setUsesStdPackage(); - AstVar* const varp - = new AstVar{nodep->fileline(), VVarType::MEMBER, "type_option", VFlagChildDType{}, - new AstRefDType{nodep->fileline(), "vl_covergroup_type_options_t", - new AstClassOrPackageRef{nodep->fileline(), "std", - nullptr, nullptr}, - nullptr}}; - nodep->addMembersp(varp); - } - - // IEEE: function void sample() - { - AstFunc* const funcp = new AstFunc{nodep->fileline(), "sample", nullptr, nullptr}; - funcp->addStmtsp(sampleArgs); - funcp->classMethod(true); - funcp->dtypep(funcp->findVoidDType()); - nodep->addMembersp(funcp); - } - - // IEEE: function void start(), void stop() - for (const string& name : {"start"s, "stop"s}) { - AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr}; - funcp->classMethod(true); - funcp->dtypep(funcp->findVoidDType()); - nodep->addMembersp(funcp); - } - - // IEEE: static function real get_coverage(optional ref int, optional ref int) - // IEEE: function real get_inst_coverage(optional ref int, optional ref int) - for (const string& name : {"get_coverage"s, "get_inst_coverage"s}) { - AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr}; - funcp->fileline()->warnOff(V3ErrorCode::NORETURN, true); - funcp->isStatic(name == "get_coverage"); - funcp->classMethod(true); - funcp->dtypep(funcp->findVoidDType()); - nodep->addMembersp(funcp); - { - AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, name, - nodep->findDoubleDType()}; - varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); - varp->funcLocal(true); - varp->direction(VDirection::OUTPUT); - varp->funcReturn(true); - funcp->fvarp(varp); - } - for (const string& varname : {"covered_bins"s, "total_bins"s}) { - AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, varname, - nodep->findStringDType()}; - varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); - varp->funcLocal(true); - varp->direction(VDirection::INPUT); - varp->valuep(new AstVarRef{nodep->fileline(), defaultVarp, VAccess::READ}); - funcp->addStmtsp(varp); - } - } - // IEEE: function void set_inst_name(string) - { - AstFunc* const funcp - = new AstFunc{nodep->fileline(), "set_inst_name", nullptr, nullptr}; - funcp->classMethod(true); - funcp->dtypep(funcp->findVoidDType()); - nodep->addMembersp(funcp); - AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, "name", - nodep->findStringDType()}; - varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); - varp->funcLocal(true); - varp->direction(VDirection::INPUT); - funcp->addStmtsp(varp); - } - } AstDisplay* createDisplayError(FileLine* fileline) { AstDisplay* nodep = new AstDisplay{fileline, VDisplayType::DT_ERROR, "", nullptr, nullptr}; AstNode::addNext(nodep, new AstStop{fileline, false}); diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 062a5603f..5bae5d0aa 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -1355,6 +1355,7 @@ class TaskVisitor final : public VNVisitor { cfuncp->isVirtual(nodep->isVirtual()); cfuncp->dpiPure(nodep->dpiPure()); if (nodep->name() == "new") cfuncp->isConstructor(true); + if (nodep->isCovergroupSample()) cfuncp->isCovergroupSample(true); if (cfuncp->dpiExportImpl()) cfuncp->cname(nodep->cname()); if (cfuncp->dpiImportWrapper()) cfuncp->cname(nodep->cname()); diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 612f68a0f..0c2e104e9 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -343,11 +343,15 @@ class TimingSuspendableVisitor final : public VNVisitor { } } void visit(AstNodeCCall* nodep) override { - new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(nodep->funcp()), getSuspendDepVtx(m_procp), - P_CALL}; + AstCFunc* const funcp = nodep->funcp(); + UASSERT_OBJ(funcp, nodep, "AstNodeCCall must have non-null funcp post-link"); + UASSERT_OBJ(m_procp, nodep, "AstNodeCCall must be inside a procedure/CFunc/Begin"); - new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(nodep->funcp()), - getNeedsProcDepVtx(m_procp), P_CALL}; + UINFO(9, "V3Timing: Processing CCall to " << funcp->name() << " in dependency graph\n"); + new V3GraphEdge{&m_suspGraph, getSuspendDepVtx(funcp), getSuspendDepVtx(m_procp), P_CALL}; + + new V3GraphEdge{&m_procGraph, getNeedsProcDepVtx(funcp), getNeedsProcDepVtx(m_procp), + P_CALL}; iterateChildren(nodep); } @@ -976,8 +980,16 @@ class TimingControlVisitor final : public VNVisitor { } } void visit(AstNodeCCall* nodep) override { - if (nodep->funcp()->needProcess()) m_hasProcess = true; - if (hasFlags(nodep->funcp(), T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable + AstCFunc* const funcp = nodep->funcp(); + + // Skip automatic covergroup sampling calls + if (funcp->isCovergroupSample()) { + iterateChildren(nodep); + return; + } + + if (funcp->needProcess()) m_hasProcess = true; + if (hasFlags(funcp, T_SUSPENDEE) && !nodep->user1SetOnce()) { // If suspendable // Calls to suspendables are always void return type, hence parent must be StmtExpr AstStmtExpr* const stmtp = VN_AS(nodep->backp(), StmtExpr); stmtp->replaceWith(new AstCAwait{nodep->fileline(), nodep->unlinkFrBack()}); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 7f45a9d23..e8df0f4df 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -225,6 +225,7 @@ class WidthVisitor final : public VNVisitor { const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations const AstEnumItem* m_enumItemp = nullptr; // Current enum item AstNodeFTask* m_ftaskp = nullptr; // Current function/task + AstClass* m_cgClassp = nullptr; // Current covergroup class AstNodeModule* m_modep = nullptr; // Current module const AstConstraint* m_constraintp = nullptr; // Current constraint AstNodeProcedure* m_procedurep = nullptr; // Current final/always @@ -1896,10 +1897,35 @@ class WidthVisitor final : public VNVisitor { if (m_vup->prelim()) iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); } void visit(AstCgOptionAssign* nodep) override { - // We report COVERIGN on the whole covergroup; if get more fine-grained add this - // nodep->v3warn(COVERIGN, "Ignoring unsupported: coverage option"); + // Extract covergroup option values and store in AstClass before deleting. + // m_cgClassp is always set here: AstCgOptionAssign only appears in covergroup + // class bodies, and visitClass sets m_cgClassp before iterating children. + if (nodep->optionType() == VCoverOptionType::AUTO_BIN_MAX) { + // By V3Width time, V3Param has already folded any parameter references. + // If the value is still not a constant, it is a runtime expression - emit error. + if (AstConst* constp = VN_CAST(nodep->valuep(), Const)) { + m_cgClassp->cgAutoBinMax(constp->toSInt()); + UINFO(6, " Covergroup " << m_cgClassp->name() + << " option.auto_bin_max = " << constp->toSInt() << endl); + } else { + nodep->valuep()->v3warn(COVERIGN, "Ignoring unsupported: non-constant " + "'option.auto_bin_max'; using default value"); + } + } + // Add more options here as needed (weight, goal, at_least, per_instance, comment) + + // Delete the assignment node (we've extracted the value) VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); } + void visit(AstCoverpoint* nodep) override { + // The coverpoint expression is self-determined (IEEE 1800-2023 19.5). Width it + // with a context so a bit/part-select (AstSel) is sized here; otherwise it would + // reach assertAtExpr() with m_vup==null and fail as an internal error. + userIterateAndNext(nodep->exprp(), WidthVP{SELF, BOTH}.p()); + userIterateAndNext(nodep->binsp(), nullptr); + if (nodep->iffp()) iterateCheckBool(nodep, "iff condition", nodep->iffp(), BOTH); + userIterateAndNext(nodep->optionsp(), nullptr); + } void visit(AstPow* nodep) override { // Pow is special, output sign only depends on LHS sign, but // function result depends on both signs @@ -3539,7 +3565,16 @@ class WidthVisitor final : public VNVisitor { return AstEqWild::newTyped(itemp->fileline(), exprp, itemp->unlinkFrBack()); } void visit(AstInsideRange* nodep) override { - // Just do each side; AstInside will rip these nodes out later + // Just do each side; AstInside will rip these nodes out later. + // When m_vup is null, this range appears outside a normal expression context (e.g. + // in a covergroup bin declaration). Pre-fold constant arithmetic in that case + // (e.g., AstNegate(Const) -> Const) so children have their types set before widthing. + // We cannot do this unconditionally: in a normal 'inside' expression (m_vup set), + // range bounds may be enum refs not yet widthed, and constifyEdit would crash. + if (!m_vup) { + V3Const::constifyEdit(nodep->lhsp()); // lhsp may change + V3Const::constifyEdit(nodep->rhsp()); // rhsp may change + } userIterateAndNext(nodep->lhsp(), m_vup); userIterateAndNext(nodep->rhsp(), m_vup); nodep->dtypeFrom(nodep->lhsp()); @@ -7712,6 +7747,8 @@ class WidthVisitor final : public VNVisitor { // Must do extends first, as we may in functions under this class // start following a tree of extends that takes us to other classes userIterateAndNext(nodep->extendsp(), nullptr); + VL_RESTORER(m_cgClassp); + if (nodep->isCovergroup()) m_cgClassp = nodep; userIterateChildren(nodep, nullptr); // First size all members } void visit(AstNodeModule* nodep) override { diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 77e9272d3..13bb95c6f 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -38,6 +38,7 @@ #include "V3Control.h" #include "V3Coverage.h" #include "V3CoverageJoin.h" +#include "V3Covergroup.h" #include "V3Dead.h" #include "V3Delayed.h" #include "V3Depth.h" @@ -240,6 +241,10 @@ static void process() { // the AST context needed to recover and lower FSMs reliably. if (v3Global.opt.coverageNonFsm()) V3Coverage::coverage(v3Global.rootp()); + // Functional coverage code generation + // Generate code for covergroups/coverpoints + if (v3Global.useCovergroup()) V3Covergroup::covergroup(v3Global.rootp()); + // Resolve randsequence if they are used by the design if (v3Global.useRandSequence()) V3RandSequence::randSequenceNetlist(v3Global.rootp()); diff --git a/src/VlcPoint.h b/src/VlcPoint.h index f8758cd20..4537ef182 100644 --- a/src/VlcPoint.h +++ b/src/VlcPoint.h @@ -66,6 +66,11 @@ 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(); } + string crossBins() const { return keyExtract(VL_CIK_CROSS_BINS, m_name.c_str()); } string thresh() const { // string as maybe "" return keyExtract(VL_CIK_THRESH, m_name.c_str()); @@ -123,6 +128,23 @@ public: os << std::setw(6) << std::setfill('0') << count(); os << " point: type=" << type() << " comment=" << comment() << " hier=" << hier(); os << "\n"; + if (isCross()) { + const string bins = crossBins(); + if (!bins.empty()) { + os << " // cross: ["; + bool first = true; + size_t start = 0; + while (true) { + const size_t comma = bins.find(',', start); + if (!first) os << ", "; + os << bins.substr(start, comma == string::npos ? string::npos : comma - start); + first = false; + if (comma == string::npos) break; + start = comma + 1; + } + os << "]\n"; + } + } } }; diff --git a/src/VlcTop.cpp b/src/VlcTop.cpp index e46f33571..ccaec527e 100644 --- a/src/VlcTop.cpp +++ b/src/VlcTop.cpp @@ -25,8 +25,10 @@ #include #include +#include #include #include +#include #include #include @@ -346,8 +348,6 @@ void VlcTop::rank() { } } -//###################################################################### - void VlcTop::annotateCalc() { // Calculate per-line information into filedata structure for (const auto& i : m_points) { diff --git a/src/verilog.y b/src/verilog.y index 370693668..829f8c4a4 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3958,16 +3958,16 @@ value_range: // ==IEEE: value_range/open_value_range covergroup_value_range: // ==IEEE-2012: covergroup_value_range cgexpr { $$ = $1; } | '[' cgexpr ':' cgexpr ']' - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); DEL($2, $4); } + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: covergroup value range '[...]'"); DEL($2, $4); } // // IEEE-2023: added all four: // // Skipped as '$' is part of our expr // // IEEE-2023: '[' '$' ':' cgexpr ']' // // Skipped as '$' is part of our expr // // IEEE-2023: '[' cgexpr ':' '$' ']' | '[' cgexpr yP_PLUSSLASHMINUS cgexpr ']' - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); DEL($2, $4); } + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: covergroup value range '[...]'"); DEL($2, $4); } | '[' cgexpr yP_PLUSPCTMINUS cgexpr ']' - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); DEL($2, $4); } + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: covergroup value range '[...]'"); DEL($2, $4); } ; caseCondList: // IEEE: part of case_item @@ -6970,40 +6970,27 @@ covergroup_declaration: // ==IEEE: covergroup_declaration yCOVERGROUP idAny cgPortListE coverage_eventE ';' /*cont*/ coverage_spec_or_optionListE /*cont*/ yENDGROUP endLabelE - { AstClass *cgClassp = new AstClass{$2, *$2, PARSEP->libname()}; - cgClassp->isCovergroup(true); - AstFunc* const newp = new AstFunc{$1, "new", nullptr, nullptr}; - newp->fileline()->warnOff(V3ErrorCode::NORETURN, true); - newp->classMethod(true); - newp->isConstructor(true); - newp->dtypep(cgClassp->dtypep()); - newp->addStmtsp($3); - newp->addStmtsp($6); - cgClassp->addMembersp(newp); - GRAMMARP->createCoverGroupMethods(cgClassp, $4); - - $$ = cgClassp; - GRAMMARP->endLabel($8, $$, $8); - BBCOVERIGN($1, "Ignoring unsupported: covergroup"); - } + { AstSenTree* clockp = nullptr; + AstNode* sampleArgsp = nullptr; + if ($4) { + if (VN_IS($4, SenItem)) { + clockp = new AstSenTree{$1, VN_AS($4, SenItem)}; + } else { + sampleArgsp = $4; + } + } + $$ = new AstCovergroup{$1, *$2, static_cast($3), + static_cast(sampleArgsp), $6, clockp}; + // Every covergroup has option/type_option members (added by V3LinkParse) + // referencing std:: types, so mark std as needed at parse time. + v3Global.setUsesStdPackage(); + GRAMMARP->endLabel($8, $$, $8); } | yCOVERGROUP yEXTENDS idAny ';' /*cont*/ coverage_spec_or_optionListE /*cont*/ yENDGROUP endLabelE - { AstClass *cgClassp = new AstClass{$3, *$3, PARSEP->libname()}; - cgClassp->isCovergroup(true); - AstFunc* const newp = new AstFunc{$1, "new", nullptr, nullptr}; - newp->fileline()->warnOff(V3ErrorCode::NORETURN, true); - newp->classMethod(true); - newp->isConstructor(true); - newp->dtypep(cgClassp->dtypep()); - newp->addStmtsp($5); - cgClassp->addMembersp(newp); - GRAMMARP->createCoverGroupMethods(cgClassp, nullptr); - - $$ = cgClassp; - GRAMMARP->endLabel($7, $$, $7); - BBCOVERIGN($1, "Ignoring unsupported: covergroup"); - } + { $$ = nullptr; + BBUNSUP($1, "Unsupported: covergroup inheritance (extends)"); + DEL($5); } ; cgPortListE: @@ -7036,12 +7023,61 @@ coverage_spec_or_option: // ==IEEE: coverage_spec_or_option coverage_option: // ==IEEE: coverage_option // // option/type_option aren't really keywords id/*yOPTION | yTYPE_OPTION*/ '.' idAny/*member_identifier*/ '=' expr - { if (*$1 == "option") { - $$ = new AstCgOptionAssign{$1, false, *$3, $5}; - } else if (*$1 == "type_option") { - $$ = new AstCgOptionAssign{$1, true, *$3, $5}; + { $$ = nullptr; + if (*$1 == "option" || *$1 == "type_option") { + const bool typeOpt = (*$1 == "type_option"); + VCoverOptionType optType = VCoverOptionType::UNKNOWN; + bool valid = true; + if (!typeOpt) { + // IEEE 1800-2023 Table 19-1: option.* names + if (*$3 == "at_least") optType = VCoverOptionType::AT_LEAST; + else if (*$3 == "auto_bin_max") optType = VCoverOptionType::AUTO_BIN_MAX; + else if (*$3 == "comment") optType = VCoverOptionType::COMMENT; + else if (*$3 == "cross_num_print_missing") optType = VCoverOptionType::CROSS_NUM_PRINT_MISSING; + else if (*$3 == "cross_retain_auto_bins") optType = VCoverOptionType::CROSS_RETAIN_AUTO_BINS; + else if (*$3 == "detect_overlap") optType = VCoverOptionType::DETECT_OVERLAP; + else if (*$3 == "get_inst_coverage") optType = VCoverOptionType::GET_INST_COVERAGE; + else if (*$3 == "goal") optType = VCoverOptionType::GOAL; + else if (*$3 == "name") optType = VCoverOptionType::NAME; + else if (*$3 == "per_instance") optType = VCoverOptionType::PER_INSTANCE; + else if (*$3 == "weight") optType = VCoverOptionType::WEIGHT; + else { + $1->v3error("Unknown coverage option name 'option." + << *$3 << "'" + << "; not a valid option per IEEE 1800-2023 Table 19-1"); + valid = false; + } + } else { + // IEEE 1800-2023 Table 19-3: type_option.* names + if (*$3 == "comment") optType = VCoverOptionType::COMMENT; + else if (*$3 == "distribute_first") optType = VCoverOptionType::DISTRIBUTE_FIRST; + else if (*$3 == "goal") optType = VCoverOptionType::GOAL; + else if (*$3 == "merge_instances") optType = VCoverOptionType::MERGE_INSTANCES; + else if (*$3 == "real_interval") optType = VCoverOptionType::REAL_INTERVAL; + else if (*$3 == "strobe") optType = VCoverOptionType::STROBE; + else if (*$3 == "weight") optType = VCoverOptionType::WEIGHT; + else { + // Specific message for option.* names used under type_option.* + if (*$3 == "at_least" || *$3 == "auto_bin_max" + || *$3 == "cross_num_print_missing" || *$3 == "cross_retain_auto_bins" + || *$3 == "detect_overlap" || *$3 == "get_inst_coverage" + || *$3 == "name" || *$3 == "per_instance") { + $1->v3error("'type_option." << *$3 + << "' is not valid; use 'option." << *$3 << "' instead"); + } else { + $1->v3error("Unknown coverage type option name 'type_option." + << *$3 << "'" + << "; not a valid type option per IEEE 1800-2023 Table 19-3"); + } + valid = false; + } + } + if (valid) { + $$ = new AstCgOptionAssign{$1, typeOpt, optType, *$3, $5}; + } else { + DEL($5); + } } else { - $$ = nullptr; $1->v3error("Syntax error; expected 'option' or 'type_option': '" << *$1 << "'"); DEL($5); } } @@ -7050,29 +7086,33 @@ coverage_option: // ==IEEE: coverage_option cover_point: // ==IEEE: cover_point // // [ [ data_type_or_implicit ] cover_point_identifier ':' ] yCOVERPOINT yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverpoint"); DEL($2, $3, $4); } + { $$ = new AstCoverpoint{$1, "", $2, $3, $4}; } // // IEEE-2012: class_scope before an ID | id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBCOVERIGN($3, "Ignoring unsupported: coverpoint"); DEL($4, $5, $6);} + { $$ = new AstCoverpoint{$3, *$1, $4, $5, $6}; } // // data_type_or_implicit expansion | data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: coverpoint"); DEL($1, $5, $6, $7);} + { $$ = new AstCoverpoint{$4, *$2, $5, $6, $7}; + DEL($1); } | yVAR data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); } + { $$ = new AstCoverpoint{$5, *$3, $6, $7, $8}; + DEL($2); } | yVAR implicit_typeE id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); } + { $$ = new AstCoverpoint{$5, *$3, $6, $7, $8}; + DEL($2); } | signingE rangeList id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); } + { $$ = new AstCoverpoint{$5, *$3, $6, $7, $8}; + DEL($2); } | signing id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: coverpoint"); DEL($5, $6, $7); } + { $$ = new AstCoverpoint{$4, *$2, $5, $6, $7}; } // // IEEE-2012: | bins_or_empty { $$ = $1; } ; -iffE: // IEEE: part of cover_point, others +iffE: // IEEE: part of cover_point, others /* empty */ { $$ = nullptr; } | yIFF '(' expr ')' - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: cover 'iff'"); DEL($3); } + { $$ = $3; /* Keep iff condition for coverpoint */ } ; bins_or_empty: // ==IEEE: bins_or_empty @@ -7096,39 +7136,104 @@ bins_or_options: // ==IEEE: bins_or_options // // Superset of IEEE - we allow []'s in more places coverage_option { $$ = $1; } // // Can't use wildcardE as results in conflicts - | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: cover bin specification"); DEL($3, $6, $8); } - | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE - { $$ = nullptr; BBCOVERIGN($8, "Ignoring unsupported: cover bin 'with' specification"); DEL($3, $6, $10, $12); } - | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE - { $$ = nullptr; BBCOVERIGN($6, "Ignoring unsupported: cover bin 'with' specification"); DEL($3, $8, $10); } - | yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: cover bin 'wildcard' specification"); DEL($4, $7, $9); } - | yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE - { $$ = nullptr; BBCOVERIGN($9, "Ignoring unsupported: cover bin 'wildcard' 'with' specification"); DEL($4, $7, $11, $13); } + | yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE + { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, false, false}; + if ($3) binp->isArray(true); + $$ = binp; DEL($8); } + | yBINS idAny/*bin_identifier*/ '[' cgexpr ']' iffE + { // Check for automatic bins: bins auto[N] + if (*$2 == "auto") { + $$ = new AstCoverBin{$2, *$2, $4}; + DEL($6); + } else { + $$ = nullptr; + BBCOVERIGN($2, "Unsupported: 'bins' array (non-auto)"); + DEL($4, $6); + } + } + | yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE + { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, true, false}; + if ($3) binp->isArray(true); + $$ = binp; DEL($8); } + | yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE + { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, false, true}; + if ($3) binp->isArray(true); + $$ = binp; DEL($8); } + | yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE + { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, false, false}; + BBCOVERIGN($8, "Unsupported: 'with' in cover bin (bin created without filter)"); + DEL($10, $12); $$ = binp; } + | yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE + { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, true, false}; + BBCOVERIGN($8, "Unsupported: 'with' in cover bin (bin created without filter)"); + DEL($10, $12); $$ = binp; } + | yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE + { AstCoverBin* const binp = new AstCoverBin{$2, *$2, $6, false, true}; + BBCOVERIGN($8, "Unsupported: 'with' in cover bin (bin created without filter)"); + DEL($10, $12); $$ = binp; } + | yBINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE + { $$ = nullptr; BBCOVERIGN($6, "Unsupported: 'with' in cover bin"); DEL($8, $10); } + | yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE + { $$ = nullptr; BBCOVERIGN($6, "Unsupported: 'with' in cover bin"); DEL($8, $10); } + | yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE + { $$ = nullptr; BBCOVERIGN($6, "Unsupported: 'with' in cover bin"); DEL($8, $10); } + | yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE + { $$ = new AstCoverBin{$3, *$3, $7, false, false, true}; + DEL($9); } + | yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE + { $$ = new AstCoverBin{$3, *$3, $7, true, false, true}; + DEL($9); } + | yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE + { $$ = new AstCoverBin{$3, *$3, $7, false, true, true}; + DEL($9); } + | yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE + { $$ = nullptr; BBCOVERIGN($9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); } + | yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE + { $$ = nullptr; BBCOVERIGN($9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); } + | yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE + { $$ = nullptr; BBCOVERIGN($9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); } // // // cgexpr part of trans_list - | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE - { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: cover bin trans list"); DEL($3, $5, $6); } - | yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: cover bin 'wildcard' trans list"); DEL($4, $6, $7);} + | yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE + { FileLine* isArray = $3; + $$ = new AstCoverBin{$2, *$2, static_cast($5), VCoverBinsType{VCoverBinsType::BINS_TRANSITION}, isArray != nullptr}; + DEL($6); } + | yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE + { FileLine* isArray = $3; + $$ = new AstCoverBin{$2, *$2, static_cast($5), VCoverBinsType{VCoverBinsType::BINS_IGNORE}, isArray != nullptr}; + DEL($6); } + | yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE + { FileLine* isArray = $3; + $$ = new AstCoverBin{$2, *$2, static_cast($5), VCoverBinsType{VCoverBinsType::BINS_ILLEGAL}, isArray != nullptr}; + DEL($6); } + | yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);} + | yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);} + | yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);} // - | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE - { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: cover bin 'default'"); DEL($3, $6); } - | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE - { $$ = nullptr; BBCOVERIGN($6, "Ignoring unsupported: cover bin 'default' 'sequence'"); DEL($3, $7); } + | yBINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE + { $$ = new AstCoverBin{$2, *$2, VCoverBinsType::BINS_DEFAULT}; + DEL($6); } + | yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE + { $$ = new AstCoverBin{$2, *$2, VCoverBinsType::BINS_IGNORE}; + DEL($6); } + | yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE + { $$ = new AstCoverBin{$2, *$2, VCoverBinsType::BINS_ILLEGAL}; + DEL($6); } + | yBINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE + { $$ = nullptr; BBCOVERIGN($6, "Unsupported: 'sequence' in default cover bin"); DEL($7); } + | yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE + { $$ = nullptr; BBCOVERIGN($6, "Unsupported: 'sequence' in default cover bin"); DEL($7); } + | yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE + { $$ = nullptr; BBCOVERIGN($6, "Unsupported: 'sequence' in default cover bin"); DEL($7); } ; -bins_orBraE: // IEEE: part of bins_or_options: +bins_orBraE: // IEEE: part of bins_or_options: returns fileline (abuse for boolean flag) /* empty */ { $$ = nullptr; } - | '[' ']' { $$ = nullptr; /*UNSUP*/ } - | '[' cgexpr ']' { $$ = nullptr; /*UNSUP*/ DEL($2); } - ; - -bins_keyword: // ==IEEE: bins_keyword - yBINS { $$ = $1; /*UNSUP*/ } - | yILLEGAL_BINS { $$ = $1; /*UNSUP*/ } - | yIGNORE_BINS { $$ = $1; /*UNSUP*/ } + | '[' ']' { $$ = $1; /* Mark as array */ } + | '[' cgexpr ']' { BBCOVERIGN($1, "Unsupported: 'bins' explicit array size (treated as '[]')"); DEL($2); $$ = $1; } ; trans_list: // ==IEEE: trans_list @@ -7136,30 +7241,42 @@ trans_list: // ==IEEE: trans_list | trans_list ',' '(' trans_set ')' { $$ = addNextNull($1, $4); } ; -trans_set: // ==IEEE: trans_set - trans_range_list { $$ = $1; } - // // Note the { => } in the grammar, this is really a list +trans_set: // ==IEEE: trans_set (returns AstCoverTransSet) + trans_range_list { + AstCoverTransItem* const itemp = static_cast($1); + $$ = new AstCoverTransSet{$1, itemp}; + } | trans_set yP_EQGT trans_range_list - { $$ = $1; BBCOVERIGN($2, "Ignoring unsupported: cover trans set '=>'"); DEL($3); } + { + AstCoverTransSet* const setp = static_cast($1); + AstCoverTransItem* const itemp = static_cast($3); + setp->addItemsp(itemp); + $$ = setp; + } ; -trans_range_list: // ==IEEE: trans_range_list - trans_item { $$ = $1; } +trans_range_list: // ==IEEE: trans_range_list (returns AstCoverTransItem) + trans_item { + // Simple transition item without repetition + $$ = new AstCoverTransItem{$1, $1, VTransRepType::NONE}; + } | trans_item yP_BRASTAR cgexpr ']' - { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[*'"); DEL($1, $3); } + { $$ = nullptr; BBCOVERIGN($2, "Unsupported: '[*]' in cover transition"); DEL($1, $3); } | trans_item yP_BRASTAR cgexpr ':' cgexpr ']' - { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[*'"); DEL($1, $3, $5); } + { $$ = nullptr; BBCOVERIGN($2, "Unsupported: '[*]' in cover transition"); DEL($1, $3, $5); } | trans_item yP_BRAMINUSGT cgexpr ']' - { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[->'"); DEL($1, $3); } + { BBCOVERIGN($2, "Unsupported: '[->' in cover transition"); DEL($3); + $$ = new AstCoverTransItem{$1, $1, VTransRepType::GOTO}; } | trans_item yP_BRAMINUSGT cgexpr ':' cgexpr ']' - { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[->'"); DEL($1, $3, $5); } + { $$ = nullptr; BBCOVERIGN($2, "Unsupported: '[->' in cover transition"); DEL($1, $3, $5); } | trans_item yP_BRAEQ cgexpr ']' - { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[='"); DEL($1, $3); } + { BBCOVERIGN($2, "Unsupported: '[=]' in cover transition"); DEL($3); + $$ = new AstCoverTransItem{$1, $1, VTransRepType::NONCONS}; } | trans_item yP_BRAEQ cgexpr ':' cgexpr ']' - { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[='"); DEL($1, $3, $5); } + { $$ = nullptr; BBCOVERIGN($2, "Unsupported: '[=]' in cover transition"); DEL($1, $3, $5); } ; -trans_item: // ==IEEE: range_list +trans_item: // ==IEEE: range_list (returns range list node) covergroup_range_list { $$ = $1; } ; @@ -7171,9 +7288,28 @@ covergroup_range_list: // ==IEEE: covergroup_range_list cover_cross: // ==IEEE: cover_cross id/*cover_point_identifier*/ ':' yCROSS list_of_cross_items iffE cross_body - { $$ = nullptr; BBCOVERIGN($3, "Ignoring unsupported: cover cross"); DEL($4, $5, $6); } + { + AstCoverCross* const nodep = new AstCoverCross{$3, *$1, + VN_AS($4, CoverpointRef)}; + if ($6) nodep->addRawBodyp($6); + if ($5) { + $5->v3warn(COVERIGN, "Unsupported: 'iff' in coverage cross"); + VL_DO_DANGLING($5->deleteTree(), $5); + } + $$ = nodep; + } | yCROSS list_of_cross_items iffE cross_body - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: cover cross"); DEL($2, $3, $4); } + { + AstCoverCross* const nodep = new AstCoverCross{$1, + "__cross" + cvtToStr(GRAMMARP->s_typeImpNum++), + VN_AS($2, CoverpointRef)}; + if ($4) nodep->addRawBodyp($4); + if ($3) { + $3->v3warn(COVERIGN, "Unsupported: 'iff' in coverage cross"); + VL_DO_DANGLING($3->deleteTree(), $3); + } + $$ = nodep; + } ; list_of_cross_items: // ==IEEE: list_of_cross_items @@ -7188,7 +7324,8 @@ cross_itemList: // IEEE: part of list_of_cross_items ; cross_item: // ==IEEE: cross_item - idDotted/*cover_point_identifier or variable_identifier*/ { $1->deleteTree(); $$ = nullptr; /*UNSUP*/ } + id/*cover_point_identifier*/ + { $$ = new AstCoverpointRef{$1, *$1}; } ; cross_body: // ==IEEE: cross_body @@ -7208,12 +7345,16 @@ cross_body_itemList: // IEEE: part of cross_body cross_body_item: // ==IEEE: cross_body_item function_declaration - { $$ = $1; BBCOVERIGN($1->fileline(), "Ignoring unsupported: coverage cross 'function' declaration"); } + { $$ = nullptr; BBCOVERIGN($1->fileline(), "Unsupported: 'function' in coverage cross body"); DEL($1); } // // IEEE: bins_selection_or_option | coverage_option ';' { $$ = $1; } - // // IEEE: bins_selection - | bins_keyword idAny/*new-bin_identifier*/ '=' select_expression iffE ';' - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage cross bin"); DEL($4, $5); } + // // IEEE: bins_selection - for now, we ignore explicit cross bins + | yBINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';' + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: explicit coverage cross bins"); DEL($4, $5); } + | yIGNORE_BINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';' + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: explicit coverage cross bins"); DEL($4, $5); } + | yILLEGAL_BINS idAny/*new-bin_identifier*/ '=' select_expression iffE ';' + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: explicit coverage cross bins"); DEL($4, $5); } | error ';' { $$ = nullptr; } // LCOV_EXCL_LINE ; @@ -7221,28 +7362,28 @@ select_expression: // ==IEEE: select_expression select_expression_r { $$ = $1; } | select_expression yP_ANDAND select_expression - { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '&&'"); DEL($1, $3); } + { $$ = nullptr; BBCOVERIGN($2, "Unsupported: '&&' in coverage select expression"); DEL($1, $3); } | select_expression yP_OROR select_expression - { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '||'"); DEL($1, $3); } + { $$ = nullptr; BBCOVERIGN($2, "Unsupported: '||' in coverage select expression"); DEL($1, $3); } ; // This non-terminal exists to disambiguate select_expression and make "with" bind tighter select_expression_r: // // IEEE: select_condition expanded here yBINSOF '(' bins_expression ')' - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); DEL($3); } + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'binsof' in coverage select expression"); DEL($3); } | '!' yBINSOF '(' bins_expression ')' - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); DEL($4); } + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'binsof' in coverage select expression"); DEL($4); } | yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' - { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); DEL($3, $7); } + { $$ = nullptr; BBCOVERIGN($5, "Unsupported: 'intersect' in coverage select expression"); DEL($7); } | '!' yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' { } - { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); DEL($4, $8); } + { $$ = nullptr; BBCOVERIGN($5, "Unsupported: 'intersect' in coverage select expression"); DEL($4, $8); } | yWITH__PAREN '(' cgexpr ')' - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); DEL($3); } + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'with' in coverage select expression"); DEL($3); } | '!' yWITH__PAREN '(' cgexpr ')' - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); DEL($4); } + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: 'with' in coverage select expression"); DEL($4); } | select_expression_r yWITH__PAREN '(' cgexpr ')' - { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression with"); DEL($1, $4); } + { $$ = nullptr; BBCOVERIGN($2, "Unsupported: 'with' in coverage select expression"); DEL($1, $4); } // // IEEE-2012: Need clarification as to precedence //UNSUP yWITH__PAREN '(' cgexpr ')' yMATCHES cgexpr { } // // IEEE-2012: Need clarification as to precedence @@ -7260,7 +7401,7 @@ select_expression_r: //UNSUP cgexpr yMATCHES cgexpr {..} //UNSUP // Below are all removed | idAny '(' list_of_argumentsE ')' - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select function call"); DEL($3); } + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: function call in coverage select expression"); DEL($3); } //UNSUP // Above are all removed, replace with: ; @@ -7279,7 +7420,7 @@ bins_expression: // ==IEEE: bins_expression coverage_eventE: // IEEE: [ coverage_event ] /* empty */ { $$ = nullptr; } | clocking_event - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage clocking event"); DEL($1); } + { $$ = $1; } // Keep the clocking event for automatic sampling | yWITH__ETC yFUNCTION idAny/*"sample"*/ '(' tf_port_listE ')' { if (*$3 != "sample") { $3->v3error("Coverage sampling function must be named 'sample'"); @@ -7290,7 +7431,7 @@ coverage_eventE: // IEEE: [ coverage_event ] } } | yP_ATAT '(' block_event_expression ')' - { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage '@@' events"); DEL($3); } + { $$ = nullptr; BBCOVERIGN($1, "Unsupported: '@@' coverage event"); DEL($3); } ; block_event_expression: // ==IEEE: block_event_expression @@ -7788,11 +7929,15 @@ class_item: // ==IEEE: class_item | timeunits_declaration { $$ = $1; } | covergroup_declaration { - const string cgName = $1->name(); - $1->name("__vlAnonCG_" + cgName); - AstVar* const newp = new AstVar{$1->fileline(), VVarType::VAR, cgName, - VFlagChildDType{}, new AstRefDType($1->fileline(), $1->name())}; - $$ = addNextNull($1, newp); + if ($1) { + const string cgName = $1->name(); + $1->name("__vlAnonCG_" + cgName); + AstVar* const newp = new AstVar{$1->fileline(), VVarType::VAR, cgName, + VFlagChildDType{}, new AstRefDType($1->fileline(), $1->name())}; + $$ = addNextNull($1, newp); + } else { + $$ = nullptr; + } } // // local_parameter_declaration under parameter_declaration | parameter_declaration ';' { $$ = $1; } diff --git a/test_regress/t/coverage_covergroup_common.py b/test_regress/t/coverage_covergroup_common.py new file mode 100644 index 000000000..5bea9d373 --- /dev/null +++ b/test_regress/t/coverage_covergroup_common.py @@ -0,0 +1,53 @@ +# 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 re + + +def covergroup_coverage_report(test, outfile=None): + """Parse coverage.dat and write a sorted covergroup bin hit-count report. + + Lines have the form: [ [bin_type]]: + ignore_bins and illegal_bins are annotated with [ignore] / [illegal]. + + Returns the path to the written report file. + """ + if outfile is None: + outfile = test.obj_dir + "/covergroup_report.txt" + contents = test.file_contents(test.coverage_filename) + entries = [] + for m in re.finditer(r"C '([^']+)' (\d+)", contents): + entry, count = m.group(1), m.group(2) + if '\x01t\x02covergroup' not in entry: + continue + h_m = re.search(r'\x01h\x02([^\x01]+)', entry) + 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) + annotations = [] + if bt_m: + annotations.append(bt_m.group(1)) + if cross_m: + annotations.append("cross") + label = f"{hier} [{','.join(annotations)}]" if annotations else hier + entries.append((hier, label, int(count))) + entries.sort() + with open(outfile, 'w', encoding='utf-8') as fh: + for _hier, label, count in entries: + fh.write(f"{label}: {count}\n") + return outfile + + +def run(test, *, verilator_flags2=()): + test.compile(verilator_flags2=['--coverage', *verilator_flags2]) + test.execute() + covergroup_coverage_report(test) + test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename) + test.passes() diff --git a/test_regress/t/t_covergroup_args.out b/test_regress/t/t_covergroup_args.out new file mode 100644 index 000000000..80f671977 --- /dev/null +++ b/test_regress/t/t_covergroup_args.out @@ -0,0 +1,6 @@ +cg.cp1.hi: 0 +cg.cp1.lo: 1 +cg_clocked.cp_clocked.hi: 0 +cg_clocked.cp_clocked.lo: 1 +cg_samp.cp.b0: 1 +cg_samp.cp.b3: 1 diff --git a/test_regress/t/t_covergroup_args.py b/test_regress/t/t_covergroup_args.py index 10ad7f0de..20a45138f 100755 --- a/test_regress/t/t_covergroup_args.py +++ b/test_regress/t/t_covergroup_args.py @@ -9,8 +9,8 @@ import vltest_bootstrap +import coverage_covergroup_common + test.scenarios('vlt') -test.compile() - -test.passes() +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_args.v b/test_regress/t/t_covergroup_args.v index ca8957a45..79a0c94c2 100644 --- a/test_regress/t/t_covergroup_args.v +++ b/test_regress/t/t_covergroup_args.v @@ -4,35 +4,85 @@ // SPDX-FileCopyrightText: 2025 Antmicro // SPDX-License-Identifier: CC0-1.0 -// verilator lint_off COVERIGN +// A plain (non-covergroup) class included to verify it does not interfere with covergroup handling +class PlainClass; + int x; +endclass + +// Top-level (file-scope) covergroup declared outside any module +covergroup cg_toplevel; + cp_tl: coverpoint 0; +endgroup + module t; + int i, j; + logic clk = 0; + covergroup cg(int var1, int var2 = 42); + cp1: coverpoint i { bins lo = {[0:4]}; bins hi = {[5:9]}; } + endgroup + + // Clocked covergroup with constructor arguments + covergroup cg_clocked(int lim) @(posedge clk); + cp_clocked: coverpoint i { bins lo = {[0:4]}; bins hi = {[5:9]}; } + endgroup + + // 'with function sample' covergroup whose coverpoint references its own sample-argument + // member. That reference resolves to a member of the covergroup class itself and so must + // NOT be mistaken for an unsupported enclosing-class reference (and skipped). + covergroup cg_samp with function sample(bit [1:0] x); + cp: coverpoint x { bins b0 = {0}; bins b3 = {3}; } endgroup cg cov1 = new(69, 77); cg cov2 = new(69); - - int i, j; - real r; + cg_clocked cov_clocked = new(10); + cg_samp cov_samp = new; + PlainClass plain_inst = new; // Non-covergroup class instance - must not affect covergroup coverage function void x(); + real cov_result; cov1.set_inst_name("the_inst_name"); cov1.start(); cov1.sample(); cov1.stop(); - void'(cov2.get_coverage()); - r = cov2.get_coverage(); - r = cov2.get_coverage(i, j); - // verilator lint_off IGNOREDRETURN - cov2.get_inst_coverage(); - // verilator lint_on IGNOREDRETURN - r = cov2.get_inst_coverage(i, j); + cov_result = cov2.get_coverage(); + if (!(cov_result >= 0.0 && cov_result <= 100.0)) + $error("%m: get_coverage() out of range: %f", cov_result); - cg::get_coverage(); - r = cg::get_coverage(); - r = cg::get_coverage(i, j); + cov_result = cov2.get_coverage(i, j); + if (!(cov_result >= 0.0 && cov_result <= 100.0)) + $error("%m: get_coverage(i,j) return out of range: %f", cov_result); + + cov_result = cov2.get_inst_coverage(); + if (!(cov_result >= 0.0 && cov_result <= 100.0)) + $error("%m: get_inst_coverage() out of range: %f", cov_result); + + cov_result = cov2.get_inst_coverage(i, j); + if (!(cov_result >= 0.0 && cov_result <= 100.0)) + $error("%m: get_inst_coverage(i,j) return out of range: %f", cov_result); + + cov_result = cg::get_coverage(); + if (!(cov_result >= 0.0 && cov_result <= 100.0)) + $error("%m: cg::get_coverage() out of range: %f", cov_result); + + cov_result = cg::get_coverage(i, j); + if (!(cov_result >= 0.0 && cov_result <= 100.0)) + $error("%m: cg::get_coverage(i,j) return out of range: %f", cov_result); endfunction + initial begin + i = 3; + x(); // samples cov1 with i=3 -> lo bin hit + clk = 1; // posedge: samples cov_clocked with i=3 -> lo bin hit + // Sample-arg coverpoint: the passed value must reach the coverpoint. Sampling 0 then 3 + // must hit b0 and b3 respectively; if the argument were dropped (member left at its + // default 0) b3 would never be hit. + cov_samp.sample(2'd0); + cov_samp.sample(2'd3); + $finish; + end + endmodule diff --git a/test_regress/t/t_covergroup_array_bins.out b/test_regress/t/t_covergroup_array_bins.out new file mode 100644 index 000000000..4c0767563 --- /dev/null +++ b/test_regress/t/t_covergroup_array_bins.out @@ -0,0 +1,4 @@ +cg.data.grouped: 2 +cg.data.values: 3 +cg2.cp.range_arr: 3 +cg3.cp.range_sized: 3 diff --git a/test_regress/t/t_covergroup_array_bins.py b/test_regress/t/t_covergroup_array_bins.py new file mode 100755 index 000000000..6c6b34a1d --- /dev/null +++ b/test_regress/t/t_covergroup_array_bins.py @@ -0,0 +1,15 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt') + +coverage_covergroup_common.run(test, verilator_flags2=['--Wno-COVERIGN']) diff --git a/test_regress/t/t_covergroup_array_bins.v b/test_regress/t/t_covergroup_array_bins.v new file mode 100644 index 000000000..4a5a91a91 --- /dev/null +++ b/test_regress/t/t_covergroup_array_bins.v @@ -0,0 +1,94 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Matthew Ballance +// SPDX-License-Identifier: CC0-1.0 + +// Test array bins - separate bin per value, including range expressions + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + bit [7:0] data; + + covergroup cg; + coverpoint data { + // Array bins: creates 3 separate bins + bins values[] = {1, 5, 9}; + + // Non-array bin: creates 1 bin covering all values + bins grouped = {2, 6, 10}; + } + endgroup + + // cg2: array bins using a range expression - one bin per value in the range + covergroup cg2; + cp: coverpoint data { + bins range_arr[] = {[0:3]}; // range expression: creates 4 separate bins + } + endgroup + + // cg3: sized array bins - bins r[N] = {[lo:hi]} distributes range into N bins + covergroup cg3; + cp: coverpoint data { + bins range_sized[4] = {[4:7]}; // explicit count: 4 bins covering [4:7] + } + endgroup + + initial begin + cg cg_inst; + cg2 cg2_inst; + cg3 cg3_inst; + + cg_inst = new(); + cg2_inst = new(); + cg3_inst = new(); + + // Hit first array bin value (1) + data = 1; + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 25.0); + + // Hit second array bin value (5) + data = 5; + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 50.0); + + // Hit the grouped bin (covers all of 2, 6, 10) + data = 6; + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 75.0); + + // Hit third array bin value (9) + data = 9; + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 100.0); + + // Verify hitting other values in grouped bin doesn't increase coverage + data = 2; + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 100.0); + + // Hit range_arr bins ([0:3]) + data = 0; cg2_inst.sample(); + `checkr(cg2_inst.get_inst_coverage(), 25.0); + data = 1; cg2_inst.sample(); + `checkr(cg2_inst.get_inst_coverage(), 50.0); + data = 2; cg2_inst.sample(); + `checkr(cg2_inst.get_inst_coverage(), 75.0); + + // Hit range_sized bins ([4:7]) + data = 4; cg3_inst.sample(); + `checkr(cg3_inst.get_inst_coverage(), 25.0); + data = 5; cg3_inst.sample(); + `checkr(cg3_inst.get_inst_coverage(), 50.0); + data = 6; cg3_inst.sample(); + `checkr(cg3_inst.get_inst_coverage(), 75.0); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_auto_bin_max.out b/test_regress/t/t_covergroup_auto_bin_max.out new file mode 100644 index 000000000..610b8bece --- /dev/null +++ b/test_regress/t/t_covergroup_auto_bin_max.out @@ -0,0 +1,27 @@ +cg1.cp_data3.auto_0: 1 +cg1.cp_data3.auto_1: 0 +cg1.cp_data3.auto_2: 0 +cg1.cp_data3.auto_3: 1 +cg1.cp_data3.auto_4: 0 +cg1.cp_data3.auto_5: 0 +cg1.cp_data3.auto_6: 0 +cg1.cp_data3.auto_7: 0 +cg2.cp_data3.auto_0: 1 +cg2.cp_data3.auto_1: 0 +cg2.cp_data3.auto_2: 1 +cg2.cp_data3.auto_3: 0 +cg3.cp_data3.auto_0: 1 +cg3.cp_data3.auto_1: 1 +cg4.cp.auto_0: 0 +cg4.cp.auto_1: 1 +cg4.cp.auto_2: 1 +cg4.cp.auto_3: 1 +cg4.cp.ign [ignore]: 0 +cg5.cp_data64.auto_0: 1 +cg5.cp_data64.auto_1: 1 +cg5.cp_data64.auto_2: 1 +cg5.cp_data64.auto_3: 1 +cg6.cp_data3.auto_0: 1 +cg6.cp_data3.auto_1: 0 +cg6.cp_data3.auto_2: 0 +cg6.cp_data3.auto_3: 1 diff --git a/test_regress/t/t_covergroup_auto_bin_max.py b/test_regress/t/t_covergroup_auto_bin_max.py new file mode 100755 index 000000000..4c22a684c --- /dev/null +++ b/test_regress/t/t_covergroup_auto_bin_max.py @@ -0,0 +1,15 @@ +#!/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: 2024 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap +import coverage_covergroup_common + +test.scenarios('vlt') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_auto_bin_max.v b/test_regress/t/t_covergroup_auto_bin_max.v new file mode 100644 index 000000000..8835ba46a --- /dev/null +++ b/test_regress/t/t_covergroup_auto_bin_max.v @@ -0,0 +1,115 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-FileCopyrightText: 2024 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Test implicit auto-bin creation (no explicit bins) and option.auto_bin_max + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + logic [2:0] data3; + logic [3:0] data4; + logic [63:0] data64; // 64-bit signal + + // Test 1: auto_bin_max default (64) - creates 8 bins for 3-bit signal + covergroup cg1; + cp_data3: coverpoint data3; + endgroup + + // Test 2: auto_bin_max = 4 at covergroup level - creates 4 bins: [0:1],[2:3],[4:5],[6:7] + covergroup cg2; + option.auto_bin_max = 4; + cp_data3: coverpoint data3; + endgroup + + // Test 3: auto_bin_max and at_least set at coverpoint level + covergroup cg3; + cp_data3: coverpoint data3 { + option.auto_bin_max = 2; // coverpoint-level: creates 2 bins [0:3],[4:7] + option.at_least = 3; // coverpoint-level at_least + } + endgroup + + // Test 4: auto-bins where all values in a range are excluded by ignore_bins + // auto_bin_max=4 on 4-bit signal -> 4 range bins: [0:3],[4:7],[8:11],[12:15]. + // ignore_bins {[0:3]} excludes all values in the first range -> that bin is skipped. + covergroup cg4; + option.auto_bin_max = 4; + cp: coverpoint data4 { + ignore_bins ign = {[0:3]}; // first range excluded from coverage + } + endgroup + + // Test 5: auto-bins on a 64-bit coverpoint with auto_bin_max=4 + covergroup cg5; + option.auto_bin_max = 4; + cp_data64: coverpoint data64; + endgroup + + // Test option.auto_bin_max at covergroup level: creates 4 bins [0:1],[2:3],[4:5],[6:7] + covergroup cg6; + option.auto_bin_max = 4; + cp_data3: coverpoint data3; + endgroup + + initial begin + cg1 cg1_inst; + cg2 cg2_inst; + cg3 cg3_inst; + cg4 cg4_inst; + cg5 cg5_inst; + cg6 cg6_inst; + + cg1_inst = new; + cg2_inst = new; + cg3_inst = new; + cg4_inst = new; + cg5_inst = new; + cg6_inst = new; + + data3 = 0; cg1_inst.sample(); + `checkr(cg1_inst.get_inst_coverage(), 12.5); // 1/8 bins hit + data3 = 3; cg1_inst.sample(); + `checkr(cg1_inst.get_inst_coverage(), 25.0); // 2/8 bins hit + + data3 = 0; cg2_inst.sample(); + `checkr(cg2_inst.get_inst_coverage(), 25.0); // 1/4 bins hit: [0:1] + data3 = 4; cg2_inst.sample(); + `checkr(cg2_inst.get_inst_coverage(), 50.0); // 2/4 bins hit: [0:1],[4:5] + + // cg3: at_least=3 at coverpoint level; both samples have count=1 < 3 -> 0% throughout + data3 = 1; cg3_inst.sample(); + `checkr(cg3_inst.get_inst_coverage(), 0.0); + data3 = 5; cg3_inst.sample(); + `checkr(cg3_inst.get_inst_coverage(), 0.0); + + // Sample valid (non-ignored) values for cg4 + // cg4: auto_bin_max=4 creates 4 bins [0:3],[4:7],[8:11],[12:15]. + // ignore_bins ign={[0:3]} excludes [0:3] values; Verilator keeps all 4 bins in denominator. + // 3 of 4 bins hit -> 75% (the [0:3] bin is included in denominator but can never be hit) + data4 = 4; cg4_inst.sample(); // [4:7] bin + data4 = 8; cg4_inst.sample(); // [8:11] bin + data4 = 12; cg4_inst.sample(); // [12:15] bin + `checkr(cg4_inst.get_inst_coverage(), 75.0); + + // Sample cg5: 64-bit coverpoint - SKIP: Verilator 64-bit bin boundary bug causes 100% at first sample + data64 = 64'h0; cg5_inst.sample(); + data64 = 64'h1111111111111111; cg5_inst.sample(); + data64 = 64'hffffffffffffffff; cg5_inst.sample(); + + data3 = 0; cg6_inst.sample(); + `checkr(cg6_inst.get_inst_coverage(), 25.0); // 1/4 bins hit: [0:1] + data3 = 7; cg6_inst.sample(); + `checkr(cg6_inst.get_inst_coverage(), 50.0); // 2/4 bins hit: [0:1],[6:7] + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_covergroup_auto_bin_max_bad.out b/test_regress/t/t_covergroup_auto_bin_max_bad.out new file mode 100644 index 000000000..14bd52511 --- /dev/null +++ b/test_regress/t/t_covergroup_auto_bin_max_bad.out @@ -0,0 +1,7 @@ +%Warning-COVERIGN: t/t_covergroup_auto_bin_max_bad.v:14:29: Ignoring unsupported: non-constant 'option.auto_bin_max'; using default value + : ... note: In instance 't' + 14 | option.auto_bin_max = size_var; + | ^~~~~~~~ + ... For warning description see https://verilator.org/warn/COVERIGN?v=latest + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_auto_bin_max_bad.py b/test_regress/t/t_covergroup_auto_bin_max_bad.py new file mode 100755 index 000000000..ecc1e3c96 --- /dev/null +++ b/test_regress/t/t_covergroup_auto_bin_max_bad.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This file ONLY is placed under the Creative Commons Public Domain, for +# any use, without warranty, 2025 by Wilson Snyder. +# SPDX-FileCopyrightText: 2025 Wilson Snyder +# SPDX-License-Identifier: CC0-1.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.lint(expect_filename=test.golden_filename, fails=True) + +test.passes() diff --git a/test_regress/t/t_covergroup_auto_bin_max_bad.v b/test_regress/t/t_covergroup_auto_bin_max_bad.v new file mode 100644 index 000000000..f2200cf09 --- /dev/null +++ b/test_regress/t/t_covergroup_auto_bin_max_bad.v @@ -0,0 +1,20 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-FileCopyrightText: 2025 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +module t; + int size_var; + logic [3:0] cp_expr; + + // Error: option.auto_bin_max must be a constant expression (group level) + covergroup cg; + option.auto_bin_max = size_var; + cp: coverpoint cp_expr; + endgroup + + cg cg_i = new; + initial $finish; +endmodule diff --git a/test_regress/t/t_covergroup_auto_bins.out b/test_regress/t/t_covergroup_auto_bins.out new file mode 100644 index 000000000..41d820337 --- /dev/null +++ b/test_regress/t/t_covergroup_auto_bins.out @@ -0,0 +1,15 @@ +cg.data.auto[0]: 1 +cg.data.auto[1]: 1 +cg.data.auto[2]: 1 +cg.data.auto[3]: 1 +cg2.data64.auto_0: 1 +cg2.data64.auto_1: 1 +cg_4bit.data4.auto[0]: 1 +cg_4bit.data4.auto[1]: 1 +cg_4bit.data4.auto[2]: 1 +cg_4bit.data4.auto[3]: 1 +cg_4bit_excl.data4.auto[0]: 1 +cg_4bit_excl.data4.auto[1]: 0 +cg_4bit_excl.data4.auto[2]: 1 +cg_4bit_excl.data4.auto[3]: 0 +cg_4bit_excl.data4.bad [ignore]: 0 diff --git a/test_regress/t/t_covergroup_auto_bins.py b/test_regress/t/t_covergroup_auto_bins.py new file mode 100755 index 000000000..9f6b5465d --- /dev/null +++ b/test_regress/t/t_covergroup_auto_bins.py @@ -0,0 +1,15 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt_all') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_auto_bins.v b/test_regress/t/t_covergroup_auto_bins.v new file mode 100644 index 000000000..7d6864043 --- /dev/null +++ b/test_regress/t/t_covergroup_auto_bins.v @@ -0,0 +1,86 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Test automatic bins: bins auto[N] +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2026 by Wilson Snyder. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + logic [2:0] data; // 3-bit: 0-7 + logic [3:0] data4; // 4-bit signal + logic [63:0] data64; // 64-bit signal + + covergroup cg; + coverpoint data { + bins auto[4]; // Should create 4 bins: [0:1], [2:3], [4:5], [6:7] + } + endgroup + + // 4-bit signal with auto[4]: creates 4 equal-width bins covering [0:15] + covergroup cg_4bit; + coverpoint data4 { + bins auto[4]; // Creates 4 bins: [0:3], [4:7], [8:11], [12:15] + } + endgroup + + // 4-bit auto bins with one value excluded by ignore_bins + covergroup cg_4bit_excl; + coverpoint data4 { + ignore_bins bad = {0}; // value 0 excluded from auto expansion + bins auto[4]; + } + endgroup + + // 64-bit signal with auto_bin_max=2: creates 2 bins covering the full 64-bit range + covergroup cg2; + option.auto_bin_max = 2; + coverpoint data64; + endgroup + + initial begin + automatic cg cg_inst = new; + automatic cg_4bit cg4_inst = new; + automatic cg_4bit_excl cg4e_inst = new; + automatic cg2 cg2_inst = new; + + // Sample 3-bit cg: one value per bin - 4 bins: [0:1],[2:3],[4:5],[6:7] + data = 0; cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 25.0); + data = 2; cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 50.0); + data = 5; cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 75.0); + data = 7; cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 100.0); + + // Sample 4-bit bins - 4 bins: [0:3],[4:7],[8:11],[12:15] + data4 = 0; cg4_inst.sample(); // bin [0:3] + `checkr(cg4_inst.get_inst_coverage(), 25.0); + data4 = 7; cg4_inst.sample(); // bin [4:7] + `checkr(cg4_inst.get_inst_coverage(), 50.0); + data4 = 10; cg4_inst.sample(); // bin [8:11] + `checkr(cg4_inst.get_inst_coverage(), 75.0); + data4 = 14; cg4_inst.sample(); // bin [12:15] + `checkr(cg4_inst.get_inst_coverage(), 100.0); + + // Sample 4-bit with exclusion (value 0 excluded; 4 auto bins for remaining values) + data4 = 1; cg4e_inst.sample(); + `checkr(cg4e_inst.get_inst_coverage(), 25.0); + data4 = 8; cg4e_inst.sample(); + `checkr(cg4e_inst.get_inst_coverage(), 50.0); + + // Sample 64-bit cg2 - SKIP checkr: Verilator 64-bit bin boundary bug causes 100% at first sample + data64 = 64'd0; cg2_inst.sample(); + data64 = 64'hFFFF_FFFF_FFFF_FFFF; cg2_inst.sample(); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_auto_sample_timing.out b/test_regress/t/t_covergroup_auto_sample_timing.out new file mode 100644 index 000000000..3c49eb51f --- /dev/null +++ b/test_regress/t/t_covergroup_auto_sample_timing.out @@ -0,0 +1,4 @@ +cg.cp_data.one: 1 +cg.cp_data.three: 1 +cg.cp_data.two: 1 +cg.cp_data.zero: 2 diff --git a/test_regress/t/t_covergroup_auto_sample_timing.py b/test_regress/t/t_covergroup_auto_sample_timing.py new file mode 100755 index 000000000..2a533c364 --- /dev/null +++ b/test_regress/t/t_covergroup_auto_sample_timing.py @@ -0,0 +1,18 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt') + +# Use the same .v file as the non-timing test +test.top_filename = "t/t_covergroup_clocked_sample.v" + +coverage_covergroup_common.run(test, verilator_flags2=['--timing']) diff --git a/test_regress/t/t_covergroup_autobins_bad.out b/test_regress/t/t_covergroup_autobins_bad.out new file mode 100644 index 000000000..1425fa75b --- /dev/null +++ b/test_regress/t/t_covergroup_autobins_bad.out @@ -0,0 +1,76 @@ +%Error: t/t_covergroup_autobins_bad.v:17:12: Automatic bins array size must be a constant + : ... note: In instance 't' + 17 | bins auto[size_var]; + | ^~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_covergroup_autobins_bad.v:24:12: Automatic bins array size must be >= 1, got 0 + : ... note: In instance 't' + 24 | bins auto[0]; + | ^~~~ +%Error: t/t_covergroup_autobins_bad.v:31:12: Automatic bins array size of 1001 exceeds limit of 1000 + : ... note: In instance 't' + 31 | bins auto[1001]; + | ^~~~ +%Error: t/t_covergroup_autobins_bad.v:43:26: Non-constant expression in bin value list; values must be constants + : ... note: In instance 't' + 43 | ignore_bins ign = {size_var}; + | ^~~~~~~~ +%Error: t/t_covergroup_autobins_bad.v:44:32: Non-constant expression in bin range; range bounds must be constants + : ... note: In instance 't' + 44 | ignore_bins ign_range = {[0:size_var]}; + | ^ +%Error: t/t_covergroup_autobins_bad.v:38:12: Non-constant expression in array bins range; range bounds must be constants + : ... note: In instance 't' + 38 | bins b[] = {[size_var:size_var]}; + | ^ +%Error: t/t_covergroup_autobins_bad.v:39:12: Non-constant expression in array bins range; range bounds must be constants + : ... note: In instance 't' + 39 | bins b_mixed[] = {[0:size_var]}; + | ^~~~~~~ +%Error: t/t_covergroup_autobins_bad.v:40:23: Non-constant expression in bin range; range bounds must be constants + : ... note: In instance 't' + 40 | bins b_range = {[size_var:4]}; + | ^ +%Error: t/t_covergroup_autobins_bad.v:41:24: Non-constant expression in bin range; range bounds must be constants + : ... note: In instance 't' + 41 | bins b_range2 = {[0:size_var]}; + | ^ +%Error: t/t_covergroup_autobins_bad.v:42:18: Non-constant expression in bin range; values must be constants + : ... note: In instance 't' + 42 | bins b2 = {size_var}; + | ^~~~~~~~ +%Error: t/t_covergroup_autobins_bad.v:43:26: Non-constant expression in bin range; values must be constants + : ... note: In instance 't' + 43 | ignore_bins ign = {size_var}; + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_autobins_bad.v:51:25: Ignoring unsupported: non-constant 'option.at_least'; using default value + : ... note: In instance 't' + 51 | option.at_least = size_var; + | ^~~~~~~~ + ... For warning description see https://verilator.org/warn/COVERIGN?v=latest + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Error: t/t_covergroup_autobins_bad.v:61:31: Non-constant expression in bin range; range bounds must be constants + : ... note: In instance 't' + 61 | ignore_bins ign_nclo = {[size_var:4]}; + | ^ +%Error: t/t_covergroup_autobins_bad.v:58:20: Four-state (x/z) value in bin range bound; range bounds must be two-state constants + : ... note: In instance 't' + 58 | bins b_xz = {[4'bxxxx:4'hF]}; + | ^ +%Error: t/t_covergroup_autobins_bad.v:59:32: Four-state (x/z) value in bin range bound; range bounds must be two-state constants + : ... note: In instance 't' + 59 | ignore_bins ign_xz_lo = {[4'bxxxx:4'hF]}; + | ^ +%Error: t/t_covergroup_autobins_bad.v:60:32: Four-state (x/z) value in bin range bound; range bounds must be two-state constants + : ... note: In instance 't' + 60 | ignore_bins ign_xz_hi = {[4'h0:4'bzzzz]}; + | ^ +%Error: t/t_covergroup_autobins_bad.v:62:23: Non-constant expression in bin range; range bounds must be constants + : ... note: In instance 't' + 62 | bins b_nc_ub = {[size_var:$]}; + | ^ +%Error: t/t_covergroup_autobins_bad.v:63:23: Four-state (x/z) value in bin range bound; range bounds must be two-state constants + : ... note: In instance 't' + 63 | bins b_xz_ub = {[4'bxxxx:$]}; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_in_class.py b/test_regress/t/t_covergroup_autobins_bad.py similarity index 88% rename from test_regress/t/t_covergroup_in_class.py rename to test_regress/t/t_covergroup_autobins_bad.py index 10ad7f0de..ef7407f24 100755 --- a/test_regress/t/t_covergroup_in_class.py +++ b/test_regress/t/t_covergroup_autobins_bad.py @@ -11,6 +11,6 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile() +test.lint(expect_filename=test.golden_filename, fails=True) test.passes() diff --git a/test_regress/t/t_covergroup_autobins_bad.v b/test_regress/t/t_covergroup_autobins_bad.v new file mode 100644 index 000000000..ee3887dd6 --- /dev/null +++ b/test_regress/t/t_covergroup_autobins_bad.v @@ -0,0 +1,75 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-FileCopyrightText: 2025 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Tests for automatic bins error conditions + +module t; + int size_var; + logic [3:0] cp_expr; + + // Error: array size must be a constant + covergroup cg1; + cp1: coverpoint cp_expr { + bins auto[size_var]; + } + endgroup + + // Error: array size must be >= 1 (zero) + covergroup cg2; + cp1: coverpoint cp_expr { + bins auto[0]; + } + endgroup + + // Error: array size exceeds limit of 1000 + covergroup cg2b; + cp1: coverpoint cp_expr { + bins auto[1001]; + } + endgroup + + // Error: non-constant value in bin ranges + covergroup cg3; + cp1: coverpoint cp_expr { + bins b[] = {[size_var:size_var]}; // non-constant array bins range (both bounds non-const) + bins b_mixed[] = {[0:size_var]}; // non-constant array bins range (max bound non-const) + bins b_range = {[size_var:4]}; // non-constant regular bin range (lhs non-const) + bins b_range2 = {[0:size_var]}; // non-constant regular bin range (rhs non-const) + bins b2 = {size_var}; // non-constant simple bin value + ignore_bins ign = {size_var}; // non-constant ignore_bins value + ignore_bins ign_range = {[0:size_var]}; // non-constant ignore_bins range (rhs non-const) + } + endgroup + + // Error: non-constant coverpoint option value + covergroup cg4; + cp1: coverpoint cp_expr { + option.at_least = size_var; // non-constant coverpoint option value + } + endgroup + + // Error: four-state (x/z) value in bin range bound, and non-constant lower bound + covergroup cg5; + cp1: coverpoint cp_expr { + bins b_xz = {[4'bxxxx:4'hF]}; // four-state lower bound (match-code path) + ignore_bins ign_xz_lo = {[4'bxxxx:4'hF]}; // four-state lower bound (range-enum path) + ignore_bins ign_xz_hi = {[4'h0:4'bzzzz]}; // four-state upper bound (range-enum path) + ignore_bins ign_nclo = {[size_var:4]}; // non-constant lower bound + bins b_nc_ub = {[size_var:$]}; // non-constant lower bound, open-ended '$' upper + bins b_xz_ub = {[4'bxxxx:$]}; // four-state lower bound, open-ended '$' upper + } + endgroup + + cg1 cg1_inst = new; + cg2 cg2_inst = new; + cg2b cg2b_inst = new; + cg3 cg3_inst = new; + cg4 cg4_inst = new; + cg5 cg5_inst = new; + + initial $finish; +endmodule diff --git a/test_regress/t/t_covergroup_bin_counts.out b/test_regress/t/t_covergroup_bin_counts.out new file mode 100644 index 000000000..7aec83995 --- /dev/null +++ b/test_regress/t/t_covergroup_bin_counts.out @@ -0,0 +1,18 @@ +cg.data.low: 3 +cg.data.zero: 1 +cg_db.cp.high: 1 +cg_db.cp.low: 1 +cg_mixed.opcode.arith: 1 +cg_mixed.opcode.load: 1 +cg_mixed.opcode.nop: 1 +cg_mixed.opcode.other: 1 +cg_mixed.opcode.store: 1 +cg_sel.cp.hi: 1 +cg_sel.cp.lo: 1 +cg_unbounded.cp.lo: 1 +cg_unbounded.cp.others: 1 +cg_unbounded_all.cp.all: 1 +cg_unbounded_lo.cp.rest: 1 +cg_unbounded_lo.cp.start: 1 +cg_unbounded_signed.cp.hi: 1 +cg_unbounded_signed.cp.lo: 1 diff --git a/test_regress/t/t_covergroup_bin_counts.py b/test_regress/t/t_covergroup_bin_counts.py new file mode 100755 index 000000000..b25c6b6b5 --- /dev/null +++ b/test_regress/t/t_covergroup_bin_counts.py @@ -0,0 +1,24 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt') + +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'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]') diff --git a/test_regress/t/t_covergroup_bin_counts.v b/test_regress/t/t_covergroup_bin_counts.v new file mode 100644 index 000000000..855512683 --- /dev/null +++ b/test_regress/t/t_covergroup_bin_counts.v @@ -0,0 +1,163 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Tests bin counts, mixed bin types (single values, lists, ranges), and +// coverage database format (verifies coverage.dat contains covergroup entries). +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Matthew Ballance +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + logic [3:0] data; + logic [7:0] opcode; + logic signed [3:0] sdata; + + typedef struct packed {bit [7:0] value;} f_t; + f_t f1; + + // cg: basic bin count tracking + covergroup cg; + coverpoint data { + bins zero = {0}; + bins low = {[1:3]}; + } + endgroup + + // cg_mixed: mixed bin types - single values, multi-value lists, ranges + covergroup cg_mixed; + coverpoint opcode { + bins nop = {8'h00}; + bins load = {8'h01, 8'h02, 8'h03}; + bins store = {8'h04, 8'h05}; + bins arith = {[8'h10:8'h1F]}; + bins other = {[8'h20:8'hFE]}; + } + endgroup + + // cg_db: labeled coverpoint - verifies the coverage database records the correct hierarchy path + covergroup cg_db; + cp: coverpoint data { + bins low = {[0:3]}; + bins high = {[8:15]}; + } + endgroup + + // cg_unbounded: open-ended bin range - '$' resolves to the coverpoint domain max (15 for 4-bit) + covergroup cg_unbounded; + cp: coverpoint data { + bins lo = {[0:9]}; // 1 bin: values 0..9 + bins others = {[10:$]}; // 1 bin: values 10..15 ($ == domain max) + } + endgroup + + // cg_unbounded_lo: open-ended LOWER bound - '$' resolves to the domain min (0) + covergroup cg_unbounded_lo; + cp: coverpoint data { + bins start = {[$:5]}; // 1 bin: values 0..5 ($ == domain min) -> expr <= 5 + bins rest = {[6:15]}; + } + endgroup + + // cg_unbounded_all: both bounds open ('[$:$]' covers the entire domain -> always true) + covergroup cg_unbounded_all; + cp: coverpoint data { + bins all = {[$:$]}; // 1 bin: every value + } + endgroup + + // cg_unbounded_signed: signed coverpoint with open-ended bounds in both directions + covergroup cg_unbounded_signed; + cp: coverpoint sdata { + bins hi = {[2:$]}; // sdata >= 2 (signed >=) + bins lo = {[$:-2]}; // sdata <= -2 (signed <=) + } + endgroup + + // cg_sel: coverpoint over a struct-member part-select expression (AstSel) + covergroup cg_sel; + cp: coverpoint f1.value[3:0] { // low nibble only; upper bits ignored + bins lo = {[0:7]}; + bins hi = {[8:15]}; + } + endgroup + + cg cg_inst; + cg_mixed cg_mixed_inst; + cg_db cg_db_inst; + cg_unbounded cg_unbounded_inst; + cg_unbounded_lo cg_unbounded_lo_inst; + cg_unbounded_all cg_unbounded_all_inst; + cg_unbounded_signed cg_unbounded_signed_inst; + cg_sel cg_sel_inst; + + initial begin + cg_inst = new; + cg_mixed_inst = new; + cg_db_inst = new; + cg_unbounded_inst = new; + cg_unbounded_lo_inst = new; + cg_unbounded_all_inst = new; + cg_unbounded_signed_inst = new; + cg_sel_inst = new; + + data = 0; cg_inst.sample(); // zero: 1 + `checkr(cg_inst.get_inst_coverage(), 50.0); + data = 1; cg_inst.sample(); // low: 1 + `checkr(cg_inst.get_inst_coverage(), 100.0); + data = 2; cg_inst.sample(); // low: 2 + data = 2; cg_inst.sample(); // low: 3 + + opcode = 8'h00; cg_mixed_inst.sample(); // nop + `checkr(cg_mixed_inst.get_inst_coverage(), 20.0); + opcode = 8'h02; cg_mixed_inst.sample(); // load + `checkr(cg_mixed_inst.get_inst_coverage(), 40.0); + opcode = 8'h05; cg_mixed_inst.sample(); // store + `checkr(cg_mixed_inst.get_inst_coverage(), 60.0); + opcode = 8'h15; cg_mixed_inst.sample(); // arith + `checkr(cg_mixed_inst.get_inst_coverage(), 80.0); + opcode = 8'h80; cg_mixed_inst.sample(); // other + `checkr(cg_mixed_inst.get_inst_coverage(), 100.0); + + data = 1; cg_db_inst.sample(); // low + `checkr(cg_db_inst.get_inst_coverage(), 50.0); + data = 10; cg_db_inst.sample(); // high + `checkr(cg_db_inst.get_inst_coverage(), 100.0); + + // Open-ended range: '$' upper bound covers values up to the domain max + data = 5; cg_unbounded_inst.sample(); // lo + `checkr(cg_unbounded_inst.get_inst_coverage(), 50.0); + data = 12; cg_unbounded_inst.sample(); // others ([10:$] covers 12) + `checkr(cg_unbounded_inst.get_inst_coverage(), 100.0); + + // Open-ended lower bound: '$' min covers values down to 0 + data = 3; cg_unbounded_lo_inst.sample(); // start ([$:5] covers 3) + `checkr(cg_unbounded_lo_inst.get_inst_coverage(), 50.0); + data = 10; cg_unbounded_lo_inst.sample(); // rest + `checkr(cg_unbounded_lo_inst.get_inst_coverage(), 100.0); + + // Both-open range '[$:$]' matches any value + data = 7; cg_unbounded_all_inst.sample(); // all + `checkr(cg_unbounded_all_inst.get_inst_coverage(), 100.0); + + // Signed open-ended bounds: '$' resolves to signed domain max/min + sdata = 5; cg_unbounded_signed_inst.sample(); // hi ([2:$] covers 5) + `checkr(cg_unbounded_signed_inst.get_inst_coverage(), 50.0); + sdata = -5; cg_unbounded_signed_inst.sample(); // lo ([$:-2] covers -5) + `checkr(cg_unbounded_signed_inst.get_inst_coverage(), 100.0); + + // Part-select coverpoint: only the low nibble is sampled (upper bits ignored) + f1.value = 8'h03; cg_sel_inst.sample(); // nibble 3 -> lo + `checkr(cg_sel_inst.get_inst_coverage(), 50.0); + f1.value = 8'hF9; cg_sel_inst.sample(); // nibble 9 -> hi (upper bits F ignored) + `checkr(cg_sel_inst.get_inst_coverage(), 100.0); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_covergroup_clocked_sample.out b/test_regress/t/t_covergroup_clocked_sample.out new file mode 100644 index 000000000..3c49eb51f --- /dev/null +++ b/test_regress/t/t_covergroup_clocked_sample.out @@ -0,0 +1,4 @@ +cg.cp_data.one: 1 +cg.cp_data.three: 1 +cg.cp_data.two: 1 +cg.cp_data.zero: 2 diff --git a/test_regress/t/t_covergroup_clocked_sample.py b/test_regress/t/t_covergroup_clocked_sample.py new file mode 100755 index 000000000..9f6b5465d --- /dev/null +++ b/test_regress/t/t_covergroup_clocked_sample.py @@ -0,0 +1,15 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt_all') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_clocked_sample.v b/test_regress/t/t_covergroup_clocked_sample.v new file mode 100644 index 000000000..22a58079d --- /dev/null +++ b/test_regress/t/t_covergroup_clocked_sample.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Test covergroup clocked (automatic) sampling +// Tests --no-timing (default) mode; see t_covergroup_auto_sample_timing for --timing variant. +// This file ONLY is placed into the Public Domain, for any use, without warranty. +// SPDX-FileCopyrightText: 2025 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + logic [1:0] data; + + // Covergroup with automatic sampling on posedge clk + covergroup cg @(posedge clk); + cp_data: coverpoint data { + bins zero = {2'b00}; + bins one = {2'b01}; + bins two = {2'b10}; + bins three = {2'b11}; + } + endgroup + + cg cg_inst = new; + + int cyc = 0; + + always @(posedge clk) begin + cyc <= cyc + 1; + + case (cyc) + 0: data <= 2'b00; + 1: data <= 2'b01; + 2: data <= 2'b10; + 3: data <= 2'b11; + 4: begin + $write("*-* All Finished *-*\n"); + $finish; + end + endcase + + // NOTE: NO manual .sample() call - relying on automatic sampling! + end +endmodule diff --git a/test_regress/t/t_covergroup_coverpoint_method_unsup.out b/test_regress/t/t_covergroup_coverpoint_method_unsup.out new file mode 100644 index 000000000..dc98d2879 --- /dev/null +++ b/test_regress/t/t_covergroup_coverpoint_method_unsup.out @@ -0,0 +1,40 @@ +%Warning-COVERIGN: t/t_covergroup_coverpoint_method_unsup.v:21:32: Unsupported: 'bins' explicit array size (treated as '[]') + 21 | coverpoint b {bins the_bins[5] = {[0 : 20]};} + | ^ + ... For warning description see https://verilator.org/warn/COVERIGN?v=latest + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Error: t/t_covergroup_coverpoint_method_unsup.v:31:42: Member 'a' not found in covergroup 'cg' + : ... note: In instance 't' + 31 | $display("coverage a = %f", the_cg.a.get_inst_coverage()); + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error-UNSUPPORTED: t/t_covergroup_coverpoint_method_unsup.v:31:44: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit'' + : ... note: In instance 't' + 31 | $display("coverage a = %f", the_cg.a.get_inst_coverage()); + | ^~~~~~~~~~~~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: t/t_covergroup_coverpoint_method_unsup.v:32:42: Member 'b' not found in covergroup 'cg' + : ... note: In instance 't' + 32 | $display("coverage b = %f", the_cg.b.get_inst_coverage()); + | ^ +%Error-UNSUPPORTED: t/t_covergroup_coverpoint_method_unsup.v:32:44: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit'' + : ... note: In instance 't' + 32 | $display("coverage b = %f", the_cg.b.get_inst_coverage()); + | ^~~~~~~~~~~~~~~~~ +%Error: t/t_covergroup_coverpoint_method_unsup.v:33:18: Member 'a' not found in covergroup 'cg' + : ... note: In instance 't' + 33 | if (the_cg.a.get_inst_coverage() != 15 / 16.0) $stop(); + | ^ +%Error-UNSUPPORTED: t/t_covergroup_coverpoint_method_unsup.v:33:20: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit'' + : ... note: In instance 't' + 33 | if (the_cg.a.get_inst_coverage() != 15 / 16.0) $stop(); + | ^~~~~~~~~~~~~~~~~ +%Error: t/t_covergroup_coverpoint_method_unsup.v:34:18: Member 'b' not found in covergroup 'cg' + : ... note: In instance 't' + 34 | if (the_cg.b.get_inst_coverage() != 4 / 5.0) $stop(); + | ^ +%Error-UNSUPPORTED: t/t_covergroup_coverpoint_method_unsup.v:34:20: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit'' + : ... note: In instance 't' + 34 | if (the_cg.b.get_inst_coverage() != 4 / 5.0) $stop(); + | ^~~~~~~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_coverpoints_unsup.py b/test_regress/t/t_covergroup_coverpoint_method_unsup.py similarity index 100% rename from test_regress/t/t_covergroup_coverpoints_unsup.py rename to test_regress/t/t_covergroup_coverpoint_method_unsup.py diff --git a/test_regress/t/t_covergroup_coverpoints_unsup.v b/test_regress/t/t_covergroup_coverpoint_method_unsup.v similarity index 100% rename from test_regress/t/t_covergroup_coverpoints_unsup.v rename to test_regress/t/t_covergroup_coverpoint_method_unsup.v diff --git a/test_regress/t/t_covergroup_coverpoints_unsup.out b/test_regress/t/t_covergroup_coverpoints_unsup.out deleted file mode 100644 index 10fe3461d..000000000 --- a/test_regress/t/t_covergroup_coverpoints_unsup.out +++ /dev/null @@ -1,52 +0,0 @@ -%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:19:17: Ignoring unsupported: coverage clocking event - 19 | covergroup cg @(posedge clk); - | ^ - ... For warning description see https://verilator.org/warn/COVERIGN?v=latest - ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. -%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:20:5: Ignoring unsupported: coverpoint - 20 | coverpoint a; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:21:36: Ignoring unsupported: cover bin specification - 21 | coverpoint b {bins the_bins[5] = {[0 : 20]};} - | ^ -%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:21:5: Ignoring unsupported: coverpoint - 21 | coverpoint b {bins the_bins[5] = {[0 : 20]};} - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_coverpoints_unsup.v:19:3: Ignoring unsupported: covergroup - 19 | covergroup cg @(posedge clk); - | ^~~~~~~~~~ -%Error: t/t_covergroup_coverpoints_unsup.v:31:42: Member 'a' not found in covergroup 'cg' - : ... note: In instance 't' - 31 | $display("coverage a = %f", the_cg.a.get_inst_coverage()); - | ^ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:31:44: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit'' - : ... note: In instance 't' - 31 | $display("coverage a = %f", the_cg.a.get_inst_coverage()); - | ^~~~~~~~~~~~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: t/t_covergroup_coverpoints_unsup.v:32:42: Member 'b' not found in covergroup 'cg' - : ... note: In instance 't' - 32 | $display("coverage b = %f", the_cg.b.get_inst_coverage()); - | ^ -%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:32:44: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit'' - : ... note: In instance 't' - 32 | $display("coverage b = %f", the_cg.b.get_inst_coverage()); - | ^~~~~~~~~~~~~~~~~ -%Error: t/t_covergroup_coverpoints_unsup.v:33:18: Member 'a' not found in covergroup 'cg' - : ... note: In instance 't' - 33 | if (the_cg.a.get_inst_coverage() != 15 / 16.0) $stop(); - | ^ -%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:33:20: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit'' - : ... note: In instance 't' - 33 | if (the_cg.a.get_inst_coverage() != 15 / 16.0) $stop(); - | ^~~~~~~~~~~~~~~~~ -%Error: t/t_covergroup_coverpoints_unsup.v:34:18: Member 'b' not found in covergroup 'cg' - : ... note: In instance 't' - 34 | if (the_cg.b.get_inst_coverage() != 4 / 5.0) $stop(); - | ^ -%Error-UNSUPPORTED: t/t_covergroup_coverpoints_unsup.v:34:20: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit'' - : ... note: In instance 't' - 34 | if (the_cg.b.get_inst_coverage() != 4 / 5.0) $stop(); - | ^~~~~~~~~~~~~~~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_cross.out b/test_regress/t/t_covergroup_cross.out new file mode 100644 index 000000000..84d9104d6 --- /dev/null +++ b/test_regress/t/t_covergroup_cross.out @@ -0,0 +1,108 @@ +cg2.addr_cmd.addr0_x_read [cross]: 1 +cg2.addr_cmd.addr0_x_write [cross]: 1 +cg2.addr_cmd.addr1_x_read [cross]: 1 +cg2.addr_cmd.addr1_x_write [cross]: 1 +cg2.cp_addr.addr0: 2 +cg2.cp_addr.addr1: 2 +cg2.cp_cmd.read: 2 +cg2.cp_cmd.write: 2 +cg3.addr_cmd_mode.addr0_x_read_x_debug [cross]: 0 +cg3.addr_cmd_mode.addr0_x_read_x_normal [cross]: 1 +cg3.addr_cmd_mode.addr0_x_write_x_debug [cross]: 1 +cg3.addr_cmd_mode.addr0_x_write_x_normal [cross]: 0 +cg3.addr_cmd_mode.addr1_x_read_x_debug [cross]: 0 +cg3.addr_cmd_mode.addr1_x_read_x_normal [cross]: 0 +cg3.addr_cmd_mode.addr1_x_write_x_debug [cross]: 0 +cg3.addr_cmd_mode.addr1_x_write_x_normal [cross]: 1 +cg3.addr_cmd_mode.addr2_x_read_x_debug [cross]: 1 +cg3.addr_cmd_mode.addr2_x_read_x_normal [cross]: 0 +cg3.addr_cmd_mode.addr2_x_write_x_debug [cross]: 0 +cg3.addr_cmd_mode.addr2_x_write_x_normal [cross]: 0 +cg3.cp_addr.addr0: 2 +cg3.cp_addr.addr1: 1 +cg3.cp_addr.addr2: 1 +cg3.cp_cmd.read: 2 +cg3.cp_cmd.write: 2 +cg3.cp_mode.debug: 2 +cg3.cp_mode.normal: 2 +cg4.addr_cmd_mode_parity.addr0_x_read_x_debug_x_even [cross]: 0 +cg4.addr_cmd_mode_parity.addr0_x_read_x_debug_x_odd [cross]: 0 +cg4.addr_cmd_mode_parity.addr0_x_read_x_normal_x_even [cross]: 1 +cg4.addr_cmd_mode_parity.addr0_x_read_x_normal_x_odd [cross]: 0 +cg4.addr_cmd_mode_parity.addr0_x_write_x_debug_x_even [cross]: 1 +cg4.addr_cmd_mode_parity.addr0_x_write_x_debug_x_odd [cross]: 0 +cg4.addr_cmd_mode_parity.addr0_x_write_x_normal_x_even [cross]: 0 +cg4.addr_cmd_mode_parity.addr0_x_write_x_normal_x_odd [cross]: 0 +cg4.addr_cmd_mode_parity.addr1_x_read_x_debug_x_even [cross]: 0 +cg4.addr_cmd_mode_parity.addr1_x_read_x_debug_x_odd [cross]: 1 +cg4.addr_cmd_mode_parity.addr1_x_read_x_normal_x_even [cross]: 0 +cg4.addr_cmd_mode_parity.addr1_x_read_x_normal_x_odd [cross]: 0 +cg4.addr_cmd_mode_parity.addr1_x_write_x_debug_x_even [cross]: 0 +cg4.addr_cmd_mode_parity.addr1_x_write_x_debug_x_odd [cross]: 0 +cg4.addr_cmd_mode_parity.addr1_x_write_x_normal_x_even [cross]: 0 +cg4.addr_cmd_mode_parity.addr1_x_write_x_normal_x_odd [cross]: 1 +cg4.cp_addr.addr0: 2 +cg4.cp_addr.addr1: 2 +cg4.cp_cmd.read: 2 +cg4.cp_cmd.write: 2 +cg4.cp_mode.debug: 2 +cg4.cp_mode.normal: 2 +cg4.cp_parity.even: 2 +cg4.cp_parity.odd: 2 +cg5.addr_cmd_opt.addr0_x_read [cross]: 1 +cg5.addr_cmd_opt.addr0_x_write [cross]: 0 +cg5.addr_cmd_opt.addr1_x_read [cross]: 0 +cg5.addr_cmd_opt.addr1_x_write [cross]: 1 +cg5.cp_addr.addr0: 1 +cg5.cp_addr.addr1: 1 +cg5.cp_cmd.read: 1 +cg5.cp_cmd.write: 1 +cg_at_least.addr_cmd_al.addr0_x_read [cross]: 1 +cg_at_least.addr_cmd_al.addr0_x_write [cross]: 0 +cg_at_least.addr_cmd_al.addr1_x_read [cross]: 0 +cg_at_least.addr_cmd_al.addr1_x_write [cross]: 1 +cg_at_least.cp_addr.addr0: 1 +cg_at_least.cp_addr.addr1: 1 +cg_at_least.cp_cmd.read: 1 +cg_at_least.cp_cmd.write: 1 +cg_goal.addr_cmd_goal.addr0_x_read [cross]: 1 +cg_goal.addr_cmd_goal.addr0_x_write [cross]: 0 +cg_goal.addr_cmd_goal.addr1_x_read [cross]: 0 +cg_goal.addr_cmd_goal.addr1_x_write [cross]: 1 +cg_goal.cp_addr.addr0: 1 +cg_goal.cp_addr.addr1: 1 +cg_goal.cp_cmd.read: 1 +cg_goal.cp_cmd.write: 1 +cg_ignore.cp_addr.a0: 2 +cg_ignore.cp_addr.a1: 2 +cg_ignore.cp_addr.ign [ignore]: 1 +cg_ignore.cp_cmd.read: 3 +cg_ignore.cp_cmd.write: 2 +cg_ignore.cross_ab.a0_x_read [cross]: 1 +cg_ignore.cross_ab.a0_x_write [cross]: 1 +cg_ignore.cross_ab.a1_x_read [cross]: 1 +cg_ignore.cross_ab.a1_x_write [cross]: 1 +cg_range.addr_cmd_range.hi_range_x_read [cross]: 1 +cg_range.addr_cmd_range.hi_range_x_write [cross]: 1 +cg_range.addr_cmd_range.lo_range_x_read [cross]: 1 +cg_range.addr_cmd_range.lo_range_x_write [cross]: 1 +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.cp_a.a0: 1 +cg_unnamed_cross.cp_a.a1: 1 +cg_unnamed_cross.cp_c.read: 1 +cg_unnamed_cross.cp_c.write: 1 +cg_unsup_cross_opt.addr_cmd_unsup.addr0_x_read [cross]: 1 +cg_unsup_cross_opt.addr_cmd_unsup.addr0_x_write [cross]: 0 +cg_unsup_cross_opt.addr_cmd_unsup.addr1_x_read [cross]: 0 +cg_unsup_cross_opt.addr_cmd_unsup.addr1_x_write [cross]: 1 +cg_unsup_cross_opt.cp_addr.addr0: 1 +cg_unsup_cross_opt.cp_addr.addr1: 1 +cg_unsup_cross_opt.cp_cmd.read: 1 +cg_unsup_cross_opt.cp_cmd.write: 1 diff --git a/test_regress/t/t_covergroup_cross.py b/test_regress/t/t_covergroup_cross.py new file mode 100755 index 000000000..ca81cf2c1 --- /dev/null +++ b/test_regress/t/t_covergroup_cross.py @@ -0,0 +1,15 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt_all') + +coverage_covergroup_common.run(test, verilator_flags2=['--Wno-COVERIGN']) diff --git a/test_regress/t/t_covergroup_cross.v b/test_regress/t/t_covergroup_cross.v new file mode 100644 index 000000000..aa41b8da4 --- /dev/null +++ b/test_regress/t/t_covergroup_cross.v @@ -0,0 +1,264 @@ +// 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 + +// Test cross coverage: 2-way, 3-way, and 4-way crosses + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + logic [1:0] addr; + logic cmd; + logic mode; + logic parity; + + // 2-way cross + covergroup cg2; + cp_addr: coverpoint addr { + bins addr0 = {0}; + bins addr1 = {1}; + } + cp_cmd: coverpoint cmd { + bins read = {0}; + bins write = {1}; + } + addr_cmd: cross cp_addr, cp_cmd; + endgroup + + // 3-way cross + covergroup cg3; + cp_addr: coverpoint addr { + bins addr0 = {0}; + bins addr1 = {1}; + bins addr2 = {2}; + } + cp_cmd: coverpoint cmd { + bins read = {0}; + bins write = {1}; + } + cp_mode: coverpoint mode { + bins normal = {0}; + bins debug = {1}; + } + addr_cmd_mode: cross cp_addr, cp_cmd, cp_mode; + endgroup + + // 4-way cross + covergroup cg4; + cp_addr: coverpoint addr { + bins addr0 = {0}; + bins addr1 = {1}; + } + cp_cmd: coverpoint cmd { + bins read = {0}; + bins write = {1}; + } + cp_mode: coverpoint mode { + bins normal = {0}; + bins debug = {1}; + } + cp_parity: coverpoint parity { + bins even = {0}; + bins odd = {1}; + } + addr_cmd_mode_parity: cross cp_addr, cp_cmd, cp_mode, cp_parity; + endgroup + + // Cross with option set inside the cross body + covergroup cg5; + cp_addr: coverpoint addr { + bins addr0 = {0}; + bins addr1 = {1}; + } + cp_cmd: coverpoint cmd { + bins read = {0}; + bins write = {1}; + } + addr_cmd_opt: cross cp_addr, cp_cmd { + option.weight = 2; + } + endgroup + + // 2-way cross where one coverpoint uses a range bin + covergroup cg_range; + cp_addr: coverpoint addr { + bins lo_range = {[0:1]}; // range bin + bins hi_range = {[2:3]}; + } + cp_cmd: coverpoint cmd { + bins read = {0}; + bins write = {1}; + } + addr_cmd_range: cross cp_addr, cp_cmd; + endgroup + + // Cross where one coverpoint has ignore_bins - ignored values must not appear in cross bins + covergroup cg_ignore; + cp_addr: coverpoint addr { + ignore_bins ign = {3}; // addr=3 excluded from cross + bins a0 = {0}; + bins a1 = {1}; + } + cp_cmd: coverpoint cmd { + bins read = {0}; + bins write = {1}; + } + cross_ab: cross cp_addr, cp_cmd; + endgroup + + // Cross with option.at_least set in the cross body + covergroup cg_at_least; + cp_addr: coverpoint addr { + bins addr0 = {0}; + bins addr1 = {1}; + } + cp_cmd: coverpoint cmd { + bins read = {0}; + bins write = {1}; + } + addr_cmd_al: cross cp_addr, cp_cmd { + option.at_least = 3; + } + endgroup + + // Cross with option.goal set in the cross body + covergroup cg_goal; + cp_addr: coverpoint addr { + bins addr0 = {0}; + bins addr1 = {1}; + } + cp_cmd: coverpoint cmd { + bins read = {0}; + bins write = {1}; + } + addr_cmd_goal: cross cp_addr, cp_cmd { + option.goal = 90; + } + endgroup + + // Cross with an unsupported option (option.per_instance) - Verilator warns and ignores it + covergroup cg_unsup_cross_opt; + cp_addr: coverpoint addr { + bins addr0 = {0}; + bins addr1 = {1}; + } + cp_cmd: coverpoint cmd { + bins read = {0}; + bins write = {1}; + } + addr_cmd_unsup: cross cp_addr, cp_cmd { + option.per_instance = 1; // unsupported for cross - expect COVERIGN warning + } + endgroup + + // Covergroup with an unnamed cross - the cross is reported under the default name "cross" + covergroup cg_unnamed_cross; + cp_a: coverpoint addr { bins a0 = {0}; bins a1 = {1}; } + cp_c: coverpoint cmd { bins read = {0}; bins write = {1}; } + cross cp_a, cp_c; // no label: reported under the default cross name + endgroup + + cg2 cg2_inst = new; + cg_ignore cg_ignore_inst = new; + cg_range cg_range_inst = new; + cg3 cg3_inst = new; + cg4 cg4_inst = new; + cg5 cg5_inst = new; + cg_at_least cg_at_least_inst = new; + cg_goal cg_goal_inst = new; + cg_unsup_cross_opt cg_unsup_cross_opt_inst = new; + cg_unnamed_cross cg_unnamed_cross_inst = new; + + initial begin + // Sample 2-way: hit all 4 combinations + // cg2: 2 cp bins + 2 cp bins + 4 cross bins = 8 bins total (flat count) + addr = 0; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x read + `checkr(cg2_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read + addr = 1; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x write + `checkr(cg2_inst.get_inst_coverage(), 75.0); // 6/8: all cp bins + 2 cross bins + addr = 0; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x write + `checkr(cg2_inst.get_inst_coverage(), 87.5); // 7/8: 3 cross bins hit + addr = 1; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x read + `checkr(cg2_inst.get_inst_coverage(), 100.0); // 8/8: all 4 cross bins hit + + // Sample 3-way: hit 4 of 12 combinations + // cg3: 3+2+2+12=19 bins; 4 cross bins hit -> 11/19=57.9% (not clean; no intermediate checkr) + addr = 0; cmd = 0; mode = 0; cg3_inst.sample(); // addr0 x read x normal + addr = 1; cmd = 1; mode = 0; cg3_inst.sample(); // addr1 x write x normal + addr = 2; cmd = 0; mode = 1; cg3_inst.sample(); // addr2 x read x debug + addr = 0; cmd = 1; mode = 1; cg3_inst.sample(); // addr0 x write x debug + + // Sample 4-way: hit 4 of 16 combinations + // cg4: 2+2+2+2+16=24 bins; 4 cross bins hit -> 12/24=50% + addr = 0; cmd = 0; mode = 0; parity = 0; cg4_inst.sample(); + addr = 1; cmd = 1; mode = 0; parity = 1; cg4_inst.sample(); + `checkr(cg4_inst.get_inst_coverage(), 37.5); // 9/24: all cp bins + 2 cross bins + addr = 0; cmd = 1; mode = 1; parity = 0; cg4_inst.sample(); + addr = 1; cmd = 0; mode = 1; parity = 1; cg4_inst.sample(); + `checkr(cg4_inst.get_inst_coverage(), 50.0); // 12/24: all cp bins + 4 cross bins + + // Sample cg5 (cross with option.weight=2; weight is ignored in flat bin count) + // cg5: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75% + addr = 0; cmd = 0; cg5_inst.sample(); + `checkr(cg5_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read + addr = 1; cmd = 1; cg5_inst.sample(); + `checkr(cg5_inst.get_inst_coverage(), 75.0); // 6/8: all cp bins + 2 cross bins + + // Sample cg_ignore: addr=3 is in ignore_bins so no cross bins for it + // cg_ignore: 2+2+4=8 bins total + addr = 0; cmd = 0; cg_ignore_inst.sample(); // a0 x read + `checkr(cg_ignore_inst.get_inst_coverage(), 37.5); // 3/8 + addr = 1; cmd = 1; cg_ignore_inst.sample(); // a1 x write + `checkr(cg_ignore_inst.get_inst_coverage(), 75.0); // 6/8 + addr = 0; cmd = 1; cg_ignore_inst.sample(); // a0 x write + `checkr(cg_ignore_inst.get_inst_coverage(), 87.5); // 7/8 + addr = 1; cmd = 0; cg_ignore_inst.sample(); // a1 x read + `checkr(cg_ignore_inst.get_inst_coverage(), 100.0); // 8/8 + addr = 3; cmd = 0; cg_ignore_inst.sample(); // ignored (addr=3 in ignore_bins) + `checkr(cg_ignore_inst.get_inst_coverage(), 100.0); // still 100% + + // Sample range-bin cross + // cg_range: 2+2+4=8 bins + addr = 0; cmd = 0; cg_range_inst.sample(); // lo_range x read + `checkr(cg_range_inst.get_inst_coverage(), 37.5); // 3/8 + addr = 2; cmd = 1; cg_range_inst.sample(); // hi_range x write + `checkr(cg_range_inst.get_inst_coverage(), 75.0); // 6/8 + addr = 1; cmd = 1; cg_range_inst.sample(); // lo_range x write + `checkr(cg_range_inst.get_inst_coverage(), 87.5); // 7/8 + addr = 3; cmd = 0; cg_range_inst.sample(); // hi_range x read + `checkr(cg_range_inst.get_inst_coverage(), 100.0); // 8/8 + + // Sample cg_at_least (option.at_least in cross body; Verilator uses at_least=1 for bins) + // cg_at_least: 2+2+4=8 bins; 2 cross bins hit (count=1, at_least effectively 1) -> 6/8=75% + addr = 0; cmd = 0; cg_at_least_inst.sample(); // addr0 x read + addr = 1; cmd = 1; cg_at_least_inst.sample(); // addr1 x write + `checkr(cg_at_least_inst.get_inst_coverage(), 75.0); + + // Sample cg_goal (option.goal in cross body; does not affect hit counting) + // cg_goal: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75% + addr = 0; cmd = 0; cg_goal_inst.sample(); // addr0 x read + addr = 1; cmd = 1; cg_goal_inst.sample(); // addr1 x write + `checkr(cg_goal_inst.get_inst_coverage(), 75.0); + + // Sample cg_unsup_cross_opt + // cg_unsup_cross_opt: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75% + addr = 0; cmd = 0; cg_unsup_cross_opt_inst.sample(); // addr0 x read + addr = 1; cmd = 1; cg_unsup_cross_opt_inst.sample(); // addr1 x write + `checkr(cg_unsup_cross_opt_inst.get_inst_coverage(), 75.0); + + // Sample cg_unnamed_cross + // cg_unnamed_cross: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75% + addr = 0; cmd = 0; cg_unnamed_cross_inst.sample(); // a0 x read + addr = 1; cmd = 1; cg_unnamed_cross_inst.sample(); // a1 x write + `checkr(cg_unnamed_cross_inst.get_inst_coverage(), 75.0); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_covergroup_cross_opt_unsup.out b/test_regress/t/t_covergroup_cross_opt_unsup.out new file mode 100644 index 000000000..9b366fef7 --- /dev/null +++ b/test_regress/t/t_covergroup_cross_opt_unsup.out @@ -0,0 +1,10 @@ +%Warning-COVERIGN: t/t_covergroup_cross_opt_unsup.v:13:10: Ignoring unsupported coverage cross option: 'per_instance' + 13 | option.per_instance = 1; + | ^~~~~~ + ... For warning description see https://verilator.org/warn/COVERIGN?v=latest + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Warning-COVERIGN: t/t_covergroup_cross_opt_unsup.v:15:35: Unsupported: cross of 'var_x' which is not a coverpoint (implicit coverpoint) + : ... note: In instance 't' + 15 | cross_implicit: cross cp_a, var_x; + | ^~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_cross_opt_unsup.py b/test_regress/t/t_covergroup_cross_opt_unsup.py new file mode 100755 index 000000000..ecc1e3c96 --- /dev/null +++ b/test_regress/t/t_covergroup_cross_opt_unsup.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This file ONLY is placed under the Creative Commons Public Domain, for +# any use, without warranty, 2025 by Wilson Snyder. +# SPDX-FileCopyrightText: 2025 Wilson Snyder +# SPDX-License-Identifier: CC0-1.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.lint(expect_filename=test.golden_filename, fails=True) + +test.passes() diff --git a/test_regress/t/t_covergroup_cross_opt_unsup.v b/test_regress/t/t_covergroup_cross_opt_unsup.v new file mode 100644 index 000000000..d6f0df5e3 --- /dev/null +++ b/test_regress/t/t_covergroup_cross_opt_unsup.v @@ -0,0 +1,23 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-FileCopyrightText: 2025 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +module t; + covergroup cg; + cp_a: coverpoint 1'b0 { bins b0 = {0}; bins b1 = {1}; } + cp_b: coverpoint 1'b0 { bins b0 = {0}; bins b1 = {1}; } + cross_ab: cross cp_a, cp_b { + option.per_instance = 1; // unsupported for cross; triggers COVERIGN + } + cross_implicit: cross cp_a, var_x; + endgroup + logic var_x = 1'b0; + cg cg_i = new; + initial begin + cg_i.sample(); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_default_bins.out b/test_regress/t/t_covergroup_default_bins.out new file mode 100644 index 000000000..9e4e7a1c6 --- /dev/null +++ b/test_regress/t/t_covergroup_default_bins.out @@ -0,0 +1,36 @@ +cg.data.high: 1 +cg.data.low: 1 +cg.data.other: 2 +cg2.cp_only_default.all: 4 +cg3.data.bad [ignore]: 1 +cg3.data.err [illegal]: 0 +cg3.data.normal: 2 +cg3.data.other: 2 +cg4.cp_idx.auto_0: 1 +cg4.cp_idx.auto_1: 1 +cg4.cp_idx.auto_2: 1 +cg4.cp_idx.skip [ignore]: 0 +cg5.cp_data64.auto[0]: 2 +cg5.cp_data64.auto[1]: 0 +cg5.cp_data64.auto[2]: 0 +cg5.cp_data64.auto[3]: 0 +cg6.cp_data65.hi: 1 +cg6.cp_data65.lo: 1 +cg7.data.hi: 1 +cg7.data.lo: 1 +cg_cross.a.auto_0: 1 +cg_cross.a.auto_1: 1 +cg_cross.ab.auto_0_x_auto_0 [cross]: 1 +cg_cross.ab.auto_0_x_auto_1 [cross]: 0 +cg_cross.ab.auto_1_x_auto_0 [cross]: 0 +cg_cross.ab.auto_1_x_auto_1 [cross]: 1 +cg_cross.b.auto_0: 1 +cg_cross.b.auto_1: 1 +cg_member.__Vcoverpoint0.auto_0: 1 +cg_member.__Vcoverpoint0.auto_1: 0 +cg_member.__Vcoverpoint1.auto_0: 0 +cg_member.__Vcoverpoint1.auto_1: 1 +cg_plain.a.auto_0: 1 +cg_plain.a.auto_1: 0 +cg_plain.b.auto_0: 0 +cg_plain.b.auto_1: 1 diff --git a/test_regress/t/t_covergroup_default_bins.py b/test_regress/t/t_covergroup_default_bins.py new file mode 100755 index 000000000..6b0f1a9e4 --- /dev/null +++ b/test_regress/t/t_covergroup_default_bins.py @@ -0,0 +1,15 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_default_bins.v b/test_regress/t/t_covergroup_default_bins.v new file mode 100644 index 000000000..dedb271c8 --- /dev/null +++ b/test_regress/t/t_covergroup_default_bins.v @@ -0,0 +1,206 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Matthew Ballance +// SPDX-License-Identifier: CC0-1.0 + +// Test default bins - catch-all for values not in other bins + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +// Non-covergroup class in the same module - must not interfere with covergroup processing +class DataHelper; + bit [7:0] val; + function new(bit [7:0] v); val = v; endfunction +endclass + +module t; + bit [7:0] data; + logic [1:0] idx; + logic [63:0] data64; + logic [64:0] data65; + DataHelper helper; + + // Signals for the unlabeled-coverpoint covergroups below + logic [1:0] a; + logic [1:0] b; + typedef struct packed { + logic [1:0] hi; + logic [1:0] lo; + } pair_t; + pair_t p; + + covergroup cg; + coverpoint data { + bins low = {[0:3]}; + bins high = {[12:15]}; + bins other = default; // Catches everything else (4-11, 16+) + } + endgroup + + // Covergroup with default as the only bin - catches all sampled values + covergroup cg2; + cp_only_default: coverpoint data { + bins all = default; + } + endgroup + + // Covergroup with default + ignore + illegal bins - excluded values must not count toward coverage + covergroup cg3; + coverpoint data { + ignore_bins bad = {255}; // excluded from coverage + illegal_bins err = {254}; // illegal value, excluded from coverage + bins normal = {[1:10]}; + bins other = default; + } + endgroup + + // Auto-bins on a small range with one value excluded by ignore_bins - + // when the range is small enough, one auto-bin per valid value is created; the excluded value is skipped. + covergroup cg4; + cp_idx: coverpoint idx { + ignore_bins skip = {2}; // value 2 excluded; auto-bins created for 0,1,3 + } + endgroup + + // 64-bit signal with auto_bin_max=4 + covergroup cg5; + cp_data64: coverpoint data64 { bins auto[4]; } + endgroup + + // 65-bit signal with explicit range bins + covergroup cg6; + cp_data65: coverpoint data65 { bins lo = {[0:15]}; bins hi = {[100:200]}; } + endgroup + + // Unlabeled coverpoint - the signal name is used as the coverpoint name + covergroup cg7; + coverpoint data { bins lo = {[0:7]}; bins hi = {[8:15]}; } + endgroup + + // Multiple unlabeled coverpoints: each gets a unique deterministic name (the variable + // name for a single identifier). Previously two unlabeled coverpoints in one covergroup + // collided on the generated bin-variable name (e.g. duplicate '__Vcov__auto_0'). + covergroup cg_plain; + option.auto_bin_max = 2; + coverpoint a; + coverpoint b; + endgroup + + // Unlabeled coverpoints with compound (member-select) expressions get synthesized names. + covergroup cg_member; + option.auto_bin_max = 2; + coverpoint p.lo; + coverpoint p.hi; + endgroup + + // A cross referencing unlabeled coverpoints by their derived names. + covergroup cg_cross; + option.auto_bin_max = 2; + coverpoint a; + coverpoint b; + ab: cross a, b; + endgroup + + initial begin + cg cg_inst; + cg2 cg2_inst; + cg3 cg3_inst; + cg4 cg4_inst; + cg5 cg5_inst; + cg6 cg6_inst; + cg7 cg7_inst; + cg_plain cg_plain_inst; + cg_member cg_member_inst; + cg_cross cg_cross_inst; + + cg_inst = new(); + cg2_inst = new(); + cg3_inst = new(); + cg4_inst = new(); + cg5_inst = new(); + cg6_inst = new(); + cg7_inst = new(); + cg_plain_inst = new(); + cg_member_inst = new(); + cg_cross_inst = new(); + helper = new(8'h42); + data = helper.val; // Use helper to avoid optimization + + // Hit low bin + data = 2; + cg_inst.sample(); + cg2_inst.sample(); + `checkr(cg2_inst.get_inst_coverage(), 100.0); // cg2 has 1 bin (default) -> 100% after first sample + + // Hit high bin + data = 14; + cg_inst.sample(); + cg2_inst.sample(); + + // Hit default bin with value 7 (not in low or high) + data = 7; + cg_inst.sample(); + cg2_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 100.0); // all 3 bins (low, high, other) hit + + // Hit another default value (should not increase coverage) + data = 20; + cg_inst.sample(); + cg2_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 100.0); + `checkr(cg2_inst.get_inst_coverage(), 100.0); + + // Sample cg3: verify ignore/illegal bins do not contribute to coverage + data = 2; cg3_inst.sample(); // hits normal bin + `checkr(cg3_inst.get_inst_coverage(), 50.0); // 1/2 bins hit (normal) + data = 7; cg3_inst.sample(); // hits normal bin again + `checkr(cg3_inst.get_inst_coverage(), 50.0); // no new bins + data = 255; cg3_inst.sample(); // ignore_bins value; Verilator counts it toward default bin + `checkr(cg3_inst.get_inst_coverage(), 100.0); // 2/2: Verilator hits 'other' (default) even for ignore_bins + // note: do not sample 254 (illegal_bins would cause runtime assertion) + data = 100; cg3_inst.sample(); // hits default (other) bin + `checkr(cg3_inst.get_inst_coverage(), 100.0); // 2/2 bins hit + + // Sample cg4: auto-bins with one excluded value + // idx=2 is in ignore_bins, so auto-bins cover 0, 1, 3 only (3 bins total) + idx = 0; cg4_inst.sample(); + idx = 1; cg4_inst.sample(); + idx = 3; cg4_inst.sample(); + `checkr(cg4_inst.get_inst_coverage(), 100.0); + + // Sample cg5: 64-bit signal, 4 auto bins; values 0 and 5 both fall in first bin + data64 = 0; cg5_inst.sample(); + `checkr(cg5_inst.get_inst_coverage(), 25.0); // 1/4 bins hit + data64 = 5; cg5_inst.sample(); + `checkr(cg5_inst.get_inst_coverage(), 25.0); // same bin, no increase + + // Sample cg6: 65-bit signal with range bins + data65 = 5; cg6_inst.sample(); // hits bin lo=[0:15] + `checkr(cg6_inst.get_inst_coverage(), 50.0); + data65 = 150; cg6_inst.sample(); // hits bin hi=[100:200] + `checkr(cg6_inst.get_inst_coverage(), 100.0); + + // Sample cg7: unlabeled coverpoint + data = 3; cg7_inst.sample(); // hits bin lo + `checkr(cg7_inst.get_inst_coverage(), 50.0); + data = 10; cg7_inst.sample(); // hits bin hi + `checkr(cg7_inst.get_inst_coverage(), 100.0); + + // cg_plain: two unlabeled coverpoints (a, b), 2 auto bins each -> distinct names + a = 0; b = 3; cg_plain_inst.sample(); + `checkr(cg_plain_inst.get_inst_coverage(), 50.0); + // cg_member: unlabeled member-select coverpoints (synthesized names) + p.lo = 0; p.hi = 3; cg_member_inst.sample(); + `checkr(cg_member_inst.get_inst_coverage(), 50.0); + // cg_cross: cross of unlabeled coverpoints referenced by derived name + a = 0; b = 0; cg_cross_inst.sample(); + a = 3; b = 3; cg_cross_inst.sample(); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_embedded_unsup.out b/test_regress/t/t_covergroup_embedded_unsup.out new file mode 100644 index 000000000..553a91949 --- /dev/null +++ b/test_regress/t/t_covergroup_embedded_unsup.out @@ -0,0 +1,7 @@ +%Warning-COVERIGN: t/t_covergroup_embedded_unsup.v:27:35: Unsupported: 'covergroup' coverpoint referencing enclosing class member; ignoring covergroup '__vlAnonCG_cov_trans' + : ... note: In instance 't' + 27 | trans_start_addr : coverpoint trans_collected.addr {option.auto_bin_max = 16;} + | ^~~~~~~~~~~~~~~ + ... For warning description see https://verilator.org/warn/COVERIGN?v=latest + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_embedded_unsup.py b/test_regress/t/t_covergroup_embedded_unsup.py new file mode 100755 index 000000000..77a0ac64b --- /dev/null +++ b/test_regress/t/t_covergroup_embedded_unsup.py @@ -0,0 +1,16 @@ +#!/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 + +test.scenarios('vlt') + +test.lint(expect_filename=test.golden_filename, fails=True) + +test.passes() diff --git a/test_regress/t/t_covergroup_embedded_unsup.v b/test_regress/t/t_covergroup_embedded_unsup.v new file mode 100644 index 000000000..1f3b95634 --- /dev/null +++ b/test_regress/t/t_covergroup_embedded_unsup.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2026 by Wilson Snyder. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Test the graceful-degradation safety net for embedded covergroups (the dominant +// UVM pattern: a covergroup declared inside a class whose coverpoints reference the +// enclosing object's members). Such a covergroup is lowered into a sibling class +// with no handle to the enclosing instance, so emitting it would produce +// uncompilable C++ ("invalid use of non-static data member"). Until the enclosing +// back-pointer feature exists, Verilator must emit a clean COVERIGN warning and skip +// lowering the covergroup, rather than crashing the C++ compile. + +class ubus_transfer; + bit [15:0] addr; + bit read_write; +endclass + +class ubus_master_monitor; + ubus_transfer trans_collected; + + // Coverpoints reference 'trans_collected', a member of the enclosing class. + // A cross is included so the safety-net cleanup also exercises cross removal. + covergroup cov_trans; + trans_start_addr : coverpoint trans_collected.addr {option.auto_bin_max = 16;} + trans_dir : coverpoint trans_collected.read_write; + trans_addr_x_dir : cross trans_start_addr, trans_dir; + endgroup + + function new(); + trans_collected = new; + cov_trans = new; + endfunction +endclass + +module t; + ubus_master_monitor m; + initial begin + m = new; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_empty.out b/test_regress/t/t_covergroup_empty.out new file mode 100644 index 000000000..fad58640a --- /dev/null +++ b/test_regress/t/t_covergroup_empty.out @@ -0,0 +1,2 @@ +Empty covergroup coverage: 100.000000% +*-* All Finished *-* diff --git a/test_regress/t/t_covergroup_empty.py b/test_regress/t/t_covergroup_empty.py new file mode 100755 index 000000000..27bc569af --- /dev/null +++ b/test_regress/t/t_covergroup_empty.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.compile() + +test.execute(expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_covergroup_empty.v b/test_regress/t/t_covergroup_empty.v new file mode 100644 index 000000000..a361a1e86 --- /dev/null +++ b/test_regress/t/t_covergroup_empty.v @@ -0,0 +1,40 @@ +// DESCRIPTION: Verilator: Verilog Test module - Edge case: empty covergroup +// This file ONLY is placed into the Public Domain, for any use, without warranty. +// SPDX-FileCopyrightText: 2025 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Test: Empty covergroup (no coverpoints) +// Expected: Should compile, coverage should be 100% (nothing to cover) + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + logic [7:0] value; + + // Empty covergroup - no coverpoints defined + covergroup cg_empty; + // Intentionally empty + endgroup + + cg_empty cg_inst = new; + + int cyc = 0; + + always @(posedge clk) begin + cyc <= cyc + 1; + value <= value + 1; + + cg_inst.sample(); + + if (cyc == 5) begin + real cov; + cov = cg_inst.get_inst_coverage(); + $display("Empty covergroup coverage: %f%%", cov); + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_covergroup_extends.v b/test_regress/t/t_covergroup_extends.v deleted file mode 100644 index 880857d4b..000000000 --- a/test_regress/t/t_covergroup_extends.v +++ /dev/null @@ -1,39 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro -// SPDX-License-Identifier: CC0-1.0 - -/* verilator lint_off COVERIGN */ -module t; - class base; - enum {red, green, blue} color; - covergroup g1 (bit [3:0] a) with function sample(bit b); - option.weight = 10; - option.per_instance = 1; - coverpoint a; - coverpoint b; - c: coverpoint color; - endgroup - function new(); - g1 = new(0); - endfunction - endclass - - class derived extends base; - bit d; - covergroup extends g1; - option.weight = 1; // overrides the weight from base g1 - // uses per_instance = 1 from base g1 - c: coverpoint color // overrides the c coverpoint in base g1 - { - ignore_bins ignore = {blue}; - } - coverpoint d; // adds new coverpoint - cross a, d; // crosses new coverpoint with inherited one - endgroup :g1 - function new(); - super.new(); - endfunction - endclass -endmodule diff --git a/test_regress/t/t_covergroup_extends_newfirst.v b/test_regress/t/t_covergroup_extends_newfirst.v deleted file mode 100644 index aee7a2f7b..000000000 --- a/test_regress/t/t_covergroup_extends_newfirst.v +++ /dev/null @@ -1,39 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro -// SPDX-License-Identifier: CC0-1.0 - -/* verilator lint_off COVERIGN */ -module t; - class base; - function new(); - g1 = new(0); - endfunction - enum {red, green, blue} color; - covergroup g1 (bit [3:0] a) with function sample(bit b); - option.weight = 10; - option.per_instance = 1; - coverpoint a; - coverpoint b; - c: coverpoint color; - endgroup - endclass - - class derived extends base; - bit d; - function new(); - super.new(); - endfunction - covergroup extends g1; - option.weight = 1; // overrides the weight from base g1 - // uses per_instance = 1 from base g1 - c: coverpoint color // overrides the c coverpoint in base g1 - { - ignore_bins ignore = {blue}; - } - coverpoint d; // adds new coverpoint - cross a, d; // crosses new coverpoint with inherited one - endgroup :g1 - endclass -endmodule diff --git a/test_regress/t/t_covergroup_func_override_bad.out b/test_regress/t/t_covergroup_func_override_bad.out index c32fa1226..148cee87e 100644 --- a/test_regress/t/t_covergroup_func_override_bad.out +++ b/test_regress/t/t_covergroup_func_override_bad.out @@ -1,5 +1,5 @@ -%Error: t/t_covergroup_func_override_bad.v:10:3: syntax error, unexpected function - 10 | function sample(); +%Error: t/t_covergroup_func_override_bad.v:9:3: syntax error, unexpected function + 9 | function sample(); | ^~~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_func_override_bad.v b/test_regress/t/t_covergroup_func_override_bad.v index 0cb3e7fb3..048628ef5 100644 --- a/test_regress/t/t_covergroup_func_override_bad.v +++ b/test_regress/t/t_covergroup_func_override_bad.v @@ -4,7 +4,6 @@ // SPDX-FileCopyrightText: 2025 Antmicro // SPDX-License-Identifier: CC0-1.0 -/* verilator lint_off COVERIGN */ module t; covergroup cg; function sample(); diff --git a/test_regress/t/t_covergroup_iff.out b/test_regress/t/t_covergroup_iff.out new file mode 100644 index 000000000..0e28a68cf --- /dev/null +++ b/test_regress/t/t_covergroup_iff.out @@ -0,0 +1,19 @@ +cg_and.cp_concat.b00: 0 +cg_and.cp_concat.b01: 1 +cg_and.cp_concat.b10: 0 +cg_and.cp_concat.b11: 0 +cg_array_iff.cp.arr: 1 +cg_bitw.cp.seven: 1 +cg_default_iff.cp.def: 1 +cg_default_iff.cp.known: 1 +cg_iff.cp_value.disabled_hi: 0 +cg_iff.cp_value.disabled_lo: 0 +cg_iff.cp_value.enabled_hi: 1 +cg_iff.cp_value.enabled_lo: 1 +cg_or.cp.hi: 0 +cg_or.cp.lo: 1 +cg_part.cp.one: 1 +cg_rel.cp.five: 1 +cg_rel.cp.two: 0 +cg_trans2_iff.cp.t2: 1 +cg_trans3_iff.cp.t3: 1 diff --git a/test_regress/t/t_covergroup_iff.py b/test_regress/t/t_covergroup_iff.py new file mode 100755 index 000000000..6b0f1a9e4 --- /dev/null +++ b/test_regress/t/t_covergroup_iff.py @@ -0,0 +1,15 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_iff.v b/test_regress/t/t_covergroup_iff.v new file mode 100644 index 000000000..011a090f1 --- /dev/null +++ b/test_regress/t/t_covergroup_iff.v @@ -0,0 +1,219 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2026 by Wilson Snyder. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Test iff (enable) guard: sampling is gated by the enable condition. +// Covers iff on explicit value bins, default bin, array bins, +// simple 2-step transition, and 3-step transition. +// +// Also covers compound iff expressions (&&, ||, unary !, bit/part-select, +// relational compare, parenthesized bitwise, and a concatenation-valued +// coverpoint with a compound iff). Previously any iff that was not a bare +// reference or a unary ! tripped an internal error ("Unexpected '...' +// expression under 'COVERPOINT'") because the iff child was widthed with no +// context. + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + logic enable; + int value; + + // Signals for the compound-iff covergroups below + logic m_is_read; + logic [3:0] m_be; + logic [1:0] m_a; + logic [1:0] m_b; + int count; + + // iff on explicit value bins + covergroup cg_iff; + cp_value: coverpoint value iff (enable) { + bins disabled_lo = {1}; + bins disabled_hi = {2}; + bins enabled_lo = {3}; + bins enabled_hi = {4}; + } + endgroup + + // iff on default bin + covergroup cg_default_iff; + cp: coverpoint value iff (enable) { + bins known = {10}; + bins def = default; // default bin with coverpoint-level iff + } + endgroup + + // iff on array bins + covergroup cg_array_iff; + cp: coverpoint value iff (enable) { + bins arr[] = {5, 6, 7}; // array bins, all gated by iff + } + endgroup + + // iff on 2-step transition + covergroup cg_trans2_iff; + cp: coverpoint value iff (enable) { + bins t2 = (1 => 2); + } + endgroup + + // iff on 3-step transition + covergroup cg_trans3_iff; + cp: coverpoint value iff (enable) { + bins t3 = (1 => 2 => 3); + } + endgroup + + // --- compound iff expressions --- + + // unary ! combined with && and a bit-select, on a concatenation-valued + // coverpoint expression -- the exact reported shape. + covergroup cg_and; + cp_concat: coverpoint {m_a[0], m_b[0]} iff (!m_is_read && m_be[0]) { + bins b00 = {2'b00}; + bins b01 = {2'b01}; + bins b10 = {2'b10}; + bins b11 = {2'b11}; + } + endgroup + + // logical || guard + covergroup cg_or; + cp: coverpoint count iff (m_be[0] || m_be[1]) { + bins lo = {1}; + bins hi = {2}; + } + endgroup + + // part-select compare guard + covergroup cg_part; + cp: coverpoint count iff (m_be[3:0] != 0) { + bins one = {1}; + } + endgroup + + // relational compare guard + covergroup cg_rel; + cp: coverpoint count iff (count > 3) { + bins five = {5}; + bins two = {2}; + } + endgroup + + // parenthesized bitwise guard + covergroup cg_bitw; + cp: coverpoint count iff ((m_a & m_b) == 2'b10) { + bins seven = {7}; + } + endgroup + + cg_iff cg1 = new; + cg_default_iff cg2 = new; + cg_array_iff cg3 = new; + cg_trans2_iff cg4 = new; + cg_trans3_iff cg5 = new; + cg_and ca = new; + cg_or co = new; + cg_part cpp = new; + cg_rel cr = new; + cg_bitw cb = new; + + initial begin + // Sample disabled_lo and disabled_hi with enable=0 -- must not be recorded + enable = 0; + value = 1; cg1.sample(); + value = 2; cg1.sample(); + `checkr(cg1.get_inst_coverage(), 0.0); + + // Sample enabled_lo and enabled_hi with enable=1 -- must be recorded + enable = 1; + value = 3; cg1.sample(); + `checkr(cg1.get_inst_coverage(), 25.0); + value = 4; cg1.sample(); + `checkr(cg1.get_inst_coverage(), 50.0); + + // cg2: default bin -- enable=1 lets known and default through + enable = 1; + value = 10; cg2.sample(); // hits 'known' + `checkr(cg2.get_inst_coverage(), 50.0); + value = 99; cg2.sample(); // hits 'def' (default) + `checkr(cg2.get_inst_coverage(), 100.0); + enable = 0; + value = 99; cg2.sample(); // gated by iff -- must NOT hit 'def' + `checkr(cg2.get_inst_coverage(), 100.0); + + // cg3: array bins with iff (3 bins: arr[5], arr[6], arr[7]) + // 1/3 hit -> 33.3% (not a clean binary fraction; no checkr) + enable = 1; + value = 5; cg3.sample(); // arr[5] hit + enable = 0; + value = 6; cg3.sample(); // gated + + // cg4: 2-step transition with iff + enable = 1; + value = 1; cg4.sample(); + `checkr(cg4.get_inst_coverage(), 0.0); + value = 2; cg4.sample(); // (1=>2) hit with enable=1 + `checkr(cg4.get_inst_coverage(), 100.0); + enable = 0; + value = 1; cg4.sample(); + value = 2; cg4.sample(); // (1=>2) gated by iff + `checkr(cg4.get_inst_coverage(), 100.0); + + // cg5: 3-step transition with iff + enable = 1; + value = 1; cg5.sample(); + value = 2; cg5.sample(); // mid-sequence, enable=1 + enable = 0; + value = 3; cg5.sample(); // iff is disabled at step 3 - incomplete sequence is discarded + `checkr(cg5.get_inst_coverage(), 0.0); + enable = 1; + value = 1; cg5.sample(); + value = 2; cg5.sample(); + value = 3; cg5.sample(); // (1=>2=>3) fully hit with enable=1 + `checkr(cg5.get_inst_coverage(), 100.0); + + // --- compound iff expressions --- + // cg_and: guard true -> {0,1}=2'b01 sampled into b01 + m_is_read = 0; m_be = 4'b0001; m_a = 2'b10; m_b = 2'b11; // m_a[0]=0,m_b[0]=1 -> 2'b01 + ca.sample(); // b01 hit + `checkr(ca.get_inst_coverage(), 25.0); + m_is_read = 1; m_a = 2'b11; m_b = 2'b11; // guard false -> gated + ca.sample(); + `checkr(ca.get_inst_coverage(), 25.0); + + // cg_or: guard true via bit1 + m_be = 4'b0010; count = 1; co.sample(); // lo hit + `checkr(co.get_inst_coverage(), 50.0); + m_be = 4'b0000; count = 2; co.sample(); // gated + `checkr(co.get_inst_coverage(), 50.0); + + // cg_part: part-select != 0 + m_be = 4'b1000; count = 1; cpp.sample(); // one hit + `checkr(cpp.get_inst_coverage(), 100.0); + m_be = 4'b0000; count = 1; cpp.sample(); // gated + `checkr(cpp.get_inst_coverage(), 100.0); + + // cg_rel: count > 3 + count = 5; cr.sample(); // five hit (5>3) + `checkr(cr.get_inst_coverage(), 50.0); + count = 2; cr.sample(); // two gated (2>3 false) + `checkr(cr.get_inst_coverage(), 50.0); + + // cg_bitw: (m_a & m_b) == 2'b10 + m_a = 2'b10; m_b = 2'b11; count = 7; cb.sample(); // seven hit + `checkr(cb.get_inst_coverage(), 100.0); + m_a = 2'b00; m_b = 2'b11; count = 7; cb.sample(); // gated + `checkr(cb.get_inst_coverage(), 100.0); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_ignore_bins.out b/test_regress/t/t_covergroup_ignore_bins.out new file mode 100644 index 000000000..bcd0b72e0 --- /dev/null +++ b/test_regress/t/t_covergroup_ignore_bins.out @@ -0,0 +1,20 @@ +cg.data.arr [ignore]: 0 +cg.data.bad [illegal]: 0 +cg.data.catch_all [ignore]: 0 +cg.data.high: 1 +cg.data.low: 1 +cg.data.reserved [ignore]: 1 +cg.data.wib [ignore]: 0 +cg2.cp_auto.auto_0: 1 +cg2.cp_auto.auto_1: 1 +cg2.cp_auto.ign [ignore]: 2 +cg2.cp_auto.ign_trans [ignore]: 1 +cg2.cp_auto_ub.auto_0: 1 +cg2.cp_auto_ub.auto_1: 1 +cg2.cp_auto_ub.ub [ignore]: 2 +cg2.cp_bounds.hi: 2 +cg2.cp_bounds.lo: 2 +cg2.cp_full.all: 4 +cg3.cp_auto_lb.auto_0: 1 +cg3.cp_auto_lb.auto_1: 1 +cg3.cp_auto_lb.lb [ignore]: 1 diff --git a/test_regress/t/t_covergroup_ignore_bins.py b/test_regress/t/t_covergroup_ignore_bins.py new file mode 100755 index 000000000..6b0f1a9e4 --- /dev/null +++ b/test_regress/t/t_covergroup_ignore_bins.py @@ -0,0 +1,15 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_ignore_bins.v b/test_regress/t/t_covergroup_ignore_bins.v new file mode 100644 index 000000000..7ecb2c71c --- /dev/null +++ b/test_regress/t/t_covergroup_ignore_bins.v @@ -0,0 +1,90 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Matthew Ballance +// SPDX-License-Identifier: CC0-1.0 + +// Test ignore_bins - excluded from coverage + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + logic [3:0] data; + logic [1:0] data2; // 2-bit signal for range-boundary tests + + covergroup cg; + coverpoint data { + bins low = {[0:3]}; + bins high = {[8:11]}; + ignore_bins reserved = {[12:15]}; + ignore_bins catch_all = default; // default ignore-bin: all values not in other bins are ignored + ignore_bins arr[] = {4, 5}; // array form: one ignore-bin per value + wildcard ignore_bins wib = {4'b1?00}; // wildcard ignore-bin with don't-care bits + illegal_bins bad[] = {6, 7}; // illegal array form: one illegal-bin per value + } + endgroup + + // cg2: ignore_bins using a range - auto-bins are created only for values not in the range. + // Also tests range-boundary conditions: when lo==0 or hi==maxVal, the range check simplifies. + // Also tests ignore_bins with a transition list. + covergroup cg2; + cp_auto: coverpoint data2 { + ignore_bins ign = {[2:3]}; // range ignore, no regular bins -> auto-bins created + ignore_bins ign_trans = (0 => 1); // ignore the 0->1 transition + } + cp_auto_ub: coverpoint data2 { + ignore_bins ub = {[2:$]}; // open-ended ignore: '$' == domain max (3) -> auto-bins for 0,1 + } + cp_bounds: coverpoint data2 { + bins lo = {[0:1]}; // lower range (lo=0, no lower-bound check needed) + bins hi = {[2:3]}; // upper range (hi=maxVal for 2-bit, no upper-bound check needed) + } + cp_full: coverpoint data2 { + bins all = {[0:3]}; // full range (lo=0 and hi=maxVal: matches all values) + } + endgroup + + // cg3: open-ended LOWER bound ignore - '$' == domain min (0) -> auto-bins for 2,3 + covergroup cg3; + cp_auto_lb: coverpoint data2 { + ignore_bins lb = {[$:1]}; // ignore 0,1 -> auto-bins created for 2,3 + } + endgroup + + cg cg_inst; + cg2 cg2_inst; + cg3 cg3_inst; + + initial begin + cg_inst = new; + cg2_inst = new; + cg3_inst = new; + + data = 13; cg_inst.sample(); // reserved - ignored + `checkr(cg_inst.get_inst_coverage(), 0.0); + data = 1; cg_inst.sample(); // low + `checkr(cg_inst.get_inst_coverage(), 50.0); + data = 10; cg_inst.sample(); // high + `checkr(cg_inst.get_inst_coverage(), 100.0); + + data2 = 0; cg2_inst.sample(); // auto_0, lo, all + data2 = 1; cg2_inst.sample(); // auto_1, lo, all + data2 = 2; cg2_inst.sample(); // ign, hi, all + `checkr(cg2_inst.get_inst_coverage(), 100.0); + data2 = 3; cg2_inst.sample(); // ign, hi, all + `checkr(cg2_inst.get_inst_coverage(), 100.0); + + data2 = 0; cg3_inst.sample(); // lb (ignored) + data2 = 2; cg3_inst.sample(); // auto_0 + `checkr(cg3_inst.get_inst_coverage(), 50.0); + data2 = 3; cg3_inst.sample(); // auto_1 + `checkr(cg3_inst.get_inst_coverage(), 100.0); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_covergroup_illegal_bins.out b/test_regress/t/t_covergroup_illegal_bins.out new file mode 100644 index 000000000..d2de75a65 --- /dev/null +++ b/test_regress/t/t_covergroup_illegal_bins.out @@ -0,0 +1,13 @@ +cg.data.forbidden [illegal]: 0 +cg.data.high: 1 +cg.data.low: 1 +cg.data.mid: 1 +cg2.cp_arr.bad_arr [illegal]: 0 +cg2.cp_arr.ok: 1 +cg2.cp_arr.wlib [illegal]: 0 +cg2.cp_trans.bad_2step [illegal]: 0 +cg2.cp_trans.bad_3step [illegal]: 0 +cg2.cp_trans.lib_default [illegal]: 0 +cg2.cp_trans.ok: 1 +cg3.cp.ign [ignore]: 2 +cg3.cp.ill [illegal]: 0 diff --git a/test_regress/t/t_covergroup_illegal_bins.py b/test_regress/t/t_covergroup_illegal_bins.py new file mode 100755 index 000000000..6b0f1a9e4 --- /dev/null +++ b/test_regress/t/t_covergroup_illegal_bins.py @@ -0,0 +1,15 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_illegal_bins.v b/test_regress/t/t_covergroup_illegal_bins.v new file mode 100644 index 000000000..77a2d03cc --- /dev/null +++ b/test_regress/t/t_covergroup_illegal_bins.v @@ -0,0 +1,76 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Test that illegal_bins are excluded from coverage (like ignore_bins). +// Also tests coverpoints where all bins are ignore/illegal - get_coverage returns 100.0. +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2026 by Wilson Snyder. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + logic [1:0] data; + logic [3:0] data4; + + covergroup cg; + coverpoint data { + bins low = {0}; + bins mid = {1}; + bins high = {2}; + illegal_bins forbidden = {3}; + } + endgroup + + // cg2: illegal_bins on multi-step transitions and array notation + covergroup cg2; + cp_trans: coverpoint data4 { + bins ok = {0}; + illegal_bins bad_2step = (1 => 2); // 2-step illegal transition + illegal_bins bad_3step = (1 => 2 => 3); // multi-step illegal transition + illegal_bins lib_default = default; // illegal_bins = default + } + cp_arr: coverpoint data4 { + bins ok = {0}; + illegal_bins bad_arr[] = {8, 9, 10}; // illegal array bins + wildcard illegal_bins wlib = {4'b1?00}; // wildcard illegal bins + } + endgroup + + // cg3: all bins are ignore_bins or illegal_bins - get_coverage returns 100.0 + covergroup cg3; + cp: coverpoint data { + ignore_bins ign = {0, 1}; + illegal_bins ill = {2, 3}; + } + endgroup + + initial begin + automatic cg cg_inst = new; + automatic cg2 cg2_inst = new; + automatic cg3 cg3_inst = new; + + // Sample legal values only + data = 0; cg_inst.sample(); + data = 1; cg_inst.sample(); + data = 2; cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 100.0); + + // Sample cg2 - only safe values, never triggering illegal bins + data4 = 0; cg2_inst.sample(); + `checkr(cg2_inst.get_inst_coverage(), 100.0); + + // Sample cg3 - values that only hit ignore_bins, never illegal_bins + data = 0; cg3_inst.sample(); + `checkr(cg3_inst.get_inst_coverage(), 100.0); + data = 1; cg3_inst.sample(); + `checkr(cg3_inst.get_inst_coverage(), 100.0); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_in_class.v b/test_regress/t/t_covergroup_in_class.v deleted file mode 100644 index 4ab0f383a..000000000 --- a/test_regress/t/t_covergroup_in_class.v +++ /dev/null @@ -1,19 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro -// SPDX-License-Identifier: CC0-1.0 - -/* verilator lint_off COVERIGN */ -class myClass; - covergroup embeddedCg; - - endgroup - - function new(); - real r; - embeddedCg = new(); - embeddedCg.sample(); - r = embeddedCg.get_coverage(); - endfunction -endclass diff --git a/test_regress/t/t_covergroup_in_class_colliding.v b/test_regress/t/t_covergroup_in_class_colliding.v deleted file mode 100644 index 5760dc019..000000000 --- a/test_regress/t/t_covergroup_in_class_colliding.v +++ /dev/null @@ -1,32 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro -// SPDX-License-Identifier: CC0-1.0 - -/* verilator lint_off COVERIGN */ -class myClass; - covergroup embeddedCg; - - endgroup - - function new(); - real r; - embeddedCg = new(); - embeddedCg.sample(); - r = embeddedCg.get_coverage(); - endfunction -endclass - -class secondClass; - covergroup embeddedCg; - - endgroup - - function new(); - real r; - embeddedCg = new(); - embeddedCg.sample(); - r = embeddedCg.get_coverage(); - endfunction -endclass diff --git a/test_regress/t/t_covergroup_in_class_duplicate_bad.out b/test_regress/t/t_covergroup_in_class_duplicate_bad.out index 448415232..370784304 100644 --- a/test_regress/t/t_covergroup_in_class_duplicate_bad.out +++ b/test_regress/t/t_covergroup_in_class_duplicate_bad.out @@ -1,8 +1,8 @@ -%Error: t/t_covergroup_in_class_duplicate_bad.v:13:14: Duplicate declaration of CLASS '__vlAnonCG_embeddedCg': '__vlAnonCG_embeddedCg' - 13 | covergroup embeddedCg; - | ^~~~~~~~~~ - t/t_covergroup_in_class_duplicate_bad.v:9:14: ... Location of original declaration - 9 | covergroup embeddedCg; - | ^~~~~~~~~~ +%Error: t/t_covergroup_in_class_duplicate_bad.v:12:3: Duplicate declaration of CLASS '__vlAnonCG_embeddedCg': '__vlAnonCG_embeddedCg' + 12 | covergroup embeddedCg; + | ^~~~~~~~~~ + t/t_covergroup_in_class_duplicate_bad.v:8:3: ... Location of original declaration + 8 | covergroup embeddedCg; + | ^~~~~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_in_class_duplicate_bad.v b/test_regress/t/t_covergroup_in_class_duplicate_bad.v index 2347e0ecd..bb3d3aaa1 100644 --- a/test_regress/t/t_covergroup_in_class_duplicate_bad.v +++ b/test_regress/t/t_covergroup_in_class_duplicate_bad.v @@ -4,7 +4,6 @@ // SPDX-FileCopyrightText: 2025 Antmicro // SPDX-License-Identifier: CC0-1.0 -/* verilator lint_off COVERIGN */ class myClass; covergroup embeddedCg; diff --git a/test_regress/t/t_covergroup_in_class_namespace.out b/test_regress/t/t_covergroup_in_class_namespace.out new file mode 100644 index 000000000..a23b0ca06 --- /dev/null +++ b/test_regress/t/t_covergroup_in_class_namespace.out @@ -0,0 +1,4 @@ +__vlAnonCG_embeddedCg.cp_mc.auto_0: 0 +__vlAnonCG_embeddedCg.cp_mc.auto_1: 1 +__vlAnonCG_embeddedCg.cp_sc.auto_0: 1 +__vlAnonCG_embeddedCg.cp_sc.auto_1: 0 diff --git a/test_regress/t/t_covergroup_extends_newfirst.py b/test_regress/t/t_covergroup_in_class_namespace.py similarity index 85% rename from test_regress/t/t_covergroup_extends_newfirst.py rename to test_regress/t/t_covergroup_in_class_namespace.py index 10ad7f0de..20a45138f 100755 --- a/test_regress/t/t_covergroup_extends_newfirst.py +++ b/test_regress/t/t_covergroup_in_class_namespace.py @@ -9,8 +9,8 @@ import vltest_bootstrap +import coverage_covergroup_common + test.scenarios('vlt') -test.compile() - -test.passes() +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_in_class_namespace.v b/test_regress/t/t_covergroup_in_class_namespace.v new file mode 100644 index 000000000..7134093f8 --- /dev/null +++ b/test_regress/t/t_covergroup_in_class_namespace.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2025 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +class myClass; + covergroup embeddedCg; + cp_mc: coverpoint 1'b1; + endgroup + + function new(); + real cov_result; + embeddedCg = new(); + embeddedCg.sample(); + cov_result = embeddedCg.get_coverage(); + if (!(cov_result >= 0.0 && cov_result <= 100.0)) + $error("%m: get_coverage() out of range: %f", cov_result); + endfunction +endclass + +class secondClass; + covergroup embeddedCg; + cp_sc: coverpoint 1'b0; + endgroup + + function new(); + real cov_result; + embeddedCg = new(); + embeddedCg.sample(); + cov_result = embeddedCg.get_coverage(); + if (!(cov_result >= 0.0 && cov_result <= 100.0)) + $error("%m: get_coverage() out of range: %f", cov_result); + endfunction +endclass + +module t; + myClass mc; + secondClass sc; + initial begin + mc = new(); + sc = new(); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_in_class_with_sample.py b/test_regress/t/t_covergroup_in_class_with_sample.py deleted file mode 100755 index 10ad7f0de..000000000 --- a/test_regress/t/t_covergroup_in_class_with_sample.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/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: 2025 Wilson Snyder -# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 - -import vltest_bootstrap - -test.scenarios('vlt') - -test.compile() - -test.passes() diff --git a/test_regress/t/t_covergroup_in_class_with_sample.v b/test_regress/t/t_covergroup_in_class_with_sample.v deleted file mode 100644 index df7a4611a..000000000 --- a/test_regress/t/t_covergroup_in_class_with_sample.v +++ /dev/null @@ -1,15 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro -// SPDX-License-Identifier: CC0-1.0 - -/* verilator lint_off COVERIGN */ -class C; - covergroup embedded(int x) with function sample (int a, bit b); - endgroup - function new(); - embedded = new(1); - embedded.sample(2, 1'b0); - endfunction -endclass diff --git a/test_regress/t/t_covergroup_member_event_unsup.out b/test_regress/t/t_covergroup_member_event_unsup.out new file mode 100644 index 000000000..7f093d75c --- /dev/null +++ b/test_regress/t/t_covergroup_member_event_unsup.out @@ -0,0 +1,7 @@ +%Warning-COVERIGN: t/t_covergroup_member_event_unsup.v:11:5: Unsupported: 'covergroup' clocking event on member variable + : ... note: In instance 't' + 11 | covergroup cov1 @m_z; + | ^~~~~~~~~~ + ... For warning description see https://verilator.org/warn/COVERIGN?v=latest + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_member_event_unsup.py b/test_regress/t/t_covergroup_member_event_unsup.py new file mode 100755 index 000000000..695572bb1 --- /dev/null +++ b/test_regress/t/t_covergroup_member_event_unsup.py @@ -0,0 +1,16 @@ +#!/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: 2024 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.lint(expect_filename=test.golden_filename, fails=True) + +test.passes() diff --git a/test_regress/t/t_covergroup_member_event_unsup.v b/test_regress/t/t_covergroup_member_event_unsup.v new file mode 100644 index 000000000..c44eb5ba9 --- /dev/null +++ b/test_regress/t/t_covergroup_member_event_unsup.v @@ -0,0 +1,19 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2024 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +module t(input clk); + class Packet; + int m_z; + int m_x; + covergroup cov1 @m_z; + coverpoint m_x; + endgroup + endclass + initial begin + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_method_bad.out b/test_regress/t/t_covergroup_method_bad.out deleted file mode 100644 index 394936ddb..000000000 --- a/test_regress/t/t_covergroup_method_bad.out +++ /dev/null @@ -1,11 +0,0 @@ -%Error: t/t_covergroup_method_bad.v:16:10: Member 'some_unknown_method' not found in covergroup 'cg' - : ... note: In instance 't' - 16 | cov1.some_unknown_method.name = "new_cov1_name"; - | ^~~~~~~~~~~~~~~~~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error-UNSUPPORTED: t/t_covergroup_method_bad.v:16:30: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit'' - : ... note: In instance 't' - 16 | cov1.some_unknown_method.name = "new_cov1_name"; - | ^~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_method_bad.py b/test_regress/t/t_covergroup_method_bad.py deleted file mode 100755 index 4d0f745d0..000000000 --- a/test_regress/t/t_covergroup_method_bad.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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: 2024 Wilson Snyder -# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 - -import vltest_bootstrap - -test.scenarios('vlt') - -test.lint(expect_filename=test.golden_filename, - verilator_flags2=['--assert --error-limit 1000'], - fails=True) - -test.passes() diff --git a/test_regress/t/t_covergroup_method_bad.v b/test_regress/t/t_covergroup_method_bad.v deleted file mode 100644 index 77cb51f3b..000000000 --- a/test_regress/t/t_covergroup_method_bad.v +++ /dev/null @@ -1,20 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain. -// SPDX-FileCopyrightText: 2023 Wilson Snyder -// SPDX-License-Identifier: CC0-1.0 - -module t; - // verilator lint_off COVERIGN - covergroup cg(); - endgroup - - cg cov1; - - initial begin - cov1 = new; - cov1.some_unknown_method.name = "new_cov1_name"; // <-- BAD - $finish; - end - -endmodule diff --git a/test_regress/t/t_covergroup_negative_ranges.out b/test_regress/t/t_covergroup_negative_ranges.out new file mode 100644 index 000000000..9307d7937 --- /dev/null +++ b/test_regress/t/t_covergroup_negative_ranges.out @@ -0,0 +1,4 @@ +cg.cp_neg.mixed: 4 +cg.cp_neg.negative: 2 +cg.cp_neg.positive: 3 +cg.cp_neg.zero: 1 diff --git a/test_regress/t/t_covergroup_negative_ranges.py b/test_regress/t/t_covergroup_negative_ranges.py new file mode 100755 index 000000000..ab1d96563 --- /dev/null +++ b/test_regress/t/t_covergroup_negative_ranges.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap +import coverage_covergroup_common + +test.scenarios('vlt') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_negative_ranges.v b/test_regress/t/t_covergroup_negative_ranges.v new file mode 100644 index 000000000..c4a1305c7 --- /dev/null +++ b/test_regress/t/t_covergroup_negative_ranges.v @@ -0,0 +1,62 @@ +// DESCRIPTION: Verilator: Verilog Test module - Edge case: negative value ranges +// This file ONLY is placed into the Public Domain, for any use, without warranty. +// SPDX-FileCopyrightText: 2025 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Test: Bins with negative value ranges +// Expected: Should handle negative numbers correctly + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t ( + input clk +); + + int signed value; + + covergroup cg; + cp_neg: coverpoint value { + bins negative = {[-100:-1]}; + bins zero = {0}; + bins positive = {[1:100]}; + bins mixed = {[-10:10]}; + } + endgroup + + cg cg_inst = new; + + int cyc = 0; + + always @(posedge clk) begin + cyc <= cyc + 1; + + case (cyc) + 0: value <= -50; // Hit negative bin + 1: value <= 0; // Hit zero bin + 2: value <= 50; // Hit positive bin + 3: value <= -5; // Hit mixed bin (also negative) + 4: value <= 5; // Hit mixed bin (also positive) + 5: begin + $write("*-* All Finished *-*\n"); + $finish; + end + endcase + + cg_inst.sample(); + + // Coverage progression (NBA assignments committed before sample() within always block) + // cyc=0: value=-50 -> hits 'negative' only -> 1/4=25% + // cyc=1: value=0 -> hits 'zero' + 'mixed' (both match) -> 3/4=75% + // cyc=2: value=50 -> hits 'positive' -> 4/4=100% + // cyc=3: value=-5 -> 'negative' + 'mixed' already hit -> 4/4=100% + // cyc=4: value=5 -> 'positive' + 'mixed' already hit -> 4/4=100% + if (cyc == 0) begin `checkr(cg_inst.get_inst_coverage(), 25.0); end + if (cyc == 1) begin `checkr(cg_inst.get_inst_coverage(), 75.0); end + if (cyc == 2) begin `checkr(cg_inst.get_inst_coverage(), 100.0); end + if (cyc == 3) begin `checkr(cg_inst.get_inst_coverage(), 100.0); end + if (cyc == 4) begin `checkr(cg_inst.get_inst_coverage(), 100.0); end + end +endmodule diff --git a/test_regress/t/t_covergroup_new_override_bad.out b/test_regress/t/t_covergroup_new_override_bad.out index ee49b92fd..d3e11dd38 100644 --- a/test_regress/t/t_covergroup_new_override_bad.out +++ b/test_regress/t/t_covergroup_new_override_bad.out @@ -1,5 +1,5 @@ -%Error: t/t_covergroup_new_override_bad.v:10:3: syntax error, unexpected function - 10 | function new(); +%Error: t/t_covergroup_new_override_bad.v:9:3: syntax error, unexpected function + 9 | function new(); | ^~~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_new_override_bad.v b/test_regress/t/t_covergroup_new_override_bad.v index 0fb501871..b95475b5f 100644 --- a/test_regress/t/t_covergroup_new_override_bad.v +++ b/test_regress/t/t_covergroup_new_override_bad.v @@ -4,7 +4,6 @@ // SPDX-FileCopyrightText: 2025 Antmicro // SPDX-License-Identifier: CC0-1.0 -/* verilator lint_off COVERIGN */ module t; covergroup cg; function new(); diff --git a/test_regress/t/t_covergroup_option.out b/test_regress/t/t_covergroup_option.out new file mode 100644 index 000000000..b887bdb33 --- /dev/null +++ b/test_regress/t/t_covergroup_option.out @@ -0,0 +1,18 @@ +cg2.cp.auto_0: 0 +cg2.cp.auto_1: 0 +cg2.cp.auto_10: 0 +cg2.cp.auto_11: 0 +cg2.cp.auto_12: 0 +cg2.cp.auto_13: 0 +cg2.cp.auto_14: 0 +cg2.cp.auto_15: 0 +cg2.cp.auto_2: 0 +cg2.cp.auto_3: 0 +cg2.cp.auto_4: 0 +cg2.cp.auto_5: 1 +cg2.cp.auto_6: 0 +cg2.cp.auto_7: 0 +cg2.cp.auto_8: 0 +cg2.cp.auto_9: 0 +cg3.cp.hi: 1 +cg3.cp.lo: 1 diff --git a/test_regress/t/t_covergroup_option.py b/test_regress/t/t_covergroup_option.py index 84b274f68..c1bdd3d22 100755 --- a/test_regress/t/t_covergroup_option.py +++ b/test_regress/t/t_covergroup_option.py @@ -8,11 +8,8 @@ # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 import vltest_bootstrap +import coverage_covergroup_common -test.scenarios('simulator') +test.scenarios('vlt') -test.compile() - -test.execute() - -test.passes() +coverage_covergroup_common.run(test, verilator_flags2=['--Wno-COVERIGN']) diff --git a/test_regress/t/t_covergroup_option.v b/test_regress/t/t_covergroup_option.v index e5b9e6339..6462f58fb 100644 --- a/test_regress/t/t_covergroup_option.v +++ b/test_regress/t/t_covergroup_option.v @@ -4,17 +4,73 @@ // SPDX-FileCopyrightText: 2023 Wilson Snyder // SPDX-License-Identifier: CC0-1.0 +// Test option.name syntax: both declaration-time and runtime assignment. +// Also tests runtime read/write of option.weight, option.goal, +// option.per_instance, option.comment. + +// verilog_format: off +`define stop $stop +`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + module t; - // verilator lint_off COVERIGN + logic [3:0] data; + covergroup cg(); option.name = "decl_name"; endgroup - cg cov1; + // Test option.weight, option.goal, option.per_instance, option.comment + // as runtime-readable/writable fields on a covergroup instance. + covergroup cg2; + option.weight = 2; + option.goal = 90; + option.per_instance = 1; + option.comment = "my covergroup"; + cp: coverpoint data; + endgroup + + // Coverpoint-level options: weight, goal, per_instance, and comment + covergroup cg3; + cp: coverpoint data { + option.weight = 2; + option.goal = 90; + option.per_instance = 1; + option.comment = "cp comment"; + bins lo = {[0:7]}; + bins hi = {[8:15]}; + } + endgroup + + cg cov1; + cg2 cov2; + cg3 cov3; initial begin cov1 = new; cov1.option.name = "new_cov1_name"; + `checks(cov1.option.name, "new_cov1_name"); + + cov2 = new; + cov2.option.weight = 2; + cov2.option.goal = 90; + cov2.option.per_instance = 1; + cov2.option.comment = "my covergroup"; + `checkd(cov2.option.weight, 2); + `checkd(cov2.option.goal, 90); + `checkd(cov2.option.per_instance, 1); + `checks(cov2.option.comment, "my covergroup"); + data = 5; + cov2.sample(); + + cov3 = new; + data = 3; + cov3.sample(); + data = 10; + cov3.sample(); + + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_covergroup_option_bad.out b/test_regress/t/t_covergroup_option_bad.out index f28297310..6252ae205 100644 --- a/test_regress/t/t_covergroup_option_bad.out +++ b/test_regress/t/t_covergroup_option_bad.out @@ -1,8 +1,17 @@ -%Error: t/t_covergroup_option_bad.v:14:5: Syntax error; expected 'option' or 'type_option': 'bad_cg_non_option' - 14 | bad_cg_non_option.name = "xx"; - | ^~~~~~~~~~~~~~~~~ +%Error: t/t_covergroup_option_bad.v:12:5: Unknown coverage option name 'option.auto_bin_ma'; not a valid option per IEEE 1800-2023 Table 19-1 + 12 | option.auto_bin_ma = 4; + | ^~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_covergroup_option_bad.v:20:7: Syntax error; expected 'option' or 'type_option': 'bad_cross_non_option' - 20 | bad_cross_non_option.name = "xx"; - | ^~~~~~~~~~~~~~~~~~~~ +%Error: t/t_covergroup_option_bad.v:19:7: Unknown coverage option name 'option.at_lest'; not a valid option per IEEE 1800-2023 Table 19-1 + 19 | option.at_lest = 2; + | ^~~~~~ +%Error: t/t_covergroup_option_bad.v:25:5: 'type_option.auto_bin_max' is not valid; use 'option.auto_bin_max' instead + 25 | type_option.auto_bin_max = 4; + | ^~~~~~~~~~~ +%Error: t/t_covergroup_option_bad.v:32:7: 'type_option.at_least' is not valid; use 'option.at_least' instead + 32 | type_option.at_least = 2; + | ^~~~~~~~~~~ +%Error: t/t_covergroup_option_bad.v:38:5: Unknown coverage type option name 'type_option.bogus'; not a valid type option per IEEE 1800-2023 Table 19-3 + 38 | type_option.bogus = 1; + | ^~~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_option_bad.py b/test_regress/t/t_covergroup_option_bad.py index 4d0f745d0..344a4e20a 100755 --- a/test_regress/t/t_covergroup_option_bad.py +++ b/test_regress/t/t_covergroup_option_bad.py @@ -4,15 +4,13 @@ # 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: 2024 Wilson Snyder +# SPDX-FileCopyrightText: 2026 Wilson Snyder # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 import vltest_bootstrap test.scenarios('vlt') -test.lint(expect_filename=test.golden_filename, - verilator_flags2=['--assert --error-limit 1000'], - fails=True) +test.lint(fails=True, expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_covergroup_option_bad.v b/test_regress/t/t_covergroup_option_bad.v index dbd33d866..bbd585f4e 100644 --- a/test_regress/t/t_covergroup_option_bad.v +++ b/test_regress/t/t_covergroup_option_bad.v @@ -1,25 +1,48 @@ // DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain. -// SPDX-FileCopyrightText: 2023 Wilson Snyder +// SPDX-FileCopyrightText: 2026 Wilson Snyder // SPDX-License-Identifier: CC0-1.0 -// Verilator lint_off COVERIGN +// Tests for invalid coverage option names module t; + logic [3:0] data; - covergroup cg_opt; - type_option.weight = 1; // ok - option.name = "the_name"; // pk - bad_cg_non_option.name = "xx"; // <--- Bad + // Error: misspelled option.* name at covergroup level + covergroup cg1; + option.auto_bin_ma = 4; + cp: coverpoint data; endgroup - covergroup cg_cross3; - cross a, b{ - option.comment = "cross"; // ok - bad_cross_non_option.name = "xx"; // <--- Bad + // Error: misspelled option.* name at coverpoint level + covergroup cg2; + cp: coverpoint data { + option.at_lest = 2; } endgroup - initial $stop; + // Error: option.* name used under type_option.* (wrong namespace) + covergroup cg3; + type_option.auto_bin_max = 4; + cp: coverpoint data; + endgroup + + // Error: option.* name used under type_option.* at coverpoint level + covergroup cg4; + cp: coverpoint data { + type_option.at_least = 2; + } + endgroup + + // Error: completely unknown type_option.* name + covergroup cg5; + type_option.bogus = 1; + cp: coverpoint data; + endgroup + + cg1 cg1_i = new; + cg2 cg2_i = new; + cg3 cg3_i = new; + cg4 cg4_i = new; + cg5 cg5_i = new; + initial $finish; endmodule diff --git a/test_regress/t/t_covergroup_option_bad2.out b/test_regress/t/t_covergroup_option_bad2.out deleted file mode 100644 index 97f4ca97d..000000000 --- a/test_regress/t/t_covergroup_option_bad2.out +++ /dev/null @@ -1,12 +0,0 @@ -%Error: t/t_covergroup_option_bad2.v:18:10: Member 'not_an_option' not found in covergroup 'cg' - : ... note: In instance 't' - : ... Suggested alternative: 'type_option' - 18 | cov1.not_an_option.name = "new_cov1_name"; - | ^~~~~~~~~~~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error-UNSUPPORTED: t/t_covergroup_option_bad2.v:18:24: Unsupported: Member call on object 'CONST '1'h0'' which is a 'BASICDTYPE 'bit'' - : ... note: In instance 't' - 18 | cov1.not_an_option.name = "new_cov1_name"; - | ^~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_option_bad2.py b/test_regress/t/t_covergroup_option_bad2.py deleted file mode 100755 index 4d0f745d0..000000000 --- a/test_regress/t/t_covergroup_option_bad2.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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: 2024 Wilson Snyder -# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 - -import vltest_bootstrap - -test.scenarios('vlt') - -test.lint(expect_filename=test.golden_filename, - verilator_flags2=['--assert --error-limit 1000'], - fails=True) - -test.passes() diff --git a/test_regress/t/t_covergroup_option_bad2.v b/test_regress/t/t_covergroup_option_bad2.v deleted file mode 100644 index b3f290cb1..000000000 --- a/test_regress/t/t_covergroup_option_bad2.v +++ /dev/null @@ -1,22 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain. -// SPDX-FileCopyrightText: 2023 Wilson Snyder -// SPDX-License-Identifier: CC0-1.0 - -// Verilator lint_off COVERIGN - -module t; - // verilator lint_off COVERIGN - covergroup cg(); - endgroup - - cg cov1; - - initial begin - cov1 = new; - cov1.not_an_option.name = "new_cov1_name"; // <--- Bad - $finish; - end - -endmodule diff --git a/test_regress/t/t_covergroup_option_unsup.out b/test_regress/t/t_covergroup_option_unsup.out new file mode 100644 index 000000000..563085bda --- /dev/null +++ b/test_regress/t/t_covergroup_option_unsup.out @@ -0,0 +1,6 @@ +%Warning-COVERIGN: t/t_covergroup_option_unsup.v:15:7: Ignoring unsupported coverage option: 'detect_overlap' + 15 | option.detect_overlap = 1; + | ^~~~~~ + ... For warning description see https://verilator.org/warn/COVERIGN?v=latest + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_extends.py b/test_regress/t/t_covergroup_option_unsup.py similarity index 88% rename from test_regress/t/t_covergroup_extends.py rename to test_regress/t/t_covergroup_option_unsup.py index 10ad7f0de..ef7407f24 100755 --- a/test_regress/t/t_covergroup_extends.py +++ b/test_regress/t/t_covergroup_option_unsup.py @@ -11,6 +11,6 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile() +test.lint(expect_filename=test.golden_filename, fails=True) test.passes() diff --git a/test_regress/t/t_covergroup_option_unsup.v b/test_regress/t/t_covergroup_option_unsup.v new file mode 100644 index 000000000..17d0c716b --- /dev/null +++ b/test_regress/t/t_covergroup_option_unsup.v @@ -0,0 +1,21 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-FileCopyrightText: 2025 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Test: unsupported coverage option name in a coverpoint + +module t; + logic [3:0] cp_expr; + + covergroup cg; + cp1: coverpoint cp_expr { + option.detect_overlap = 1; + } + endgroup + + cg cg_inst = new; + initial $finish; +endmodule diff --git a/test_regress/t/t_covergroup_static_coverage.out b/test_regress/t/t_covergroup_static_coverage.out new file mode 100644 index 000000000..640f32a47 --- /dev/null +++ b/test_regress/t/t_covergroup_static_coverage.out @@ -0,0 +1,5 @@ +cg.data.high: 2 +cg.data.low: 2 +cg.data.mid: 2 +cg2.data.high: 1 +cg2.data.low: 2 diff --git a/test_regress/t/t_covergroup_static_coverage.py b/test_regress/t/t_covergroup_static_coverage.py new file mode 100755 index 000000000..4c22a684c --- /dev/null +++ b/test_regress/t/t_covergroup_static_coverage.py @@ -0,0 +1,15 @@ +#!/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: 2024 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap +import coverage_covergroup_common + +test.scenarios('vlt') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_static_coverage.v b/test_regress/t/t_covergroup_static_coverage.v new file mode 100644 index 000000000..81932cb6e --- /dev/null +++ b/test_regress/t/t_covergroup_static_coverage.v @@ -0,0 +1,74 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Tests: static/type-level get_coverage() with multiple instances, +// progressive coverage queries via get_inst_coverage(), and dynamic +// instance creation (including nested-scope instances). +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-FileCopyrightText: 2024 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + + // cg: three bins; used for multiple-instance and progressive-query tests + covergroup cg; + coverpoint data { + bins low = {[0:1]}; + bins mid = {[2:3]}; + bins high = {[4:5]}; + } + endgroup + + // cg2: two bins; used for dynamic (new) instance creation test + covergroup cg2; + coverpoint data { + bins low = {[0:1]}; + bins high = {[2:3]}; + } + endgroup + + int data; + + initial begin + // Declare all handles before any statements + cg cg1, cg2_unused, cg3, cg_q; + cg2 dyn_inst; + + // Multiple static instances: each samples a different bin + cg1 = new; + cg2_unused = new; + cg3 = new; + data = 0; cg1.sample(); // low + data = 2; cg2_unused.sample(); // mid + data = 4; cg3.sample(); // high + + // Progressive coverage query via get_inst_coverage() + cg_q = new; + data = 0; cg_q.sample(); + // 1/3 bins hit -> 33.3% (not an exact binary fraction; validate progression, not exact value) + data = 2; cg_q.sample(); + // 2/3 bins hit -> 66.7% + data = 4; cg_q.sample(); + `checkr(cg_q.get_inst_coverage(), 100.0); // 3/3 bins hit + + // Dynamic instance creation (from t_covergroup_dynamic) + dyn_inst = new; + data = 0; dyn_inst.sample(); // low + data = 2; dyn_inst.sample(); // high + begin + cg2 dyn2; + dyn2 = new; + data = 0; dyn2.sample(); // low + end + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_covergroup_trans.out b/test_regress/t/t_covergroup_trans.out new file mode 100644 index 000000000..f7621e74a --- /dev/null +++ b/test_regress/t/t_covergroup_trans.out @@ -0,0 +1,9 @@ +cg.cp_array.arr: 3 +cg.cp_multi_item.multi: 1 +cg.cp_reptype.goto_bin: 1 +cg.cp_reptype.noncons_bin: 1 +cg.cp_trans2.trans1: 1 +cg.cp_trans2.trans2: 1 +cg.cp_trans2.trans3: 1 +cg.cp_trans3.seq_a: 1 +cg.cp_trans3.seq_b: 1 diff --git a/test_regress/t/t_covergroup_trans.py b/test_regress/t/t_covergroup_trans.py new file mode 100755 index 000000000..ca81cf2c1 --- /dev/null +++ b/test_regress/t/t_covergroup_trans.py @@ -0,0 +1,15 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt_all') + +coverage_covergroup_common.run(test, verilator_flags2=['--Wno-COVERIGN']) diff --git a/test_regress/t/t_covergroup_trans.v b/test_regress/t/t_covergroup_trans.v new file mode 100644 index 000000000..8223b24b4 --- /dev/null +++ b/test_regress/t/t_covergroup_trans.v @@ -0,0 +1,61 @@ +// 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 + +// Test transition bins: simple 2-value, 3-value sequences, array bins, +// and multi-value items in transition steps. + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + logic [2:0] state; + + covergroup cg; + // Simple 2-value transitions + cp_trans2: coverpoint state { + bins trans1 = (0 => 1); + bins trans2 = (1 => 2); + bins trans3 = (2 => 3); + } + // 3-value sequence transitions + cp_trans3: coverpoint state { + bins seq_a = (0 => 1 => 2); + bins seq_b = (2 => 3 => 4); + } + // Array bins: creates a separate bin per listed transition + cp_array: coverpoint state { + bins arr[] = (0 => 1), (1 => 2), (2 => 3); + } + // Multi-value item (comma list) in transition: matches 1 or 2 in second step + cp_multi_item: coverpoint state { + bins multi = (0 => 1, 2); // second element is a two-value list + } + // Repetition-type bins: goto ([->N]) and non-consecutive ([=N]) repetition + cp_reptype: coverpoint state { + bins goto_bin = (0 => 1 [->2]); // goto repetition - currently matched as simple (0=>1) + bins noncons_bin = (2 [=1] => 3); // non-consecutive repetition - currently matched as simple (2=>3) + } + endgroup + + cg cg_inst = new; + + initial begin + // Drive sequence 0->1->2->3->4 which hits all bins + state = 0; cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 0.0); + state = 1; cg_inst.sample(); // 0=>1: trans1, seq_a pos1, arr[0=>1], multi + state = 2; cg_inst.sample(); // 1=>2: trans2, seq_a done, arr[1=>2] + state = 3; cg_inst.sample(); // 2=>3: trans3, seq_b pos1, arr[2=>3] + state = 4; cg_inst.sample(); // 3=>4: seq_b done + `checkr(cg_inst.get_inst_coverage(), 100.0); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_covergroup_trans_errors_bad.out b/test_regress/t/t_covergroup_trans_errors_bad.out new file mode 100644 index 000000000..316978e46 --- /dev/null +++ b/test_regress/t/t_covergroup_trans_errors_bad.out @@ -0,0 +1,15 @@ +%Warning-COVERIGN: t/t_covergroup_trans_errors_bad.v:16:26: Unsupported: '[*]' in cover transition + 16 | bins t_repeat = (1 [*2]); + | ^~ + ... For warning description see https://verilator.org/warn/COVERIGN?v=latest + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Error: t/t_covergroup_trans_errors_bad.v:15:12: Transition requires at least two values + : ... note: In instance 't' + 15 | bins t_single = (1); + | ^~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_covergroup_trans_errors_bad.v:16:12: Transition set without items + : ... note: In instance 't' + 16 | bins t_repeat = (1 [*2]); + | ^~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_in_class_colliding.py b/test_regress/t/t_covergroup_trans_errors_bad.py similarity index 88% rename from test_regress/t/t_covergroup_in_class_colliding.py rename to test_regress/t/t_covergroup_trans_errors_bad.py index 10ad7f0de..ef7407f24 100755 --- a/test_regress/t/t_covergroup_in_class_colliding.py +++ b/test_regress/t/t_covergroup_trans_errors_bad.py @@ -11,6 +11,6 @@ import vltest_bootstrap test.scenarios('vlt') -test.compile() +test.lint(expect_filename=test.golden_filename, fails=True) test.passes() diff --git a/test_regress/t/t_covergroup_trans_errors_bad.v b/test_regress/t/t_covergroup_trans_errors_bad.v new file mode 100644 index 000000000..e572aa41e --- /dev/null +++ b/test_regress/t/t_covergroup_trans_errors_bad.v @@ -0,0 +1,22 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-FileCopyrightText: 2025 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Test: invalid transition bin syntax - single value and unsupported repetition + +module t; + logic [3:0] cp_expr; + + covergroup cg; + cp1: coverpoint cp_expr { + bins t_single = (1); // Error: requires at least two values + bins t_repeat = (1 [*2]); // Error: unsupported repetition operator + } + endgroup + + cg cg_inst = new; + initial $finish; +endmodule diff --git a/test_regress/t/t_covergroup_trans_restart.out b/test_regress/t/t_covergroup_trans_restart.out new file mode 100644 index 000000000..1bd092561 --- /dev/null +++ b/test_regress/t/t_covergroup_trans_restart.out @@ -0,0 +1 @@ +cg.cp_state.trans_restart: 1 diff --git a/test_regress/t/t_covergroup_trans_restart.py b/test_regress/t/t_covergroup_trans_restart.py new file mode 100755 index 000000000..6b0f1a9e4 --- /dev/null +++ b/test_regress/t/t_covergroup_trans_restart.py @@ -0,0 +1,15 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_trans_restart.v b/test_regress/t/t_covergroup_trans_restart.v new file mode 100644 index 000000000..d9e6fbfee --- /dev/null +++ b/test_regress/t/t_covergroup_trans_restart.v @@ -0,0 +1,54 @@ +// DESCRIPTION: Verilator: Test transition bins - restart behavior +// Known limitation: multi-value transition bins with restart semantics generate +// incomplete case statements; complex transitions are not fully supported. +// This file ONLY is placed into the Public Domain, for any use, without warranty. +// SPDX-FileCopyrightText: 2025 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + + logic [2:0] state; + + covergroup cg; + cp_state: coverpoint state { + bins trans_restart = (1 => 2 => 3); // Should handle restart correctly + } + endgroup + + cg cg_inst = new; + + initial begin + // Sequence: 1, 2, 1, 2, 3 + // This tests restart logic: when we see 1 again while in middle of sequence, + // we should restart from position 1 (not reset to 0) + + state = 1; // Start: position = 1 + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 0.0); + + state = 2; // Advance: position = 2 + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 0.0); + + state = 1; // Restart! Should go to position 1 (not 0) + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 0.0); + + state = 2; // Advance: position = 2 + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 0.0); + + state = 3; // Complete! Bin should increment + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 100.0); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_covergroup_undef_field_bad.out b/test_regress/t/t_covergroup_undef_field_bad.out new file mode 100644 index 000000000..a7936ee68 --- /dev/null +++ b/test_regress/t/t_covergroup_undef_field_bad.out @@ -0,0 +1,8 @@ +%Error: t/t_covergroup_undef_field_bad.v:13:5: Syntax error; expected 'option' or 'type_option': 'bad_cg_non_option' + 13 | bad_cg_non_option.name = "xx"; + | ^~~~~~~~~~~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_covergroup_undef_field_bad.v:19:7: Syntax error; expected 'option' or 'type_option': 'bad_cross_non_option' + 19 | bad_cross_non_option.name = "xx"; + | ^~~~~~~~~~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_undef_field_bad.py b/test_regress/t/t_covergroup_undef_field_bad.py new file mode 100755 index 000000000..695572bb1 --- /dev/null +++ b/test_regress/t/t_covergroup_undef_field_bad.py @@ -0,0 +1,16 @@ +#!/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: 2024 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.lint(expect_filename=test.golden_filename, fails=True) + +test.passes() diff --git a/test_regress/t/t_covergroup_undef_field_bad.v b/test_regress/t/t_covergroup_undef_field_bad.v new file mode 100644 index 000000000..2c34b5e1a --- /dev/null +++ b/test_regress/t/t_covergroup_undef_field_bad.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2023 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + + +module t; + + covergroup cg_opt; + type_option.weight = 1; // ok + option.name = "the_name"; // pk + bad_cg_non_option.name = "xx"; // <--- Bad + endgroup + + covergroup cg_cross3; + cross a, b{ + option.comment = "cross"; // ok + bad_cross_non_option.name = "xx"; // <--- Bad + } + endgroup + + initial $stop; +endmodule diff --git a/test_regress/t/t_covergroup_unsup.out b/test_regress/t/t_covergroup_unsup.out index 11fd37c51..eb30476de 100644 --- a/test_regress/t/t_covergroup_unsup.out +++ b/test_regress/t/t_covergroup_unsup.out @@ -1,408 +1,244 @@ -%Warning-COVERIGN: t/t_covergroup_unsup.v:38:3: Ignoring unsupported: covergroup - 38 | covergroup cg_empty; - | ^~~~~~~~~~ - ... For warning description see https://verilator.org/warn/COVERIGN?v=latest - ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. -%Warning-COVERIGN: t/t_covergroup_unsup.v:41:3: Ignoring unsupported: covergroup - 41 | covergroup cg_opt; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:60:33: Ignoring unsupported: coverage clocking event - 60 | covergroup cg_clockingevent() @(posedge clk); - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:60:3: Ignoring unsupported: covergroup - 60 | covergroup cg_clockingevent() @(posedge clk); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:62:3: Ignoring unsupported: covergroup - 62 | covergroup cg_withfunction() with function sample (a); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:64:24: Ignoring unsupported: coverage '@@' events +%Warning-COVERIGN: t/t_covergroup_unsup.v:64:24: Unsupported: '@@' coverage event 64 | covergroup cg_atat() @@ (begin funca or end funcb); | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:64:3: Ignoring unsupported: covergroup - 64 | covergroup cg_atat() @@ (begin funca or end funcb); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:66:3: Ignoring unsupported: covergroup - 66 | covergroup cg_bracket; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:69:3: Ignoring unsupported: covergroup - 69 | covergroup cg_bracket2; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:73:5: Ignoring unsupported: coverpoint - 73 | coverpoint a; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:72:3: Ignoring unsupported: covergroup - 72 | covergroup cg_cp; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:76:18: Ignoring unsupported: cover 'iff' - 76 | coverpoint a iff (b); - | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:76:5: Ignoring unsupported: coverpoint - 76 | coverpoint a iff (b); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:75:3: Ignoring unsupported: covergroup - 75 | covergroup cg_cp_iff; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:79:22: Ignoring unsupported: cover 'iff' - 79 | id: coverpoint a iff (b); - | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:79:9: Ignoring unsupported: coverpoint - 79 | id: coverpoint a iff (b); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:78:3: Ignoring unsupported: covergroup - 78 | covergroup cg_id_cp_iff; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:82:26: Ignoring unsupported: cover 'iff' - 82 | int id: coverpoint a iff (b); - | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:82:13: Ignoring unsupported: coverpoint - 82 | int id: coverpoint a iff (b); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:81:3: Ignoring unsupported: covergroup - 81 | covergroup cg_id_cp_id1; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:85:30: Ignoring unsupported: cover 'iff' - 85 | var int id: coverpoint a iff (b); - | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:85:17: Ignoring unsupported: coverpoint - 85 | var int id: coverpoint a iff (b); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:84:3: Ignoring unsupported: covergroup - 84 | covergroup cg_id_cp_id2; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:88:32: Ignoring unsupported: cover 'iff' - 88 | var [3:0] id: coverpoint a iff (b); - | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:88:19: Ignoring unsupported: coverpoint - 88 | var [3:0] id: coverpoint a iff (b); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:87:3: Ignoring unsupported: covergroup - 87 | covergroup cg_id_cp_id3; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:91:28: Ignoring unsupported: cover 'iff' - 91 | [3:0] id: coverpoint a iff (b); - | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:91:15: Ignoring unsupported: coverpoint - 91 | [3:0] id: coverpoint a iff (b); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:90:3: Ignoring unsupported: covergroup - 90 | covergroup cg_id_cp_id4; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:94:29: Ignoring unsupported: cover 'iff' - 94 | signed id: coverpoint a iff (b); - | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:94:16: Ignoring unsupported: coverpoint - 94 | signed id: coverpoint a iff (b); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:93:3: Ignoring unsupported: covergroup - 93 | covergroup cg_id_cp_id5; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:98:16: Ignoring unsupported: cover 'iff' + ... For warning description see https://verilator.org/warn/COVERIGN?v=latest + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Warning-COVERIGN: t/t_covergroup_unsup.v:98:21: Unsupported: 'iff' in coverage cross 98 | cross a, b iff (!rst); - | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:98:5: Ignoring unsupported: cover cross - 98 | cross a, b iff (!rst); - | ^~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:97:3: Ignoring unsupported: covergroup - 97 | covergroup cg_cross; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:101:16: Ignoring unsupported: cover 'iff' + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:101:21: Unsupported: 'iff' in coverage cross 101 | cross a, b iff (!rst) {} - | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:101:5: Ignoring unsupported: cover cross - 101 | cross a, b iff (!rst) {} - | ^~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:100:3: Ignoring unsupported: covergroup - 100 | covergroup cg_cross2; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:104:5: Ignoring unsupported: cover cross - 104 | cross a, b { option.comment = "cross"; option.weight = 12; } - | ^~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:103:3: Ignoring unsupported: covergroup - 103 | covergroup cg_cross3; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:108:21: Ignoring unsupported: coverage cross 'function' declaration + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:108:21: Unsupported: 'function' in coverage cross body 108 | function void crossfunc; endfunction | ^~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:109:18: Ignoring unsupported: coverage select function call +%Warning-COVERIGN: t/t_covergroup_unsup.v:109:18: Unsupported: function call in coverage select expression 109 | bins one = crossfunc(); | ^~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:109:7: Ignoring unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:109:7: Unsupported: explicit coverage cross bins 109 | bins one = crossfunc(); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:107:5: Ignoring unsupported: cover cross - 107 | cross a, b { - | ^~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:106:3: Ignoring unsupported: covergroup - 106 | covergroup cg_cross4; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:113:26: Ignoring unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:113:31: Unsupported: 'iff' in coverage cross 113 | my_cg_id: cross a, b iff (!rst); - | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:113:15: Ignoring unsupported: cover cross - 113 | my_cg_id: cross a, b iff (!rst); - | ^~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:112:3: Ignoring unsupported: covergroup - 112 | covergroup cg_cross_id; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:118:15: Ignoring unsupported: cover bin specification - 118 | { bins ba = {a}; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:119:22: Ignoring unsupported: cover 'iff' - 119 | { bins bar = {a} iff (!rst); } - | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:119:16: Ignoring unsupported: cover bin specification - 119 | { bins bar = {a} iff (!rst); } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:120:24: Ignoring unsupported: cover bin specification - 120 | { illegal_bins ila = {a}; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:121:23: Ignoring unsupported: cover bin specification - 121 | { ignore_bins iga = {a}; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:123:17: Ignoring unsupported: cover bin specification - 123 | { bins ba[] = {a}; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:124:18: Ignoring unsupported: cover bin specification - 124 | { bins ba[2] = {a}; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:126:21: Ignoring unsupported: cover bin 'with' specification - 126 | { bins ba = {a} with ( b ); } + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:123:14: Unsupported: 'bins' explicit array size (treated as '[]') + 123 | { bins ba[2] = {a}; } + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:127:21: Unsupported: 'with' in cover bin (bin created without filter) + 127 | { bins ba = {a} with ( b ); } | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:128:25: Ignoring unsupported: cover bin 'wildcard' specification - 128 | { wildcard bins bwa = {a}; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:129:32: Ignoring unsupported: cover bin 'wildcard' 'with' specification - 129 | { wildcard bins bwaw = {a} with ( b ); } +%Warning-COVERIGN: t/t_covergroup_unsup.v:130:32: Unsupported: 'with' in wildcard cover bin + 130 | { wildcard bins bwaw = {a} with ( b ); } | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:131:18: Ignoring unsupported: cover bin 'default' - 131 | { bins def = default; } - | ^~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:132:27: Ignoring unsupported: cover bin 'default' 'sequence' - 132 | { bins defs = default sequence; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:133:27: Unsupported: 'sequence' in default cover bin + 133 | { bins defs = default sequence; } | ^~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:134:16: Ignoring unsupported: cover bin trans list - 134 | { bins bts = ( 1, 2 ); } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:135:7: Ignoring unsupported: cover bin 'wildcard' trans list - 135 | { wildcard bins wbts = ( 1, 2 ); } +%Warning-COVERIGN: t/t_covergroup_unsup.v:136:7: Unsupported: 'wildcard' transition list in cover bin + 136 | { wildcard bins wbts = ( 1, 2 ); } | ^~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:136:31: Ignoring unsupported: covergroup value range - 136 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:31: Unsupported: covergroup value range '[...]' + 137 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:136:42: Ignoring unsupported: covergroup value range - 136 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:42: Unsupported: covergroup value range '[...]' + 137 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:136:57: Ignoring unsupported: covergroup value range - 136 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:57: Unsupported: covergroup value range '[...]' + 137 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:136:17: Ignoring unsupported: cover bin trans list - 136 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:138:25: Ignoring unsupported: cover trans set '=>' - 138 | { bins bts2 = ( 1,5 => 6,7 ) ; } - | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:138:17: Ignoring unsupported: cover bin trans list - 138 | { bins bts2 = ( 1,5 => 6,7 ) ; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:139:23: Ignoring unsupported: cover '[*' - 139 | { bins bts2 = ( 3 [*5] ) ; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:140:23: Unsupported: '[*]' in cover transition + 140 | { bins bts2 = ( 3 [*5] ) ; } | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:139:17: Ignoring unsupported: cover bin trans list - 139 | { bins bts2 = ( 3 [*5] ) ; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:140:23: Ignoring unsupported: cover '[*' - 140 | { bins bts2 = ( 3 [*5:6] ) ; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:141:23: Unsupported: '[*]' in cover transition + 141 | { bins bts2 = ( 3 [*5:6] ) ; } | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:140:17: Ignoring unsupported: cover bin trans list - 140 | { bins bts2 = ( 3 [*5:6] ) ; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:141:23: Ignoring unsupported: cover '[->' - 141 | { bins bts2 = ( 3 [->5] ) ; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:142:23: Unsupported: '[->' in cover transition + 142 | { bins bts2 = ( 3 [->5] ) ; } | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:141:17: Ignoring unsupported: cover bin trans list - 141 | { bins bts2 = ( 3 [->5] ) ; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:142:23: Ignoring unsupported: cover '[->' - 142 | { bins bts2 = ( 3 [->5:6] ) ; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:143:23: Unsupported: '[->' in cover transition + 143 | { bins bts2 = ( 3 [->5:6] ) ; } | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:142:17: Ignoring unsupported: cover bin trans list - 142 | { bins bts2 = ( 3 [->5:6] ) ; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:143:23: Ignoring unsupported: cover '[=' - 143 | { bins bts2 = ( 3 [=5] ) ; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:144:23: Unsupported: '[=]' in cover transition + 144 | { bins bts2 = ( 3 [=5] ) ; } | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:143:17: Ignoring unsupported: cover bin trans list - 143 | { bins bts2 = ( 3 [=5] ) ; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:144:23: Ignoring unsupported: cover '[=' - 144 | { bins bts2 = ( 3 [=5:6] ) ; } +%Warning-COVERIGN: t/t_covergroup_unsup.v:145:23: Unsupported: '[=]' in cover transition + 145 | { bins bts2 = ( 3 [=5:6] ) ; } | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:144:17: Ignoring unsupported: cover bin trans list - 144 | { bins bts2 = ( 3 [=5:6] ) ; } - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:116:3: Ignoring unsupported: covergroup - 116 | covergroup cg_binsoroptions_bk1; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:149:24: Ignoring unsupported: cover bin 'with' specification - 149 | bins div_by_2 = a with (item % 2 == 0); +%Warning-COVERIGN: t/t_covergroup_unsup.v:151:12: Unsupported: 'bins' array (non-auto) + 151 | { bins nonAuto[4]; } + | ^~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:153:35: Unsupported: 'with' in cover bin (bin created without filter) + 153 | { ignore_bins ib_with = {1,2} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:154:37: Unsupported: 'with' in cover bin (bin created without filter) + 154 | { illegal_bins lib_with = {1,2} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:156:29: Unsupported: 'with' in cover bin + 156 | { ignore_bins ib_cp = a with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:157:31: Unsupported: 'with' in cover bin + 157 | { illegal_bins lib_cp = a with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:159:45: Unsupported: 'with' in wildcard cover bin + 159 | { wildcard ignore_bins wib_with = {1,2} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:160:47: Unsupported: 'with' in wildcard cover bin + 160 | { wildcard illegal_bins wlib_with = {1,2} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:162:7: Unsupported: 'wildcard' transition list in cover bin + 162 | { wildcard ignore_bins wib_trans = ( 1 => 2 ); } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:163:7: Unsupported: 'wildcard' transition list in cover bin + 163 | { wildcard illegal_bins wlib_trans = ( 1 => 2 ); } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:165:40: Unsupported: 'sequence' in default cover bin + 165 | { ignore_bins ib_def_seq = default sequence; } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:166:42: Unsupported: 'sequence' in default cover bin + 166 | { illegal_bins lib_def_seq = default sequence; } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:171:24: Unsupported: 'with' in cover bin + 171 | bins div_by_2 = a with (item % 2 == 0); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:150:32: Ignoring unsupported: cover bin 'with' specification - 150 | bins div_by_2_paren[] = a with (item % 2 == 0); +%Warning-COVERIGN: t/t_covergroup_unsup.v:172:32: Unsupported: 'with' in cover bin + 172 | bins div_by_2_paren[] = a with (item % 2 == 0); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:148:5: Ignoring unsupported: coverpoint - 148 | coverpoint a { - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:147:3: Ignoring unsupported: covergroup - 147 | covergroup cg_coverpoint_ref; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:156:20: Ignoring unsupported: coverage select expression 'binsof' - 156 | bins bin_a = binsof(a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:178:20: Unsupported: 'binsof' in coverage select expression + 178 | bins bin_a = binsof(a); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:156:7: Ignoring unsupported: coverage cross bin - 156 | bins bin_a = binsof(a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:178:7: Unsupported: explicit coverage cross bins + 178 | bins bin_a = binsof(a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:157:21: Ignoring unsupported: coverage select expression 'binsof' - 157 | bins bin_ai = binsof(a) iff (!rst); +%Warning-COVERIGN: t/t_covergroup_unsup.v:179:21: Unsupported: 'binsof' in coverage select expression + 179 | bins bin_ai = binsof(a) iff (!rst); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:157:31: Ignoring unsupported: cover 'iff' - 157 | bins bin_ai = binsof(a) iff (!rst); - | ^~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:157:7: Ignoring unsupported: coverage cross bin - 157 | bins bin_ai = binsof(a) iff (!rst); +%Warning-COVERIGN: t/t_covergroup_unsup.v:179:7: Unsupported: explicit coverage cross bins + 179 | bins bin_ai = binsof(a) iff (!rst); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:158:20: Ignoring unsupported: coverage select expression 'binsof' - 158 | bins bin_c = binsof(cp.x); +%Warning-COVERIGN: t/t_covergroup_unsup.v:180:20: Unsupported: 'binsof' in coverage select expression + 180 | bins bin_c = binsof(cp.x); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:158:7: Ignoring unsupported: coverage cross bin - 158 | bins bin_c = binsof(cp.x); +%Warning-COVERIGN: t/t_covergroup_unsup.v:180:7: Unsupported: explicit coverage cross bins + 180 | bins bin_c = binsof(cp.x); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:159:21: Ignoring unsupported: coverage select expression 'binsof' - 159 | bins bin_na = ! binsof(a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:181:21: Unsupported: 'binsof' in coverage select expression + 181 | bins bin_na = ! binsof(a); | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:159:7: Ignoring unsupported: coverage cross bin - 159 | bins bin_na = ! binsof(a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:181:7: Unsupported: explicit coverage cross bins + 181 | bins bin_na = ! binsof(a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:161:30: Ignoring unsupported: coverage select expression 'intersect' - 161 | bins bin_d = binsof(a) intersect { b }; +%Warning-COVERIGN: t/t_covergroup_unsup.v:183:30: Unsupported: 'intersect' in coverage select expression + 183 | bins bin_d = binsof(a) intersect { b }; | ^~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:161:7: Ignoring unsupported: coverage cross bin - 161 | bins bin_d = binsof(a) intersect { b }; +%Warning-COVERIGN: t/t_covergroup_unsup.v:183:7: Unsupported: explicit coverage cross bins + 183 | bins bin_d = binsof(a) intersect { b }; | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:162:31: Ignoring unsupported: coverage select expression 'intersect' - 162 | bins bin_nd = ! binsof(a) intersect { b }; +%Warning-COVERIGN: t/t_covergroup_unsup.v:184:31: Unsupported: 'intersect' in coverage select expression + 184 | bins bin_nd = ! binsof(a) intersect { b }; | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:162:7: Ignoring unsupported: coverage cross bin - 162 | bins bin_nd = ! binsof(a) intersect { b }; +%Warning-COVERIGN: t/t_covergroup_unsup.v:184:7: Unsupported: explicit coverage cross bins + 184 | bins bin_nd = ! binsof(a) intersect { b }; | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:164:20: Ignoring unsupported: coverage select expression with - 164 | bins bin_e = with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:186:20: Unsupported: 'with' in coverage select expression + 186 | bins bin_e = with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:164:7: Ignoring unsupported: coverage cross bin - 164 | bins bin_e = with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:186:7: Unsupported: explicit coverage cross bins + 186 | bins bin_e = with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:165:24: Ignoring unsupported: coverage select expression with - 165 | bins bin_not_e = ! with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:187:24: Unsupported: 'with' in coverage select expression + 187 | bins bin_not_e = ! with (a); | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:165:7: Ignoring unsupported: coverage cross bin - 165 | bins bin_not_e = ! with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:187:7: Unsupported: explicit coverage cross bins + 187 | bins bin_not_e = ! with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:167:23: Ignoring unsupported: coverage select expression 'binsof' - 167 | bins bin_par = (binsof(a)); +%Warning-COVERIGN: t/t_covergroup_unsup.v:189:23: Unsupported: 'binsof' in coverage select expression + 189 | bins bin_par = (binsof(a)); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:167:7: Ignoring unsupported: coverage cross bin - 167 | bins bin_par = (binsof(a)); +%Warning-COVERIGN: t/t_covergroup_unsup.v:189:7: Unsupported: explicit coverage cross bins + 189 | bins bin_par = (binsof(a)); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:168:22: Ignoring unsupported: coverage select expression 'binsof' - 168 | bins bin_and = binsof(a) && binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:190:22: Unsupported: 'binsof' in coverage select expression + 190 | bins bin_and = binsof(a) && binsof(b); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:168:35: Ignoring unsupported: coverage select expression 'binsof' - 168 | bins bin_and = binsof(a) && binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:190:35: Unsupported: 'binsof' in coverage select expression + 190 | bins bin_and = binsof(a) && binsof(b); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:168:32: Ignoring unsupported: coverage select expression '&&' - 168 | bins bin_and = binsof(a) && binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:190:32: Unsupported: '&&' in coverage select expression + 190 | bins bin_and = binsof(a) && binsof(b); | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:168:7: Ignoring unsupported: coverage cross bin - 168 | bins bin_and = binsof(a) && binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:190:7: Unsupported: explicit coverage cross bins + 190 | bins bin_and = binsof(a) && binsof(b); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:169:21: Ignoring unsupported: coverage select expression 'binsof' - 169 | bins bin_or = binsof(a) || binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:191:21: Unsupported: 'binsof' in coverage select expression + 191 | bins bin_or = binsof(a) || binsof(b); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:169:34: Ignoring unsupported: coverage select expression 'binsof' - 169 | bins bin_or = binsof(a) || binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:191:34: Unsupported: 'binsof' in coverage select expression + 191 | bins bin_or = binsof(a) || binsof(b); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:169:31: Ignoring unsupported: coverage select expression '||' - 169 | bins bin_or = binsof(a) || binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:191:31: Unsupported: '||' in coverage select expression + 191 | bins bin_or = binsof(a) || binsof(b); | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:169:7: Ignoring unsupported: coverage cross bin - 169 | bins bin_or = binsof(a) || binsof(b); +%Warning-COVERIGN: t/t_covergroup_unsup.v:191:7: Unsupported: explicit coverage cross bins + 191 | bins bin_or = binsof(a) || binsof(b); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:170:23: Ignoring unsupported: coverage select expression 'binsof' - 170 | bins bin_with = binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:192:23: Unsupported: 'binsof' in coverage select expression + 192 | bins bin_with = binsof(a) with (a); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:170:33: Ignoring unsupported: coverage select expression with - 170 | bins bin_with = binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:192:33: Unsupported: 'with' in coverage select expression + 192 | bins bin_with = binsof(a) with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:170:7: Ignoring unsupported: coverage cross bin - 170 | bins bin_with = binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:192:7: Unsupported: explicit coverage cross bins + 192 | bins bin_with = binsof(a) with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:171:26: Ignoring unsupported: coverage select expression 'binsof' - 171 | bins bin_or_with = binsof(a) || binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:26: Unsupported: 'binsof' in coverage select expression + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:171:39: Ignoring unsupported: coverage select expression 'binsof' - 171 | bins bin_or_with = binsof(a) || binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:39: Unsupported: 'binsof' in coverage select expression + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:171:49: Ignoring unsupported: coverage select expression with - 171 | bins bin_or_with = binsof(a) || binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:49: Unsupported: 'with' in coverage select expression + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:171:36: Ignoring unsupported: coverage select expression '||' - 171 | bins bin_or_with = binsof(a) || binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:36: Unsupported: '||' in coverage select expression + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:171:7: Ignoring unsupported: coverage cross bin - 171 | bins bin_or_with = binsof(a) || binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:7: Unsupported: explicit coverage cross bins + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:172:27: Ignoring unsupported: coverage select expression 'binsof' - 172 | bins bin_and_with = binsof(a) && binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:27: Unsupported: 'binsof' in coverage select expression + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:172:40: Ignoring unsupported: coverage select expression 'binsof' - 172 | bins bin_and_with = binsof(a) && binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:40: Unsupported: 'binsof' in coverage select expression + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:172:50: Ignoring unsupported: coverage select expression with - 172 | bins bin_and_with = binsof(a) && binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:50: Unsupported: 'with' in coverage select expression + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:172:37: Ignoring unsupported: coverage select expression '&&' - 172 | bins bin_and_with = binsof(a) && binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:37: Unsupported: '&&' in coverage select expression + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); | ^~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:172:7: Ignoring unsupported: coverage cross bin - 172 | bins bin_and_with = binsof(a) && binsof(a) with (a); +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:7: Unsupported: explicit coverage cross bins + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:173:34: Ignoring unsupported: coverage select expression 'binsof' - 173 | bins bin_multiple_fields = binsof(p.inner_packet.field); +%Warning-COVERIGN: t/t_covergroup_unsup.v:195:34: Unsupported: 'binsof' in coverage select expression + 195 | bins bin_multiple_fields = binsof(p.inner_packet.field); | ^~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:173:7: Ignoring unsupported: coverage cross bin - 173 | bins bin_multiple_fields = binsof(p.inner_packet.field); +%Warning-COVERIGN: t/t_covergroup_unsup.v:195:7: Unsupported: explicit coverage cross bins + 195 | bins bin_multiple_fields = binsof(p.inner_packet.field); | ^~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:155:5: Ignoring unsupported: cover cross - 155 | cross a, b { - | ^~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:154:3: Ignoring unsupported: covergroup - 154 | covergroup cg_cross_bins; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:177:3: Ignoring unsupported: covergroup - 177 | covergroup cgArgs(int cg_lim); - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:184:21: Ignoring unsupported: coverage clocking event - 184 | covergroup cov1 @m_z; - | ^ -%Warning-COVERIGN: t/t_covergroup_unsup.v:185:7: Ignoring unsupported: coverpoint - 185 | coverpoint m_x; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:186:7: Ignoring unsupported: coverpoint - 186 | coverpoint m_y; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:184:5: Ignoring unsupported: covergroup - 184 | covergroup cov1 @m_z; - | ^~~~~~~~~~ -%Warning-COVERIGN: t/t_covergroup_unsup.v:194:5: Ignoring unsupported: covergroup - 194 | covergroup extends cg_empty; +%Warning-COVERIGN: t/t_covergroup_unsup.v:197:30: Unsupported: 'binsof' in coverage select expression + 197 | ignore_bins ib_cross = binsof(a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:197:7: Unsupported: explicit coverage cross bins + 197 | ignore_bins ib_cross = binsof(a); + | ^~~~~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:198:32: Unsupported: 'binsof' in coverage select expression + 198 | illegal_bins lib_cross = binsof(a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:198:7: Unsupported: explicit coverage cross bins + 198 | illegal_bins lib_cross = binsof(a); + | ^~~~~~~~~~~~ +%Error-UNSUPPORTED: t/t_covergroup_unsup.v:223:5: Unsupported: covergroup inheritance (extends) + 223 | covergroup extends cg_empty; | ^~~~~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_unsup.py b/test_regress/t/t_covergroup_unsup.py index 4d0f745d0..695572bb1 100755 --- a/test_regress/t/t_covergroup_unsup.py +++ b/test_regress/t/t_covergroup_unsup.py @@ -11,8 +11,6 @@ import vltest_bootstrap test.scenarios('vlt') -test.lint(expect_filename=test.golden_filename, - verilator_flags2=['--assert --error-limit 1000'], - fails=True) +test.lint(expect_filename=test.golden_filename, fails=True) test.passes() diff --git a/test_regress/t/t_covergroup_unsup.v b/test_regress/t/t_covergroup_unsup.v index 36e6d7aa7..34d94f2c0 100644 --- a/test_regress/t/t_covergroup_unsup.v +++ b/test_regress/t/t_covergroup_unsup.v @@ -101,7 +101,7 @@ module t ( cross a, b iff (!rst) {} endgroup covergroup cg_cross3; - cross a, b { option.comment = "cross"; option.weight = 12; } + cross a, b { option.comment = "cross"; option.weight = 12; option.per_instance = 1; } endgroup covergroup cg_cross4; cross a, b { @@ -114,7 +114,6 @@ module t ( endgroup covergroup cg_binsoroptions_bk1; - // bins_keyword id/*bin_identifier*/ bins_orBraE '=' '{' open_range_list '}' iffE { bins ba = {a}; } { bins bar = {a} iff (!rst); } { illegal_bins ila = {a}; } @@ -122,6 +121,8 @@ module t ( { bins ba[] = {a}; } { bins ba[2] = {a}; } + { ignore_bins iga[] = {a}; } + { illegal_bins ila[] = {a}; } { bins ba = {a} with ( b ); } @@ -144,6 +145,27 @@ module t ( { bins bts2 = ( 3 [=5:6] ) ; } endgroup + // Additional bins syntax for grammar coverage (all generate COVERIGN warnings) + covergroup cg_bins_ext; + // Non-auto bins array without value: bins name[N] (no = {value}) + { bins nonAuto[4]; } + // ignore_bins/illegal_bins with 'with' filter on range list + { ignore_bins ib_with = {1,2} with ( b ); } + { illegal_bins lib_with = {1,2} with ( b ); } + // ignore_bins/illegal_bins with 'with' filter on coverpoint ref + { ignore_bins ib_cp = a with ( b ); } + { illegal_bins lib_cp = a with ( b ); } + // wildcard ignore/illegal bins with 'with' filter + { wildcard ignore_bins wib_with = {1,2} with ( b ); } + { wildcard illegal_bins wlib_with = {1,2} with ( b ); } + // wildcard ignore/illegal bins with transition list + { wildcard ignore_bins wib_trans = ( 1 => 2 ); } + { wildcard illegal_bins wlib_trans = ( 1 => 2 ); } + // ignore/illegal bins = default sequence + { ignore_bins ib_def_seq = default sequence; } + { illegal_bins lib_def_seq = default sequence; } + endgroup + covergroup cg_coverpoint_ref; coverpoint a { bins div_by_2 = a with (item % 2 == 0); @@ -171,6 +193,9 @@ module t ( bins bin_or_with = binsof(a) || binsof(a) with (a); bins bin_and_with = binsof(a) && binsof(a) with (a); bins bin_multiple_fields = binsof(p.inner_packet.field); + // explicit cross ignore/illegal bins (unsupported) + ignore_bins ib_cross = binsof(a); + illegal_bins lib_cross = binsof(a); } endgroup @@ -182,18 +207,23 @@ module t ( int m_y; int m_z; covergroup cov1 @m_z; - coverpoint m_x; - coverpoint m_y; + cp_x: coverpoint m_x; + cp_y: coverpoint m_y; +`ifdef T_COVERGROUP_UNSUP_IGN + xy_cross: cross cp_x, cp_y; // cross is cleaned up when the covergroup has an unsupported event +`endif endgroup `ifndef T_COVERGROUP_UNSUP_IGN function new(); cov1 = new; endfunction `endif endclass +`ifndef T_COVERGROUP_UNSUP_IGN class CgEmb; covergroup extends cg_empty; endgroup endclass +`endif initial begin automatic cg_empty cov1 = new; diff --git a/test_regress/t/t_covergroup_unsup_ign.out b/test_regress/t/t_covergroup_unsup_ign.out new file mode 100644 index 000000000..3d070237d --- /dev/null +++ b/test_regress/t/t_covergroup_unsup_ign.out @@ -0,0 +1,31 @@ +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:76:5: Logical operator COVERPOINT 'a' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 76 | coverpoint a iff (b); + | ^~~~~~~~~~ + ... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest + ... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message. +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:79:9: Logical operator COVERPOINT 'id' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 79 | id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:82:13: Logical operator COVERPOINT 'id' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 82 | int id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:85:17: Logical operator COVERPOINT 'id' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 85 | var int id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:88:19: Logical operator COVERPOINT 'id' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 88 | var [3:0] id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:91:15: Logical operator COVERPOINT 'id' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 91 | [3:0] id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:94:16: Logical operator COVERPOINT 'id' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 94 | signed id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_covergroup_unsup_ign.py b/test_regress/t/t_covergroup_unsup_ign.py index 75107c00e..4fe9db965 100755 --- a/test_regress/t/t_covergroup_unsup_ign.py +++ b/test_regress/t/t_covergroup_unsup_ign.py @@ -12,6 +12,8 @@ import vltest_bootstrap test.scenarios('vlt') test.top_filename = "t/t_covergroup_unsup.v" -test.lint(verilator_flags2=['--assert --coverage --Wno-COVERIGN +define+T_COVERGROUP_UNSUP_IGN']) +test.lint(verilator_flags2=['--assert --coverage --Wno-COVERIGN +define+T_COVERGROUP_UNSUP_IGN'], + expect_filename=test.golden_filename, + fails=True) test.passes() diff --git a/test_regress/t/t_covergroup_unsup_ign2.out b/test_regress/t/t_covergroup_unsup_ign2.out new file mode 100644 index 000000000..73065a5c5 --- /dev/null +++ b/test_regress/t/t_covergroup_unsup_ign2.out @@ -0,0 +1,306 @@ +%Warning-COVERIGN: t/t_covergroup_unsup.v:64:24: Unsupported: '@@' coverage event + 64 | covergroup cg_atat() @@ (begin funca or end funcb); + | ^~ + ... For warning description see https://verilator.org/warn/COVERIGN?v=latest + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Warning-COVERIGN: t/t_covergroup_unsup.v:98:21: Unsupported: 'iff' in coverage cross + 98 | cross a, b iff (!rst); + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:101:21: Unsupported: 'iff' in coverage cross + 101 | cross a, b iff (!rst) {} + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:108:21: Unsupported: 'function' in coverage cross body + 108 | function void crossfunc; endfunction + | ^~~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:109:18: Unsupported: function call in coverage select expression + 109 | bins one = crossfunc(); + | ^~~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:109:7: Unsupported: explicit coverage cross bins + 109 | bins one = crossfunc(); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:113:31: Unsupported: 'iff' in coverage cross + 113 | my_cg_id: cross a, b iff (!rst); + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:123:14: Unsupported: 'bins' explicit array size (treated as '[]') + 123 | { bins ba[2] = {a}; } + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:127:21: Unsupported: 'with' in cover bin (bin created without filter) + 127 | { bins ba = {a} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:130:32: Unsupported: 'with' in wildcard cover bin + 130 | { wildcard bins bwaw = {a} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:133:27: Unsupported: 'sequence' in default cover bin + 133 | { bins defs = default sequence; } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:136:7: Unsupported: 'wildcard' transition list in cover bin + 136 | { wildcard bins wbts = ( 1, 2 ); } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:31: Unsupported: covergroup value range '[...]' + 137 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:42: Unsupported: covergroup value range '[...]' + 137 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:57: Unsupported: covergroup value range '[...]' + 137 | { bins bts2 = ( 2, 3 ), ( [5:6] ), ( [5 +/- 2] ), ( [ 5 +%- 20.0] ) ; } + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:140:23: Unsupported: '[*]' in cover transition + 140 | { bins bts2 = ( 3 [*5] ) ; } + | ^~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:141:23: Unsupported: '[*]' in cover transition + 141 | { bins bts2 = ( 3 [*5:6] ) ; } + | ^~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:142:23: Unsupported: '[->' in cover transition + 142 | { bins bts2 = ( 3 [->5] ) ; } + | ^~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:143:23: Unsupported: '[->' in cover transition + 143 | { bins bts2 = ( 3 [->5:6] ) ; } + | ^~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:144:23: Unsupported: '[=]' in cover transition + 144 | { bins bts2 = ( 3 [=5] ) ; } + | ^~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:145:23: Unsupported: '[=]' in cover transition + 145 | { bins bts2 = ( 3 [=5:6] ) ; } + | ^~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:151:12: Unsupported: 'bins' array (non-auto) + 151 | { bins nonAuto[4]; } + | ^~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:153:35: Unsupported: 'with' in cover bin (bin created without filter) + 153 | { ignore_bins ib_with = {1,2} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:154:37: Unsupported: 'with' in cover bin (bin created without filter) + 154 | { illegal_bins lib_with = {1,2} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:156:29: Unsupported: 'with' in cover bin + 156 | { ignore_bins ib_cp = a with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:157:31: Unsupported: 'with' in cover bin + 157 | { illegal_bins lib_cp = a with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:159:45: Unsupported: 'with' in wildcard cover bin + 159 | { wildcard ignore_bins wib_with = {1,2} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:160:47: Unsupported: 'with' in wildcard cover bin + 160 | { wildcard illegal_bins wlib_with = {1,2} with ( b ); } + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:162:7: Unsupported: 'wildcard' transition list in cover bin + 162 | { wildcard ignore_bins wib_trans = ( 1 => 2 ); } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:163:7: Unsupported: 'wildcard' transition list in cover bin + 163 | { wildcard illegal_bins wlib_trans = ( 1 => 2 ); } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:165:40: Unsupported: 'sequence' in default cover bin + 165 | { ignore_bins ib_def_seq = default sequence; } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:166:42: Unsupported: 'sequence' in default cover bin + 166 | { illegal_bins lib_def_seq = default sequence; } + | ^~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:171:24: Unsupported: 'with' in cover bin + 171 | bins div_by_2 = a with (item % 2 == 0); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:172:32: Unsupported: 'with' in cover bin + 172 | bins div_by_2_paren[] = a with (item % 2 == 0); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:178:20: Unsupported: 'binsof' in coverage select expression + 178 | bins bin_a = binsof(a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:178:7: Unsupported: explicit coverage cross bins + 178 | bins bin_a = binsof(a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:179:21: Unsupported: 'binsof' in coverage select expression + 179 | bins bin_ai = binsof(a) iff (!rst); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:179:7: Unsupported: explicit coverage cross bins + 179 | bins bin_ai = binsof(a) iff (!rst); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:180:20: Unsupported: 'binsof' in coverage select expression + 180 | bins bin_c = binsof(cp.x); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:180:7: Unsupported: explicit coverage cross bins + 180 | bins bin_c = binsof(cp.x); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:181:21: Unsupported: 'binsof' in coverage select expression + 181 | bins bin_na = ! binsof(a); + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:181:7: Unsupported: explicit coverage cross bins + 181 | bins bin_na = ! binsof(a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:183:30: Unsupported: 'intersect' in coverage select expression + 183 | bins bin_d = binsof(a) intersect { b }; + | ^~~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:183:7: Unsupported: explicit coverage cross bins + 183 | bins bin_d = binsof(a) intersect { b }; + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:184:31: Unsupported: 'intersect' in coverage select expression + 184 | bins bin_nd = ! binsof(a) intersect { b }; + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:184:7: Unsupported: explicit coverage cross bins + 184 | bins bin_nd = ! binsof(a) intersect { b }; + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:186:20: Unsupported: 'with' in coverage select expression + 186 | bins bin_e = with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:186:7: Unsupported: explicit coverage cross bins + 186 | bins bin_e = with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:187:24: Unsupported: 'with' in coverage select expression + 187 | bins bin_not_e = ! with (a); + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:187:7: Unsupported: explicit coverage cross bins + 187 | bins bin_not_e = ! with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:189:23: Unsupported: 'binsof' in coverage select expression + 189 | bins bin_par = (binsof(a)); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:189:7: Unsupported: explicit coverage cross bins + 189 | bins bin_par = (binsof(a)); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:190:22: Unsupported: 'binsof' in coverage select expression + 190 | bins bin_and = binsof(a) && binsof(b); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:190:35: Unsupported: 'binsof' in coverage select expression + 190 | bins bin_and = binsof(a) && binsof(b); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:190:32: Unsupported: '&&' in coverage select expression + 190 | bins bin_and = binsof(a) && binsof(b); + | ^~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:190:7: Unsupported: explicit coverage cross bins + 190 | bins bin_and = binsof(a) && binsof(b); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:191:21: Unsupported: 'binsof' in coverage select expression + 191 | bins bin_or = binsof(a) || binsof(b); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:191:34: Unsupported: 'binsof' in coverage select expression + 191 | bins bin_or = binsof(a) || binsof(b); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:191:31: Unsupported: '||' in coverage select expression + 191 | bins bin_or = binsof(a) || binsof(b); + | ^~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:191:7: Unsupported: explicit coverage cross bins + 191 | bins bin_or = binsof(a) || binsof(b); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:192:23: Unsupported: 'binsof' in coverage select expression + 192 | bins bin_with = binsof(a) with (a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:192:33: Unsupported: 'with' in coverage select expression + 192 | bins bin_with = binsof(a) with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:192:7: Unsupported: explicit coverage cross bins + 192 | bins bin_with = binsof(a) with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:26: Unsupported: 'binsof' in coverage select expression + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:39: Unsupported: 'binsof' in coverage select expression + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:49: Unsupported: 'with' in coverage select expression + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:36: Unsupported: '||' in coverage select expression + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); + | ^~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:193:7: Unsupported: explicit coverage cross bins + 193 | bins bin_or_with = binsof(a) || binsof(a) with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:27: Unsupported: 'binsof' in coverage select expression + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:40: Unsupported: 'binsof' in coverage select expression + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:50: Unsupported: 'with' in coverage select expression + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:37: Unsupported: '&&' in coverage select expression + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); + | ^~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:194:7: Unsupported: explicit coverage cross bins + 194 | bins bin_and_with = binsof(a) && binsof(a) with (a); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:195:34: Unsupported: 'binsof' in coverage select expression + 195 | bins bin_multiple_fields = binsof(p.inner_packet.field); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:195:7: Unsupported: explicit coverage cross bins + 195 | bins bin_multiple_fields = binsof(p.inner_packet.field); + | ^~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:197:30: Unsupported: 'binsof' in coverage select expression + 197 | ignore_bins ib_cross = binsof(a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:197:7: Unsupported: explicit coverage cross bins + 197 | ignore_bins ib_cross = binsof(a); + | ^~~~~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:198:32: Unsupported: 'binsof' in coverage select expression + 198 | illegal_bins lib_cross = binsof(a); + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:198:7: Unsupported: explicit coverage cross bins + 198 | illegal_bins lib_cross = binsof(a); + | ^~~~~~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:104:18: Ignoring unsupported coverage cross option: 'comment' + 104 | cross a, b { option.comment = "cross"; option.weight = 12; option.per_instance = 1; } + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:104:44: Ignoring unsupported coverage cross option: 'weight' + 104 | cross a, b { option.comment = "cross"; option.weight = 12; option.per_instance = 1; } + | ^~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:104:64: Ignoring unsupported coverage cross option: 'per_instance' + 104 | cross a, b { option.comment = "cross"; option.weight = 12; option.per_instance = 1; } + | ^~~~~~ +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:76:5: Logical operator COVERPOINT 'a' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 76 | coverpoint a iff (b); + | ^~~~~~~~~~ + ... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest + ... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message. +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:79:9: Logical operator COVERPOINT 'id' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 79 | id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:82:13: Logical operator COVERPOINT 'id' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 82 | int id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:85:17: Logical operator COVERPOINT 'id' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 85 | var int id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:88:19: Logical operator COVERPOINT 'id' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 88 | var [3:0] id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:91:15: Logical operator COVERPOINT 'id' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 91 | [3:0] id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Warning-WIDTHTRUNC: t/t_covergroup_unsup.v:94:16: Logical operator COVERPOINT 'id' expects 1 bit on the iff condition, but iff condition's VARREF 'b' generates 32 bits. + : ... note: In instance 't' + 94 | signed id: coverpoint a iff (b); + | ^~~~~~~~~~ +%Warning-COVERIGN: t/t_covergroup_unsup.v:98:11: Unsupported: cross of 'a' which is not a coverpoint (implicit coverpoint) + : ... note: In instance 't' + 98 | cross a, b iff (!rst); + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:101:11: Unsupported: cross of 'a' which is not a coverpoint (implicit coverpoint) + : ... note: In instance 't' + 101 | cross a, b iff (!rst) {} + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:104:11: Unsupported: cross of 'a' which is not a coverpoint (implicit coverpoint) + : ... note: In instance 't' + 104 | cross a, b { option.comment = "cross"; option.weight = 12; option.per_instance = 1; } + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:107:11: Unsupported: cross of 'a' which is not a coverpoint (implicit coverpoint) + : ... note: In instance 't' + 107 | cross a, b { + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:113:21: Unsupported: cross of 'a' which is not a coverpoint (implicit coverpoint) + : ... note: In instance 't' + 113 | my_cg_id: cross a, b iff (!rst); + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:177:11: Unsupported: cross of 'a' which is not a coverpoint (implicit coverpoint) + : ... note: In instance 't' + 177 | cross a, b { + | ^ +%Warning-COVERIGN: t/t_covergroup_unsup.v:209:5: Unsupported: 'covergroup' clocking event on member variable + : ... note: In instance 't' + 209 | covergroup cov1 @m_z; + | ^~~~~~~~~~ diff --git a/test_regress/t/t_covergroup_unsup_ign2.py b/test_regress/t/t_covergroup_unsup_ign2.py index 6efe69388..9ea990866 100755 --- a/test_regress/t/t_covergroup_unsup_ign2.py +++ b/test_regress/t/t_covergroup_unsup_ign2.py @@ -14,6 +14,7 @@ test.top_filename = "t/t_covergroup_unsup.v" test.lint(verilator_flags2=[ '--assert --coverage --Wwarn-UNSUPPORTED -Wno-fatal +define+T_COVERGROUP_UNSUP_IGN' -]) +], + expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_covergroup_wildcard_bins.out b/test_regress/t/t_covergroup_wildcard_bins.out new file mode 100644 index 000000000..6cedb321a --- /dev/null +++ b/test_regress/t/t_covergroup_wildcard_bins.out @@ -0,0 +1,5 @@ +cg.data.high: 1 +cg.data.low: 2 +cg.data.mid_range: 1 +cg.data.pattern: 2 +cg.data.wc_point: 2 diff --git a/test_regress/t/t_covergroup_wildcard_bins.py b/test_regress/t/t_covergroup_wildcard_bins.py new file mode 100755 index 000000000..6b0f1a9e4 --- /dev/null +++ b/test_regress/t/t_covergroup_wildcard_bins.py @@ -0,0 +1,15 @@ +#!/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 +import coverage_covergroup_common + +test.scenarios('vlt') + +coverage_covergroup_common.run(test) diff --git a/test_regress/t/t_covergroup_wildcard_bins.v b/test_regress/t/t_covergroup_wildcard_bins.v new file mode 100644 index 000000000..2f80f336b --- /dev/null +++ b/test_regress/t/t_covergroup_wildcard_bins.v @@ -0,0 +1,80 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Matthew Ballance +// SPDX-License-Identifier: CC0-1.0 + +// Test wildcard bins with don't care matching + +// verilog_format: off +`define stop $stop +`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t; + bit [7:0] data; + + covergroup cg; + coverpoint data { + // Match any value with upper nibble = 4'b0000 + wildcard bins low = {8'b0000_????}; + + // Match any value with upper nibble = 4'b1111 + wildcard bins high = {8'b1111_????}; + + // Match specific pattern with don't cares + wildcard bins pattern = {8'b10?0_11??}; + + // Non-wildcard range bin: [min:max] with min != max + bins mid_range = {[8'h40 : 8'h4F]}; + + // Wildcard bin using single-value range [5:5] (min==max, equivalent to a single value) + wildcard bins wc_point = {[8'd5 : 8'd5]}; + } + endgroup + + initial begin + cg cg_inst; + + cg_inst = new(); + + // Test low bin (upper nibble = 0000) + // Note: 8'b0000_0101 = 5 = 8'd5, so it matches BOTH 'low' and 'wc_point' + data = 8'b0000_0101; + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 40.0); // 2/5: low + wc_point hit simultaneously + + // Test high bin (upper nibble = 1111) + data = 8'b1111_1010; // Should match 'high' + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 60.0); // 3/5 + + // Test pattern bin (10?0_11??) + data = 8'b1000_1101; // Should match 'pattern' (10[0]0_11[0]1) + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 80.0); // 4/5 + + // Verify another pattern match + data = 8'b1010_1111; // Should also match 'pattern' (10[1]0_11[1]1) + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 80.0); // 4/5 - same bin, no increase + + // Test mid_range bin: [0x40:0x4F] + data = 8'h45; // Should match 'mid_range' + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 100.0); // 5/5: all bins now hit + + // wc_point (value 5) was already hit in the first sample; confirm no regression + data = 8'd5; + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 100.0); + + // Verify non-matching value doesn't change coverage + data = 8'b0101_0101; // Shouldn't match any bin + cg_inst.sample(); + `checkr(cg_inst.get_inst_coverage(), 100.0); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_covergroup_with_function_foo_bad.out b/test_regress/t/t_covergroup_with_function_foo_bad.out index be309ea15..dbbe6ec1a 100644 --- a/test_regress/t/t_covergroup_with_function_foo_bad.out +++ b/test_regress/t/t_covergroup_with_function_foo_bad.out @@ -1,5 +1,5 @@ -%Error: t/t_covergroup_with_function_foo_bad.v:9:35: Coverage sampling function must be named 'sample' - 9 | covergroup cg_bad with function foo(int x); +%Error: t/t_covergroup_with_function_foo_bad.v:8:35: Coverage sampling function must be named 'sample' + 8 | covergroup cg_bad with function foo(int x); | ^~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_with_function_foo_bad.v b/test_regress/t/t_covergroup_with_function_foo_bad.v index 5d4c91d33..23b152c48 100644 --- a/test_regress/t/t_covergroup_with_function_foo_bad.v +++ b/test_regress/t/t_covergroup_with_function_foo_bad.v @@ -4,7 +4,6 @@ // SPDX-FileCopyrightText: 2025 Antmicro // SPDX-License-Identifier: CC0-1.0 -/* verilator lint_off COVERIGN */ module t; covergroup cg_bad with function foo(int x); endgroup diff --git a/test_regress/t/t_covergroup_with_sample_args.py b/test_regress/t/t_covergroup_with_sample_args.py deleted file mode 100755 index 10ad7f0de..000000000 --- a/test_regress/t/t_covergroup_with_sample_args.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/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: 2025 Wilson Snyder -# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 - -import vltest_bootstrap - -test.scenarios('vlt') - -test.compile() - -test.passes() diff --git a/test_regress/t/t_covergroup_with_sample_args.v b/test_regress/t/t_covergroup_with_sample_args.v deleted file mode 100644 index a89b6de38..000000000 --- a/test_regress/t/t_covergroup_with_sample_args.v +++ /dev/null @@ -1,17 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro -// SPDX-License-Identifier: CC0-1.0 - -/* verilator lint_off COVERIGN */ -module t; - covergroup cg_with_sample(int init_val) with function sample (int addr, bit is_read); - endgroup - - cg_with_sample cov1 = new(42); - - function void run(); - cov1.sample(16, 1'b1); - endfunction -endmodule diff --git a/test_regress/t/t_covergroup_with_sample_args_default.py b/test_regress/t/t_covergroup_with_sample_args_default.py deleted file mode 100755 index 10ad7f0de..000000000 --- a/test_regress/t/t_covergroup_with_sample_args_default.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/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: 2025 Wilson Snyder -# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 - -import vltest_bootstrap - -test.scenarios('vlt') - -test.compile() - -test.passes() diff --git a/test_regress/t/t_covergroup_with_sample_args_default.v b/test_regress/t/t_covergroup_with_sample_args_default.v deleted file mode 100644 index 16ccf987b..000000000 --- a/test_regress/t/t_covergroup_with_sample_args_default.v +++ /dev/null @@ -1,18 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro -// SPDX-License-Identifier: CC0-1.0 - -/* verilator lint_off COVERIGN */ -module t; - covergroup cg_with_sample(int init) with function sample (int addr, bit is_read = 1'b0); - endgroup - - cg_with_sample cov1 = new(7); - - function void run(); - cov1.sample(5); - cov1.sample(6, 1'b1); - endfunction -endmodule diff --git a/test_regress/t/t_covergroup_with_sample_args_too_few_bad.out b/test_regress/t/t_covergroup_with_sample_args_too_few_bad.out index 156582a69..809c6f855 100644 --- a/test_regress/t/t_covergroup_with_sample_args_too_few_bad.out +++ b/test_regress/t/t_covergroup_with_sample_args_too_few_bad.out @@ -1,6 +1,6 @@ -%Error: t/t_covergroup_with_sample_args_too_few_bad.v:16:10: Missing argument on non-defaulted argument 'is_read' in function call to FUNC 'sample' +%Error: t/t_covergroup_with_sample_args_too_few_bad.v:15:10: Missing argument on non-defaulted argument 'is_read' in function call to FUNC 'sample' : ... note: In instance 't' - 16 | cov1.sample(1); + 15 | cov1.sample(1); | ^~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_with_sample_args_too_few_bad.v b/test_regress/t/t_covergroup_with_sample_args_too_few_bad.v index f58dd1fe8..e85dd76d6 100644 --- a/test_regress/t/t_covergroup_with_sample_args_too_few_bad.v +++ b/test_regress/t/t_covergroup_with_sample_args_too_few_bad.v @@ -4,7 +4,6 @@ // SPDX-FileCopyrightText: 2025 Antmicro // SPDX-License-Identifier: CC0-1.0 -/* verilator lint_off COVERIGN */ module t; covergroup cg_with_sample(int init) with function sample (int addr, bit is_read); endgroup diff --git a/test_regress/t/t_covergroup_with_sample_args_too_many_bad.out b/test_regress/t/t_covergroup_with_sample_args_too_many_bad.out index a42bb7bda..2c37fce5a 100644 --- a/test_regress/t/t_covergroup_with_sample_args_too_many_bad.out +++ b/test_regress/t/t_covergroup_with_sample_args_too_many_bad.out @@ -4,6 +4,6 @@ | ^~~~~~ : ... Location of function 'sample' declaration: 9 | covergroup cg_with_sample(int init) with function sample (int addr, bit is_read = 1'b0); - | ^~~~~~~~~~~~~~ + | ^~~~~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_with_sample_args_too_many_bad.py b/test_regress/t/t_covergroup_with_sample_args_too_many_bad.py index 1d5ccb8f4..578412ac0 100755 --- a/test_regress/t/t_covergroup_with_sample_args_too_many_bad.py +++ b/test_regress/t/t_covergroup_with_sample_args_too_many_bad.py @@ -9,8 +9,8 @@ import vltest_bootstrap -test.scenarios('linter') +test.scenarios('simulator') -test.lint(fails=True, expect_filename=test.golden_filename) +test.compile(fails=True, expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_covergroup_with_sample_args_too_many_bad.v b/test_regress/t/t_covergroup_with_sample_args_too_many_bad.v index 718188ae6..232f57b11 100644 --- a/test_regress/t/t_covergroup_with_sample_args_too_many_bad.v +++ b/test_regress/t/t_covergroup_with_sample_args_too_many_bad.v @@ -1,10 +1,10 @@ // DESCRIPTION: Verilator: Verilog Test module // -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-FileCopyrightText: 2025 Wilson Snyder // SPDX-License-Identifier: CC0-1.0 -/* verilator lint_off COVERIGN */ module t; covergroup cg_with_sample(int init) with function sample (int addr, bit is_read = 1'b0); endgroup diff --git a/test_regress/t/t_covergroup_with_sample_namedargs.py b/test_regress/t/t_covergroup_with_sample_namedargs.py deleted file mode 100755 index 10ad7f0de..000000000 --- a/test_regress/t/t_covergroup_with_sample_namedargs.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/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: 2025 Wilson Snyder -# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 - -import vltest_bootstrap - -test.scenarios('vlt') - -test.compile() - -test.passes() diff --git a/test_regress/t/t_covergroup_with_sample_namedargs.v b/test_regress/t/t_covergroup_with_sample_namedargs.v deleted file mode 100644 index 0aa2c2e4e..000000000 --- a/test_regress/t/t_covergroup_with_sample_namedargs.v +++ /dev/null @@ -1,15 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro -// SPDX-License-Identifier: CC0-1.0 - -/* verilator lint_off COVERIGN */ -module t; - covergroup cgN with function sample (int addr, bit is_read); - endgroup - cgN cov = new(); - function void run(); - cov.sample(.addr(11), .is_read(1'b1)); - endfunction -endmodule diff --git a/test_regress/t/t_covergroup_with_sample_zeroargs.py b/test_regress/t/t_covergroup_with_sample_zeroargs.py deleted file mode 100755 index 10ad7f0de..000000000 --- a/test_regress/t/t_covergroup_with_sample_zeroargs.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/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: 2025 Wilson Snyder -# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 - -import vltest_bootstrap - -test.scenarios('vlt') - -test.compile() - -test.passes() diff --git a/test_regress/t/t_covergroup_with_sample_zeroargs.v b/test_regress/t/t_covergroup_with_sample_zeroargs.v deleted file mode 100644 index 208a4e819..000000000 --- a/test_regress/t/t_covergroup_with_sample_zeroargs.v +++ /dev/null @@ -1,15 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro -// SPDX-License-Identifier: CC0-1.0 - -/* verilator lint_off COVERIGN */ -module t; - covergroup cg0 with function sample (); - endgroup - cg0 cov = new(); - function void run(); - cov.sample(); - endfunction -endmodule diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index d551f8faa..b65ab991e 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -754,6 +754,229 @@ module Vt_debug_emitv_t; end restrict (@(posedge clk) ##1 a[0] ); + logic [2:0] cg_sig; + logic [1:0] cg_sig2; + covergroup Vt_debug_emitv_cg_basic; + function new; + cp_sig: coverpoint cg_sig { + bins low = {['sh0:'sh3]}; + bins high = {['sh4:'sh6]}; + bins multi = {'sh0, 'sh1, 'sh2}; + bins dflt = default; + ignore_bins ign = {'sh7}; + illegal_bins ill = {'sh5}; + }; + cp_options: coverpoint cg_sig2 { + + ???? // COVEROPTION + 'sh2}; + endfunction + int signed __Vint; + struct { + string name; + int signed weight; + int signed goal; + string comment; + int signed at_least; + int signed auto_bin_max; + int signed cross_num_print_missing; + bit cross_retain_auto_bins; + bit detect_overlap; + bit per_instance; + bit get_inst_coverage; + } option; + struct { + int signed weight; + int signed goal; + string comment; + bit strobe; + bit merge_instances; + bit distribute_first; + real real_interval; + } type_option; + function sample; + endfunction + function start; + endfunction + function stop; + endfunction + function get_coverage; + get_coverage = /*CRESET*/; + input string covered_bins; + input string total_bins; + endfunction + function get_inst_coverage; + get_inst_coverage = /*CRESET*/; + input string covered_bins; + input string total_bins; + endfunction + function set_inst_name; + input string name; + endfunction + endcovergroup + covergroup Vt_debug_emitv_cg_clocked; + @(posedge clk)function new; + cp_cyc: coverpoint cg_sig; + endfunction + int signed __Vint; + struct { + string name; + int signed weight; + int signed goal; + string comment; + int signed at_least; + int signed auto_bin_max; + int signed cross_num_print_missing; + bit cross_retain_auto_bins; + bit detect_overlap; + bit per_instance; + bit get_inst_coverage; + } option; + struct { + int signed weight; + int signed goal; + string comment; + bit strobe; + bit merge_instances; + bit distribute_first; + real real_interval; + } type_option; + function sample; + endfunction + function start; + endfunction + function stop; + endfunction + function get_coverage; + get_coverage = /*CRESET*/; + input string covered_bins; + input string total_bins; + endfunction + function get_inst_coverage; + get_inst_coverage = /*CRESET*/; + input string covered_bins; + input string total_bins; + endfunction + function set_inst_name; + input string name; + endfunction + endcovergroup + covergroup Vt_debug_emitv_cg_trans; + function new; + cp_t: coverpoint cg_sig { + bins t01 = (3'h0 => 3'h1); + bins t12 = (3'h1 => 3'h2); + bins talt = (3'h2 => 3'h3), (3'h4 => 3'h5); + bins trep = (3'h0 => 3'h1); + bins tarr = (3'h0 => 3'h1), (3'h1 => 3'h2); + }; + endfunction + int signed __Vint; + struct { + string name; + int signed weight; + int signed goal; + string comment; + int signed at_least; + int signed auto_bin_max; + int signed cross_num_print_missing; + bit cross_retain_auto_bins; + bit detect_overlap; + bit per_instance; + bit get_inst_coverage; + } option; + struct { + int signed weight; + int signed goal; + string comment; + bit strobe; + bit merge_instances; + bit distribute_first; + real real_interval; + } type_option; + function sample; + endfunction + function start; + endfunction + function stop; + endfunction + function get_coverage; + get_coverage = /*CRESET*/; + input string covered_bins; + input string total_bins; + endfunction + function get_inst_coverage; + get_inst_coverage = /*CRESET*/; + input string covered_bins; + input string total_bins; + endfunction + function set_inst_name; + input string name; + endfunction + endcovergroup + covergroup Vt_debug_emitv_cg_cross; + function new; + cp_x: coverpoint cg_sig { + bins x0 = {'sh0}; + bins x1 = {'sh1}; + }; + cp_y: coverpoint cg_sig2 { + bins y0 = {'sh0}; + bins y1 = {'sh1}; + }; + cx: cross cp_x, cp_y; + endfunction + int signed __Vint; + struct { + string name; + int signed weight; + int signed goal; + string comment; + int signed at_least; + int signed auto_bin_max; + int signed cross_num_print_missing; + bit cross_retain_auto_bins; + bit detect_overlap; + bit per_instance; + bit get_inst_coverage; + } option; + struct { + int signed weight; + int signed goal; + string comment; + bit strobe; + bit merge_instances; + bit distribute_first; + real real_interval; + } type_option; + function sample; + endfunction + function start; + endfunction + function stop; + endfunction + function get_coverage; + get_coverage = /*CRESET*/; + input string covered_bins; + input string total_bins; + endfunction + function get_inst_coverage; + get_inst_coverage = /*CRESET*/; + input string covered_bins; + input string total_bins; + endfunction + function set_inst_name; + input string name; + endfunction + endcovergroup + Vt_debug_emitv_cg_basic cg_basic_instVt_debug_emitv_cg_basic; + cg_basic_inst = new(); + Vt_debug_emitv_cg_clocked cg_clocked_instVt_debug_emitv_cg_clocked; + cg_clocked_inst = new(); + Vt_debug_emitv_cg_trans cg_trans_instVt_debug_emitv_cg_trans; + cg_trans_inst = new(); + Vt_debug_emitv_cg_cross cg_cross_instVt_debug_emitv_cg_cross; + cg_cross_inst = new(); endmodule package Vt_debug_emitv_std; class Vt_debug_emitv_semaphore; @@ -821,11 +1044,7 @@ package Vt_debug_emitv_std; } state; VlProcessRef m_process; function self; - - ???? // CLASSREFDTYPE 'process' - p - ???? // CLASSREFDTYPE 'process' - ; + Vt_debug_emitv_process pVt_debug_emitv_process; begin : label5 self = /*CRESET*/; p = new(); @@ -860,11 +1079,7 @@ package Vt_debug_emitv_std; (status() == process::KILLED))); endtask task killQueue; - ref - ???? // CLASSREFDTYPE 'process' - processQueue[$] - ???? // CLASSREFDTYPE 'process' - ; + ref Vt_debug_emitv_process processQueue[$]Vt_debug_emitv_process; begin : unnamedblk1_1 int signed __Vrepeat0; __Vrepeat0 = processQueue.size(); @@ -988,6 +1203,57 @@ package Vt_debug_emitv___024unit; member = 'sh1; int signed rmember1; int signed rmember2; + covergroup Vt_debug_emitv___vlAnonCG_cg_in_class; + function new; + cp_m: coverpoint member { + bins one = {'sh1}; + bins two = {'sh2}; + }; + endfunction + int signed __Vint; + struct { + string name; + int signed weight; + int signed goal; + string comment; + int signed at_least; + int signed auto_bin_max; + int signed cross_num_print_missing; + bit cross_retain_auto_bins; + bit detect_overlap; + bit per_instance; + bit get_inst_coverage; + } option; + struct { + int signed weight; + int signed goal; + string comment; + bit strobe; + bit merge_instances; + bit distribute_first; + real real_interval; + } type_option; + function sample; + endfunction + function start; + endfunction + function stop; + endfunction + function get_coverage; + get_coverage = /*CRESET*/; + input string covered_bins; + input string total_bins; + endfunction + function get_inst_coverage; + get_inst_coverage = /*CRESET*/; + input string covered_bins; + input string total_bins; + endfunction + function set_inst_name; + input string name; + endfunction + endcovergroup + Vt_debug_emitv___vlAnonCG_cg_in_class cg_in_classVt_debug_emitv___vlAnonCG_cg_in_class; task method; if ((this != this)) begin $stop; @@ -999,11 +1265,7 @@ package Vt_debug_emitv___024unit; endfunction endclass function rand_restricted; - input - ???? // CLASSREFDTYPE 'Cls' - obj - ???? // CLASSREFDTYPE 'Cls' - ; + input Vt_debug_emitv_Cls objVt_debug_emitv_Cls; input int signed member; begin : label8 rand_restricted = randomize() with ( diff --git a/test_regress/t/t_debug_emitv.py b/test_regress/t/t_debug_emitv.py index b4f64ab37..29c864976 100755 --- a/test_regress/t/t_debug_emitv.py +++ b/test_regress/t/t_debug_emitv.py @@ -16,6 +16,10 @@ test.lint( # Likewise XML v_flags=[ "--lint-only", + # --Wno-COVERIGN: cg_trans uses a goto-repetition transition bin ([->N]); the count is + # unsupported (dropped) but the bin is still created with a non-NONE VTransRepType, which + # is what exercises VTransRepType::ascii() and AstCoverTransItem::dump()'s repType arm. + "--Wno-COVERIGN", "--dumpi-tree 9 --dumpi-V3EmitV 9 --debug-emitv", # Dev coverage of the V3EmitV code "--dump-graph --dumpi-tree-json 9 --no-json-ids" ]) diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index a98cf4929..edc1ec87b 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -23,6 +23,12 @@ class Cls; int member = 1; rand int rmember1; rand int rmember2; + covergroup cg_in_class; + cp_m: coverpoint member { + bins one = {1}; + bins two = {2}; + } + endgroup function void method; if (this != this) $stop; endfunction @@ -358,6 +364,62 @@ module t (/*AUTOARG*/ end restrict property (@(posedge clk) ##1 a[0]); + + // Covergroup constructs - exercise AstCovergroup, AstCoverpoint, AstCoverBin, AstCoverCross + logic [2:0] cg_sig; + logic [1:0] cg_sig2; + + // Basic covergroup: value bins, default bin, ignore_bins, illegal_bins, options + covergroup cg_basic; + option.per_instance = 1; + option.weight = 2; + cp_sig: coverpoint cg_sig { + bins low = {[0:3]}; + bins high = {[4:6]}; + bins multi = {0, 1, 2}; // multiple values in one bins (exercises EmitV range loop) + bins dflt = default; + ignore_bins ign = {7}; + illegal_bins ill = {5}; + } + // Coverpoint with per-coverpoint option but no explicit bins + cp_options: coverpoint cg_sig2 { + option.at_least = 2; + } + endgroup + + // Covergroup with clocking event + covergroup cg_clocked @(posedge clk); + cp_cyc: coverpoint cg_sig; + endgroup + + // Covergroup with transition bins + covergroup cg_trans; + cp_t: coverpoint cg_sig { + bins t01 = (3'b000 => 3'b001); + bins t12 = (3'b001 => 3'b010); + bins talt = (3'b010 => 3'b011), (3'b100 => 3'b101); // multiple transition sets + bins trep = (3'b000 => 3'b001 [->2]); // repetition op -> non-NONE VTransRepType (exercises ascii()) + bins tarr[] = (3'b000 => 3'b001), (3'b001 => 3'b010); // array transition bins -> m_isArray + } + endgroup + + // Covergroup with cross coverage + covergroup cg_cross; + cp_x: coverpoint cg_sig { + bins x0 = {0}; + bins x1 = {1}; + } + cp_y: coverpoint cg_sig2 { + bins y0 = {0}; + bins y1 = {1}; + } + cx: cross cp_x, cp_y; + endgroup + + cg_basic cg_basic_inst = new; + cg_clocked cg_clocked_inst = new; + cg_trans cg_trans_inst = new; + cg_cross cg_cross_inst = new; endmodule module sub(input logic clk); diff --git a/test_regress/t/t_debug_emitv_addrids.py b/test_regress/t/t_debug_emitv_addrids.py index d6f139618..dec2a3a47 100755 --- a/test_regress/t/t_debug_emitv_addrids.py +++ b/test_regress/t/t_debug_emitv_addrids.py @@ -15,6 +15,9 @@ test.top_filename = "t/t_debug_emitv.v" test.lint( # We also have dump-tree turned on, so hit a lot of AstNode*::dump() functions # Likewise XML - v_flags=["--lint-only --dumpi-tree 9 --dump-tree-addrids"]) + # --Wno-COVERIGN: shares t_debug_emitv.v, whose cg_trans uses a goto-repetition transition + # bin ([->N]); the count is unsupported (dropped) but the bin is still created with a + # non-NONE VTransRepType. + v_flags=["--lint-only --dumpi-tree 9 --dump-tree-addrids --Wno-COVERIGN"]) test.passes() diff --git a/test_regress/t/t_dist_warn_coverage.py b/test_regress/t/t_dist_warn_coverage.py index 8d4a15dc8..47835d2aa 100755 --- a/test_regress/t/t_dist_warn_coverage.py +++ b/test_regress/t/t_dist_warn_coverage.py @@ -129,6 +129,7 @@ for s in [ 'Unsupported: Per-bit array instantiations', 'Unsupported: Public functions with >64 bit outputs;', 'Unsupported: Public functions with return > 64 bits wide.', + 'Unsupported: Release statement argument is too complex array select', 'Unsupported: Replication to form', 'Unsupported: Shifting of by over 32-bit number isn\'t supported.', 'Unsupported: Size-changing cast on non-basic data type', @@ -154,6 +155,7 @@ for s in [ 'expected non-complex non-double', 'loading other than unpacked-array variable', 'loading other than unpacked/associative-array variable', + # These are safety limits requiring >1000 bins or >10000 members to trigger ]: Suppressed[s] = True diff --git a/test_regress/t/t_dump.v b/test_regress/t/t_dump.v index d24004b1d..3031ae298 100644 --- a/test_regress/t/t_dump.v +++ b/test_regress/t/t_dump.v @@ -151,4 +151,14 @@ module Test(/*AUTOARG*/ endcase end + logic [1:0] cg_v1; + logic [1:0] cg_v2; + covergroup cg @(posedge clk); + option.at_least = 2; + cp1: coverpoint cg_v1 { bins lo = {0}; } + cp2: coverpoint cg_v2; + cx: cross cp1, cp2; + endgroup + cg cg_inst = new; + endmodule diff --git a/test_regress/t/t_vlcov_covergroup.annotate.out b/test_regress/t/t_vlcov_covergroup.annotate.out new file mode 100644 index 000000000..6182eacd9 --- /dev/null +++ b/test_regress/t/t_vlcov_covergroup.annotate.out @@ -0,0 +1,551 @@ +// // verilator_coverage annotation + // 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 + + // Test cross coverage: 2-way, 3-way, and 4-way crosses + + // verilog_format: off + `define stop $stop + `define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); + // verilog_format: on + + module t; +%000001 logic [1:0] addr; +-000001 point: type=toggle comment=addr[0]:0->1 hier=top.t +-000000 point: type=toggle comment=addr[0]:1->0 hier=top.t +-000000 point: type=toggle comment=addr[1]:0->1 hier=top.t +-000000 point: type=toggle comment=addr[1]:1->0 hier=top.t +%000001 logic cmd; +-000001 point: type=toggle comment=cmd:0->1 hier=top.t +-000000 point: type=toggle comment=cmd:1->0 hier=top.t +%000001 logic mode; +-000001 point: type=toggle comment=mode:0->1 hier=top.t +-000000 point: type=toggle comment=mode:1->0 hier=top.t +%000001 logic parity; +-000001 point: type=toggle comment=parity:0->1 hier=top.t +-000000 point: type=toggle comment=parity:1->0 hier=top.t + + // 2-way cross + covergroup cg2; + cp_addr: coverpoint addr { +%000002 bins addr0 = {0}; +-000002 point: type=covergroup comment= hier=cg2.cp_addr.addr0 +%000002 bins addr1 = {1}; +-000002 point: type=covergroup comment= hier=cg2.cp_addr.addr1 + } + cp_cmd: coverpoint cmd { +%000002 bins read = {0}; +-000002 point: type=covergroup comment= hier=cg2.cp_cmd.read +%000002 bins write = {1}; +-000002 point: type=covergroup comment= hier=cg2.cp_cmd.write + } +%000001 addr_cmd: cross cp_addr, cp_cmd; +-000001 point: type=covergroup comment= hier=cg2.addr_cmd.addr0_x_read + // cross: [addr0, read] +-000001 point: type=covergroup comment= hier=cg2.addr_cmd.addr0_x_write + // cross: [addr0, write] +-000001 point: type=covergroup comment= hier=cg2.addr_cmd.addr1_x_read + // cross: [addr1, read] +-000001 point: type=covergroup comment= hier=cg2.addr_cmd.addr1_x_write + // cross: [addr1, write] + endgroup + + // 3-way cross + covergroup cg3; + cp_addr: coverpoint addr { +%000002 bins addr0 = {0}; +-000002 point: type=covergroup comment= hier=cg3.cp_addr.addr0 +%000001 bins addr1 = {1}; +-000001 point: type=covergroup comment= hier=cg3.cp_addr.addr1 +%000001 bins addr2 = {2}; +-000001 point: type=covergroup comment= hier=cg3.cp_addr.addr2 + } + cp_cmd: coverpoint cmd { +%000002 bins read = {0}; +-000002 point: type=covergroup comment= hier=cg3.cp_cmd.read +%000002 bins write = {1}; +-000002 point: type=covergroup comment= hier=cg3.cp_cmd.write + } + cp_mode: coverpoint mode { +%000002 bins normal = {0}; +-000002 point: type=covergroup comment= hier=cg3.cp_mode.normal +%000002 bins debug = {1}; +-000002 point: type=covergroup comment= hier=cg3.cp_mode.debug + } +%000001 addr_cmd_mode: cross cp_addr, cp_cmd, cp_mode; +-000000 point: type=covergroup comment= hier=cg3.addr_cmd_mode.addr0_x_read_x_debug + // cross: [addr0, read, debug] +-000001 point: type=covergroup comment= hier=cg3.addr_cmd_mode.addr0_x_read_x_normal + // cross: [addr0, read, normal] +-000001 point: type=covergroup comment= hier=cg3.addr_cmd_mode.addr0_x_write_x_debug + // cross: [addr0, write, debug] +-000000 point: type=covergroup comment= hier=cg3.addr_cmd_mode.addr0_x_write_x_normal + // cross: [addr0, write, normal] +-000000 point: type=covergroup comment= hier=cg3.addr_cmd_mode.addr1_x_read_x_debug + // cross: [addr1, read, debug] +-000000 point: type=covergroup comment= hier=cg3.addr_cmd_mode.addr1_x_read_x_normal + // cross: [addr1, read, normal] +-000000 point: type=covergroup comment= hier=cg3.addr_cmd_mode.addr1_x_write_x_debug + // cross: [addr1, write, debug] +-000001 point: type=covergroup comment= hier=cg3.addr_cmd_mode.addr1_x_write_x_normal + // cross: [addr1, write, normal] +-000001 point: type=covergroup comment= hier=cg3.addr_cmd_mode.addr2_x_read_x_debug + // cross: [addr2, read, debug] +-000000 point: type=covergroup comment= hier=cg3.addr_cmd_mode.addr2_x_read_x_normal + // cross: [addr2, read, normal] +-000000 point: type=covergroup comment= hier=cg3.addr_cmd_mode.addr2_x_write_x_debug + // cross: [addr2, write, debug] +-000000 point: type=covergroup comment= hier=cg3.addr_cmd_mode.addr2_x_write_x_normal + // cross: [addr2, write, normal] + endgroup + + // 4-way cross + covergroup cg4; + cp_addr: coverpoint addr { +%000002 bins addr0 = {0}; +-000002 point: type=covergroup comment= hier=cg4.cp_addr.addr0 +%000002 bins addr1 = {1}; +-000002 point: type=covergroup comment= hier=cg4.cp_addr.addr1 + } + cp_cmd: coverpoint cmd { +%000002 bins read = {0}; +-000002 point: type=covergroup comment= hier=cg4.cp_cmd.read +%000002 bins write = {1}; +-000002 point: type=covergroup comment= hier=cg4.cp_cmd.write + } + cp_mode: coverpoint mode { +%000002 bins normal = {0}; +-000002 point: type=covergroup comment= hier=cg4.cp_mode.normal +%000002 bins debug = {1}; +-000002 point: type=covergroup comment= hier=cg4.cp_mode.debug + } + cp_parity: coverpoint parity { +%000002 bins even = {0}; +-000002 point: type=covergroup comment= hier=cg4.cp_parity.even +%000002 bins odd = {1}; +-000002 point: type=covergroup comment= hier=cg4.cp_parity.odd + } +%000001 addr_cmd_mode_parity: cross cp_addr, cp_cmd, cp_mode, cp_parity; +-000000 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr0_x_read_x_debug_x_even + // cross: [addr0, read, debug, even] +-000000 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr0_x_read_x_debug_x_odd + // cross: [addr0, read, debug, odd] +-000001 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr0_x_read_x_normal_x_even + // cross: [addr0, read, normal, even] +-000000 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr0_x_read_x_normal_x_odd + // cross: [addr0, read, normal, odd] +-000001 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr0_x_write_x_debug_x_even + // cross: [addr0, write, debug, even] +-000000 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr0_x_write_x_debug_x_odd + // cross: [addr0, write, debug, odd] +-000000 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr0_x_write_x_normal_x_even + // cross: [addr0, write, normal, even] +-000000 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr0_x_write_x_normal_x_odd + // cross: [addr0, write, normal, odd] +-000000 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr1_x_read_x_debug_x_even + // cross: [addr1, read, debug, even] +-000001 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr1_x_read_x_debug_x_odd + // cross: [addr1, read, debug, odd] +-000000 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr1_x_read_x_normal_x_even + // cross: [addr1, read, normal, even] +-000000 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr1_x_read_x_normal_x_odd + // cross: [addr1, read, normal, odd] +-000000 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr1_x_write_x_debug_x_even + // cross: [addr1, write, debug, even] +-000000 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr1_x_write_x_debug_x_odd + // cross: [addr1, write, debug, odd] +-000000 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr1_x_write_x_normal_x_even + // cross: [addr1, write, normal, even] +-000001 point: type=covergroup comment= hier=cg4.addr_cmd_mode_parity.addr1_x_write_x_normal_x_odd + // cross: [addr1, write, normal, odd] + endgroup + + // Cross with option set inside the cross body + covergroup cg5; + cp_addr: coverpoint addr { +%000001 bins addr0 = {0}; +-000001 point: type=covergroup comment= hier=cg5.cp_addr.addr0 +%000001 bins addr1 = {1}; +-000001 point: type=covergroup comment= hier=cg5.cp_addr.addr1 + } + cp_cmd: coverpoint cmd { +%000001 bins read = {0}; +-000001 point: type=covergroup comment= hier=cg5.cp_cmd.read +%000001 bins write = {1}; +-000001 point: type=covergroup comment= hier=cg5.cp_cmd.write + } +%000001 addr_cmd_opt: cross cp_addr, cp_cmd { +-000001 point: type=covergroup comment= hier=cg5.addr_cmd_opt.addr0_x_read + // cross: [addr0, read] +-000000 point: type=covergroup comment= hier=cg5.addr_cmd_opt.addr0_x_write + // cross: [addr0, write] +-000000 point: type=covergroup comment= hier=cg5.addr_cmd_opt.addr1_x_read + // cross: [addr1, read] +-000001 point: type=covergroup comment= hier=cg5.addr_cmd_opt.addr1_x_write + // cross: [addr1, write] + option.weight = 2; + } + endgroup + + // 2-way cross where one coverpoint uses a range bin + covergroup cg_range; + cp_addr: coverpoint addr { +%000002 bins lo_range = {[0:1]}; // range bin +-000002 point: type=covergroup comment= hier=cg_range.cp_addr.lo_range +%000002 bins hi_range = {[2:3]}; +-000002 point: type=covergroup comment= hier=cg_range.cp_addr.hi_range + } + cp_cmd: coverpoint cmd { +%000002 bins read = {0}; +-000002 point: type=covergroup comment= hier=cg_range.cp_cmd.read +%000002 bins write = {1}; +-000002 point: type=covergroup comment= hier=cg_range.cp_cmd.write + } +%000001 addr_cmd_range: cross cp_addr, cp_cmd; +-000001 point: type=covergroup comment= hier=cg_range.addr_cmd_range.hi_range_x_read + // cross: [hi_range, read] +-000001 point: type=covergroup comment= hier=cg_range.addr_cmd_range.hi_range_x_write + // cross: [hi_range, write] +-000001 point: type=covergroup comment= hier=cg_range.addr_cmd_range.lo_range_x_read + // cross: [lo_range, read] +-000001 point: type=covergroup comment= hier=cg_range.addr_cmd_range.lo_range_x_write + // cross: [lo_range, write] + endgroup + + // Cross where one coverpoint has ignore_bins - ignored values must not appear in cross bins + covergroup cg_ignore; + cp_addr: coverpoint addr { +%000001 ignore_bins ign = {3}; // addr=3 excluded from cross +-000001 point: type=covergroup comment= hier=cg_ignore.cp_addr.ign +%000002 bins a0 = {0}; +-000002 point: type=covergroup comment= hier=cg_ignore.cp_addr.a0 +%000002 bins a1 = {1}; +-000002 point: type=covergroup comment= hier=cg_ignore.cp_addr.a1 + } + cp_cmd: coverpoint cmd { +%000003 bins read = {0}; +-000003 point: type=covergroup comment= hier=cg_ignore.cp_cmd.read +%000002 bins write = {1}; +-000002 point: type=covergroup comment= hier=cg_ignore.cp_cmd.write + } +%000001 cross_ab: cross cp_addr, cp_cmd; +-000001 point: type=covergroup comment= hier=cg_ignore.cross_ab.a0_x_read + // cross: [a0, read] +-000001 point: type=covergroup comment= hier=cg_ignore.cross_ab.a0_x_write + // cross: [a0, write] +-000001 point: type=covergroup comment= hier=cg_ignore.cross_ab.a1_x_read + // cross: [a1, read] +-000001 point: type=covergroup comment= hier=cg_ignore.cross_ab.a1_x_write + // cross: [a1, write] + endgroup + + // Cross with option.at_least set in the cross body + covergroup cg_at_least; + cp_addr: coverpoint addr { +%000001 bins addr0 = {0}; +-000001 point: type=covergroup comment= hier=cg_at_least.cp_addr.addr0 +%000001 bins addr1 = {1}; +-000001 point: type=covergroup comment= hier=cg_at_least.cp_addr.addr1 + } + cp_cmd: coverpoint cmd { +%000001 bins read = {0}; +-000001 point: type=covergroup comment= hier=cg_at_least.cp_cmd.read +%000001 bins write = {1}; +-000001 point: type=covergroup comment= hier=cg_at_least.cp_cmd.write + } +%000001 addr_cmd_al: cross cp_addr, cp_cmd { +-000001 point: type=covergroup comment= hier=cg_at_least.addr_cmd_al.addr0_x_read + // cross: [addr0, read] +-000000 point: type=covergroup comment= hier=cg_at_least.addr_cmd_al.addr0_x_write + // cross: [addr0, write] +-000000 point: type=covergroup comment= hier=cg_at_least.addr_cmd_al.addr1_x_read + // cross: [addr1, read] +-000001 point: type=covergroup comment= hier=cg_at_least.addr_cmd_al.addr1_x_write + // cross: [addr1, write] + option.at_least = 3; + } + endgroup + + // Cross with option.goal set in the cross body + covergroup cg_goal; + cp_addr: coverpoint addr { +%000001 bins addr0 = {0}; +-000001 point: type=covergroup comment= hier=cg_goal.cp_addr.addr0 +%000001 bins addr1 = {1}; +-000001 point: type=covergroup comment= hier=cg_goal.cp_addr.addr1 + } + cp_cmd: coverpoint cmd { +%000001 bins read = {0}; +-000001 point: type=covergroup comment= hier=cg_goal.cp_cmd.read +%000001 bins write = {1}; +-000001 point: type=covergroup comment= hier=cg_goal.cp_cmd.write + } +%000001 addr_cmd_goal: cross cp_addr, cp_cmd { +-000001 point: type=covergroup comment= hier=cg_goal.addr_cmd_goal.addr0_x_read + // cross: [addr0, read] +-000000 point: type=covergroup comment= hier=cg_goal.addr_cmd_goal.addr0_x_write + // cross: [addr0, write] +-000000 point: type=covergroup comment= hier=cg_goal.addr_cmd_goal.addr1_x_read + // cross: [addr1, read] +-000001 point: type=covergroup comment= hier=cg_goal.addr_cmd_goal.addr1_x_write + // cross: [addr1, write] + option.goal = 90; + } + endgroup + + // Cross with an unsupported option (option.per_instance) - Verilator warns and ignores it + covergroup cg_unsup_cross_opt; + cp_addr: coverpoint addr { +%000001 bins addr0 = {0}; +-000001 point: type=covergroup comment= hier=cg_unsup_cross_opt.cp_addr.addr0 +%000001 bins addr1 = {1}; +-000001 point: type=covergroup comment= hier=cg_unsup_cross_opt.cp_addr.addr1 + } + cp_cmd: coverpoint cmd { +%000001 bins read = {0}; +-000001 point: type=covergroup comment= hier=cg_unsup_cross_opt.cp_cmd.read +%000001 bins write = {1}; +-000001 point: type=covergroup comment= hier=cg_unsup_cross_opt.cp_cmd.write + } +%000001 addr_cmd_unsup: cross cp_addr, cp_cmd { +-000001 point: type=covergroup comment= hier=cg_unsup_cross_opt.addr_cmd_unsup.addr0_x_read + // cross: [addr0, read] +-000000 point: type=covergroup comment= hier=cg_unsup_cross_opt.addr_cmd_unsup.addr0_x_write + // cross: [addr0, write] +-000000 point: type=covergroup comment= hier=cg_unsup_cross_opt.addr_cmd_unsup.addr1_x_read + // cross: [addr1, read] +-000001 point: type=covergroup comment= hier=cg_unsup_cross_opt.addr_cmd_unsup.addr1_x_write + // cross: [addr1, write] + option.per_instance = 1; // unsupported for cross - expect COVERIGN warning + } + endgroup + + // Covergroup with an unnamed cross - the cross is reported under the default name "cross" + covergroup cg_unnamed_cross; +%000001 cp_a: coverpoint addr { bins a0 = {0}; bins a1 = {1}; } +-000001 point: type=covergroup comment= hier=cg_unnamed_cross.cp_a.a0 +-000001 point: type=covergroup comment= hier=cg_unnamed_cross.cp_a.a1 +%000001 cp_c: coverpoint cmd { bins read = {0}; bins write = {1}; } +-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 + // cross: [a0, read] +-000000 point: type=covergroup comment= hier=cg_unnamed_cross.__cross7.a0_x_write + // cross: [a0, write] +-000000 point: type=covergroup comment= hier=cg_unnamed_cross.__cross7.a1_x_read + // cross: [a1, read] +-000001 point: type=covergroup comment= hier=cg_unnamed_cross.__cross7.a1_x_write + // cross: [a1, write] + endgroup + +%000001 cg2 cg2_inst = new; +-000001 point: type=line comment=block hier=top.t +%000001 cg_ignore cg_ignore_inst = new; +-000001 point: type=line comment=block hier=top.t +%000001 cg_range cg_range_inst = new; +-000001 point: type=line comment=block hier=top.t +%000001 cg3 cg3_inst = new; +-000001 point: type=line comment=block hier=top.t +%000001 cg4 cg4_inst = new; +-000001 point: type=line comment=block hier=top.t +%000001 cg5 cg5_inst = new; +-000001 point: type=line comment=block hier=top.t +%000001 cg_at_least cg_at_least_inst = new; +-000001 point: type=line comment=block hier=top.t +%000001 cg_goal cg_goal_inst = new; +-000001 point: type=line comment=block hier=top.t +%000001 cg_unsup_cross_opt cg_unsup_cross_opt_inst = new; +-000001 point: type=line comment=block hier=top.t +%000001 cg_unnamed_cross cg_unnamed_cross_inst = new; +-000001 point: type=line comment=block hier=top.t + +%000001 initial begin +-000001 point: type=line comment=block hier=top.t + // Sample 2-way: hit all 4 combinations + // cg2: 2 cp bins + 2 cp bins + 4 cross bins = 8 bins total (flat count) +%000001 addr = 0; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x read +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg2_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t +%000001 addr = 1; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x write +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg2_inst.get_inst_coverage(), 75.0); // 6/8: all cp bins + 2 cross bins +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t +%000001 addr = 0; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x write +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg2_inst.get_inst_coverage(), 87.5); // 7/8: 3 cross bins hit +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t +%000001 addr = 1; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x read +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg2_inst.get_inst_coverage(), 100.0); // 8/8: all 4 cross bins hit +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t + + // Sample 3-way: hit 4 of 12 combinations + // cg3: 3+2+2+12=19 bins; 4 cross bins hit -> 11/19=57.9% (not clean; no intermediate checkr) +%000001 addr = 0; cmd = 0; mode = 0; cg3_inst.sample(); // addr0 x read x normal +-000001 point: type=line comment=block hier=top.t +%000001 addr = 1; cmd = 1; mode = 0; cg3_inst.sample(); // addr1 x write x normal +-000001 point: type=line comment=block hier=top.t +%000001 addr = 2; cmd = 0; mode = 1; cg3_inst.sample(); // addr2 x read x debug +-000001 point: type=line comment=block hier=top.t +%000001 addr = 0; cmd = 1; mode = 1; cg3_inst.sample(); // addr0 x write x debug +-000001 point: type=line comment=block hier=top.t + + // Sample 4-way: hit 4 of 16 combinations + // cg4: 2+2+2+2+16=24 bins; 4 cross bins hit -> 12/24=50% +%000001 addr = 0; cmd = 0; mode = 0; parity = 0; cg4_inst.sample(); +-000001 point: type=line comment=block hier=top.t +%000001 addr = 1; cmd = 1; mode = 0; parity = 1; cg4_inst.sample(); +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg4_inst.get_inst_coverage(), 37.5); // 9/24: all cp bins + 2 cross bins +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t +%000001 addr = 0; cmd = 1; mode = 1; parity = 0; cg4_inst.sample(); +-000001 point: type=line comment=block hier=top.t +%000001 addr = 1; cmd = 0; mode = 1; parity = 1; cg4_inst.sample(); +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg4_inst.get_inst_coverage(), 50.0); // 12/24: all cp bins + 4 cross bins +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t + + // Sample cg5 (cross with option.weight=2; weight is ignored in flat bin count) + // cg5: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75% +%000001 addr = 0; cmd = 0; cg5_inst.sample(); +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg5_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t +%000001 addr = 1; cmd = 1; cg5_inst.sample(); +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg5_inst.get_inst_coverage(), 75.0); // 6/8: all cp bins + 2 cross bins +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t + + // Sample cg_ignore: addr=3 is in ignore_bins so no cross bins for it + // cg_ignore: 2+2+4=8 bins total +%000001 addr = 0; cmd = 0; cg_ignore_inst.sample(); // a0 x read +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_ignore_inst.get_inst_coverage(), 37.5); // 3/8 +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t +%000001 addr = 1; cmd = 1; cg_ignore_inst.sample(); // a1 x write +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_ignore_inst.get_inst_coverage(), 75.0); // 6/8 +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t +%000001 addr = 0; cmd = 1; cg_ignore_inst.sample(); // a0 x write +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_ignore_inst.get_inst_coverage(), 87.5); // 7/8 +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t +%000001 addr = 1; cmd = 0; cg_ignore_inst.sample(); // a1 x read +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_ignore_inst.get_inst_coverage(), 100.0); // 8/8 +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t +%000001 addr = 3; cmd = 0; cg_ignore_inst.sample(); // ignored (addr=3 in ignore_bins) +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_ignore_inst.get_inst_coverage(), 100.0); // still 100% +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t + + // Sample range-bin cross + // cg_range: 2+2+4=8 bins +%000001 addr = 0; cmd = 0; cg_range_inst.sample(); // lo_range x read +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_range_inst.get_inst_coverage(), 37.5); // 3/8 +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t +%000001 addr = 2; cmd = 1; cg_range_inst.sample(); // hi_range x write +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_range_inst.get_inst_coverage(), 75.0); // 6/8 +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t +%000001 addr = 1; cmd = 1; cg_range_inst.sample(); // lo_range x write +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_range_inst.get_inst_coverage(), 87.5); // 7/8 +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t +%000001 addr = 3; cmd = 0; cg_range_inst.sample(); // hi_range x read +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_range_inst.get_inst_coverage(), 100.0); // 8/8 +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t + + // Sample cg_at_least (option.at_least in cross body; Verilator uses at_least=1 for bins) + // cg_at_least: 2+2+4=8 bins; 2 cross bins hit (count=1, at_least effectively 1) -> 6/8=75% +%000001 addr = 0; cmd = 0; cg_at_least_inst.sample(); // addr0 x read +-000001 point: type=line comment=block hier=top.t +%000001 addr = 1; cmd = 1; cg_at_least_inst.sample(); // addr1 x write +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_at_least_inst.get_inst_coverage(), 75.0); +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t + + // Sample cg_goal (option.goal in cross body; does not affect hit counting) + // cg_goal: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75% +%000001 addr = 0; cmd = 0; cg_goal_inst.sample(); // addr0 x read +-000001 point: type=line comment=block hier=top.t +%000001 addr = 1; cmd = 1; cg_goal_inst.sample(); // addr1 x write +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_goal_inst.get_inst_coverage(), 75.0); +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t + + // Sample cg_unsup_cross_opt + // cg_unsup_cross_opt: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75% +%000001 addr = 0; cmd = 0; cg_unsup_cross_opt_inst.sample(); // addr0 x read +-000001 point: type=line comment=block hier=top.t +%000001 addr = 1; cmd = 1; cg_unsup_cross_opt_inst.sample(); // addr1 x write +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_unsup_cross_opt_inst.get_inst_coverage(), 75.0); +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t + + // Sample cg_unnamed_cross + // cg_unnamed_cross: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75% +%000001 addr = 0; cmd = 0; cg_unnamed_cross_inst.sample(); // a0 x read +-000001 point: type=line comment=block hier=top.t +%000001 addr = 1; cmd = 1; cg_unnamed_cross_inst.sample(); // a1 x write +-000001 point: type=line comment=block hier=top.t +%000001 `checkr(cg_unnamed_cross_inst.get_inst_coverage(), 75.0); +-000001 point: type=line comment=block hier=top.t +-000000 point: type=line comment=block hier=top.t +-000001 point: type=line comment=else hier=top.t + +%000001 $write("*-* All Finished *-*\n"); +-000001 point: type=line comment=block hier=top.t +%000001 $finish; +-000001 point: type=line comment=block hier=top.t + end + + endmodule + diff --git a/test_regress/t/t_vlcov_covergroup.py b/test_regress/t/t_vlcov_covergroup.py new file mode 100755 index 000000000..05f12efa0 --- /dev/null +++ b/test_regress/t/t_vlcov_covergroup.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. 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: 2024 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.top_filename = "t/t_covergroup_cross.v" + +test.compile(verilator_flags2=['--coverage', '--Wno-COVERIGN']) + +test.execute() + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", + "--annotate", + test.obj_dir + "/annotated", + "--annotate-points", + test.obj_dir + "/coverage.dat", +], + verilator_run=True) + +test.files_identical(test.obj_dir + "/annotated/t_covergroup_cross.v", + "t/" + test.name + ".annotate.out") + +test.passes()