Compare commits

...

98 Commits

Author SHA1 Message Date
Matthew Ballance c4654e7bf5
Merge 1117a11d4d into e82bd52fa3 2026-04-20 13:09:09 +08:00
github action 1117a11d4d Apply 'make format' 2026-04-18 05:09:52 +00:00
Matthew Ballance 417c422ca2 Fix: preserve unsupported cross options in AST for line coverage tracking
The 'Enum fixes' refactor changed visit(AstCoverCross*) in V3LinkParse
to discard unsupported cross options (e.g. option.per_instance) without
adding an AstCoverOption node to optionsp. This broke V3Coverage line
tracking: since V3Coverage builds linescov by walking the AST, deleted
nodes are never visited and their source lines are excluded from the
coverage range.

Restore the original behaviour: always create an AstCoverOption node
and add it to optionsp regardless of whether the option type is
supported. The COVERIGN warning is still emitted for unsupported
options. V3Covergroup does not consume cross optionsp, so the extra
nodes are inert but allow V3Coverage to track the source line for
annotation (t_vlcov_covergroup annotation golden: line 151
'option.per_instance = 1;' now correctly annotated as %000001).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 05:09:52 +00:00
github action a3e51c7b36 Apply 'make format' 2026-04-18 05:09:52 +00:00
Matthew Ballance 6a8c045d7a Break up covergroup reporting logic
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 05:09:52 +00:00
Matthew Ballance db62c103de Add non-const range value
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 05:09:52 +00:00
Matthew Ballance cd1c8341e0 Add bin-count limit check
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 05:09:52 +00:00
Matthew Ballance 7a9a999f38 t_covergroup_autobins_bad: add non-constant range bin test cases
Add test cases for non-constant range bounds in coverpoint bins:
- bins b_range with non-constant lower bound
- bins b_range2 with non-constant upper bound

Update golden file to reflect new error locations and messages.

Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 05:09:52 +00:00
Matthew Ballance 934d66667e Convert local vars to user*p and replace direct deleteTree with pushDeletep
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:58 +00:00
Matthew Ballance 5990090d82 Resolve fuzzy match of sample
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:58 +00:00
Matthew Ballance b869cdeeea Enum fixes
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:58 +00:00
Matthew Ballance 9649a4cdb5 Remove unneeded .cpp test files
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:58 +00:00
Matthew Ballance ecbccaac6c Order enums
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:58 +00:00
Matthew Ballance ef90608281 Migrate to new test format
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:58 +00:00
Matthew Ballance d0d919c66c Correct accidentally-removed visit method (and debug output)
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:58 +00:00
Matthew Ballance 97d4920182 Show all cross bins
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:58 +00:00
github action 4f270c3e41 Apply 'make format' 2026-04-18 00:44:58 +00:00
Matthew Ballance 271ddc8044 Close code coverage gaps
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance be7a947899 More code-coverage updates
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance f8dbafc1f3 Code coverage improvements
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
github action 4ca3ff5ad3 Apply 'make format' 2026-04-18 00:44:57 +00:00
Matthew Ballance 10afed99aa Fix golden files after adding SPDX copyright line to .v files
Adding the SPDX-FileCopyrightText line to t_covergroup_auto_bin_max_bad.v
and t_covergroup_cross_opt_unsup.v shifted all line numbers by 1.
Update the corresponding .out golden files to match.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 49b4b3aac6 Fix missing SPDX-FileCopyrightText in covergroup test files
Add missing SPDX-FileCopyrightText headers to four test files that
were created without them, causing t_dist_copyright to fail.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 07e3337f80 covergroup: Close coverage gaps per patch_coverage_plan
- V3AstAttr.h: Add LCOV_EXCL_START/STOP and LCOV_EXCL_LINE to the
  unreachable methods of VCoverBinsType, VCoverOptionType, VTransRepType
  enum wrappers (default ctors, int ctors, operator en(), and the
  first/third operator== overloads that are never called by production code)

- V3AstNodes.cpp: Add LCOV_EXCL_BR_LINE to AstCoverTransItem::dump() and
  dumpJson() branches that check m_repType != VTransRepType::NONE; these are
  unreachable because all repetition bin forms ([*], [->], [=]) produce
  nullptr via BBCOVERIGN and the node is never constructed with non-NONE type

- V3Covergroup.cpp: Fix optp->prettyName() -> optp->optionType().ascii() in
  extractCoverpointOptions() error message so the option name is printed
  correctly instead of an empty string; the non-constant option-value error
  path (lines 199-202) is unreachable in practice — the SV spec requires
  option values to be constant expressions and V3Width enforces this before
  covergroup lowering

- V3LinkParse.cpp: Mark the else branch in visit(AstCoverCross*) with
  LCOV_EXCL_START/STOP; it is unreachable because all non-option items in
  cross_body_item produce nullptr via BBCOVERIGN and addNextNull filters nulls

- t_covergroup_unsup.v: Add ignore_bins/illegal_bins with [] array form to
  exercise the corresponding grammar branches separately from bins []

- t_covergroup_args.v: Add cg_clocked(int) covergroup (clocked + constructor
  args) to exercise the loop-past-sentinel branch in createCovergroupMethods
  (line 1137); add cg_toplevel at file scope to exercise the m_modp==null
  branch (line 1269 false branch) in visit(AstCovergroup*)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 084eee0761 Fix CI regressions post-rebase: test coverage and crash fixes
- V3Covergroup.cpp: fix crash when coverpoint option value is non-const
  (VN_AS → VN_CAST with graceful error emission)
- V3Width.cpp: remove non-ASCII em-dash in comment (whitespace check)
- t_covergroup_cross_opt_unsup: new test for COVERIGN on cross options
- t_covergroup_auto_bin_max_bad: new test for non-const auto_bin_max error
- t_vlcov_covergroup: add --Wno-COVERIGN flag; update golden files for
  new covergroups added to t_covergroup_cross.v

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance ee87094c35 More coverage-closure test updates
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance aa912537f3 Coverage fixes
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 77e2c0b4e2 Remove dead guards
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance feb11da024 Fix CI regressions from covergroup changes
V3DfgOptimizer: Remove SenItem special case added in commit 20970c7dd.
By the time the scoped DFG pass runs (pipeline line 439), V3Active has
already deleted all AstCovergroup nodes (line 423). The SenItem block was
therefore unnecessary — and harmful, as it set HasExtRdRefs on ALL clock
signals in sensitivity lists, preventing clock-gate deduplication.
Fixes t_opt_dedupe_clk_gate (got 4 deduped instead of expected 2).

V3Width: Restore constifyEdit guard in visit(AstInsideRange*) with null
checks. The guard was needed so that negative literals (e.g. AstNegate
wrapping a Const) in covergroup bin ranges are folded to constants before
V3Width visits them as expressions. Without it, V3Width sees a bare NEGATE
under INSIDERANGE with m_vup==nullptr and fires v3fatalSrc.
Fixes t_covergroup_negative_ranges.

Root-cause note: t_debug_width was failing due to the SenItem DFG change
above, not the constifyEdit. t_opt_const.v (used by t_debug_width) has no
inside-range expressions, so constifyEdit was never triggered for that test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance e578838c75 Increase dump coverage
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 64f6ab5069 Fix whitespace issues
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
github action 3ae3f52e2d Apply 'make format' 2026-04-18 00:44:57 +00:00
Matthew Ballance 7b0c443f65 Improve code coverage and add coverage report production to more tests
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 3088782e28 Checkpoint on closing coverage holes
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance ae4fb13879 Combine/minimize cross and transition tests
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance fe9d9846d6 Update t_vlcov_covergroup to produce its own data instead of relying on a cached file
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 8dd992efdb Adjust code patterns based on PR feedback
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
github action 4a16d39d72 Apply 'make format' 2026-04-18 00:44:57 +00:00
Matthew Ballance 3334d91e49 Fix t_debug_emitv golden file: update while-loop emit after upstream unroll change
Upstream commit 7607f0e7f changed how while loops over queue.size() are
emitted, adding an __Vrepeat0 temporary variable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 9601b74dab Fix golden file: update BASICDTYPE 'logic' to 'bit' after upstream four-state cleanup
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance e0aa381384 Added verilator_coverage test for covergroups
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
github action f07d4601f7 Apply 'make format' 2026-04-18 00:44:57 +00:00
Matthew Ballance c96c8cd4cc Fix warnings
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 957a238605 Move AST building from parser proper to LinkParse ; simplify Coverpoint construction
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 731d7d3152 Clean up unneeded dump()
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 810666f936 Refactor visitor structure to successively collect state vs having multiple flag-controlled passes
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 663b9845a2 Correct use of deleteTree
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 8a4b25d800 const-ify variables
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 8e2977b969 Add coverage report output for covergroup
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
github action bf0f8d6035 Apply 'make format' 2026-04-18 00:44:57 +00:00
Matthew Ballance 5ad6b62615 Update unsupported and clean up warnings
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 52e7874134 Update format of unsupported messages for consistency
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 8709d79092 Removed redundant local teml
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
github action 0fd0791c7c Apply 'make format' 2026-04-18 00:44:57 +00:00
Matthew Ballance e35916e95e Format fixes
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
github action 10881c40bc Apply 'make format' 2026-04-18 00:44:57 +00:00
Matthew Ballance 440d990634 Cleanup mis-merge ; move initial Ast manipulation out of Grammar
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 682556cd05 Comment new Ast classes, clean up accidentally added members, clean up a few interim workarounds, and adjust approach to stay out of sensitive paths
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance ac06e1f8be Clean up comments
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
github action 6df855cb91 Apply 'make format' 2026-04-18 00:44:57 +00:00
Matthew Ballance 1e16c8bcdd Aligned with C++14 and fixed a couple non-ascii characters
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 5633bb488d cleanup
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance c659fe900a Refactor loops in V3Active ; add three tests to vlt_all, so they run with threading
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 48e3b7888a Remove low-value tests ; Rename tests to better-align with their purpose ; Move to using coverage.dat to assess correctness
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 0cfc093dd9 Fix constifyParamsEdit regression in visit(AstInsideRange*)
The original code called V3Const::constifyParamsEdit() in
V3Width::visit(AstInsideRange*) to fold negative bin bounds
(e.g., NEGATE(100) -> -100) for covergroup bins. Since
constifyParamsEdit uses m_required=true, it errored on any
non-constant expression -- breaking the upstream test
t_constraint_array_index_simple which uses a foreach loop
variable 'i' in an inside constraint expression.

Fix: Remove the unconditional constifyParamsEdit calls from
V3Width::visit(AstInsideRange*). Instead:
- In V3Width: when m_vup is null (covergroup bin context,
  not an expression context), use constifyEdit (m_required=false)
  to fold constant arithmetic before iterating children.
- In V3Covergroup: call constifyEdit at the two sites that
  cast InsideRange bounds to AstConst for bin expansion.

This moves the covergroup-specific concern to the right layer
(V3Covergroup) while keeping V3Width safe for all contexts.

Also update golden files for t_covergroup_trans_3value,
t_covergroup_trans_restart, and t_covergroup_with_sample_args_too_many_bad
whose line numbers shifted by 1 due to the SPDX copyright header
additions in the previous commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 0f76f47864 Add missing SPDX-FileCopyrightText headers to covergroup test files
All new covergroup test files were missing the canonical
SPDX-FileCopyrightText line required by t_dist_copyright.
Add it above the existing SPDX-License-Identifier line in each file.
Also canonicalize t_covergroup_with_sample_args_too_many_bad.py to
use SPDX form instead of prose copyright comment.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance c25bb8ab17 Update golden files after rebase onto upstream/master
Upstream commit 07ed6aef5 'Tests: Verilog format' reformatted several
Verilog test files, shifting line/column numbers in compiler output.
Also pick up upstream's improved 'Too many arguments' error message
which now includes the declaration location.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 09b83a9f99 Fix non-ASCII characters in V3Covergroup.cpp comment
Replace Unicode em-dash and curly apostrophe with plain ASCII
equivalents to pass t_dist_whitespace CI check.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance d35df728d7 Restore t_covergroup_with_sample_args_too_many_bad test
This test was added upstream in a364704e3 and should not have been
deleted. The driver normalizes version strings (?v=X.Y -> ?v=latest)
and 'Exiting due to...' messages, so the upstream .out file is
compatible with our branch's error output as-is.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:57 +00:00
Matthew Ballance 7a307eaed6 Convert 'if' tests to assertion macros
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:56 +00:00
github action d7109df592 Apply 'make format' 2026-04-18 00:44:56 +00:00
Matthew Ballance cdfb990ac5 Cleaned up coverpoint-bin warning around 'excessive size'
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 880829e321 Update debug_emitv to include covergroup constructs ; update emit of covergroup debug content
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 7620ad9d7e Test clean-ups, resolve an internal error masked by the checking strategy, and add 'automatic' to remove warnings
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance d4595a0643 t_covergroup_with_sample_args_too_many_bad: remove redundant test
'sample' argument count checking is generic function/task behavior
already covered by t_func_bad and similar tests. Removing this test
eliminates fragility from upstream error message/caret changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 4868caf1cb V3Covergroup: initialize bin counters to 0, not random
Coverage bin counters (__Vcov_* member variables) were being initialized
with VL_SCOPED_RAND_RESET_I because they are plain uint32 members with no
explicit initial value. This caused 0% coverage readings at runtime when
randReset != 0 (as used in CI).

Fix all 5 bin counter AstVar creation sites to set an explicit valuep of
AstConst(0), so V3EmitCFunc emits '= 0U' instead of VL_SCOPED_RAND_RESET_I.

Affects: regular bins, default bins, array bins, transition array bins,
and cross bins.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 18d9b148e4 Fix V3Fork lifetime assertion by restoring V3LinkInc temp var lifetime
Commit b4244fc57 accidentally dropped the varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT)
line when reverting V3LinkInc.cpp to a merge base. This caused __Vincrement* temp
variables to have VLifetime::NONE, triggering V3Fork's assertion that all variables
inside fork...join_none blocks have a known lifetime.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
github action ff269866a6 Apply 'make format' 2026-04-18 00:44:56 +00:00
Matthew Ballance bbb7930b6a Correct indent on coverage tests ; add required test drivers ; back out CMakeLists pch change and adjust test that required it to no longer need it
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance b886baaca5 Whitespace fixes
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 698f66dcf3 Update golden file for covergroup fileline fix
The covergroup keyword fileline fix (using $<fl>1 instead of $<fl>2)
shifts error column from the name position to the 'covergroup' keyword.
Update the golden output for t_covergroup_in_class_duplicate_bad to match.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 3de8e398c0 Fix infinite recursion in visit(AstCovergroup*) and fileline
Two bugs in the covergroup -> AstClass transformation in V3LinkParse:

1. Infinite recursion: when a covergroup has a clocking event (e.g.
   `@(posedge clk)`), visit(AstCovergroup*) embeds a sentinel
   AstCovergroup node inside the new AstClass to carry the event for
   V3Covergroup.cpp. The subsequent iterate(cgClassp) call then visits
   the sentinel via visit(AstNodeModule*) -> iterateChildren -> which
   hits visit(AstCovergroup*) again, creating another class with another
   sentinel, infinitely.

   Fix: skip transformation in visit(AstCovergroup*) when already inside
   a covergroup class (m_modp->isCovergroup()), so sentinel nodes are
   left alone.

2. Wrong fileline column: AstCovergroup was created with the fileline of
   the identifier token ($<fl>2, the name position) rather than the
   'covergroup' keyword token ($<fl>1). This caused warnings about the
   covergroup to point to the name column instead of the keyword column.

   Fix: use $<fl>1 (the 'covergroup' keyword fileline) when constructing
   AstCovergroup in the parser.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 981268edd1 Fix: move removeStd() to after V3LinkParse
V3LinkParse's visit(AstCovergroup) creates std:: references and calls
setUsesStdPackage(). The previous removeStd() call happened before
V3LinkParse ran, so it deleted the std package before those references
were created, causing:

  %Error: Package/class for ':: reference' not found: 'std'

Move removeStd() to immediately after V3LinkParse::linkParse() inside
process() so the std package is only pruned after all parse-time
transformations have had a chance to declare their std:: usage.

Fixes test failures:
  - t_covergroup_option
  - t_covergroup_with_sample_args_too_many_bad

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
github action d789ae3297 Apply 'make format' 2026-04-18 00:44:56 +00:00
Matthew Ballance 09360d61fe Parser clean-up
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance e400d10603 verilog.y: fix accidental DEL omissions for wait_order, expect, property case
Three node deletions were accidentally dropped from the initial covergroup
commit as collateral damage:
- wait_order (no-stmt variant): restore DEL($3) for vrdList
- expect (no-stmt variant): restore DEL($3) for property_spec
- property_exprCaseIf yIF/yELSE: restore DEL($3) for condition expr

In all three cases $3 is an AstNode* that is not assigned to $$ and would
be leaked without the deletion.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 37b2b7db07 Gate V3Covergroup pass on useCovergroup() flag
Add v3Global.useCovergroup() flag (following the useRandSequence()
pattern) that is set to true when a covergroup_declaration is parsed.
Gate the V3Covergroup::covergroup() pass in Verilator.cpp on this flag
so the pass is skipped entirely for designs with no covergroups.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 7d941a917a V3Covergroup: fix coverpoint cleanup for unsupported covergroups
Two bugs fixed:
1. AstCReset: mark as ineligible for coverage expressions via
   isExprCoverageEligible() override, preventing verilogForTree
   from being called on CReset nodes (which has no V3EmitV handler).

2. generateCrossCode: when a cross references an unknown coverpoint,
   don't delete the cross node early. The caller's cleanup loop
   (in visit(AstClass*)) is responsible for deleting all coverpoints
   and crosses. Early deletion left a dangling pointer in m_coverCrosses
   causing a use-after-free segfault.

3. hasUnsupportedEvent path: added coverpoint/cross cleanup before
   returning so AST nodes don't reach downstream passes (V3EmitCFunc,
   V3MergeCond) which no longer have stub visitors for them.

All 60 covergroup tests now pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance fa371faff4 Refactoring node locations and enums ; Delete coverpoints after V3Covergroup so they don't accidentally hit the generators
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:56 +00:00
github action 832bcd0b45 Apply 'make format' 2026-04-18 00:44:56 +00:00
Matthew Ballance f5605a710b Revert V3Coverage.cpp to funccov-minimal merge base
The VLifetime::AUTOMATIC_EXPLICIT addition is not required for
functional coverage — all 60 covergroup tests pass without it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 6de7d603e1 Revert V3EmitV.cpp and V3LinkInc.cpp to funccov-minimal merge base
These changes are not required for functional coverage support and
should not be included in the upstream patch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance f9d57688be Adopt use of MemberMap
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 3c77283fe3 Rename funccov tests to covergroup
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 8b3e84d352 Refactoring before renaming tests
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
2026-04-18 00:44:56 +00:00
github action b5c5852e1c Apply 'make format' 2026-04-18 00:44:56 +00:00
Matthew Ballance 5087ee6aa7 CMake: detect PCH include flag for GCC/Clang in verilated.mk
Without this, CFG_CXXFLAGS_PCH_I was left empty in CMake builds, causing
GCC to treat the PCH filename as a linker input instead of applying it as
a -include flag, breaking compilation of any model that uses PCH.

This is a general CMake build fix (not funccov-specific); the same bug
exists in upstream CMakeLists.txt. It is included here because funccov's
cross-coverage tests expose the issue.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
Matthew Ballance 8a6c05870a Add function coverage (funccov) and covergroup support
Implement functional coverage collection via covergroups, coverpoints,
and cross coverage bins. Introduces V3CoverageFunctional pass and
verilated_funccov.h runtime support.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-18 00:44:56 +00:00
139 changed files with 6928 additions and 1001 deletions

View File

@ -129,9 +129,13 @@ verilator_coverage Arguments
.. option:: --filter-type <regex>
Skips records of coverage types that matches with <regex>
Possible values are `toggle`, `line`, `branch`, `expr`, `user` and
Possible values are `toggle`, `line`, `branch`, `expr`, `covergroup`, `user` 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
Displays a help summary, the program version, and exits.

View File

@ -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<user
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<Coverage>`.
Verilator also partially supports SystemVerilog functional coverage with
``covergroup``, ``coverpoint``, bins, cross coverage, and transition bins. See
:ref:`Functional Coverage<user 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.

View File

@ -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:`Line Coverage`
- :ref:`Toggle Coverage`
@ -192,22 +193,60 @@ 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.
.. code-block:: sv
module top;
logic [7:0] addr;
logic cmd;
// Define a covergroup
covergroup cg;
cp_addr: coverpoint addr {
bins low = {[0:127]};
bins high = {[128:255]};
}
cp_cmd: coverpoint cmd {
bins read = {0};
bins write = {1};
}
endgroup
// Instantiate the covergroup
cg cg_inst = new;
always @(posedge clk) begin
// Sample coverage explicitly
cg_inst.sample();
end
endmodule
.. _line coverage:

View File

@ -70,6 +70,7 @@ set(HEADERS
V3Control.h
V3Coverage.h
V3CoverageJoin.h
V3Covergroup.h
V3Dead.h
V3DebugBisect.h
V3Delayed.h
@ -201,6 +202,7 @@ set(HEADERS
V3WidthCommit.h
V3WidthRemove.h
VlcBucket.h
VlcCovergroup.h
VlcOptions.h
VlcPoint.h
VlcSource.h
@ -236,6 +238,7 @@ set(COMMON_SOURCES
V3Const__gen.cpp
V3Coverage.cpp
V3CoverageJoin.cpp
V3Covergroup.cpp
V3Dead.cpp
V3Delayed.cpp
V3Depth.cpp

View File

@ -249,6 +249,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Combine.o \
V3Common.o \
V3Coverage.o \
V3Covergroup.o \
V3CoverageJoin.o \
V3Dead.o \
V3Delayed.o \

View File

@ -619,11 +619,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);
}

View File

@ -1107,6 +1107,85 @@ 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 { WEIGHT, GOAL, AT_LEAST, AUTO_BIN_MAX, PER_INSTANCE, COMMENT, 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", "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<en>(_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 };

View File

@ -113,6 +113,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
@ -144,7 +145,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
}
@ -218,6 +220,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; }
@ -253,6 +257,19 @@ 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;
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; }
bool maybePointedTo() const override { return true; }
};
class AstNodeGen VL_NOT_FINAL : public AstNode {
// Generate construct
public:
@ -513,6 +530,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 = "")
@ -543,6 +561,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;
@ -618,6 +637,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 {
@ -758,13 +779,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_rawName; // 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_rawName{rawName}
, m_typeOption{typeOption} {
this->valuep(valuep);
}
@ -772,7 +796,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_rawName; }
VCoverOptionType optionType() const { return m_optType; }
bool typeOption() const { return m_typeOption; }
};
class AstClassExtends final : public AstNode {
@ -1020,6 +1045,142 @@ 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
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{isWildcard ? VCoverBinsType::BINS_WILDCARD
: (isIllegal ? VCoverBinsType::BINS_ILLEGAL
: (isIgnore ? VCoverBinsType::BINS_IGNORE
: VCoverBinsType::BINS_USER))} {
if (rangesp) 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 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} {
if (valuesp) 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) {
if (itemsp) 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} {
if (argsp) addArgsp(argsp);
if (sampleArgsp) addSampleArgsp(sampleArgsp);
if (membersp) 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
@ -2533,6 +2694,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);
if (binsp) 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 {
@ -2615,6 +2809,8 @@ class AstClass final : public AstNodeModule {
bool m_needRNG = false; // Need RNG, uses srandom/randomize
bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class
bool m_virtual = false; // Virtual class
// 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)
@ -2642,6 +2838,9 @@ public:
void needRNG(bool flag) { m_needRNG = flag; }
bool useVirtualPublic() const { return m_useVirtualPublic; }
void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; }
// 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);

View File

@ -3536,3 +3536,54 @@ const char* AstNot::widthMismatch() const VL_MT_STABLE {
BROKEN_RTN(lhsp()->widthMin() != widthMin());
return nullptr;
}
//######################################################################
// 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); }

View File

@ -797,7 +797,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);

1586
src/V3Covergroup.cpp Normal file

File diff suppressed because it is too large Load Diff

30
src/V3Covergroup.h Normal file
View File

@ -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

View File

@ -267,7 +267,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
if (nodep->sensp()) puts(" ");
iterateChildrenConst(nodep);
}
void visit(AstCReset* /*nodep*/) override { puts("/*CRESET*/"); }
void visit(AstCReset* nodep) override { puts("/*CRESET*/"); }
void visit(AstCase* nodep) override {
putfs(nodep, "");
if (nodep->priorityPragma()) puts("priority ");
@ -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());

View File

@ -130,6 +130,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
uint64_t m_currentHierBlockCost = 0; // Total cost of this hier block, used for scheduling
@ -213,6 +214,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; }
void saveJsonPtrFieldName(const std::string& fieldName);

View File

@ -1118,6 +1118,246 @@ class LinkParseVisitor final : public VNVisitor {
iterateChildren(nodep);
}
// 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
AstNode* const existingBodyp = newFuncp->stmtsp();
if (existingBodyp) existingBodyp->unlinkFrBackWithNext();
// Add function parameters and assignments
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);
newFuncp->addStmtsp(paramp);
AstNodeExpr* const lhsp = new AstParseRef{origVarp->fileline(), origVarp->name()};
AstNodeExpr* const rhsp = new AstParseRef{paramp->fileline(), paramp->name()};
newFuncp->addStmtsp(new AstAssign{origVarp->fileline(), lhsp, rhsp});
}
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};
if (sampleArgsp) {
for (AstNode* argp = sampleArgsp; 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 AstParseRef{origVarp->fileline(), origVarp->name()};
AstNodeExpr* const rhsp = new AstParseRef{paramp->fileline(), paramp->name()};
funcp->addStmtsp(new AstAssign{origVarp->fileline(), lhsp, rhsp});
}
}
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
nodep->replaceWith(cgClassp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
iterate(cgClassp);
}
void visit(AstCoverpoint* nodep) override {
cleanFileline(nodep);
// 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::UNKNOWN) {
optp->v3warn(COVERIGN,
"Ignoring unsupported coverage option: " + optp->prettyNameQ());
} else {
nodep->addOptionsp(new AstCoverOption{optp->fileline(), optp->optionType(),
optp->valuep()->cloneTree(false)});
}
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();
if (!(optType == VCoverOptionType::AT_LEAST || optType == VCoverOptionType::WEIGHT
|| optType == VCoverOptionType::GOAL || optType == VCoverOptionType::COMMENT)) {
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);

View File

@ -296,6 +296,7 @@ class CodeMotionAnalysisVisitor final : public VNVisitorConst {
}
// VISITORS
void visit(AstNode* nodep) override {
// Push a new stack entry at the start of a list, but only if the list is not a
// single element (this saves a lot of allocations in expressions)

View File

@ -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 <stack>
#include <vector>
class V3ParseGrammar final {
public:
@ -94,97 +96,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<AstNode, AstNode>(nodep, new AstStop{fileline, false});

View File

@ -1336,6 +1336,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());

View File

@ -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);
}
@ -961,8 +965,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()});

View File

@ -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
@ -1777,8 +1778,23 @@ 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()->v3error("option.auto_bin_max must be a constant expression");
}
}
// 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(AstPow* nodep) override {
@ -3420,7 +3436,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());
@ -7500,6 +7525,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 {

View File

@ -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"
@ -236,6 +237,10 @@ static void process() {
// Before we do dead code elimination and inlining, or we'll lose it.
if (v3Global.opt.coverage()) 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());

232
src/VlcCovergroup.h Normal file
View File

@ -0,0 +1,232 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: verilator_coverage: Covergroup data containers
//
// 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_VLCCOVERGROUP_H_
#define VERILATOR_VLCCOVERGROUP_H_
#include "config_build.h"
#include "verilatedos.h"
#include <iomanip>
#include <map>
#include <sstream>
#include <vector>
//********************************************************************
// VlcCgBin - One coverage bin within a coverpoint
class VlcCgBin final {
// MEMBERS
string m_name;
string m_binType; //< "ignore", "illegal", or "" (normal)
bool m_covered = false;
uint64_t m_count = 0;
public:
// CONSTRUCTORS
VlcCgBin(const string& name, const string& binType, bool covered, uint64_t count)
: m_name{name}
, m_binType{binType}
, m_covered{covered}
, m_count{count} {}
~VlcCgBin() = default;
// ACCESSORS
const string& name() const { return m_name; }
const string& binType() const { return m_binType; }
bool covered() const { return m_covered; }
uint64_t count() const { return m_count; }
};
//********************************************************************
// VlcCgCoverpoint - One coverpoint or cross within a covergroup type
class VlcCgCoverpoint final {
// MEMBERS
string m_name;
bool m_isCross = false;
std::vector<VlcCgBin> m_bins;
uint64_t m_normalTotal = 0;
uint64_t m_normalCovered = 0;
public:
// CONSTRUCTORS
VlcCgCoverpoint(const string& name, bool isCross)
: m_name{name}
, m_isCross{isCross} {}
~VlcCgCoverpoint() = default;
// ACCESSORS
const string& name() const { return m_name; }
bool isCross() const { return m_isCross; }
const std::vector<VlcCgBin>& bins() const { return m_bins; }
uint64_t normalTotal() const { return m_normalTotal; }
uint64_t normalCovered() const { return m_normalCovered; }
// METHODS
void addBin(const string& name, const string& binType, bool covered, uint64_t count) {
m_bins.emplace_back(name, binType, covered, count);
if (binType.empty()) {
++m_normalTotal;
if (covered) ++m_normalCovered;
}
}
};
//********************************************************************
// VlcCovergroupType - One covergroup type aggregated across all tests
class VlcCovergroupType final {
// MEMBERS
string m_typeName;
string m_filename;
int m_lineno = 0;
std::vector<VlcCgCoverpoint> m_coverpoints;
std::map<string, size_t> m_cpIndex; //< Coverpoint name -> index into m_coverpoints
public:
// CONSTRUCTORS
VlcCovergroupType(const string& typeName, const string& filename, int lineno)
: m_typeName{typeName}
, m_filename{filename}
, m_lineno{lineno} {}
~VlcCovergroupType() = default;
// ACCESSORS
const string& typeName() const { return m_typeName; }
const string& filename() const { return m_filename; }
int lineno() const { return m_lineno; }
const std::vector<VlcCgCoverpoint>& coverpoints() const { return m_coverpoints; }
// METHODS
VlcCgCoverpoint& findNewCoverpoint(const string& name, bool isCross) {
const auto it = m_cpIndex.find(name);
if (it != m_cpIndex.end()) return m_coverpoints[it->second];
const size_t idx = m_coverpoints.size();
m_cpIndex[name] = idx;
m_coverpoints.emplace_back(name, isCross);
return m_coverpoints[idx];
}
uint64_t normalTotal() const {
uint64_t total = 0;
for (const auto& cp : m_coverpoints) total += cp.normalTotal();
return total;
}
uint64_t normalCovered() const {
uint64_t covered = 0;
for (const auto& cp : m_coverpoints) covered += cp.normalCovered();
return covered;
}
};
//********************************************************************
// VlcCovergroups - Container of all covergroup types
class VlcCovergroups final {
// MEMBERS
std::map<string, VlcCovergroupType> m_cgMap; //< Sorted by type name
// METHODS - FORMATTING
static string pctStr(uint64_t covered, uint64_t total) {
std::ostringstream oss;
const double pct = (total == 0) ? 100.0 : (100.0 * covered / total);
oss << std::fixed << std::setprecision(2) << pct;
return oss.str();
}
public:
// CONSTRUCTORS
VlcCovergroups() = default;
~VlcCovergroups() = default;
// METHODS
VlcCovergroupType& findNewCovergroupType(const string& typeName, const string& filename,
int lineno) {
const auto it = m_cgMap.find(typeName);
if (it != m_cgMap.end()) return it->second;
return m_cgMap.emplace(typeName, VlcCovergroupType{typeName, filename, lineno})
.first->second;
}
void dump(std::ostream& os) const {
uint64_t grandTotal = 0, grandCovered = 0, grandIgnored = 0, grandIllegal = 0;
for (const auto& cgPair : m_cgMap) {
const VlcCovergroupType& cg = cgPair.second;
grandTotal += cg.normalTotal();
grandCovered += cg.normalCovered();
for (const auto& cp : cg.coverpoints()) {
for (const auto& bin : cp.bins()) {
if (bin.binType() == "ignore")
++grandIgnored;
else if (bin.binType() == "illegal")
++grandIllegal;
}
}
}
const string divider(78, '-');
os << "COVERGROUP COVERAGE REPORT\n";
os << "==========================\n";
os << "\n";
os << "TOTAL: " << grandCovered << "/" << grandTotal << " bins covered ("
<< pctStr(grandCovered, grandTotal) << "%)\n";
if (grandIgnored || grandIllegal)
os << " (" << grandIgnored << " ignored, " << grandIllegal << " illegal)\n";
for (const auto& cgPair : m_cgMap) {
const VlcCovergroupType& cg = cgPair.second;
const uint64_t cgTotal = cg.normalTotal();
const uint64_t cgCovered = cg.normalCovered();
os << "\n" << divider << "\n";
os << "Covergroup Type: " << cg.typeName() << " [" << cg.filename() << ":"
<< cg.lineno() << "]\n";
os << " Type Coverage: " << cgCovered << "/" << cgTotal << " bins ("
<< pctStr(cgCovered, cgTotal) << "%)\n";
for (const auto& cp : cg.coverpoints()) {
os << "\n";
os << " " << (cp.isCross() ? "Cross" : "Coverpoint") << ": " << cp.name() << "\n";
os << " Coverage: " << cp.normalCovered() << "/" << cp.normalTotal()
<< " bins (" << pctStr(cp.normalCovered(), cp.normalTotal()) << "%)\n";
os << " Bins:\n";
size_t maxNameLen = 0;
for (const auto& bin : cp.bins())
if (bin.name().size() > maxNameLen) maxNameLen = bin.name().size();
for (const auto& bin : cp.bins()) {
const char* status;
if (bin.binType() == "ignore")
status = "IGNORE ";
else if (bin.binType() == "illegal")
status = "ILLEGAL";
else if (bin.covered())
status = "COVERED";
else
status = "ZERO ";
os << " " << status << " " << std::left
<< std::setw(static_cast<int>(maxNameLen)) << bin.name() << std::right
<< " " << bin.count() << " hits\n";
}
}
}
os << "\n" << divider << "\n";
}
};
//######################################################################
#endif // guard

View File

@ -66,6 +66,7 @@ void VlcOptions::parseOptsList(int argc, char** argv) {
DECL_OPTION("-annotate-all", OnOff, &m_annotateAll);
DECL_OPTION("-annotate-min", Set, &m_annotateMin);
DECL_OPTION("-annotate-points", OnOff, &m_annotatePoints);
DECL_OPTION("-covergroup", OnOff, &m_covergroup);
DECL_OPTION("-debug", CbCall, []() { V3Error::debugDefault(3); });
DECL_OPTION("-debugi", CbVal, [](int v) { V3Error::debugDefault(v); });
DECL_OPTION("-filter-type", Set, &m_filterType);
@ -143,6 +144,8 @@ int main(int argc, char** argv) {
V3Error::abortIfWarnings();
if (!top.opt.annotateOut().empty()) top.annotate(top.opt.annotateOut());
if (top.opt.covergroup()) top.covergroup();
if (top.opt.rank()) {
top.rank();
top.tests().dump(false);

View File

@ -39,6 +39,7 @@ class VlcOptions final {
bool m_annotateAll = false; // main switch: --annotate-all
int m_annotateMin = 10; // main switch: --annotate-min I<count>
bool m_annotatePoints = false; // main switch: --annotate-points
bool m_covergroup = false; // main switch: --covergroup
string m_filterType = "*"; // main switch: --filter-type
VlStringSet m_readFiles; // main switch: --read
bool m_rank = false; // main switch: --rank
@ -67,6 +68,7 @@ public:
int annotateMin() const { return m_annotateMin; }
bool countOk(uint64_t count) const { return count >= static_cast<uint64_t>(m_annotateMin); }
bool annotatePoints() const { return m_annotatePoints; }
bool covergroup() const { return m_covergroup; }
bool rank() const { return m_rank; }
bool unlink() const { return m_unlink; }
string writeFile() const { return m_writeFile; }

View File

@ -65,6 +65,11 @@ public:
string comment() const { return keyExtract(VL_CIK_COMMENT, m_name.c_str()); }
string hier() const { return keyExtract(VL_CIK_HIER, m_name.c_str()); }
string type() const { return typeExtract(m_name.c_str()); }
// Covergroup-specific key accessors (long keys, no short-key alias)
string page() const { return keyExtract("page", m_name.c_str()); }
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 thresh() const {
// string as maybe ""
return keyExtract(VL_CIK_THRESH, m_name.c_str());

View File

@ -25,6 +25,9 @@
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <map>
#include <sstream>
#include <string>
#include <vector>
@ -205,6 +208,45 @@ void VlcTop::rank() {
}
}
void VlcTop::covergroup() {
UINFO(2, "covergroup...");
covergroupCalc();
m_covergroups.dump(std::cout);
}
void VlcTop::covergroupCalc() {
// Collect covergroup points from all loaded coverage data into m_covergroups
for (const auto& nameNum : m_points) {
const VlcPoint& pt = m_points.pointNumber(nameNum.second);
if (pt.type() != "covergroup") continue;
const std::string page = pt.page();
// Page format: "v_covergroup/<cgTypeName>"
const std::string pagePrefix = "v_covergroup/";
if (page.size() <= pagePrefix.size()) continue;
const std::string cgTypeName = page.substr(pagePrefix.size());
// Parse hier: "<cg_type>.<cp_name>.<bin_name>"
const std::string hier = pt.hier();
const size_t dot1 = hier.find('.');
if (dot1 == std::string::npos) continue;
const size_t dot2 = hier.find('.', dot1 + 1);
if (dot2 == std::string::npos) continue;
const std::string cpName = hier.substr(dot1 + 1, dot2 - dot1 - 1);
const std::string binName = hier.substr(dot2 + 1);
VlcCovergroupType& cg
= m_covergroups.findNewCovergroupType(cgTypeName, pt.filename(), pt.lineno());
VlcCgCoverpoint& cp = cg.findNewCoverpoint(cpName, pt.isCross());
// Threshold: use per-bin thresh key (option.at_least) if present, else 1 (SV default)
const std::string threshStr = pt.thresh();
const uint64_t binThresh = threshStr.empty() ? 1 : std::stoull(threshStr);
const uint64_t count = pt.count();
cp.addBin(binName, pt.binType(), count >= binThresh, count);
}
}
//######################################################################
void VlcTop::annotateCalc() {

View File

@ -20,6 +20,7 @@
#include "config_build.h"
#include "verilatedos.h"
#include "VlcCovergroup.h"
#include "VlcOptions.h"
#include "VlcPoint.h"
#include "VlcSource.h"
@ -37,11 +38,13 @@ private:
VlcTests m_tests; //< List of all tests (all coverage files)
VlcPoints m_points; //< List of all points
VlcSources m_sources; //< List of all source files to annotate
VlcCovergroups m_covergroups; //< Covergroup analysis data
// METHODS
void annotateCalc();
void annotateCalcNeeded();
void annotateOutputFiles(const string& dirname);
void covergroupCalc();
public:
// CONSTRUCTORS
@ -55,6 +58,7 @@ public:
// METHODS
void annotate(const string& dirname);
void covergroup();
void readCoverage(const string& filename, bool nonfatal = false);
void writeCoverage(const string& filename);
void writeInfo(const string& filename);

View File

@ -3958,16 +3958,16 @@ value_range<nodeExprp>: // ==IEEE: value_range/open_value_range
covergroup_value_range<nodeExprp>: // ==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<nodeExprp>: // IEEE: part of case_item
@ -6936,40 +6936,27 @@ covergroup_declaration<nodep>: // ==IEEE: covergroup_declaration
yCOVERGROUP idAny cgPortListE coverage_eventE ';'
/*cont*/ coverage_spec_or_optionListE
/*cont*/ yENDGROUP endLabelE
{ AstClass *cgClassp = new AstClass{$<fl>2, *$2, PARSEP->libname()};
cgClassp->isCovergroup(true);
AstFunc* const newp = new AstFunc{$<fl>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($<fl>8, $$, $8);
BBCOVERIGN($<fl>1, "Ignoring unsupported: covergroup");
}
{ AstSenTree* clockp = nullptr;
AstNode* sampleArgsp = nullptr;
if ($4) {
if (VN_IS($4, SenItem)) {
clockp = new AstSenTree{$<fl>1, VN_AS($4, SenItem)};
} else {
sampleArgsp = $4;
}
}
$$ = new AstCovergroup{$<fl>1, *$2, static_cast<AstVar*>($3),
static_cast<AstVar*>(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($<fl>8, $$, $8); }
| yCOVERGROUP yEXTENDS idAny ';'
/*cont*/ coverage_spec_or_optionListE
/*cont*/ yENDGROUP endLabelE
{ AstClass *cgClassp = new AstClass{$<fl>3, *$3, PARSEP->libname()};
cgClassp->isCovergroup(true);
AstFunc* const newp = new AstFunc{$<fl>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($<fl>7, $$, $7);
BBCOVERIGN($<fl>1, "Ignoring unsupported: covergroup");
}
{ $$ = nullptr;
BBUNSUP($1, "Unsupported: covergroup inheritance (extends)");
DEL($5); }
;
cgPortListE<nodep>:
@ -7002,10 +6989,16 @@ coverage_spec_or_option<nodep>: // ==IEEE: coverage_spec_or_option
coverage_option<nodep>: // ==IEEE: coverage_option
// // option/type_option aren't really keywords
id/*yOPTION | yTYPE_OPTION*/ '.' idAny/*member_identifier*/ '=' expr
{ if (*$1 == "option") {
$$ = new AstCgOptionAssign{$<fl>1, false, *$3, $5};
} else if (*$1 == "type_option") {
$$ = new AstCgOptionAssign{$<fl>1, true, *$3, $5};
{ if (*$1 == "option" || *$1 == "type_option") {
const bool typeOpt = (*$1 == "type_option");
VCoverOptionType optType = VCoverOptionType::UNKNOWN;
if (*$3 == "at_least") optType = VCoverOptionType::AT_LEAST;
else if (*$3 == "weight") optType = VCoverOptionType::WEIGHT;
else if (*$3 == "goal") optType = VCoverOptionType::GOAL;
else if (*$3 == "auto_bin_max") optType = VCoverOptionType::AUTO_BIN_MAX;
else if (*$3 == "per_instance") optType = VCoverOptionType::PER_INSTANCE;
else if (*$3 == "comment") optType = VCoverOptionType::COMMENT;
$$ = new AstCgOptionAssign{$<fl>1, typeOpt, optType, *$3, $5};
} else {
$$ = nullptr;
$<fl>1->v3error("Syntax error; expected 'option' or 'type_option': '" << *$1 << "'");
@ -7016,29 +7009,33 @@ coverage_option<nodep>: // ==IEEE: coverage_option
cover_point<nodep>: // ==IEEE: cover_point
// // [ [ data_type_or_implicit ] cover_point_identifier ':' ] yCOVERPOINT
yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverpoint"); DEL($2, $3, $4); }
{ $$ = new AstCoverpoint{$<fl>1, "", $2, $3, $4}; }
// // IEEE-2012: class_scope before an ID
| id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>3, "Ignoring unsupported: coverpoint"); DEL($4, $5, $6);}
{ $$ = new AstCoverpoint{$<fl>3, *$1, $4, $5, $6}; }
// // data_type_or_implicit expansion
| data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: coverpoint"); DEL($1, $5, $6, $7);}
{ $$ = new AstCoverpoint{$<fl>4, *$2, $5, $6, $7};
DEL($1); }
| yVAR data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); }
{ $$ = new AstCoverpoint{$<fl>5, *$3, $6, $7, $8};
DEL($2); }
| yVAR implicit_typeE id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); }
{ $$ = new AstCoverpoint{$<fl>5, *$3, $6, $7, $8};
DEL($2); }
| signingE rangeList id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: coverpoint"); DEL($2, $6, $7, $8); }
{ $$ = new AstCoverpoint{$<fl>5, *$3, $6, $7, $8};
DEL($2); }
| signing id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty
{ $$ = nullptr; BBCOVERIGN($<fl>4, "Ignoring unsupported: coverpoint"); DEL($5, $6, $7); }
{ $$ = new AstCoverpoint{$<fl>4, *$2, $5, $6, $7}; }
// // IEEE-2012:
| bins_or_empty { $$ = $1; }
;
iffE<nodep>: // IEEE: part of cover_point, others
iffE<nodeExprp>: // IEEE: part of cover_point, others
/* empty */ { $$ = nullptr; }
| yIFF '(' expr ')'
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover 'iff'"); DEL($3); }
{ $$ = $3; /* Keep iff condition for coverpoint */ }
;
bins_or_empty<nodep>: // ==IEEE: bins_or_empty
@ -7062,39 +7059,104 @@ bins_or_options<nodep>: // ==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($<fl>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($<fl>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($<fl>6, "Ignoring unsupported: cover bin 'with' specification"); DEL($3, $8, $10); }
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>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($<fl>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{$<fl>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{$<fl>2, *$2, $4};
DEL($6);
} else {
$$ = nullptr;
BBCOVERIGN($<fl>2, "Unsupported: 'bins' array (non-auto)");
DEL($4, $6);
}
}
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ AstCoverBin* const binp = new AstCoverBin{$<fl>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{$<fl>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{$<fl>2, *$2, $6, false, false};
BBCOVERIGN($<fl>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{$<fl>2, *$2, $6, true, false};
BBCOVERIGN($<fl>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{$<fl>2, *$2, $6, false, true};
BBCOVERIGN($<fl>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($<fl>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($<fl>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($<fl>6, "Unsupported: 'with' in cover bin"); DEL($8, $10); }
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, false, false, true};
DEL($9); }
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, true, false, true};
DEL($9); }
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, false, true, true};
DEL($9); }
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
{ $$ = nullptr; BBCOVERIGN($<fl>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($<fl>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($<fl>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($<fl>4, "Ignoring unsupported: cover bin trans list"); DEL($3, $5, $6); }
| yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: cover bin 'wildcard' trans list"); DEL($4, $6, $7);}
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ FileLine* isArray = $<fl>3;
$$ = new AstCoverBin{$<fl>2, *$2, static_cast<AstCoverTransSet*>($5), VCoverBinsType{VCoverBinsType::BINS_TRANSITION}, isArray != nullptr};
DEL($6); }
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ FileLine* isArray = $<fl>3;
$$ = new AstCoverBin{$<fl>2, *$2, static_cast<AstCoverTransSet*>($5), VCoverBinsType{VCoverBinsType::BINS_IGNORE}, isArray != nullptr};
DEL($6); }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ FileLine* isArray = $<fl>3;
$$ = new AstCoverBin{$<fl>2, *$2, static_cast<AstCoverTransSet*>($5), VCoverBinsType{VCoverBinsType::BINS_ILLEGAL}, isArray != nullptr};
DEL($6); }
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);}
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);}
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: 'wildcard' transition list in cover bin"); DEL($6, $7);}
//
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
{ $$ = nullptr; BBCOVERIGN($<fl>5, "Ignoring unsupported: cover bin 'default'"); DEL($3, $6); }
| bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Ignoring unsupported: cover bin 'default' 'sequence'"); DEL($3, $7); }
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
{ $$ = new AstCoverBin{$<fl>2, *$2, VCoverBinsType::BINS_DEFAULT};
DEL($6); }
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
{ $$ = new AstCoverBin{$<fl>2, *$2, VCoverBinsType::BINS_IGNORE};
DEL($6); }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE
{ $$ = new AstCoverBin{$<fl>2, *$2, VCoverBinsType::BINS_ILLEGAL};
DEL($6); }
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'sequence' in default cover bin"); DEL($7); }
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'sequence' in default cover bin"); DEL($7); }
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'sequence' in default cover bin"); DEL($7); }
;
bins_orBraE<nodep>: // IEEE: part of bins_or_options:
bins_orBraE<fl>: // IEEE: part of bins_or_options: returns fileline (abuse for boolean flag)
/* empty */ { $$ = nullptr; }
| '[' ']' { $$ = nullptr; /*UNSUP*/ }
| '[' cgexpr ']' { $$ = nullptr; /*UNSUP*/ DEL($2); }
;
bins_keyword<fl>: // ==IEEE: bins_keyword
yBINS { $$ = $1; /*UNSUP*/ }
| yILLEGAL_BINS { $$ = $1; /*UNSUP*/ }
| yIGNORE_BINS { $$ = $1; /*UNSUP*/ }
| '[' ']' { $$ = $<fl>1; /* Mark as array */ }
| '[' cgexpr ']' { BBCOVERIGN($<fl>1, "Unsupported: 'bins' explicit array size (treated as '[]')"); DEL($2); $$ = $<fl>1; }
;
trans_list<nodep>: // ==IEEE: trans_list
@ -7102,30 +7164,42 @@ trans_list<nodep>: // ==IEEE: trans_list
| trans_list ',' '(' trans_set ')' { $$ = addNextNull($1, $4); }
;
trans_set<nodep>: // ==IEEE: trans_set
trans_range_list { $$ = $1; }
// // Note the { => } in the grammar, this is really a list
trans_set<nodep>: // ==IEEE: trans_set (returns AstCoverTransSet)
trans_range_list {
AstCoverTransItem* const itemp = static_cast<AstCoverTransItem*>($1);
$$ = new AstCoverTransSet{$<fl>1, itemp};
}
| trans_set yP_EQGT trans_range_list
{ $$ = $1; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover trans set '=>'"); DEL($3); }
{
AstCoverTransSet* const setp = static_cast<AstCoverTransSet*>($1);
AstCoverTransItem* const itemp = static_cast<AstCoverTransItem*>($3);
setp->addItemsp(itemp);
$$ = setp;
}
;
trans_range_list<nodep>: // ==IEEE: trans_range_list
trans_item { $$ = $1; }
trans_range_list<nodep>: // ==IEEE: trans_range_list (returns AstCoverTransItem)
trans_item {
// Simple transition item without repetition
$$ = new AstCoverTransItem{$<fl>1, $1, VTransRepType::NONE};
}
| trans_item yP_BRASTAR cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[*'"); DEL($1, $3); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[*]' in cover transition"); DEL($1, $3); }
| trans_item yP_BRASTAR cgexpr ':' cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[*'"); DEL($1, $3, $5); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[*]' in cover transition"); DEL($1, $3, $5); }
| trans_item yP_BRAMINUSGT cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[->'"); DEL($1, $3); }
{ BBCOVERIGN($<fl>2, "Unsupported: '[->' in cover transition"); DEL($3);
$$ = new AstCoverTransItem{$<fl>1, $1, VTransRepType::GOTO}; }
| trans_item yP_BRAMINUSGT cgexpr ':' cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[->'"); DEL($1, $3, $5); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[->' in cover transition"); DEL($1, $3, $5); }
| trans_item yP_BRAEQ cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[='"); DEL($1, $3); }
{ BBCOVERIGN($<fl>2, "Unsupported: '[=]' in cover transition"); DEL($3);
$$ = new AstCoverTransItem{$<fl>1, $1, VTransRepType::NONCONS}; }
| trans_item yP_BRAEQ cgexpr ':' cgexpr ']'
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Ignoring unsupported: cover '[='"); DEL($1, $3, $5); }
{ $$ = nullptr; BBCOVERIGN($<fl>2, "Unsupported: '[=]' in cover transition"); DEL($1, $3, $5); }
;
trans_item<nodep>: // ==IEEE: range_list
trans_item<nodep>: // ==IEEE: range_list (returns range list node)
covergroup_range_list { $$ = $1; }
;
@ -7137,9 +7211,28 @@ covergroup_range_list<nodep>: // ==IEEE: covergroup_range_list
cover_cross<nodep>: // ==IEEE: cover_cross
id/*cover_point_identifier*/ ':' yCROSS list_of_cross_items iffE cross_body
{ $$ = nullptr; BBCOVERIGN($<fl>3, "Ignoring unsupported: cover cross"); DEL($4, $5, $6); }
{
AstCoverCross* const nodep = new AstCoverCross{$<fl>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($<fl>1, "Ignoring unsupported: cover cross"); DEL($2, $3, $4); }
{
AstCoverCross* const nodep = new AstCoverCross{$<fl>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<nodep>: // ==IEEE: list_of_cross_items
@ -7154,7 +7247,8 @@ cross_itemList<nodep>: // IEEE: part of list_of_cross_items
;
cross_item<nodep>: // ==IEEE: cross_item
idDotted/*cover_point_identifier or variable_identifier*/ { $1->deleteTree(); $$ = nullptr; /*UNSUP*/ }
id/*cover_point_identifier*/
{ $$ = new AstCoverpointRef{$<fl>1, *$1}; }
;
cross_body<nodep>: // ==IEEE: cross_body
@ -7174,12 +7268,16 @@ cross_body_itemList<nodep>: // IEEE: part of cross_body
cross_body_item<nodep>: // ==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
;
@ -7187,28 +7285,28 @@ select_expression<nodep>: // ==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<nodep>:
// // 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
@ -7226,7 +7324,7 @@ select_expression_r<nodep>:
//UNSUP cgexpr yMATCHES cgexpr {..}
//UNSUP // Below are all removed
| idAny '(' list_of_argumentsE ')'
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage select function call"); DEL($3); }
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: function call in coverage select expression"); DEL($3); }
//UNSUP // Above are all removed, replace with:
;
@ -7245,7 +7343,7 @@ bins_expression<nodep>: // ==IEEE: bins_expression
coverage_eventE<nodep>: // IEEE: [ coverage_event ]
/* empty */ { $$ = nullptr; }
| clocking_event
{ $$ = nullptr; BBCOVERIGN($<fl>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") {
$<fl>3->v3error("Coverage sampling function must be named 'sample'");
@ -7256,7 +7354,7 @@ coverage_eventE<nodep>: // IEEE: [ coverage_event ]
}
}
| yP_ATAT '(' block_event_expression ')'
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Ignoring unsupported: coverage '@@' events"); DEL($3); }
{ $$ = nullptr; BBCOVERIGN($<fl>1, "Unsupported: '@@' coverage event"); DEL($3); }
;
block_event_expression<nodep>: // ==IEEE: block_event_expression
@ -7754,11 +7852,15 @@ class_item<nodep>: // ==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; }

View File

@ -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: <hierarchy>[ [bin_type]]: <count>
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()

View File

@ -4,17 +4,39 @@
// SPDX-FileCopyrightText: 2025 Antmicro
// SPDX-License-Identifier: CC0-1.0
// A plain (non-covergroup) class - exercises the non-covergroup class scope/varscope paths
class PlainClass;
int x;
endclass
// Top-level (file-scope) covergroup: exercises the m_modp==null path in
// V3LinkParse::visit(AstCovergroup*), line 1269 false branch (libname = "")
// verilator lint_off COVERIGN
covergroup cg_toplevel;
cp_tl: coverpoint 0;
endgroup
// verilator lint_on COVERIGN
// verilator lint_off COVERIGN
module t;
int i, j;
logic clk = 0;
covergroup cg(int var1, int var2 = 42);
cp1: coverpoint i; // Non-empty body with args: exercises constructor-body path
endgroup
// Clocked covergroup with constructor args: exercises the loop-past-sentinel path in
// V3LinkParse createCovergroupMethods (iterates past AstCovergroup sentinel to find 'new')
covergroup cg_clocked(int lim) @(posedge clk);
cp_clocked: coverpoint i;
endgroup
cg cov1 = new(69, 77);
cg cov2 = new(69);
int i, j;
real r;
cg_clocked cov_clocked = new(10);
PlainClass plain_inst = new; // Non-covergroup class instance: exercises early-return paths
function void x();
cov1.set_inst_name("the_inst_name");
@ -23,16 +45,14 @@ module t;
cov1.stop();
void'(cov2.get_coverage());
r = cov2.get_coverage();
r = cov2.get_coverage(i, j);
void'(cov2.get_coverage(i, j));
// verilator lint_off IGNOREDRETURN
cov2.get_inst_coverage();
// verilator lint_on IGNOREDRETURN
r = cov2.get_inst_coverage(i, j);
void'(cov2.get_inst_coverage(i, j));
cg::get_coverage();
r = cg::get_coverage();
r = cg::get_coverage(i, j);
void'(cg::get_coverage());
void'(cg::get_coverage(i, j));
endfunction
endmodule

View File

@ -0,0 +1,4 @@
cg.data.grouped: 2
cg.data.values: 3
cg2.cp.range_arr: 3
cg3.cp.range_sized: 3

View File

@ -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'])

View File

@ -0,0 +1,78 @@
// 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 InsideRange and AstRange
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: exercises InsideRange in array bins (e.g., bins r[] = {[0:3]})
covergroup cg2;
cp: coverpoint data {
bins range_arr[] = {[0:3]}; // InsideRange -> 4 separate bins
}
endgroup
// cg3: exercises AstRange in array bins (e.g., bins r[N] = {[lo:hi]})
covergroup cg3;
cp: coverpoint data {
bins range_sized[4] = {[4:7]}; // AstRange with explicit count
}
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();
// Hit second array bin value (5)
data = 5;
cg_inst.sample();
// Hit the grouped bin (covers all of 2, 6, 10)
data = 6;
cg_inst.sample();
// Hit third array bin value (9)
data = 9;
cg_inst.sample();
// Verify hitting other values in grouped bin doesn't increase coverage
data = 2;
cg_inst.sample();
// Hit range_arr bins (InsideRange [0:3])
data = 0; cg2_inst.sample();
data = 1; cg2_inst.sample();
data = 2; cg2_inst.sample();
// Hit range_sized bins (AstRange [4:7])
data = 4; cg3_inst.sample();
data = 5; cg3_inst.sample();
data = 6; cg3_inst.sample();
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -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

View File

@ -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, verilator_flags2=['-Wno-UNSIGNED', '-Wno-CMPCONST'])

View File

@ -0,0 +1,99 @@
// 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
module t;
logic [2:0] data3;
logic [3:0] data4; // 4-bit signal for range-bin path tests
logic [63:0] data64; // 64-bit signal for width>=64 branch in createAutoBins
// 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 at *coverpoint* level (lines 207, 209)
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: range-bin skip path (lines 287, 356-359).
// 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: 64-bit coverpoint exercises the width>=64 branches in createAutoBins
// (V3Covergroup.cpp:269-270: maxVal/numTotalValues use UINT64_MAX sentinel)
covergroup cg5;
option.auto_bin_max = 4;
cp_data64: coverpoint data64;
endgroup
// Test type_option.auto_bin_max: treated same as option.auto_bin_max, creates 4 bins [0:1],[2:3],[4:5],[6:7]
covergroup cg6;
type_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();
data3 = 3; cg1_inst.sample();
data3 = 0; cg2_inst.sample();
data3 = 4; cg2_inst.sample();
data3 = 1; cg3_inst.sample();
data3 = 5; cg3_inst.sample();
// Sample valid (non-ignored) values for cg4
data4 = 4; cg4_inst.sample(); // [4:7] bin
data4 = 8; cg4_inst.sample(); // [8:11] bin
data4 = 12; cg4_inst.sample(); // [12:15] bin
// Sample cg5 (64-bit): exercises width>=64 path in createAutoBins
data64 = 64'h0; cg5_inst.sample();
data64 = 64'h1111111111111111; cg5_inst.sample();
data64 = 64'hffffffffffffffff; cg5_inst.sample();
data3 = 0; cg6_inst.sample();
data3 = 7; cg6_inst.sample();
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,6 @@
%Error: t/t_covergroup_auto_bin_max_bad.v:14:29: option.auto_bin_max must be a constant expression
: ... note: In instance 't'
14 | option.auto_bin_max = size_var;
| ^~~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -0,0 +1,73 @@
// 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
module t;
/* verilator lint_off CMPCONST */
logic [2:0] data; // 3-bit: 0-7
logic [3:0] data4; // 4-bit: exercises width<64 path in maxVal computation
logic [63:0] data64; // 64-bit: exercises width>=64 (UINT64_MAX) path
covergroup cg;
coverpoint data {
bins auto[4]; // Should create 4 bins: [0:1], [2:3], [4:5], [6:7]
}
endgroup
// 4-bit auto bins: exercises (width < 64) path: maxVal = (1<<4)-1 = 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 ignore_bins: exercises excluded-value skip path
covergroup cg_4bit_excl;
coverpoint data4 {
ignore_bins bad = {0}; // value 0 excluded from auto expansion
bins auto[4];
}
endgroup
// auto_bin_max=2 on 64-bit: exercises numTotalValues=UINT64_MAX path
covergroup cg2;
option.auto_bin_max = 2;
coverpoint data64;
endgroup
/* verilator lint_on CMPCONST */
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
data = 0; cg_inst.sample();
data = 2; cg_inst.sample();
data = 5; cg_inst.sample();
data = 7; cg_inst.sample();
// Sample 4-bit bins
data4 = 0; cg4_inst.sample(); // bin [0:3]
data4 = 7; cg4_inst.sample(); // bin [4:7]
data4 = 10; cg4_inst.sample(); // bin [8:11]
data4 = 14; cg4_inst.sample(); // bin [12:15]
// Sample 4-bit with exclusion (value 0 excluded; bins start at 1)
data4 = 1; cg4e_inst.sample();
data4 = 8; cg4e_inst.sample();
// Sample both 64-bit range bins
data64 = 64'd0; cg2_inst.sample(); // auto[0]
data64 = 64'hFFFF_FFFF_FFFF_FFFF; cg2_inst.sample(); // auto[1]
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,21 @@
#!/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')
# Use the same .v file as the non-timing test
test.top_filename = "t/t_covergroup_clocked_sample.v"
test.compile(v_flags2=["--timing"])
test.execute()
test.passes()

View File

@ -0,0 +1,50 @@
%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};
| ^~~~~~~~
%Error: t/t_covergroup_autobins_bad.v:51:25: option.at_least must be a constant expression
: ... note: In instance 't'
51 | option.at_least = size_var;
| ^~~~~~~~
%Error: Exiting due to

View File

@ -4,7 +4,7 @@
# 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: 2025 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
@ -12,7 +12,7 @@ import vltest_bootstrap
test.scenarios('vlt')
test.lint(expect_filename=test.golden_filename,
verilator_flags2=['--assert --error-limit 1000'],
verilator_flags2=['--error-limit 1000'],
fails=True)
test.passes()

View File

@ -0,0 +1,62 @@
// 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
cg1 cg1_inst = new;
cg2 cg2_inst = new;
cg2b cg2b_inst = new;
cg3 cg3_inst = new;
cg4 cg4_inst = new;
initial $finish;
endmodule

View File

@ -0,0 +1,9 @@
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

View File

@ -0,0 +1,31 @@
#!/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')
test.compile(verilator_flags2=['--coverage'])
test.execute()
coverage_covergroup_common.covergroup_coverage_report(test)
test.files_identical(test.obj_dir + '/covergroup_report.txt', test.golden_filename)
# 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]')
test.passes()

View File

@ -0,0 +1,68 @@
// 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
module t (/*AUTOARG*/);
logic [3:0] data;
logic [7:0] opcode;
// 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 exercises cg.cp.low/high hierarchy in coverage.dat
covergroup cg_db;
cp: coverpoint data {
bins low = {[0:3]};
bins high = {[8:15]};
}
endgroup
cg cg_inst;
cg_mixed cg_mixed_inst;
cg_db cg_db_inst;
initial begin
cg_inst = new;
cg_mixed_inst = new;
cg_db_inst = new;
data = 0; cg_inst.sample(); // zero: 1
data = 1; cg_inst.sample(); // low: 1
data = 2; cg_inst.sample(); // low: 2
data = 2; cg_inst.sample(); // low: 3
opcode = 8'h00; cg_mixed_inst.sample(); // nop
opcode = 8'h02; cg_mixed_inst.sample(); // load
opcode = 8'h05; cg_mixed_inst.sample(); // store
opcode = 8'h15; cg_mixed_inst.sample(); // arith
opcode = 8'h80; cg_mixed_inst.sample(); // other
data = 1; cg_db_inst.sample(); // low
data = 10; cg_db_inst.sample(); // high
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,4 @@
cg.cp_data.one: 1
cg.cp_data.three: 1
cg.cp_data.two: 1
cg.cp_data.zero: 2

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
import coverage_covergroup_common
test.scenarios('vlt_all')
coverage_covergroup_common.run(
test, verilator_flags2=['--dumpi-tree 3 --dumpi-tree-json 3 --Wno-COVERIGN'])

View File

@ -0,0 +1,229 @@
// 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
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 inside body: exercises addOptionsp in visit(AstCoverCross*)
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 with range bin: exercises lo!=hi path in buildBinCondition
covergroup cg_range;
cp_addr: coverpoint addr {
bins lo_range = {[0:1]}; // range bin (lo != hi) -> makeRangeCondition path
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: exercises BINS_USER FALSE branch
// in collectBins during cross code generation (L1139)
covergroup cg_ignore;
cp_addr: coverpoint addr {
ignore_bins ign = {3}; // BINS_IGNORE: not BINS_USER, exercises L1139 FALSE path
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 at_least option in body: covers AT_LEAST case in V3LinkParse visit(AstCoverCross*)
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 goal option in body: covers GOAL case in V3LinkParse visit(AstCoverCross*)
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 unsupported option: covers COVERIGN warning in V3LinkParse visit(AstCoverCross*)
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; triggers COVERIGN at V3LinkParse:1380
}
endgroup
// Covergroup with unnamed cross: exercises crossName.empty() fallback to "cross" (L1395)
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 -> crossName is empty
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
addr = 0; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x read
addr = 1; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x write
addr = 0; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x write
addr = 1; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x read
// Sample 3-way: hit 4 of 12 combinations
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
addr = 0; cmd = 0; mode = 0; parity = 0; cg4_inst.sample();
addr = 1; cmd = 1; mode = 0; parity = 1; cg4_inst.sample();
addr = 0; cmd = 1; mode = 1; parity = 0; cg4_inst.sample();
addr = 1; cmd = 0; mode = 1; parity = 1; cg4_inst.sample();
// Sample cg5 (cross with option)
addr = 0; cmd = 0; cg5_inst.sample();
addr = 1; cmd = 1; cg5_inst.sample();
// Sample cg_ignore: addr=3 is in ignore_bins so no cross bins for it
addr = 0; cmd = 0; cg_ignore_inst.sample(); // a0 x read
addr = 1; cmd = 1; cg_ignore_inst.sample(); // a1 x write
addr = 0; cmd = 1; cg_ignore_inst.sample(); // a0 x write
addr = 1; cmd = 0; cg_ignore_inst.sample(); // a1 x read
addr = 3; cmd = 0; cg_ignore_inst.sample(); // ignored (addr=3 in ignore_bins)
// Sample range-bin cross
addr = 0; cmd = 0; cg_range_inst.sample(); // lo_range x read
addr = 2; cmd = 1; cg_range_inst.sample(); // hi_range x write
addr = 1; cmd = 1; cg_range_inst.sample(); // lo_range x write
addr = 3; cmd = 0; cg_range_inst.sample(); // hi_range x read
// Sample cg_at_least (option.at_least in cross body)
addr = 0; cmd = 0; cg_at_least_inst.sample(); // addr0 x read
addr = 1; cmd = 1; cg_at_least_inst.sample(); // addr1 x write
// Sample cg_goal (option.goal in cross body)
addr = 0; cmd = 0; cg_goal_inst.sample(); // addr0 x read
addr = 1; cmd = 1; cg_goal_inst.sample(); // addr1 x write
// Sample cg_unsup_cross_opt: option.per_instance triggers COVERIGN in V3LinkParse
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
// Sample cg_unnamed_cross: exercises unnamed cross (crossName fallback to "cross")
addr = 0; cmd = 0; cg_unnamed_cross_inst.sample(); // a0 x read
addr = 1; cmd = 1; cg_unnamed_cross_inst.sample(); // a1 x write
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,6 @@
%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.
%Error: Exiting due to

View File

@ -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()

View File

@ -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
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
}
endgroup
cg cg_i = new;
initial begin
cg_i.sample();
$finish;
end
endmodule

View File

@ -0,0 +1,20 @@
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

View File

@ -4,13 +4,12 @@
# 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-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')
test.compile()
test.passes()
coverage_covergroup_common.run(test)

View File

@ -0,0 +1,139 @@
// 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
// Non-covergroup class: exercises V3Active isCovergroup()=false branch
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; // 64-bit: exercises width>=64 auto-bin path (L139)
logic [64:0] data65; // 65-bit: exercises exprWidth>64 in makeRangeCondition
DataHelper helper; // Module-level class var: exercises V3Active isCovergroup()=false
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: exercises defaultCondp=BitTrue path
covergroup cg2;
cp_only_default: coverpoint data {
bins all = default;
}
endgroup
// Covergroup with default + ignore + illegal bins: exercises BINS_IGNORE/BINS_ILLEGAL
// skip paths in generateDefaultBinMatchCode (L558-L559)
covergroup cg3;
coverpoint data {
ignore_bins bad = {255}; // BINS_IGNORE skip path
illegal_bins err = {254}; // BINS_ILLEGAL skip path
bins normal = {[1:10]};
bins other = default;
}
endgroup
// Covergroup with auto-bins + ignore_bins on small range: exercises L295 excluded-value continue
// When numValidValues <= auto_bin_max, single-value auto-bins are created per value; the
// excluded.find() check at L295 fires for the ignore_bins value (idx=2).
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 4 auto-bins: exercises width>=64 branch in auto-bin range calculation
covergroup cg5;
cp_data64: coverpoint data64 { bins auto[4]; }
endgroup
// 65-bit signal with range bins: exercises exprWidth>64 path in makeRangeCondition
covergroup cg6;
cp_data65: coverpoint data65 { bins lo = {[0:15]}; bins hi = {[100:200]}; }
endgroup
// Unlabeled coverpoint: exercises cpName fallback via exprp()->name() (L1394-1398)
covergroup cg7;
coverpoint data { bins lo = {[0:7]}; bins hi = {[8:15]}; }
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_inst = new();
cg2_inst = new();
cg3_inst = new();
cg4_inst = new();
cg5_inst = new();
cg6_inst = new();
cg7_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();
// 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();
// Hit another default value (should not increase coverage)
data = 20;
cg_inst.sample();
cg2_inst.sample();
// Sample cg3: exercises BINS_IGNORE/BINS_ILLEGAL skip in default-bin detection loop
data = 2; cg3_inst.sample(); // hits normal bin
data = 7; cg3_inst.sample(); // hits normal bin again
data = 255; cg3_inst.sample(); // ignore_bins (not counted)
// note: do not sample 254 (illegal_bins would cause runtime assertion)
data = 100; cg3_inst.sample(); // hits default (other) bin
// Sample cg4: exercises auto-bin generation with excluded value (L295)
// idx=2 is in ignore_bins, so auto-bins cover 0,1,3 only
idx = 0; cg4_inst.sample();
idx = 1; cg4_inst.sample();
idx = 3; cg4_inst.sample();
// Sample cg5: 64-bit signal, sample into bin b[0] (value 0 is in first quarter)
data64 = 0; cg5_inst.sample();
data64 = 5; cg5_inst.sample();
// Sample cg6: 65-bit signal with range bins
data65 = 5; cg6_inst.sample(); // hits bin lo=[0:15]
data65 = 150; cg6_inst.sample(); // hits bin hi=[100:200]
// Sample cg7: unlabeled coverpoint (exercises exprp()->name() path)
data = 3; cg7_inst.sample(); // hits bin lo
data = 10; cg7_inst.sample(); // hits bin hi
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,2 @@
Empty covergroup coverage: 100.000000%
*-* All Finished *-*

View File

@ -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()

View File

@ -0,0 +1,45 @@
// 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
if (cyc > 10) begin
$display("ERROR: Test timed out");
$stop;
end
end
endmodule

View File

@ -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

View File

@ -0,0 +1,5 @@
%Error-UNSUPPORTED: t/t_covergroup_extends_newfirst.v:29:5: Unsupported: covergroup inheritance (extends) is not implemented
29 | covergroup extends g1;
| ^~~~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -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

View File

@ -0,0 +1,9 @@
cg_array_iff.cp.arr: 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_trans2_iff.cp.t2: 1
cg_trans3_iff.cp.t3: 1

View File

@ -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)

View File

@ -0,0 +1,107 @@
// 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.
module t;
logic enable;
int value;
// --- Original: iff on explicit value bins (lines 565-567) ---
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 (lines 633-635/641) ---
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 (lines 982-983) ---
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 (lines 1109-1110) ---
covergroup cg_trans2_iff;
cp: coverpoint value iff (enable) {
bins t2 = (1 => 2);
}
endgroup
// --- iff on 3-step transition (lines 724-725, 762-763) ---
covergroup cg_trans3_iff;
cp: coverpoint value iff (enable) {
bins t3 = (1 => 2 => 3);
}
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;
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();
// Sample enabled_lo and enabled_hi with enable=1 -- must be recorded
enable = 1;
value = 3; cg1.sample();
value = 4; cg1.sample();
// cg2: default bin -- enable=1 lets known and default through
enable = 1;
value = 10; cg2.sample(); // hits 'known'
value = 99; cg2.sample(); // hits 'def' (default)
enable = 0;
value = 99; cg2.sample(); // gated by iff -- must NOT hit 'def'
// cg3: array bins with iff
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();
value = 2; cg4.sample(); // (1=>2) hit with enable=1
enable = 0;
value = 1; cg4.sample();
value = 2; cg4.sample(); // (1=>2) gated by iff
// 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 fails at step 3 -- triggers restart path (line 762-763)
enable = 1;
value = 1; cg5.sample();
value = 2; cg5.sample();
value = 3; cg5.sample(); // (1=>2=>3) fully hit with enable=1
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,14 @@
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: 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_bounds.hi: 2
cg2.cp_bounds.lo: 2
cg2.cp_full.all: 4

View File

@ -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)

View File

@ -0,0 +1,64 @@
// 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
module t (/*AUTOARG*/);
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; // null rangesp: exercises generateBinMatchCode !fullCondp
ignore_bins arr[] = {4, 5}; // array form: exercises bins_orBraE $3 true branch
wildcard ignore_bins wib = {4'b1?00}; // wildcard ignore bins (L7084-7085)
illegal_bins bad[] = {6, 7}; // illegal array form: exercises bins_orBraE $3 true branch
}
endgroup
// Exercises:
// extractValuesFromRange AstInsideRange branch (ignore_bins range, no regular bins)
// createImplicitAutoBins with excluded range values
// makeRangeCondition: skipUpperCheck=true (hi=maxVal) and both-skip (BitTrue)
// ignore_bins with transition list (L7102-7104)
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_bins with transition (L7102-7104)
}
cp_bounds: coverpoint data2 {
bins lo = {[0:1]}; // lo=0: skipLowerCheck -> AstLte
bins hi = {[2:3]}; // hi=maxVal (2-bit): skipUpperCheck -> AstGte
}
cp_full: coverpoint data2 {
bins all = {[0:3]}; // lo=0 and hi=maxVal: both skip -> AstConst(BitTrue)
}
endgroup
cg cg_inst;
cg2 cg2_inst;
initial begin
cg_inst = new;
cg2_inst = new;
data = 13; cg_inst.sample(); // reserved - ignored
data = 1; cg_inst.sample(); // low
data = 10; cg_inst.sample(); // high
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
data2 = 3; cg2_inst.sample(); // ign, hi, all
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -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: 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

View File

@ -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)

View File

@ -0,0 +1,68 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Test that illegal_bins are excluded from coverage (like ignore_bins).
// Also tests coverpoint where all bins are ignore/illegal (totalBins==0 path).
//
// 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
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: exercises illegal_bins with 3-step transition (lines 744-747) and
// illegal_bins array notation (lines 996-998)
covergroup cg2;
cp_trans: coverpoint data4 {
bins ok = {0};
illegal_bins bad_2step = (1 => 2); // 2-step illegal transition (simple path)
illegal_bins bad_3step = (1 => 2 => 3); // multi-step illegal transition
illegal_bins lib_default = default; // illegal_bins = default (L7123-7124)
}
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 (L7087-7088)
}
endgroup
// cg3: all bins are ignore_bins or illegal_bins -> totalBins==0 -> 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();
// Sample cg2 - only safe values, never triggering illegal bins
data4 = 0; cg2_inst.sample();
// Sample cg3 - values that only hit ignore_bins, never illegal_bins
data = 0; cg3_inst.sample();
data = 1; cg3_inst.sample();
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -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

View File

@ -1,8 +1,8 @@
%Error: t/t_covergroup_in_class_duplicate_bad.v:13:14: Duplicate declaration of CLASS '__vlAnonCG_embeddedCg': '__vlAnonCG_embeddedCg'
%Error: t/t_covergroup_in_class_duplicate_bad.v:13:3: 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
| ^~~~~~~~~~
t/t_covergroup_in_class_duplicate_bad.v:9:3: ... Location of original declaration
9 | covergroup embeddedCg;
| ^~~~~~~~~~
| ^~~~~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -11,6 +11,6 @@ import vltest_bootstrap
test.scenarios('vlt')
test.compile()
test.compile(verilator_flags2=['--coverage'])
test.passes()

View File

@ -4,17 +4,15 @@
// 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();
void'(embeddedCg.get_coverage());
endfunction
endclass
@ -24,9 +22,8 @@ class secondClass;
endgroup
function new();
real r;
embeddedCg = new();
embeddedCg.sample();
r = embeddedCg.get_coverage();
void'(embeddedCg.get_coverage());
endfunction
endclass

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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, verilator_flags2=['--assert'], fails=True)
test.passes()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,4 @@
cg.cp_neg.mixed: 4
cg.cp_neg.negative: 2
cg.cp_neg.positive: 3
cg.cp_neg.zero: 1

View File

@ -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)

View File

@ -0,0 +1,54 @@
// 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
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
int signed value;
/* verilator lint_off CMPCONST */
covergroup cg;
cp_neg: coverpoint value {
bins negative = {[-100:-1]};
bins zero = {0};
bins positive = {[1:100]};
bins mixed = {[-10:10]};
}
endgroup
/* verilator lint_on CMPCONST */
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();
if (cyc > 10) begin
$display("ERROR: Test timed out");
$stop;
end
end
endmodule

View File

@ -13,6 +13,4 @@ test.scenarios('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -4,17 +4,60 @@
// SPDX-FileCopyrightText: 2023 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// Test option.name syntax: both declaration-time and runtime assignment compile.
// Note: option.name does not currently affect the coverage.dat hierarchy key;
// the type name is used regardless.
// Also tests option.weight, option.goal, option.per_instance, option.comment.
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
// Covergroup-level options (parsed but passed through constructor body)
covergroup cg2;
option.weight = 2;
option.goal = 90;
option.per_instance = 1;
option.comment = "my covergroup";
cp: coverpoint data;
endgroup
// Coverpoint-level options: exercises visit(AstCoverpoint*) option-type branches
// in V3LinkParse (weight/goal/per_instance/comment dispatch)
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";
cov2 = new;
data = 5;
cov2.sample();
cov3 = new;
data = 3;
cov3.sample();
data = 10;
cov3.sample();
$finish;
end

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,6 @@
%Warning-COVERIGN: t/t_covergroup_option_unsup.v:15:7: Ignoring unsupported coverage option: 'foobar'
15 | option.foobar = 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

View File

@ -11,6 +11,6 @@ import vltest_bootstrap
test.scenarios('vlt')
test.compile()
test.lint(expect_filename=test.golden_filename, fails=True)
test.passes()

Some files were not shown because too many files have changed in this diff Show More