Compare commits

..
375 Commits
Author SHA1 Message Date
Wilson Snyder 983a06a16f Merge from master for release. 2026-09-05 08:44:29 -04:00
Wilson Snyder ea338be98e Version bump 2026-09-05 08:28:17 -04:00
Wilson Snyder c8a8f02c09 Commentary: Changes update 2026-09-05 08:27:32 -04:00
Nick Brereton b1c06fdb09 Fix use-after-free of captured interface typedef in deleted generate branch (#8287) 2026-09-04 14:27:11 -04:00
Andrii 18095ac924 Fix incorrect reference to sync/async in SYNCASYNCNET warning (#8281)
Signed-off-by: Andrii Andrieiev <[email protected]>
2026-09-04 11:01:10 -04:00
Aditya Shevade efc390a37d Fix std::randomize() with-clause array index arguments (#8279) (#8280) 2026-09-04 11:00:10 -04:00
Aditya Shevade 228635918e Fix mis-randomizing an unpacked struct member when not rand/randc (#8277) (#8278) 2026-09-03 20:08:44 -04:00
Wilson Snyder c917f04c65 Internals: Fix many but not all missing member dumps. No functional change. 2026-09-02 22:27:19 -04:00
Wilson Snyder c4a4f35ec4 Commentary: Changes update 2026-09-02 22:15:13 -04:00
Wilson Snyder e012f7c79d Fix segmentation fault on context destruction (#8265).
Fixes #8265.
2026-09-02 08:00:32 -04:00
Wilson Snyder 80907b1e26 Internals: Fix many but not all missing bool member dumps. No functional change. 2026-09-01 22:20:20 -04:00
Wilson Snyder a5f4f7eade Internals: Fix covergroupRefMember overload (#8259).
Fixes #8259.
2026-09-01 21:31:19 -04:00
Wilson Snyder c7aa95439c Internals: Cleanup some member name vs accessor name mismatches. No functional change. 2026-09-01 20:10:04 -04:00
Wilson Snyder a3932e259b Internals: Cleanup duplicate superRef. No functional change. 2026-09-01 20:01:20 -04:00
Wilson Snyder e6cee9d54c Tests: Check for binary files 2026-09-01 18:30:29 -04:00
Wilson Snyder f694e9e1e8 Revert NFA assertion outcomes (#7946 revert) (#8258) 2026-09-01 11:15:05 -04:00
Wilson Snyder 667f64813f Commentary: Changes update 2026-09-01 08:46:25 -04:00
Wilson Snyder 67818a3430 Internals: Fix unknown node message in V3Simulate. No simulation change intended. 2026-09-01 08:43:43 -04:00
Wilson Snyder 1c2fa7234d Fix removing extra width warnings after another error 2026-08-31 18:04:58 -04:00
Szymon Gizler be83d1393d Fix continuing on wait(0) (#7372) (#8231) 2026-08-31 13:06:23 -04:00
Matthew Ballance 9474a01d54 Add definitions for odr-used constexpr, as required by C++14 (#8256) 2026-08-31 10:38:18 -04:00
Yilou Wang c81be029a6 Fix NFA assertion scheduling to fix outcome counts (#7946) 2026-08-29 15:53:08 -04:00
Wilson Snyder 3c121c0a2e Fix initial statements with gate optimizaion (#8250) (#8253) 2026-08-29 14:29:03 -04:00
Wilson Snyder 0f96460042 Commentary: Changes update 2026-08-29 11:46:16 -04:00
Wilson Snyder 648035df72 Tests: Cleanup some time formats 2026-08-29 11:13:47 -04:00
Geza Lore dcf45da1c0 Fix excessive SIMILARNAME warnings (#8233) 2026-08-28 15:55:41 -04:00
Kamil Danecki d2920a627d Support solve before with associative array (#8232) 2026-08-28 15:19:08 -04:00
Paul Rigge 2954756190 Fix VL_MULS_* UB on signed int overflow (#8247) (#8248) 2026-08-28 14:53:22 -04:00
Geza Lore c70439961f Optimize associative constant patterns to fixed point (#8147) (#8209)
Re-associating constants can enable further simplification e.g. when
those constant become all zero/all ones. Run these patterns to fixed
point to capture the simplification, by revisiting the node.
2026-08-28 20:46:33 +02:00
Geza Lore ab20edbbaf Internals: Use single source of truth VCMethod enumeration (#8249) 2026-08-28 14:32:08 -04:00
Geza Lore 297713df8b Fix folding of short circuiting operators guarding member access (#8238)
V3Const eagerly deletes nodes (we want that in V3Const instead of using
pushDeletep, as otherwise the transient peak memory use during
verilation can be very high)

The std::map used to cache the predicate checking if a subtree contains
a member access then stale if a new node is allocated in the same address.

Fix by using unique node IDs stored in user4 as cache keys.

This is the bug uncovered by #8147

Related to #6963
2026-08-28 20:16:03 +02:00
Geza Lore 6a05cc680b CI: Update RTLMeter case lists - also for PRs 2026-08-28 17:40:46 +02:00
Geza Lore 50e42bc197 CI: Update RTLMeter case lists
- Only run Servant:*:phil on multi-threaded clang build
- Run HummingbirdV2-E203
2026-08-28 16:51:14 +02:00
Geza Lore c64afda7fc Fix MULTIDRIVENPROC on static loop induction variables (#8242)
Disable MULTIDRIVENPROC for static variables used as loop induction
variables. Same way as we disable similar warning in Dfg. While bad
style and bad for performance, it's common legacy code style.
2026-08-28 15:27:50 +02:00
Igor Zaworski 1ad9acd22e Fix scheduling of variables written in non-inlined functions in suspendable processes (#7909) 2026-08-27 08:33:49 -04:00
Jakub Wasilewski 2f856971ed Add Unigen2 constrained randomization algorithm (#8042)
Signed-off-by: Jakub Wasilewski <[email protected]>
2026-08-27 08:33:14 -04:00
Geza Lore 8546d5db06 Internals: Move the eval loop into the runtime library (#8225)
The loops modelling the SystemVerilog scheduling regions are no longer
generated. They now live in 'VerilatedEvalLoop' in the runtime library.
The generated model holds one as a member, passing itself to it, and
exposes each evaluation entry point to it as a pure virtual method on
VerilatedModel. The model's 'eval' and 'eval_step' remain the top level
entry points, and are backward compatible.

V3Sched no longer emits '_eval' or '_eval_settle', etc.. Instead every
evaluation entry point called from the runtime is enumerated by 'VEval',

Scheduling creates all entry points, for all scheduling regions, even if
they are empty, and the runtime eval loop calls everything
unconditionally. If regions are empty, this is simply a call to an empty
function. This will hurt performance on very small models, but should
not be noticeable on anything meaningful, so it is likely best to keep
to reduce complexity.

A scheduling entry points evaluate a single iteration and returns
whether it did any work, they are effectively the previous
`_eval_phase_*` functions.
2026-08-27 07:53:02 -04:00
Geza Lore 59f221986c Fix errors on stream connected to instance port (#8234) 2026-08-27 07:51:35 -04:00
Sumanth Kadiyala 99c6f9ced8 Internals: Add flag to replace VNUser union w/ std::variant for type validation (#8213) 2026-08-26 20:09:21 -04:00
Eddie Rydell 7bff0196b1 Fix procedural tristate assignment overrides (#6785) (#8130) 2026-08-26 19:23:46 -04:00
Kamil Danecki f055eeabe0 Support soft foreach in constraint (#8174) 2026-08-26 11:05:12 -04:00
Artur Bieniek 7f9333723f Fix sampled-value functions output on the first cycle (#8162) 2026-08-26 10:30:59 -04:00
Pawel Klopotek cce0277e1a Fix backslash behavior in string literals (#8211)
Signed-off-by: Pawel Klopotek <[email protected]>
2026-08-26 10:02:39 -04:00
Wilson Snyder 51a2dc3efb Commentary: Changes update 2026-08-26 06:09:57 -04:00
Wilson Snyder 0dcdea3325 Commentary: AGENTS tweaks 2026-08-26 06:09:50 -04:00
Wilson Snyder 1cd2588241 Tests: Print stats with --benchmark 2026-08-26 06:02:27 -04:00
Geza Lore 087b15bdb3 Remove source location from DIDNOTCONVERGE error (#8225 prep) (#8227) 2026-08-26 04:55:36 -04:00
Aliaksei Chapyzhenka 5e4151e3e0 Internals: Add WASI (wasm32-wasip1) build support (#8216) (#8217) 2026-08-25 19:13:07 -04:00
Aisha b23b53227d Fix SIMILARNAME warning on parameters and localparams (#8186) (#8226) 2026-08-25 16:19:41 -04:00
Yilou Wang 8cbaf505ee Fix solver session stability to avoid breaking randomize (#7991) 2026-08-25 13:13:59 -04:00
Matthew Ballance 0b85b80ece Fix method calls to be a reorder barrier (#8222)
Signed-off-by: Matthew Ballance <[email protected]>
2026-08-25 13:09:14 -04:00
Pawel Klopotek 821478070d Fix V3Task Inout pin resizing error (#8212) 2026-08-25 10:27:24 -04:00
Artur Bieniek 69f42ea480 Support s_until/s_until_with in property expression (#8220) 2026-08-25 10:26:43 -04:00
Artur Bieniek f6d56c6440 Optimize consecutive repetiton and goto repetition using ring-buffer (#8096) 2026-08-25 08:26:46 -04:00
Geza Lore 538c6a6b76 Fix unoptimized code generation for postponed region (#8218) 2026-08-25 10:31:36 +02:00
Wilson Snyder 1c9a9e0cce Tests: Rename some mis-grouped tests 2026-08-24 18:41:25 -04:00
Wilson Snyder 6fc18710bd Commentary: Changes update 2026-08-24 18:16:58 -04:00
Yilou Wang 67cbc36ef0 Fix randomize misreading solver replies and applying partial models (Part3a of #7991) (#8136) 2026-08-24 18:14:06 -04:00
dependabot[bot] 2cd0efbc0f CI: Bump docker/setup-buildx-action in the everything group (#8210) 2026-08-24 12:28:51 -04:00
Yilou Wang 3c027baa44 Fix $past in final blocks on a sampling-tick finish (#8153) 2026-08-24 11:09:02 -04:00
Artur Bieniek 33ad9d530e Fix V3Width SVA bound limit guard, leave headroom for alignment, respect --max-num-width (#8192)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-24 08:57:18 -04:00
Jonathan Drolet d0321b8502 Fix VPI force data corruption (#8160) 2026-08-24 08:21:13 -04:00
Geza Lore ee8e2c0e12 Internals: Fix regression badge in README 2026-08-24 08:03:00 +02:00
Geza Lore 8fc78e31d7 CI: Name build-test.yml 'Regressions' 2026-08-24 08:03:00 +02:00
Wilson Snyder 477b48fb3d CI: Add pr-conflicting.yml 2026-08-23 18:48:57 -04:00
Wilson Snyder df0d801790 Internals: Duplicate code cleanup. No functional change intended. 2026-08-23 16:24:59 -04:00
Wilson Snyder d2760be0d4 Fix fork no-timing and constructor no-timing message 2026-08-23 16:24:59 -04:00
Wilson Snyder d18bcc2a69 Commentary: Changes update 2026-08-23 16:24:59 -04:00
Geza Lore 8a45a2d46e CI: Reduce verbosity of the Pages job 2026-08-23 19:56:26 +02:00
Marco Bartoli 63a59fce22 Fix typedef clocking input sampling (#7688 partial) (#8204) 2026-08-23 10:38:28 -04:00
Marco Bartoli c4b84b20f7 Support covergroup events with reference members or ref arguments (#8201) 2026-08-23 10:35:43 -04:00
Aditya Shevade d5f7878eb4 Fix internal error on an enum literal inside a with lambda body (#8190) (#8193) 2026-08-23 07:47:43 -04:00
github action 459711ba71 Apply 'make format' [ci skip] 2026-08-22 19:46:51 +00:00
Marco Bartoli 0820506d74 Support covergroup/function/task (ref/virtual/array) interface arguments (#8180) 2026-08-22 15:46:07 -04:00
Geza Lore 063b938f41 CI: Fold up old notification comments on PRs
Notifications posted on pull requests now carry a marker naming their
source, as an HTML comment on their first line, which is invisible when
rendered. When posting, 'ci-pages-notify.bash' finds its own earlier
notifications with the same marker on that pull request, and deletes them.
A pull request therefore holds a single comment per source, rather than
accumulating one per run.

The superseded comments are not lost entirely. Each notification carries a
one line summary linking its report, stashed in an HTML comment, and the
notification superseding it inherits that summary, together with the
history the superseded one held itself, into a collapsed 'Workflow history'
section. That section therefore lists all the earlier reports, stays flat
however many runs a pull request sees, and holds no entry for the report
shown right above it.

The 'pr-notification' artifact is now uploaded via the new reusable
'upload-pr-notification' action, which validates the content the reporting
scripts produced, and records the source key and the pull request number
alongside it.

Fixes #8037
2026-08-22 18:40:14 +02:00
Geza Lore cb618d6f9c CI: Factor out coverage report generation 2026-08-22 18:40:14 +02:00
Wilson Snyder 4aceba1106 Fix internal on backslash quoted strings (#8198).
Fixes #8198.
2026-08-22 10:51:45 -04:00
Wilson Snyder d359a9ce3c Commentary: Changes update 2026-08-22 10:47:46 -04:00
Artur Bieniek 3dbfa7708f Add NEVERMATCH warning when an SVA sequence can never match (#8176) (#8183)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-22 08:31:23 +02:00
Geza Lore cda258cbed Fix crash on multiple missing type parameters (#8184) 2026-08-21 17:15:41 -04:00
Wilson Snyder cccf7ef1b3 Internals: Fix clang warning (earlier commits) 2026-08-21 17:03:10 -04:00
Wilson Snyder 8844593abb Internals: Cleanup code duplication. No functional change intended. 2026-08-21 16:51:19 -04:00
Wilson Snyder 9badc5485d Internals: Cleanup code duplication. No functional change intended. 2026-08-21 16:51:19 -04:00
Wilson Snyder fd319beb8a Internals: Cleanup code duplication. No functional change intended. 2026-08-21 16:51:19 -04:00
Ryszard Rozak c9e270a9fa Internals: V3Randomize cleanup. No functional change intended (#8187)
Signed-off-by: Ryszard Rozak <[email protected]>
2026-08-21 16:36:52 -04:00
Geza Lore d1067002da Fix crash on empty '#()' parameter list on class instance (#8182)
The following used to crash:

```systemverilog
    class base #(type T = int); endclass
    class derived extends base; endclass
    derived #() d; // <- crash due to '#()'
```
2026-08-21 18:16:09 +02:00
Geza Lore a7ecba4f4e Fix crash on forward-referencing parameters (#8175) 2026-08-21 17:47:10 +02:00
Wilson Snyder 99c26d3994 Update linfstwriter from upstream (#7193 prep) (#8181) 2026-08-21 16:35:15 +02:00
Todd Strader e656b11cd1 Internals: Dev coverage tweaks (#8161) 2026-08-21 12:27:52 +02:00
Artur Bieniek f7c087468d Support overlong within sequences as never-matching (#8177)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-20 15:20:54 -04:00
Andrii 227e432695 Fix invalid conversion of VlQueue (#8172)
Signed-off-by: Andrii Andrieiev <[email protected]>
2026-08-20 07:09:05 -04:00
Geza Lore 38ae4e617d CI: Cache archives downloaded by apt (#8159)
Belt-and-braces in case GitHub apt mirrors start acting up again: On any
successful apt install, cache the deb files downloaded/needed for the
step, so on next run the mirrors need not be consulted. (That is: trade
apt mirror access with GitHub cache access, which is more reliable)
2026-08-20 11:16:17 +02:00
Andrii e717c513e9 Fix null pointer dereference in killQueue (#8171)
Signed-off-by: Andrii Andrieiev <[email protected]>
2026-08-20 10:55:45 +02:00
Kornel Uriasz f6f6f84047 Fix array reference in reduction constraints (#8158)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-08-19 15:52:04 +02:00
Geza Lore cacadd6c7d CI: Add timeout to apt install steps (#8154)
These tend to hang for hours on occasion. Install all apt dependencies
via ci-install.bash, and add a hard timeout and retry for each attempt.

Also removed unnecessary man-db config (GitHub has this off by default
now) and ineffective install path exclusions.
2026-08-19 15:34:57 +02:00
Geza Lore 65094514d6 Tests: Automatically disable ASLR under TSAN (#8156) (#8157)
Fixes #8156
2026-08-19 15:25:07 +02:00
Wilson Snyder f88b59f39f Revert fixed point (#8147 revert) (#8155) 86b7cfb05c 2026-08-19 07:54:42 -04:00
Wilson Snyder 700858c112 Commentary: Changes update 2026-08-19 07:50:32 -04:00
Geza Lore 2ba4bd9211 Internals: Add more statistics in V3Expand
- Per pattern stats, similar to DfgPeephole
- Impure rejection count
- Number of temporaries introduced

Tweak output for consistency
2026-08-19 13:28:01 +02:00
Geza Lore 7523a0cd8e Fix exponential expansion of nested selects (#8137) (#8151)
Fixes #8137
2026-08-19 11:00:47 +02:00
Artur Bieniek 8c2b7410f0 Optimize NFA delay-ring edge traversal, drop complexity to linear (#8145)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-19 04:02:11 -04:00
github action 70b5ce802b Apply 'make format' [ci skip] 2026-08-19 06:46:09 +00:00
Sumanth KadiyalaandSumanth Kadiyala 88e87b8abb Fix segmentation fault on parameters (#8102) (#8149)
Co-authored-by: Sumanth Kadiyala <[email protected]>
2026-08-19 02:45:35 -04:00
Pawel Klopotek 7628a540af Fix invalid C++ on cast (#8113)
Signed-off-by: Pawel Klopotek <[email protected]>
2026-08-19 08:29:29 +02:00
Artur Bieniek 06ee8b7261 Use unsigned in NFA, guard against overflow using V3Width. (#8152)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-19 08:05:05 +02:00
Pawel Klopotek f5ae58ddab Fix unpacked array passing to logic task arg error (#8148)
Signed-off-by: Pawel Klopotek <[email protected]>
2026-08-19 08:04:28 +02:00
Geza Lore 86b7cfb05c Optimize associative constant patterns to fixed point (#8147)
Re-associating constants can enable further simplification e.g. when
those constant become all zero/all ones. Run these patterns to fixed
point to capture the simplification, by revisiting the node.
2026-08-18 19:41:24 +02:00
Yilou Wang 8a29360ade Fix NFA assertion crash and mis-counted property if/case (#8074) 2026-08-18 18:30:38 +02:00
Marco Bartoli b032379de2 Support coverpoint and cross iff (#8125) 2026-08-18 09:46:31 -04:00
Geza Lore ba893abb2c Optimize circular logic in Dfg (#7902)
Introduce a new DfgPrev vertex, representing the value of a variable
before any in-graph assignments. This can be used to break all remaining
cycles in the graph, so all Dfgs become acyclic after V3DfgBreakCycles.
The circular dataflow is still represented, and is taken care of by the
scheduler, it is just the DfgGraph that represents the logic that
becomes acyclic.

This makes V3DfgBreakCycles a mandatory transform, so drop the disabling
-fno-dfg-break-cycles option (still parsed, but has no effect).

Note the effect of this is small, as most cycles can be fixed up by
driver tracing, which is unchanged, but this is required for some
upcoming work.
2026-08-18 14:54:12 +02:00
Geza Lore 94a63dd407 Fix streaming concatenation uninitialized RMW access (#8143) 2026-08-18 12:03:51 +02:00
Geza Lore d9fb606869 Internals: Move DPI export AstScopeName out of statement list (#6280) (#8139)
AstScopeName is an AstNodeExpr, but V3Task stashed the DPI export scoping
marker into the enclosing AstCFunc's statement list (addStmtsp), placing an
expression in a statement position.

Give AstCFunc a dedicated 'scopeNamep' operand to hold the marker instead.
Part of #6280
2026-08-18 11:25:12 +02:00
Geza Lore 8baff6ae33 Internals: Don't wrap elaboration severity task in an AstInitial (#6280) (#8142)
An elaboration severity system task ($info/$warning/$error/$fatal used
as a module/generate/program/checker item), which is a module item,was
parsed as an AstInitial wrapping an AstElabDisplay. That put
AstElabDisplay (a non-statement) into the procedural statement list of
an AstInitial.

AstElabDisplay is evaluated by V3Width, which then deletes it. The
AstInitial wrapper is historic and has no effect.

Part of #6280
2026-08-18 11:19:50 +02:00
Geza Lore 7c6b225b7e Internals: Make AstConstraintBefore an AstNodeStmt (#6280) (#8141)
AstConstraintBefore ('solve x before y') is a constraint item that appears
in constraint bodies alongside its siblings AstConstraintExpr,
AstConstraintUnique and AstConstraintIf, all of which are AstNodeStmt.
Part of #6280.
2026-08-18 11:19:40 +02:00
Geza Lore 2a9757bf38 Internals: Make AstCLocalScope an AstNodeStmt (#6280) (#8140)
AstCLocalScope wraps statements in an unnamed C++ scope ('{ ... }') and is
only ever placed in statement position. Part of #6280.
2026-08-18 11:19:27 +02:00
Kornel Uriasz 74d12c5b5e Fix 'get-value' for empty constrained arrays (#8138)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-08-18 10:05:00 +02:00
Geza Lore d4a18d4dfb Fix unordered data hazards in multi-threaded scheduling (#8133)
The OrderGraph used during V3Order step deliberately omits some variable
accesses from the dependency graph. E.g.: a read of a variable that is
in the reading block's own hybrid sensitivity list emits no edge, nor
does a read ignored due to a force/release, nor an access to a variable
marked 'ignoreSchedWrite' and friends. For serial mode that is fine, the
logic runs one block at a time. In parallel mode two such blocks can run
concurrently, and if one writes what the other reads, that is a data
race at runtime.

These accesses cannot be recovered from the graph edges. They are now
collected from the AST while the OrderGraph is built, and held by the
OrderLogicVertex performing them.

FixDataHazards is reworked around these access lists stored in
OrderLogicVertex, so it is now aware of all variable accesses the logic
makes, including those not encoded by the dependency graph edges. The
previous heuristic of fixing data hazards by merging same-rank MTasks is
removed. Additional edges are inserted instead to prescribe a fixed
ordering of conflicting MTasks. To insert edges without unduly
increasing the critical path, or introducing cycles, new edges are
added such that they preserve topological ordering, and they are
inserted between vertices sorted by critical path length. See algorithm
details in the code.

Also add a data hazard checker under '--debug-partition', reporting every
unordered accessor pair left in the final MTask graph.

This fixes the race demonstrated by t_sched_hybrid_hazard (#7913),
which is no longer expected to fail.

Under ThreadSanitizer over the vltmt tests: 17 failing before, 3 after,
with no regressions. The 3 remaining are different defects.
2026-08-18 08:50:50 +02:00
github action 96ea587df0 Apply 'make format' [ci skip] 2026-08-18 06:07:36 +00:00
Sunimali Rathnayake 2d8d8add42 Fix public table offsets larger than 32 bits (#8135) 2026-08-18 08:06:53 +02:00
Geza Lore 7dcd4e0b65 Improve MTask coarsening in multi-threaded scheduling (#8120)
This is a large refactor of the MTask graph and coarsening algorithm, in
prep for fixing the bug described in #7913, it can also improve the
resulting multi-threaded schedule.

Two major changes:

OrderMTaskGraph now maintains the critical paths of the MTasks through
mutation. There are 2 ways to mutate the graph, which are done via
methods on the graph itself: adding an edge (used during construction,
and will be used later during fixing data hazards), or merging an MTask
into another (used during contraction). All critical path measures are
automatically updated and propagated on any mutation, so no external
algorithm needs to maintain them explicitly.

The merge candidate scoreboard used during contraction is simplified to
remove deferral of updated scores. This simplifies the code and results
in a greedily more optimal schedule. (The previous tranched rescore was
an optimization to work around the previous std::set based scoreboard,
however since the algorithm now uses an efficient PairingHeap,
verilation time is not impacted by the more accurate scoring, while
yielding better results).

Combining these two into a single patch as the code is highly
interdependent and any one change without the other would be just a
noisy transit point with unclear performance implications. Together it
should be a clear improvement.

Also added a stronger validation step run with '--debug-partition',
which checks all invariants throughout the algorithms.
2026-08-17 23:14:52 +02:00
Yilou Wang ace84ef2b3 Fix simulation hang when the solver stops answering (Part2 of #7991) (#8129) 2026-08-17 13:01:41 +02:00
Matthew Ballance a3553e7bee Add runtime support for covergroup cross-points (#7847) 2026-08-16 15:33:38 -04:00
Wilson Snyder e2f7755611 Tests: Fix whitespace 2026-08-15 07:08:53 -04:00
em2machine d2f62fda93 Fix use-after-free of captured interface typedef reference during parameter cloning (#8076) 2026-08-15 06:07:01 -04:00
em2machine 93401038c0 Fix reading another parameter resolves to 0 in cloned modules (#8086) (#8069) 2026-08-15 06:06:25 -04:00
github action 3334576b1d Apply 'make format' [ci skip] 2026-08-15 01:00:00 +00:00
Wilson Snyder 6d6d15e003 Tests: Fix side-effects in checkd randomize 2026-08-14 20:59:19 -04:00
Wilson Snyder d6b206cc7d Internals: Cleanup some VPI messages and fix test coverage 2026-08-14 19:47:45 -04:00
Wilson Snyder edd4cd4faf Tests: Cleanup false suppressions in t_dist_warn_coverage.py 2026-08-14 18:55:41 -04:00
Wilson Snyder 91216bdac1 Tests: Add uvm-2020-3.2-vlt 2026-08-14 18:31:23 -04:00
Wilson Snyder 92300ab675 Commentary: Changes update 2026-08-14 18:28:19 -04:00
Artur Bieniek 0b50390359 Fix COVERIGN on SVA goto repetition (#8118)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-14 13:52:10 -04:00
Geza Lore 621a67e819 Fix R/W references to random builtin function seeds (#8112) 2026-08-14 12:29:12 -04:00
Adam Kostrzewski 75a776c516 Fix invalid typedef linkdot (#8106)
Signed-off-by: Adam Kostrzewski <[email protected]>
2026-08-14 12:28:35 -04:00
Demin Han 743e0f4a82 Fix the clang compilation using pch (#8105)
Signed-off-by: Demin Han <[email protected]>
2026-08-14 12:19:28 -04:00
Adam Kostrzewski 649598c74c Fix unintended side-effect insertion on associative array read (#8077) 2026-08-14 15:23:44 +02:00
Geza Lore 0a3657517d Optimize temporary insertion for single bit replicates in DFG (#8110)
A '{N{bit}}' mask expands to a bit vector where every word is the same
value, obtained by negating the replicated bit. Recomputing that at each
use is no more expensive than loading it
2026-08-14 14:58:31 +02:00
Kornel Uriasz 2957939d4b Throw unsupported when reduction constraining dynamic subarray (#8111)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-08-14 07:56:31 -04:00
Yilou Wang abf199cf0e Fix randc cycling in a class that also uses solve...before (Part1 of #7991) (#8055) 2026-08-14 07:16:44 -04:00
Marco Bartoli 7da43d8302 Support class handle covergroup arguments (#8071) 2026-08-13 20:41:39 -04:00
Wilson Snyder c8853d1507 Tests: Reformat some recent tests to mostly verilog-format standard. No test functional change. 2026-08-13 18:59:37 -04:00
Wilson Snyder edf43ae76f Tests: Rename inf to avoid VAMS errors 2026-08-13 18:59:37 -04:00
Wilson Snyder 63478f2946 Commentary: Changes update 2026-08-13 18:59:37 -04:00
Artur Bieniek 95721f5040 Optimize NFA ring buffer clear operation using lazy invalidation (#8101)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-13 18:54:57 -04:00
Geza Lore 1397d5d01e Optimize VL_WORDS_I/VL_BYTES_I (#8095) 2026-08-13 14:44:04 -04:00
github action 098f375e9d Apply 'make format' [ci skip] 2026-08-12 23:13:40 +00:00
Sumanth KadiyalaandSumanth Kadiyala 44fe96b2da Add +verilator+assert+lock to ignore RTL assert control statements (#8086)
Co-authored-by: Sumanth Kadiyala <[email protected]>
2026-08-12 19:13:02 -04:00
Artur Bieniek 0ebb92da26 Fix hierarchical class scope resolution (#8094)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-12 15:29:04 -04:00
Artur Bieniek 73bcf5e0db Fix NFA range ring outgoing bit selection (#8061 repair) (#8092)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-12 12:28:32 -04:00
Artur Bieniek e962efca12 Fix untyped dtype error on sampling functions with property arguments (#8060)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-12 05:16:48 -04:00
Wilson Snyder 0b51926fff Commentary: Changes update 2026-08-11 20:05:01 -04:00
Wilson Snyder 08cd5fe30b Tests: Reformat some recent tests to mostly verilog-format standard. No test functional change. 2026-08-11 20:04:45 -04:00
Nikolai Kumar da0c31926e Fix forceable unpacked array element force with procedural assign (#8084) (#8085) 2026-08-11 17:09:33 -04:00
Todd Strader e96cfc5e56 Tests: More VPI simulator support (#8083) 2026-08-11 15:14:01 -04:00
Igor Zaworski 2ef131f7ac Fix scheduling of variables written in non-inlined functions in suspendable processes (#7924)
Repair of reverted #7836

Signed-off-by: Igor Zaworski <[email protected]>
2026-08-11 13:18:18 +01:00
Igor Zaworski 945ea27fec Fix undefined symbol solver error (#8080)
Signed-off-by: Igor Zaworski <[email protected]>
2026-08-11 07:33:37 -04:00
Artur Bieniek 490bd38962 Optimize bounded always properties using ring buffers (#8061)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-10 08:45:50 -04:00
Bartłomiej Chmiel 121fc62aff Fix VPI cbValueChange for 1-bit select (#8063) 2026-08-10 07:52:18 -04:00
Paul Campbell 5d888d2ac0 Add SIMILARNAME warning when variables have names that only differ in lexical case (#7992) (#8020) 2026-08-10 06:57:58 -04:00
Kornel Uriasz 38259d11b6 Fix skipping non-constrained enum constraining when inside object of other class (#8075)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-08-10 06:52:14 -04:00
Tyrone Marhguy 86fa00416d Fix Linux peak memory stat to use VmHWM (#8022) (#8070) 2026-08-09 15:37:57 -04:00
Marco Bartoli 645b8cdf24 Support embedded covergroup clocking events (#8028) 2026-08-09 13:55:04 -04:00
Yogish Sekhar dde6aa34ce Fix FSM coverage on empty reset branches (#8005) (#8064) 2026-08-08 19:44:46 +01:00
Wilson Snyder 7601011bba Internals: Cleanup some user() comments. No functional change.
Fixes #8048.
2026-08-08 13:05:05 -04:00
Wilson Snyder 18844537bd Commentary: Changes update 2026-08-08 12:58:32 -04:00
Kamil Danecki 7e80ea6577 Support default clocking (#7984) 2026-08-07 13:21:18 +01:00
Nikolai Kumar 82dab79096 Fix wildcard equality against a 4-state constant in an assertion (#8056) (#8057) 2026-08-07 07:48:51 +01:00
Artur Bieniek 1a4bcac666 Support weak/strong keywords in property expressions (#8054)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-06 07:48:00 +01:00
Artur Bieniek e413e67ab3 Fix force helpers in multiply instantiated modules (#8047)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-05 16:47:45 -04:00
Artur Bieniek b1ec180a3b NFA simplification: do not pass arguments which values are derivable (#8053)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-05 12:40:12 -04:00
Marco Brambilla 1f164d7ee0 Fix wait fork trigger temporary split across generated functions (#7985) (#7986)
splitCheck() cuts a function's top level statement list on node count
alone, ignoring AstVar declarations in that list. localizeVars() puts the
dynamic trigger temporaries there, which a 'wait fork' reaches, so the
declaration could land in one sub-function and its references in another.
Sub-functions are emitted as separate C++ functions, so the output failed
to compile:

    error: '__Vtrigprevexpr_h5d9da2ce__0' was not declared in this scope

V3InlineCFuncs could also inline the sub-function holding the declaration
and free the AstVar while other sub-functions still referenced it, which
--debug reports as a broken link and which segfaults an -O3 build.

The existing "Can't split function with local variables" assertion only
checked AstCFunc::varsp(), not declarations among the statements.

Only allow a sub-function boundary where it does not separate a local
declaration from a reference to it. This keeps the temporaries function
local, as #6859 requires, while restoring the guarantee #5822 made that
splitting cannot orphan them.

Signed-off-by: Marco Brambilla <[email protected]>
2026-08-05 17:33:58 +01:00
Kornel Uriasz 3fa88ae7f3 Fix array size references in constraints (#8040)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-08-05 12:07:28 -04:00
Nikolai Kumar 351d67c81e Fix pin to non-existent port with PINNOTFOUND suppressed (#8051) (#8052) 2026-08-05 12:00:32 -04:00
Sumanth KadiyalaandSumanth Kadiyala 472a131df7 Fix nondeterminism in trace stage (#8046)
Co-authored-by: Sumanth Kadiyala <[email protected]>
2026-08-05 02:51:51 -04:00
Bartosz Skorowski e6c613f28d Fix write access to dynamic arrays (#8043)
Signed-off-by: Bartosz Skorowski <[email protected]>
2026-08-04 13:27:08 -04:00
Artur Bieniek e4b6bac50a Fix mid-range consecutive repetition rejects (#8041)
Signed-off-by: Artur Bieniek <[email protected]>
2026-08-04 17:50:33 +01:00
Pawel Klopotek 3c4ea47e02 Fix conditional expression with parameterized classes (#8039)
Signed-off-by: Pawel Klopotek <[email protected]>
2026-08-04 15:50:12 +01:00
github action 5bd8c49448 Apply 'make format' [ci skip] 2026-08-04 08:37:04 +00:00
JOTEGO ba8e4f9dec Fix gate deduplication with function arguments (#8038) 2026-08-04 04:36:25 -04:00
Kornel Uriasz 15b75c7c68 Fix skipping optimization for unpacked data types (#8031)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-08-04 01:48:15 -04:00
Yilou Wang 925a6640d1 Fix queued $finish/$stop request thread ordering (#7946 prep) (#7952) 2026-08-04 01:46:54 -04:00
Geza Lore b42d7c2d9e Internals: Move critical path propagators to MTaskGraph from Contraction
Code movement only prep for further work. No functional change, output
should be identical.
2026-08-03 17:35:10 +01:00
Geza Lore d58f512a85 Internals: Remove stepCost use in MT scheduling (#8032)
MT scheduling used to round MTask costs to the nearest 5% to supposedly
reduce the amount of work required for propagating critical path change
updates. Profiling shows this doesn't actually save any verilation time,
as the floating point arithmetic itself is costly, and the propagation
savings only kick in when merging small tasks into large ones, which is
not the common case. (The float arithmetic also made this step
non-reproducible across host architectures and libm versions  due to
numerical variance.)

Remove and use precise costs instead.
2026-08-03 17:34:44 +01:00
Nick Brereton 1356c15e44 Fix use-after-free in V3LinkDotIfaceCapture (#7943) 2026-08-03 15:55:30 +01:00
dependabot[bot] 3c47e5735a CI: Bump docker/login-action from 4.5.1 to 4.6.0 in the everything group (#8033) 2026-08-03 15:55:00 +01:00
Geza Lore 0750e18ad0 Internals: Remove ordering before MTask contraction (#8019)
As the removed comment suggests, this is dubious and RTLMeter suggests
it makes no measurable difference on average (some designs improve, some
regress small amounts, on average ~1.0x)

Prep for further fixes that will remove the pre ranking.
2026-08-03 10:44:51 +01:00
David Garau a24dd8b2dc Fix generic interface param resolution in module/cell parameterization (#7970) (#7971) 2026-08-03 05:18:59 -04:00
Yilou Wang 2022bacd38 Fix timing controls in interface tasks called via virtual interface (#7959) 2026-08-03 05:15:07 -04:00
Aisha 29fd2cf90d Add MULTIDRIVEN checks for clocking block outputs (#7987) 2026-08-03 04:54:43 -04:00
Josep Sans daf5826610 Fix assignment pattern key constant expressions (#8029) (#8030) 2026-08-02 12:53:43 +01:00
Krzysztof Bieganski 66808eec27 CI: Make light debug opt-in in CI build script (#8013) (#8027) 2026-08-02 02:34:38 -04:00
BRDR LIFE a3e7f5103d Tests: Check dist bucket draw is per-element in foreach (#8023) 2026-08-01 22:37:55 +05:30
Marco Bartoli 7e116ca601 Support embedded covergroup member references (Fix #7749) (#8015) 2026-08-01 02:13:53 -04:00
github action 56accae45e Apply 'make format' [ci skip] 2026-08-01 03:38:21 +00:00
BRDR LIFE 967b1bae57 Fix unique constraint crash, guards, and size (#8018) (#8018) 2026-07-31 23:37:53 -04:00
BRDR LIFE 6b3aebfa32 Fix dist inside a foreach nested in a constraint if (#8016) 2026-07-31 23:35:01 -04:00
Yilou Wang 704c5a1c96 Fix gcov dump on warnings discarding later coverage counts (#8017) 2026-07-31 12:08:05 -04:00
Geza Lore 781f6d90bf Internals: Refactor MT scheduling (#8012)
Prep for fixing test added in #7913.

This is a large scale no functional change refactor, however, MT output
is perturbed as tied scores will be broken differently due to ordering
changes (still deterministic).

Split multi-threaded scheduling out of the monolithic
V3OrderParallel.cpp, into relatively independent parts, simplify the
data structures, and drop redundant or unused code.

New translation units:
- V3OrderMTaskGraph.h/.cpp: OrderMTaskGraph, the graph of LogicMTask
  vertices and MTaskEdge edges. LogicMTask and MTaskEdge no longer
  depend on the coarsening algorithm's merge candidate types;
  per-algorithm auxiliary data is attached externally via the vertex and
  edge user pointers.
- V3OrderMTaskFixHazards.cpp: data hazard fixup, was FixDataHazards.
- V3OrderMTaskContraction.cpp: graph coarsening, was Partitioner
  together with PropagateCp and the merge candidate types.
- V3OrderParallel.cpp: now just the partitioning driver and ExecMTask
  graph construction.

Data structure changes:
- Delete V3Scoreboard.h/.cpp. The generic template had a single user, now
  a file-local MergeCandidateScoreboard in V3OrderMTaskContraction.cpp.
- Merge candidates are now MergeCandidate/SiblingMC/EdgeMC, distinguished
  by a bit in the candidate id rather than by a vtable, and allocated by
  the scoreboard, which owns their lifetime. This removes the multiple
  inheritance previously used by MTaskEdge.

Move `hashGraphDebug` which prints the hash of a graph's shape for debugging
to generic `V3Graph::hashGraphDebug`.

Removed (can be added back later):
- Unnecesasry self tests that force special data stucture requirements.
- Per stage --stats output under --debug. (Final figures still reported.)
- Various debug dumps
2026-07-31 15:03:26 +01:00
BRDR LIFE 40323cc02c Fix dist weights ignored when an item is not a literal (#8003) 2026-07-31 10:54:38 +05:30
BRDR LIFE 9cddf46932 Fix internal error forcing an array element read at a run-time index (#8004) 2026-07-30 23:57:15 -04:00
24bit-xjkp 589c9b9433 Fix missing verilator_coverage in cmake install (#8008) (#8011)
Fixes #8008.
2026-07-30 23:13:07 -04:00
Yilou Wang 99b71a37b7 Internals: Refactor randomization next and nextPhased into shared helpers (#7991 prep) (#8009). No functional change intended. 2026-07-30 21:41:37 -04:00
Artur Bieniek b70696fc0a Fix $finish continuing event loop (#7267) (#7950)
Fixes #7267,
2026-07-30 21:39:21 -04:00
Marco Bartoli 4b770cffb4 Fix cross module disable (Part 5 of #7857) (#8006) 2026-07-30 14:56:07 +01:00
github action 42e043cb01 Apply 'make format' [ci skip] 2026-07-30 10:15:15 +00:00
24bit-xjkp c5c3c45102 Internals: Remove static inline functions in headers (#7988) (#8007) 2026-07-30 15:44:40 +05:30
Marco Bartoli c3be3c8050 Fix nested named fork disable propagation (Part 4 of #7857) (#8001) 2026-07-29 14:49:34 +01:00
Gilberto Abram cac2c3df13 Fix MULTIDRIVEN/PROC warning suppression via lint_off (#8000) 2026-07-28 21:50:19 -04:00
BRDR LIFE 2e4dbb846c Fix force not substituted into an unpacked array index (#8002) 2026-07-29 06:58:06 +05:30
Yilou Wang b5fa4212b0 Fix UNSATCONSTR reporting constraint indices above 9 (#7995) 2026-07-28 18:25:19 -04:00
github action c23b0ee972 Apply 'make format' [ci skip] 2026-07-28 21:32:05 +00:00
Marco Bartoli 48e575b885 Fix self-disable of named blocks with forks (Part 3 of #7857) (#7996) 2026-07-28 22:31:32 +01:00
BRDR LIFE 5fd1c93d43 Fix randomize() with shadowing a user variable named 'item' (#7993) (#7994)
Fixes #7993.
2026-07-28 12:29:45 -04:00
spomatasmd a6f4dd031f Fix DfgPeephole miscompile of nested shifts with overflowing shift amount (#7977)
The REPLACE_SHIFTL_SHIFTL and REPLACE_SHIFTR_SHIFTR peephole optimizations
fold '(a << b) << c' into 'a << (b + c)' (and likewise for '>>'), but computed
'b + c' in the width of the shift-amount operand. When 'b + c' overflows that
width it wraps around, producing a too-small shift amount and a wrong result.

Compute the sum one bit wider than the amounts so it cannot overflow.

Fixes #7955
2026-07-28 10:42:51 +01:00
Marco Bartoli 73b3ca7d2c Fix immediate disable of fork join branches (#7856) (#7931) 2026-07-28 10:34:02 +01:00
dependabot[bot] de4de1c3f8 CI: Bump the everything group with 2 updates (#7989) 2026-07-27 19:52:43 +05:30
Wilson Snyder f8fb1d6646 Fix SYNCASYNCNET false positive with changed non-edge logic (#7980).
Fixes #7980.
2026-07-24 09:53:36 -04:00
Wilson Snyder d2de6b42cf * Fix $fgets being mis-optimized away (#7976).
Fixes #7976.
2026-07-24 09:26:01 -04:00
Wilson Snyder 370a40348b Commentary: Changes update 2026-07-24 09:20:25 -04:00
Bartosz Skorowski ee152be17b Fix lost writes when select width exceeds variable width (#7975)
Signed-off-by: Bartosz Skorowski <[email protected]>
2026-07-24 08:58:28 -04:00
github action a799fedea6 Apply 'make format' [ci skip] 2026-07-24 12:56:19 +00:00
Aisha 8f8b5ade55 Add MULTIDRIVENPROC warning for signals driven by multiple plain always blocks (#7968) 2026-07-24 08:54:09 -04:00
Jakub Michalski bb0300f605 Fix table optimization causing not contextually convertible to bool error (#7983)
Signed-off-by: Jakub Michalski <[email protected]>
2026-07-24 08:50:03 -04:00
Kornel Uriasz 7d3021c34d Fix varRef scope in unique on dynamic array (#7981)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-07-23 15:56:51 -04:00
Wilson Snyder e0e0fadacc Tests: Update gold files 2026-07-22 12:59:20 -04:00
Igor Zaworski f511f6c34d Add FSM arc coverage to verilator_coverage .info output
Signed-off-by: Igor Zaworski <[email protected]>
2026-07-22 12:26:14 -04:00
Igor Zaworski 0a855fce47 Add FSM state coverage to verilator_coverage .info output (#7973) 2026-07-22 12:24:02 -04:00
Pawel Klopotek f149dd304c Add error when scalar is passed to array task argument (#7948) 2026-07-22 07:54:58 -04:00
Philip Axer 8113f1df7a Fix randomization of unreferenced rand members of unpacked structs (#7910) (#7911)
Fixes #7910.
2026-07-21 07:42:04 -04:00
Yilou Wang b34f685f70 Fix dist constraint on a frozen variable failing randomization (#7878) 2026-07-21 06:39:09 -04:00
Yilou Wang f22676eea9 Fix solve-before dropping all soft constraints in randomize() (#7963) 2026-07-20 14:55:49 -04:00
Patrick Creighton cdfff86c2d Support delayed tristate gates (#7960) (#7961)
Fixes #7960.
2026-07-20 09:21:23 -04:00
Bartosz Skorowski 176e333774 Fix lambda parameter types in queue min and max (#7962)
Signed-off-by: Bartosz Skorowski <[email protected]>
2026-07-20 06:53:40 -04:00
Wilson Snyder 3924fe9c18 Fix uncaught type error leading to invalid C++ output (#7814).
Fixes #7814.
2026-07-19 13:32:53 -04:00
Wilson Snyder 3813249826 Commentary: Changes update 2026-07-19 13:31:48 -04:00
Mateusz Gancarz 185a49e628 Fix unlinked error with function call in derived class parameter (#7923)
Signed-off-by: Mateusz Gancarz <[email protected]>
2026-07-19 09:55:29 -04:00
Yilou Wang f7ce38dbc1 Tests: Pre-pull for #7946 (#7953) 2026-07-18 21:01:53 -04:00
Yilou Wang 22050b97a7 Internals: Refactor dist constraint lowering helpers from lowerDistConstraints (#7878 prepull) (#7954) 2026-07-17 11:42:58 -04:00
Adam Kostrzewski 7e1efe6d76 Support dynamic containers in unique constraints (#7947)
Signed-off-by: Adam Kostrzewski <[email protected]>
2026-07-17 10:55:14 -04:00
Eryk Szpotański f39c1799d2 Commentary: Fix the verilator_coverage -filter-type documentation (#7949)
Signed-off-by: Eryk Szpotanski <[email protected]>
2026-07-17 08:18:43 -04:00
Pawel Klopotek cf27c63b2d Fix type resolution when a local name shadows a type name (#7915) 2026-07-17 08:10:23 -04:00
Artur Bieniek 796d5174f3 Fix sampled-value clocks in repeated assertions (#7944) 2026-07-16 13:07:53 -04:00
Patrick Creighton cc1b36bafd Fix FSM detection of coverage-only branches (#7941) (#7942)
Fixes #7941
2026-07-16 09:48:36 -07:00
Artur Bieniek 255bcff3ac Support [*N:$] consecutive repetition (#7940)
Signed-off-by: Artur Bieniek <[email protected]>
2026-07-16 09:29:30 -04:00
Philip Axer 877fc1e812 Fix dropping variable writes across opaque calls (#7921) (#7933)
Fixes #7921.
2026-07-15 11:03:04 -04:00
Andrii affe4cde57 Fix class parameter resolution (#7935) 2026-07-15 10:56:22 -04:00
Kornel Uriasz 232b2eb04c Fix dynamic array handling in solve...before (#7922) 2026-07-15 10:48:54 -04:00
Nick Brereton baf012cf6a Fix streaming concat as output-port lvalue into unpacked struct (#7918) 2026-07-15 10:46:25 -04:00
Nick Brereton 25377b20af Optimize VPI symbol registration and scope construction (#7936) 2026-07-15 10:31:42 -04:00
Kornel Uriasz d9fdc20492 Internals: V3Randomize.cpp cleanup (#7937). No functional change. 2026-07-15 05:32:56 -07:00
Wilson Snyder 5ead758f28 Internals: Use addNextNull. No functional change intended. 2026-07-14 19:58:25 -04:00
Wilson Snyder 34603ebd60 Commentary: Changes update 2026-07-14 19:55:50 -04:00
Igor Zaworski 3b43b304a3 Add error on static virtual functions (#7932) 2026-07-14 05:12:26 -07:00
Marco Bartoli 3afb7e1a21 Internals: Refactor disable process queue plumbing (#7919) 2026-07-13 21:25:35 -04:00
Jeffrey Song e24efae5a0 Fix stream concatenation in init array patterns (#7890 repair) (#7928) 2026-07-13 20:29:19 -04:00
Jeffrey Song 4262aea87c Internals: Use snprintf for random unique indexes (#7925) 2026-07-13 11:35:22 -07:00
Nick Brereton dde8de0a0d Fix crash streaming an unpacked array of unpacked structs (#7917) 2026-07-11 20:31:01 -07:00
Yilou Wang 7ca78a75e7 Fix scoped randomize with array members under rand_mode (#7877) 2026-07-11 20:28:49 -07:00
Yilou Wang ec124a0905 Fix mid-window disable iff (#7792) (#7869)
Fixes #7792
2026-07-11 20:44:56 -04:00
Yilou Wang ca83bc50bf Fix solve-before over array variables failing randomization (#7876) 2026-07-11 20:44:37 -04:00
Geza Lore 52287c025f Tests: Add TSan and failing multi-threaded data race test (#7913)
PR #7902 uncovered a pre-existing bug in multi-threaded scheduling,
where we can end up with an un-ordered R-W hazard in the MTask graph,
resulting in non-deterministic runtime behaviour.

This is extremely hard to actually trigger on a small example, so using
ThreadSanitizer to flag it, which can identify the race reliably.

In this patch:
- Add configure and `verilator --get-supported TSAN` to check if the
  configured compiler supports ThreadSanitizer
- Add a --tsan option to the test driver.py which builds the test with
  thread sanitizer (similar idea to --gdbsim).
- Add a tests.enable_tsan() method to allow turning on TSan in the test
  Python file.
- Add a suppressions file that waives TSan errors in the runtime library
- Finally add `t_sched_hybrid_hazard` that demonstrates the data race
  triggered after #7902. This is currently expected failing, fix later.

With the suppression, there are 17 vltmt tests failing due races in the
generated code. (Using `driver.py --vltmt --tsan --quiet -j0`)
2026-07-10 14:34:51 +01:00
Yogish Sekhar 3c7727a8f4 Fix spurious FSM COVERIGN on datapath comparisons (#7900) (#7908)
Fixes #7900.
2026-07-10 09:00:17 -04:00
Wilson Snyder 3191d98391 Commentary: Changes update 2026-07-10 08:43:12 -04:00
Wilson Snyder 3bd04a70bd Commentary (#7916) 2026-07-10 08:38:01 -04:00
Artur Bieniek feb2fe0d94 Fix release of forced port nets (#7907) (#7901)
Fixes# 7901

Signed-off-by: Artur Bieniek <[email protected]>
2026-07-10 11:35:06 +01:00
Artur Bieniek b211af9d24 Fix independent force of multiply instantiated signals (#7905)
Signed-off-by: Artur Bieniek <[email protected]>
2026-07-10 11:31:08 +01:00
Kornel Uriasz 9740203485 Fix VL_TO_STRING function for array of structs (#7912)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-07-09 08:40:24 -07:00
Adam Kostrzewski a21bd8a417 Fix VlQueue falling into wrong template spec (#7914) 2026-07-09 07:33:28 -07:00
github action 1d42c45a34 Apply 'make format' [ci skip] 2026-07-09 12:46:08 +00:00
Jaeuk Lee 38895f8f29 Fix $display accepting streaming concat arguments (#7663) (#7890)
Fixes #7663.
2026-07-09 08:41:40 -04:00
Wilson Snyder f316ec0d66 Revert #7836: Fix scheduling of variables written in non-inlined functions in suspendable processes (#7836) 2026-07-08 14:45:05 -04:00
Artur Bieniek 671ff628ff Tests: Fix t_force_release to reflect intent (#7906)
Signed-off-by: Artur Bieniek <[email protected]>
2026-07-08 19:29:49 +01:00
Patrick Creighton c7e8075972 Fix --coverage on labelled inline assert/cover property (#7898) (#7904)
Fixes #7898
2026-07-08 09:18:33 -07:00
Jakub Michalski 646dcd3838 Support user-provided DPI-C function declarations (#7626) (#7893)
Fixes #7626.
2026-07-08 05:22:24 -07:00
Jakub Michalski 93d0443998 Fix memory leak in VerilatedFst::close() (#7899)
Signed-off-by: Jakub Michalski <[email protected]>
2026-07-08 08:48:17 +01:00
Artur Bieniek 6c20fdb7bd Optimize assertion NFAs using bit-vector ring buffers (#7885)
Signed-off-by: Artur Bieniek <[email protected]>
2026-07-07 19:26:50 -04:00
Geza Lore e743838d09 Internals: Remove DfgGraph::clone (#7896)
Only used by V3DfgBreakCycles, which today either improves the graph, or
leaves it unchanged. Remove unnecessary complexity.
2026-07-07 20:51:40 +01:00
Yilou Wang f1a8192a36 Fix mixed-width inside and dist range bounds failing randomization (#7875) 2026-07-07 11:26:19 -07:00
Matthew Ballance 87d77f0133 Fix internal error for coverpoints that reference a covergroup formal parameter (#7853 partial) (#7889) 2026-07-07 11:42:39 -04:00
Igor Zaworski aca58ca90f Fix scheduling of variables written in non-inlined functions in suspendable processes (#7836)
Signed-off-by: Igor Zaworski <[email protected]>
2026-07-07 12:34:24 +01:00
Artur Bieniek a5f4d40901 Fix range delays with parameter bounds (#7882)
Signed-off-by: Artur Bieniek <[email protected]>
2026-07-06 13:13:11 -04:00
Yilou Wang 8ed0acf1ec Support a sequence used as an event control (#7797) (#7846) 2026-07-06 13:12:01 -04:00
Geza Lore 39b646349f Add --enable-light-debug configure option (#7886)
Change default debug build to use -O0 instead of -Og as the latter is
still not great debugging experience. Use -Og -gz, plus the new -ggdb1,
which together significantly cut down debug info size, to save CI cache
space, while still enabling -gdbbt tests to work.
2026-07-06 18:02:37 +01:00
Geza Lore 56b0b679c6 CI: Disable ccache on coverage jobs (#7887)
gcc --coverage is unsupported by ccache and results in a bypass. Don't
save unnecessary caches.
2026-07-06 17:05:14 +01:00
dependabot[bot] 6baea8d846 CI: Bump the everything group with 5 updates (#7884) 2026-07-06 09:50:16 -04:00
Geza Lore fcc7acbfc6 CI: Factor out artifact based caching as a reusable action (#7883) 2026-07-06 14:16:06 +01:00
Geza Lore bf4c72f93b CI: Squash macos brew warning (#7881) 2026-07-06 11:43:55 +01:00
Geza Lore c78a342bdc CI: Don't Python make venv on cache hit 2026-07-06 11:30:59 +01:00
Geza Lore c2ed0aab00 CI: Use ci-install.bash for lint-py dependencies (#7880) 2026-07-06 11:27:58 +01:00
Geza Lore 5563b3b6f8 CI: Cache Python venv (#7879)
This is smallish and mostly static, should cache well, avoid setup cost.
2026-07-06 11:03:35 +01:00
Geza Lore 5750c888a6 CI: Improve ccache handling in build and test jobs (#7874)
Cache key isolation: Include the build flavour (ccwarn/asan/gcov) in the
build ccache key, so the coverage (gcov), asan, and RTLMeter
(ccwarn-off) configurations no longer share and thrash the single
'<runs-on>-<cc>' bucket, nor warm-start from each other's (near-useless)
caches.

Prune stale entries instead of a fixed size cap: After building/testing,
evict ccache entries this run did not touch (--evict-older-than, based
on the run's own duration).  This bounds the saved cache to the current
working set, so CCACHE_MAXSIZE and CCACHE_LIMIT_MULTIPLE are no longer
needed and are removed (ccache falls back to its 5G default ceiling, but
is realistically much smaller).

ccache configuration:
- Drop CCACHE_COMPRESS (already the default in ccache 4.x).
- Add CCACHE_COMPILERCHECK=content, so a changed compiler mtime on a rebuilt
  ephemeral runner image does not invalidate the whole restored cache.
- Hoist all ccache-related variables (CACHE_BASE_KEY, CCACHE_*, CXX) to the
  workflow-level env.

Remove dead ccache scaffolding:
- reusable-lint-py: the job never compiles or caches; drop its ccache env.
- reusable-rtlmeter-run: remove the disabled persistent-cache step and its
  storage variables; keep CCACHE_DISABLE=1, since caching must stay off to
  keep RTLMeter timings representative.

Store test job ccache as artifacts for reuse
2026-07-06 01:44:32 +01:00
Geza Lore 8b564f509b CI: Remove macos-15 gcc job
On macOS, gcc is an alias for clang, so this job was effectively the
same as the clang job.
2026-07-05 21:18:36 +01:00
Geza Lore c7c778f109 CI: Improve Pages job
Code coverage and RTLMeter complete (concluding 'skipped') on branch
pushes where they do no work, and each completion fires the Pages
workflow_run trigger. Rebuilding and redeploying master's pages for
these no-op events is wasteful.

Gate the build job so a workflow_run whose upstream conclusion is
'skipped' does nothing; deploy and notify need build, so they
cascade-skip.

Route such no-op runs to their own throwaway concurrency group. In the
shared 'pages' group they would, as the newest queued run, cancel a
pending real run and then deploy nothing (concurrency keeps only the
in-progress and newest pending run). Real runs keep sharing 'pages' and
serialize as before.

Add retry to deploy step to reduce flakyness due to the Pages back end.

Fix trigger on workflow file changes.
2026-07-05 21:11:45 +01:00
Geza Lore cbd78e689c CI: Cleanup Windows build (#7873)
The Windows build never actually used ccache; the '.ccache' directory
only ever held the win_flex_bison install, and cmake/MSVC did no compiler
caching. Clean this up:

- Drop the CCACHE_* environment variables from the job (meaningless here).
- Store win_flex_bison in a dedicated 'win_flex_bison' directory instead of
  the misleadingly named '.ccache', and cache just that (static key, as it
  is a fixed third-party tool).
- Set WIN_FLEX_BISON inside ci-win-compile.ps1 as an absolute path, rather
  than in the workflow, making the script self-contained.
- Use the runner's processor count for build parallelism instead of a fixed
  '-j 3', in both ci-win-compile.ps1 and ci-win-test.ps1.
- Remove the verilator-win.zip archive and upload steps; nothing consumes
  that artifact.
- Build Verilator with the Ninja generator from an MSVC developer shell
  instead of MSBuild. MSBuild only parallelizes across projects, and
  Verilator is a single project, so it compiled serially; Ninja keeps all
  cores busy. Also compile with /Od: this job only checks that Verilator
  builds and can verilate an example, so an optimized binary is not needed
  and the otherwise-dominant optimizer time is wasted.
2026-07-05 19:15:09 +01:00
Geza Lore 8d0c75171c CI: Unify regression and RTLMeter build jobs to one (#7872)
- Merge reusable-rtlmeter-build.yml into reusable-build.yml, selected by
  an 'install' flag: archive the installed Verilator for RTLMeter, or
  the whole repository tree for the test jobs.
- Align reusable-test.yml input arguments with  reusable-build.yml
- Pass build/test options as booleans instead of 0/1.
- Inline the single-entry build matrices, and hoist matrix keys that are
  constant across a job.
2026-07-05 18:52:55 +01:00
Geza Lore 6e06cd18da CI: Decouple build and test into separate scripts, simplify workflows (#7871)
- Split the monolithic ci-script.bash into ci-build.bash and
  ci-test.bash, with shared setup in ci-common.bash.
- Pass job parameters as explicit command-line options rather than
  environment variables (--compiler, --prefix, --suite, --asan,
  --gcov, --reloc, --ccwarn).
- Detect the host OS and distribution at runtime (uname,
  /etc/os-release) instead of the legacy Travis-era CI_OS_NAME,
  CI_RUNS_ON and CI_BUILD_STAGE_NAME variables.
- Remove dead code: FreeBSD support, Windows wavediff install, the
  unused CI_COMMIT variable, and various redundant operations.
- Inline the single-entry build-job matrices.
- Rename osx to macOS
- Refuse to run these ci scripts outside GitHub Actions to protect
  developer checkouts.
2026-07-05 15:46:30 +01:00
Geza Lore 198cce29ba CI: Restore relocation testing (#7870)
PR #7776 removed Ubuntu 22.04 clang from the test matrix, which was the
only one testing the relocation of an installation. Add back under
Ubuntu 24.04 clang.
2026-07-05 14:26:30 +01:00
Geza Lore a518efbedb Internals: Prepare V3DfgBreakCycles to support multi-dimensional arrays (#7867)
This is prep for more array support later. Minor functional improvement
for tracing arrays, but no meaningful change.
2026-07-05 11:40:08 +01:00
Geza Lore 13236822d3 CI: Use faster zstd for repo archives (#7868) 2026-07-05 09:24:33 +01:00
Geza Lore 2285e5b6da Optimize more always blocks in Dfg (#7775)
Optimize always blocks that:
- drive an unused variable (so logic can be pruned)
- write only a single variable (regardless of CFG size)
2026-07-05 09:24:01 +01:00
Geza Lore 1bab793c75 CI: Fix deleting pr notifications after multiple reruns 2026-07-04 22:46:19 +01:00
Geza Lore 566f4e6efc Internals: Add new VL_RESTORER flavours (#7866)
Restrict VL_RESTORER to be usable only with trivially copyable types.
Introduce VL_RESTORER_COPY and VL_RESTORER_CLEAR, which are more
efficient versions usable for non trivially copyable types.

VL_RESTORER_COPY semantically behaves the same as VL_RESTORER, but only
does one copy at initialization, and on scope exit restores via a move.

VL_RESTORER_CLEAR swaps the variable with an new one constructed via
the no-args constructor (e.g. empty collection), which does not require
any copying at any point.

Static assertions enforce picking one of the new flavours when copying
might be expensive.
2026-07-04 17:27:07 +01:00
Wilson Snyder 83eb54ec7e Fix wild compare operators on strings 2026-07-04 11:05:34 -04:00
Wilson Snyder f82f59a024 Commentary: Changes update 2026-07-03 13:01:05 -04:00
Wilson Snyder 12fab5d5d7 Tests: Cleanup some cross-simulator test differences 2026-07-03 13:00:23 -04:00
Pawel Klopotek 7ece66d06e Fix unique0 case side effects (#7787) 2026-07-03 12:49:21 -04:00
Eryk Szpotański a64234e897 Add comments as a branch description in coverage .info files (#7843)
Signed-off-by: Eryk Szpotanski <[email protected]>
2026-07-03 11:18:29 -04:00
Dragon-Git 58bd13b623 Fix heap-use-after-free in VlRNG::VlRNG() (#7865) 2026-07-03 06:38:48 -04:00
Pawel Kojma b97df914dd Fix clang++ ambiguous overload of '==' operator (#7863) 2026-07-03 06:37:48 -04:00
Geza Lore 0e371d6e5c Fix cleaning purity cache after assertions 2026-07-02 17:47:09 +01:00
Geza Lore 1106e89c84 Optimize random initialization
Fetching a thread-local is relatively expensive. Random initializing
wides used to do it once or twice per word, and on short runs for
large designs can be noticeably expensive, so fetch once per variable
instead. Also remove unused VL_RAND_RESET_{Q,W} functions.
2026-07-02 16:38:14 +01:00
Jakub Michalski 506f703da5 Internals: Remove unused variables in verilated_funcs.h (#7862)
Signed-off-by: Jakub Michalski <[email protected]>
2026-07-02 07:16:42 -04:00
Jakub Michalski 853ee5df17 Fix DFG misoptimizing bound checks (#7755)
Signed-off-by: Jakub Michalski <[email protected]>
2026-07-02 11:33:38 +01:00
Wilson Snyder 964474837f Commentary 2026-07-01 19:37:12 -04:00
Wilson Snyder ce4be92a67 devel release 2026-07-01 18:31:23 -04:00
Wilson Snyder 3d2421f3bf Merge from master for release. 2026-07-01 18:30:39 -04:00
Wilson Snyder 28c006ee8c Merge from master for release. 2026-07-01 18:26:56 -04:00
Wilson Snyder 193b1d2c9f Version bump 2026-07-01 18:17:03 -04:00
Wilson Snyder 147fe4ee9f Commentary: Changes update 2026-07-01 18:15:56 -04:00
Wilson Snyder c233a39052 Merge from master for release. 2026-04-26 01:57:12 -04:00
Wilson Snyder 1264184fbb Revert "Fix recursive constant function in $unit scope (V3Scope.cpp:359) (#7174)" (#7178)
This reverts commit 5d1992ef05.
2026-03-02 15:07:20 -05:00
jalcim 5d1992ef05 Fix recursive constant function in $unit scope (V3Scope.cpp:359) (#7174) 2026-03-02 15:04:28 -05:00
Wilson Snyder f4369ed694 Merge from master for release. 2026-02-28 09:31:29 -05:00
Wilson Snyder 7e7b6d7fb8 Merge from master for release. 2026-01-01 07:32:37 -05:00
Wilson Snyder d915745073 Merge from master for release. 2025-11-02 11:17:12 -05:00
Wilson Snyder 0a9d9db5ae Merge from master for release. 2025-08-30 18:14:20 -04:00
Wilson Snyder 318b1c1a34 Merge from master for release. 2025-07-08 23:17:56 -04:00
Wilson Snyder 0dc93c1d5c Merge from master for release. 2025-04-27 13:04:49 -04:00
Wilson Snyder ac3f30ed6c Merge from master for release. 2025-02-24 18:32:52 -05:00
Wilson Snyder 447ab12cec Merge from master for release. 2025-01-01 09:07:59 -05:00
Wilson Snyder 2cb1a8de73 Merge from master for release. 2024-10-27 10:06:28 -04:00
Wilson Snyder 1d79f5c59b Merge from master for release. 2024-08-21 08:54:42 -04:00
Wilson Snyder 065f36ab56 Merge from master for release. 2024-06-15 08:48:03 -04:00
Wilson Snyder c561fe8baf Merge from master for release. 2024-04-05 06:05:57 -04:00
Wilson Snyder 2b4852048d Commentary - update version date 2024-02-25 07:20:30 -05:00
Wilson Snyder 0144e3ebec Merge from master for release. 2024-02-24 07:48:43 -05:00
Wilson Snyder d410e551f8 Merge from master for release. 2024-01-01 03:45:16 -05:00
Wilson Snyder 344f87abe3 Merge from master for release. 2023-10-31 19:51:59 -04:00
Wilson Snyder 57c816f906 Merge from master for release. (update) 2023-09-16 21:44:39 -04:00
Wilson Snyder 82e1dbe162 Merge from master for release. 2023-09-16 20:43:53 -04:00
Wilson Snyder e6b0bdd4df Merge from master for release. 2023-08-06 14:17:13 -04:00
Wilson Snyder 4f13c4d1bb Merge from master for release. 2023-06-13 19:37:22 -04:00
Wilson Snyder c37ec19751 Merge from master for release. 2023-04-30 07:09:22 -04:00
Wilson Snyder aaaf8e75af Merge from master for release. 2023-03-04 11:29:11 -05:00
Wilson Snyder 8cc31c6cf1 Merge from master for release. 2023-01-22 11:52:52 -05:00
Wilson Snyder 8468af1a20 Merge from master for release. 2022-12-14 22:07:37 -05:00
Wilson Snyder db39d70c75 Merge from master for release. 2022-10-29 17:52:23 -04:00
Wilson Snyder 04fff7ebd8 Merge from master for release. 2022-10-01 08:33:50 -04:00
Wilson Snyder b154af87a1 Merge from master for release. 2022-08-31 19:47:07 -04:00
Wilson Snyder 8b7480806a Merge from master for release. 2022-06-19 15:11:19 -04:00
Wilson Snyder e29f5ad559 Merge from master for release. 2022-06-19 15:08:17 -04:00
Wilson Snyder 4618fd5a43 Merge from master for release. 2022-05-02 22:22:20 -04:00
Wilson Snyder ed979c6aa1 Merge from master for release. 2022-03-12 13:14:48 -05:00
Wilson Snyder 141c5da3f9 Merge from master for release. 2021-12-05 11:15:32 -05:00
Wilson Snyder 0185ee5df3 Merge from master for release. 2021-10-17 14:46:03 -04:00
Wilson Snyder 27c08a65e1 Merge from master for release. 2021-09-01 21:02:51 -04:00
Wilson Snyder de408a5eb7 Merge from master for release. 2021-07-07 20:48:51 -04:00
Wilson Snyder fa3c072dd1 Merge from master for release. 2021-06-12 13:01:37 -04:00
Wilson Snyder aac4b7090a Merge from master for release. 2021-04-24 10:40:13 -04:00
Wilson Snyder f0d664530d Merge from master for release. 2021-03-12 15:48:45 -05:00
Wilson Snyder 702140bf93 Merge from master for release. 2021-02-25 21:49:42 -05:00
Wilson Snyder 2075db3db0 Merge from master for release. 2021-01-10 13:35:45 -05:00
Wilson Snyder b7fec513cf Merge from master for release. 2020-12-02 20:13:31 -05:00
Wilson Snyder 78e7758022 Merge from master for release. 2020-11-14 09:58:28 -05:00
Wilson Snyder 818318ddd5 Merge from master for release. 2020-10-15 08:25:44 -04:00
Wilson Snyder 39eea781bc Merge from master for release. 2020-09-07 09:38:50 -04:00
Wilson Snyder c4f7bd6a04 'Merge from master for release.' 2020-08-15 09:18:29 -04:00
Wilson Snyder dd03d0f3c9 'Merge from master for release.' 2020-07-11 09:16:56 -04:00
Wilson Snyder 369ce6af2c 'Merge from master for release.' 2020-06-06 07:49:57 -04:00
Wilson Snyder f83817510d 'Merge from master for release.' 2020-05-03 11:15:21 -04:00
Wilson Snyder bfcd779cfb 'Merge from master for release.' 2020-04-04 08:50:02 -04:00
Wilson Snyder e69c380205 'Merge from master for release.' 2020-03-08 08:39:29 -04:00
Wilson Snyder 61e14aa827 'Merge from master for release.' 2020-02-08 09:15:27 -05:00
Wilson Snyder 0c6c83e278 Merge from master for release. 2020-01-11 07:12:09 -05:00
Wilson Snyder 6f52208b68 Fix clang warning. 2019-12-09 18:28:56 -05:00
1087 changed files with 145401 additions and 13856 deletions
+165
View File
@@ -0,0 +1,165 @@
---
# DESCRIPTION: Github actions composite action
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: Artifact cache
description: >-
Cache a directory across workflow runs using a GitHub Actions artifact instead
of actions/cache (useful where artifacts are preferable, e.g. no 10 GB limit
on public repos). The base 'key' is scoped automatically: per pull request
(<key>-pr-<N>) or per branch (<key>-branch-<name>), and on a PR restore falls
back to the target branch's cache. Call once with mode=restore before the work
and once with mode=save after, passing the same 'key' and 'path'.
Branch-scoped restores are provenance-checked (the artifact must come from a
same-repo push to that branch) - artifact names are a global, unauthenticated
namespace, so otherwise a fork PR could forge one.
mode=restore calls the GitHub API via 'gh', so the caller's job must grant
'actions: read'.
inputs:
mode:
description: "'restore' or 'save'"
required: true
path:
description: "Directory to cache (archived on save, extracted into on restore)"
required: true
key:
description: "Base artifact name; scoped per PR/branch automatically"
required: true
retention-days:
description: "Artifact retention in days (mode=save)"
required: false
default: '3'
token:
description: "Token for the GitHub API (mode=restore); defaults to the job's GITHUB_TOKEN"
required: false
default: ''
outputs:
key:
description: "Echoes the input 'key', so a later save can reuse it without repeating the expression"
value: ${{ inputs.key }}
cache-hit:
description: "'true' if an artifact was restored (mode=restore)"
value: ${{ steps.restore.outputs.cache-hit }}
runs:
using: composite
steps:
- name: Check mode
shell: bash
env:
MODE: ${{ inputs.mode }}
run: |
case "$MODE" in
restore|save) ;;
*) echo "::error::artifact-cache: 'mode' must be 'restore' or 'save' (got '$MODE')"; exit 1 ;;
esac
# Scope the base key: <key>-pr-<N> for pull requests (with the target branch
# as fallback), else <key>-branch-<name>. Refs come via env, not ${{ }}
# interpolation, so a branch name with shell metacharacters can't inject.
- name: Compute artifact name
id: name
shell: bash
env:
BASE_KEY: ${{ inputs.key }}
EVENT_NAME: ${{ github.event_name }}
PR_NUMBER: ${{ github.event.pull_request.number }}
BASE_REF: ${{ github.event.pull_request.base.ref }}
REF_NAME: ${{ github.ref_name }}
run: |
set -euo pipefail
sanitize() { printf '%s' "$1" | tr '/' '-'; } # artifact names cannot contain '/'
if [ "$EVENT_NAME" = pull_request ]; then
{
echo "primary=${BASE_KEY}-pr-${PR_NUMBER}"
echo "primary-branch="
echo "fallback=${BASE_KEY}-branch-$(sanitize "$BASE_REF")"
echo "fallback-branch=$BASE_REF"
} >> "$GITHUB_OUTPUT"
else
{
echo "primary=${BASE_KEY}-branch-$(sanitize "$REF_NAME")"
echo "primary-branch=$REF_NAME"
echo "fallback="
echo "fallback-branch="
} >> "$GITHUB_OUTPUT"
fi
- name: Restore artifact cache
id: restore
if: ${{ inputs.mode == 'restore' }}
continue-on-error: true # a cold start is fine; a restore failure must not fail the caller
shell: bash
env:
GH_TOKEN: ${{ inputs.token || github.token }}
CACHE_PATH: ${{ inputs.path }}
PRIMARY: ${{ steps.name.outputs.primary }}
PRIMARY_BRANCH: ${{ steps.name.outputs.primary-branch }}
FALLBACK: ${{ steps.name.outputs.fallback }}
FALLBACK_BRANCH: ${{ steps.name.outputs.fallback-branch }}
run: |
set -euo pipefail
mkdir -p "$CACHE_PATH"
# Newest non-expired artifact named $1 (empty if none). With a non-empty
# $2 (branch), require provenance: a same-repo run (head_repository_id ==
# repository_id) whose head branch is $2, so a fork cannot forge it.
newest_run_id() {
want="$1" br="$2" gh api \
"/repos/$GITHUB_REPOSITORY/actions/artifacts?name=$1&per_page=100" \
--jq '.artifacts[]
| select(.expired == false and .name == env.want)
| select(env.br == ""
or (.workflow_run.head_branch == env.br
and .workflow_run.head_repository_id == .workflow_run.repository_id))
| [.created_at, (.workflow_run.id | tostring)] | @tsv' \
| sort | tail -n1 | cut -f2
}
restore() { # $1=name $2=branch(empty ok); returns 0 on a successful restore
local name="$1" br="$2" rid tarball
[ -n "$name" ] || return 1
rid="$(newest_run_id "$name" "$br")" || return 1
[ -n "$rid" ] || return 1
echo "Restoring '$CACHE_PATH' from artifact '$name' (run $rid)"
gh run download "$rid" -R "$GITHUB_REPOSITORY" --name "$name" --dir "$RUNNER_TEMP/artifact-cache-dl" || return 1
# Each artifact holds a single tarball; match by extension so the inner
# filename is not part of the contract.
tarball="$(find "$RUNNER_TEMP/artifact-cache-dl" -name '*.tar.zst' -print -quit)"
[ -n "$tarball" ] || return 1
tar -I zstd -x -f "$tarball" -C "$CACHE_PATH"
}
if restore "$PRIMARY" "$PRIMARY_BRANCH"; then
echo "Restored from primary key"
echo "cache-hit=true" >> "$GITHUB_OUTPUT"
elif restore "$FALLBACK" "$FALLBACK_BRANCH"; then
echo "Restored from fallback key"
echo "cache-hit=true" >> "$GITHUB_OUTPUT"
else
echo "No matching artifact found; starting cold"
echo "cache-hit=false" >> "$GITHUB_OUTPUT"
fi
- name: Pack artifact cache
id: pack
if: ${{ inputs.mode == 'save' }}
shell: bash
env:
CACHE_PATH: ${{ inputs.path }}
run: |
set -euo pipefail
packdir="$(mktemp -d)" # unique dir so concurrent saves cannot collide
tar -I 'zstd -T0' -cf "$packdir/cache.tar.zst" -C "$CACHE_PATH" .
echo "tarball=$packdir/cache.tar.zst" >> "$GITHUB_OUTPUT"
- name: Upload artifact cache
if: ${{ inputs.mode == 'save' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: ${{ steps.name.outputs.primary }}
path: ${{ steps.pack.outputs.tarball }}
retention-days: ${{ inputs.retention-days }}
overwrite: true
compression-level: 0 # tarball is already zstd-compressed
+109
View File
@@ -0,0 +1,109 @@
---
# DESCRIPTION: Github actions composite action
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: Install dependencies
description: >-
Install the dependencies of a CI stage with 'ci/ci-install.bash', caching the
'.deb' files apt downloads for that stage, so a package is fetched from the
network at most once per runner image. On hosts that do not use apt this is
just the install, there is nothing to cache.
inputs:
repo:
description: "Verilator checkout holding the ci/ scripts"
required: false
default: repo
stage:
description: "CI stage to install the dependencies of, e.g. 'build'"
required: true
runs:
using: composite
steps:
- name: Compute apt cache key
id: cachekey
if: ${{ runner.os == 'Linux' }}
shell: bash
working-directory: ${{ inputs.repo }}
env:
STAGE: ${{ inputs.stage }}
run: |
source ci/ci-common.bash
host="${DISTRO_ID:-$HOST_OS}${DISTRO_VERSION:+-$DISTRO_VERSION}"
echo "key=apt-${host}-${STAGE}" >> "$GITHUB_OUTPUT"
- name: Restore apt downloads
if: ${{ runner.os == 'Linux' }}
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
with:
path: ${{ runner.temp }}/apt-cache
key: ${{ steps.cachekey.outputs.key }}
restore-keys: |
${{ steps.cachekey.outputs.key }}-
- name: Seed apt archive from cache
if: ${{ runner.os == 'Linux' }}
shell: bash
env:
APT_CACHE_DIR: ${{ runner.temp }}/apt-cache
run: |
set -euo pipefail
# Ask apt where it keeps its downloads, rather than assume
eval "$(apt-config shell ARCHIVES Dir::Cache::archives/d)"
ARCHIVES=${ARCHIVES%/} # '/d' yields a trailing slash
# Wipe the archive first, so that whatever the runner image left behind is not stored
sudo apt-get -o DPkg::Lock::Timeout=60 clean
# The directory is absent when nothing was restored, e.g. on a cold start
if [ -d "$APT_CACHE_DIR" ] &&
[ -n "$(find "$APT_CACHE_DIR" -maxdepth 1 -name '*.deb' -print -quit)" ]; then
sudo cp "$APT_CACHE_DIR"/*.deb "$ARCHIVES"
fi
- name: Install dependencies
shell: bash
working-directory: ${{ inputs.repo }}
env:
STAGE: ${{ inputs.stage }}
run: ./ci/ci-install.bash "$STAGE"
- name: Collect apt downloads
id: collect
if: ${{ runner.os == 'Linux' }}
shell: bash
env:
APT_CACHE_DIR: ${{ runner.temp }}/apt-cache
run: |
set -euo pipefail
# Ask apt where it keeps its downloads, rather than assume
eval "$(apt-config shell ARCHIVES Dir::Cache::archives/d)"
ARCHIVES=${ARCHIVES%/} # '/d' yields a trailing slash
# Sorted names of the '.deb' files in a directory, empty if it has none
list-debs() {
[ -d "$1" ] || return 0
find "$1" -maxdepth 1 -name '*.deb' -printf '%f\n' | sort
}
# Superseded downloads can no longer be fetched, so drop them
sudo apt-get -o DPkg::Lock::Timeout=60 autoclean
downloaded=$(list-debs "$ARCHIVES")
if [ -z "$downloaded" ]; then
echo "Apt kept no '.deb' files, nothing to cache"
exit 0
fi
if [ "$downloaded" = "$(list-debs "$APT_CACHE_DIR")" ]; then
echo "Downloads unchanged, keeping the restored cache entry"
exit 0
fi
# Mirror the archive, rather than add to it, so that the cache cannot
# grow without bound as package versions churn
mkdir -p "$APT_CACHE_DIR"
rm -f "$APT_CACHE_DIR"/*.deb
sudo cp "$ARCHIVES"/*.deb "$APT_CACHE_DIR"
echo "changed=true" >> "$GITHUB_OUTPUT"
- name: Save apt downloads
if: ${{ steps.collect.outputs.changed == 'true' }}
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
with:
path: ${{ runner.temp }}/apt-cache
key: ${{ steps.cachekey.outputs.key }}-${{ github.run_id }}
+56
View File
@@ -0,0 +1,56 @@
---
# DESCRIPTION: Github actions composite action
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: Set up Python venv
description: >-
Restore, create, and optionally save the make-managed Python venv in a GitHub
Actions cache. The cache key combines the venv path (venvs are not
relocatable), the host OS and version, the Python major.minor version, and
the python-dev-requirements.txt hash, so any of those changing invalidates
it. Requires the already-configured Verilator tree to be present in 'repo'.
inputs:
repo:
description: "Verilator checkout holding the Makefile and .venv"
required: false
default: repo
save:
description: "Save the cache on a miss (set true only on trusted branches)"
required: false
default: 'false'
runs:
using: composite
steps:
- name: Compute Python venv cache key
id: cachekey
shell: bash
working-directory: ${{ inputs.repo }}
run: |
source ci/ci-common.bash
host="${DISTRO_ID:-$HOST_OS}${DISTRO_VERSION:+-$DISTRO_VERSION}"
pyver="$(python3 -c 'import sys; print("%d.%d" % sys.version_info[:2])')"
reqs="$(sha256sum python-dev-requirements.txt | cut -d' ' -f1)"
venv="${PWD#/}/.venv" # absolute venv path (leading '/' trimmed)
echo "key=venv-${host}-py${pyver}-${reqs}-${venv//\//-}" >> "$GITHUB_OUTPUT"
- name: Restore Python venv
id: venv
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
with:
path: ${{ inputs.repo }}/.venv
key: ${{ steps.cachekey.outputs.key }}
- name: Create Python venv
if: ${{ steps.venv.outputs.cache-hit != 'true' }}
shell: bash
working-directory: ${{ inputs.repo }}
run: make venv
- name: Save Python venv
if: ${{ inputs.save == 'true' && steps.venv.outputs.cache-hit != 'true' }}
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
with:
path: ${{ inputs.repo }}/.venv
key: ${{ steps.venv.outputs.cache-primary-key }}
@@ -0,0 +1,74 @@
---
# DESCRIPTION: Github actions composite action
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: Upload PR notification
description: >-
Upload the 'pr-notification' artifact, holding a comment for 'pages.yml' to
post on a pull request. The given directory must hold the content of the
comment in 'body.txt', and its single line summary in 'hist.txt'.
inputs:
path:
description: "Directory holding the notification contents"
required: true
key:
description: "Unique key for combining comments"
required: true
runs:
using: composite
steps:
- name: Validate
shell: bash
env:
NOTIFICATION_DIR: ${{ inputs.path }}
EVENT_NAME: ${{ github.event_name }}
run: |
# The notification is posted on the pull request of the triggering event
if [ "${EVENT_NAME}" != "pull_request" ]; then
echo "This action can only be used on a 'pull_request' run, but event is '${EVENT_NAME}'" >&2
exit 1
fi
# The content of the comment, and its single line summary
for f in body.txt hist.txt; do
if [ ! -f "${NOTIFICATION_DIR}/${f}" ]; then
echo "No '${f}' in '${NOTIFICATION_DIR}'" >&2
exit 1
fi
done
# Nothing else, so the artifact holds only what this action puts in it
EXTRA=$(find "${NOTIFICATION_DIR}" -mindepth 1 -not -name body.txt -not -name hist.txt)
if [ -n "${EXTRA}" ]; then
echo "Unexpected content in '${NOTIFICATION_DIR}':" >&2
echo "${EXTRA}" >&2
exit 1
fi
# 'ci-pages-notify.bash' stashes 'hist.txt' in an HTML comment, and later
# shows it in the history of the notifications superseding this one, so
# it must be a single line, and must neither open nor close an HTML comment
if [ $(wc -l < "${NOTIFICATION_DIR}/hist.txt") -ne 1 ]; then
echo "'hist.txt' must hold exactly one line" >&2
exit 1
fi
if grep -q -e '<!--' -e '-->' "${NOTIFICATION_DIR}/hist.txt"; then
echo "'hist.txt' must not contain '<!--' or '-->'" >&2
exit 1
fi
- name: Add metadata
shell: bash
env:
NOTIFICATION_DIR: ${{ inputs.path }}
NOTIFICATION_KEY: ${{ inputs.key }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
echo "${NOTIFICATION_KEY}" > "${NOTIFICATION_DIR}/key.txt"
echo "${PR_NUMBER}" > "${NOTIFICATION_DIR}/pr-number.txt"
ls -lsha "${NOTIFICATION_DIR}"
- name: Upload notification
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
path: ${{ inputs.path }}
name: pr-notification
+49 -180
View File
@@ -1,9 +1,8 @@
---
# DESCRIPTION: Github actions config
# This name is key to badges in README.rst, so we use the name build
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: build-test
name: Regressions
on:
push:
@@ -16,6 +15,7 @@ on:
permissions:
contents: read
actions: read
defaults:
run:
@@ -29,272 +29,141 @@ concurrency:
jobs:
build-2604-gcc:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
name: Build | 26.04 | gcc
uses: ./.github/workflows/reusable-build.yml
with:
cc: gcc
runs-on: ubuntu-26.04
sha: ${{ github.sha }}
os: ${{ matrix.os }}
os-name: linux
cc: ${{ matrix.cc }}
dev-asan: ${{ matrix.asan }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
- {os: ubuntu-26.04, cc: gcc, asan: 0}
build-2604-clang:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
name: Build | 26.04 | clang
uses: ./.github/workflows/reusable-build.yml
with:
cc: clang
dev-asan: true # Build (and run) with address sanitizer
runs-on: ubuntu-26.04
sha: ${{ github.sha }}
os: ${{ matrix.os }}
os-name: linux
cc: ${{ matrix.cc }}
dev-asan: ${{ matrix.asan }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
- {os: ubuntu-26.04, cc: clang, asan: 1}
build-2404-gcc:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
name: Build | 24.04 | gcc
uses: ./.github/workflows/reusable-build.yml
with:
cc: gcc
runs-on: ubuntu-24.04
sha: ${{ github.sha }}
os: ${{ matrix.os }}
os-name: linux
cc: ${{ matrix.cc }}
dev-asan: ${{ matrix.asan }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
- {os: ubuntu-24.04, cc: gcc, asan: 0}
build-2404-clang:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
name: Build | 24.04 | clang
uses: ./.github/workflows/reusable-build.yml
with:
cc: clang
runs-on: ubuntu-24.04
sha: ${{ github.sha }}
os: ${{ matrix.os }}
os-name: linux
cc: ${{ matrix.cc }}
dev-asan: ${{ matrix.asan }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
- {os: ubuntu-24.04, cc: clang, asan: 0}
build-2204-gcc:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
name: Build | 22.04 | gcc
uses: ./.github/workflows/reusable-build.yml
with:
cc: gcc
runs-on: ubuntu-22.04
sha: ${{ github.sha }}
os: ${{ matrix.os }}
os-name: linux
cc: ${{ matrix.cc }}
dev-asan: ${{ matrix.asan }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
- {os: ubuntu-22.04, cc: gcc, asan: 0}
build-osx-gcc:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
build-macos-15-clang:
name: Build | macos-15 | clang
uses: ./.github/workflows/reusable-build.yml
with:
cc: clang
runs-on: macos-15
sha: ${{ github.sha }}
os: ${{ matrix.os }}
os-name: osx
cc: ${{ matrix.cc }}
dev-asan: ${{ matrix.asan }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
- {os: macos-15, cc: gcc, asan: 0}
build-osx-clang:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}${{ matrix.asan && ' | asan' || '' }}
uses: ./.github/workflows/reusable-build.yml
with:
sha: ${{ github.sha }}
os: ${{ matrix.os }}
os-name: osx
cc: ${{ matrix.cc }}
dev-asan: ${{ matrix.asan }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
- {os: macos-15, cc: clang, asan: 0}
build-windows:
name: Build | ${{ matrix.os }} | ${{ matrix.cc }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- {os: windows-2025-vs2026, cc: msvc}
env:
CI_OS_NAME: win
CCACHE_COMPRESS: 1
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_LIMIT_MULTIPLE: 0.95
name: Build | windows-2025-vs2026 | msvc
runs-on: windows-2025-vs2026
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
path: repo
- name: Cache $CCACHE_DIR
- name: Cache win_flex_bison
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
with:
path: ${{ env.CCACHE_DIR }}
key: msbuild-msvc-cmake
path: ${{ github.workspace }}/win_flex_bison
key: win_flex_bison
- name: compile
env:
WIN_FLEX_BISON: ${{ github.workspace }}/.ccache
run: ./ci/ci-win-compile.ps1
- name: test build
run: ./ci/ci-win-test.ps1
- name: Zip up repository
run: Compress-Archive -LiteralPath install -DestinationPath verilator.zip
- name: Upload zip archive
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
path: ${{ github.workspace }}/repo/verilator.zip
name: verilator-win.zip
test-2604-gcc:
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
name: Test | 26.04 | gcc | ${{ matrix.suite }}
needs: build-2604-gcc
uses: ./.github/workflows/reusable-test.yml
with:
archive: ${{ needs.build-2604-gcc.outputs.archive }}
os: ${{ matrix.os }}
cc: ${{ matrix.cc }}
reloc: ${{ matrix.reloc }}
cc: gcc
runs-on: ubuntu-26.04
suite: ${{ matrix.suite }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
# Ubuntu 26.04 gcc
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-0}
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-1}
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-2}
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: dist-vlt-3}
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: vltmt-0}
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: vltmt-1}
- {os: ubuntu-26.04, cc: gcc, reloc: 0, suite: vltmt-2}
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
test-2604-clang:
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
name: Test | 26.04 | clang | ${{ matrix.suite }}
needs: build-2604-clang
uses: ./.github/workflows/reusable-test.yml
with:
archive: ${{ needs.build-2604-clang.outputs.archive }}
os: ${{ matrix.os }}
cc: ${{ matrix.cc }}
reloc: ${{ matrix.reloc }}
cc: clang
runs-on: ubuntu-26.04
suite: ${{ matrix.suite }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
# Ubuntu 26.04 clang
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-0}
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-1}
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-2}
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: dist-vlt-3}
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: vltmt-0}
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: vltmt-1}
- {os: ubuntu-26.04, cc: clang, reloc: 0, suite: vltmt-2}
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
test-2404-gcc:
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
name: Test | 24.04 | gcc | ${{ matrix.suite }}
needs: build-2404-gcc
uses: ./.github/workflows/reusable-test.yml
with:
archive: ${{ needs.build-2404-gcc.outputs.archive }}
os: ${{ matrix.os }}
cc: ${{ matrix.cc }}
reloc: ${{ matrix.reloc }}
cc: gcc
runs-on: ubuntu-24.04
suite: ${{ matrix.suite }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
# Ubuntu 24.04 gcc
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-0}
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-1}
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-2}
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: dist-vlt-3}
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: vltmt-0}
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: vltmt-1}
- {os: ubuntu-24.04, cc: gcc, reloc: 0, suite: vltmt-2}
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
test-2404-clang:
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
name: Test | 24.04 | clang | ${{ matrix.suite }}
needs: build-2404-clang
uses: ./.github/workflows/reusable-test.yml
with:
archive: ${{ needs.build-2404-clang.outputs.archive }}
os: ${{ matrix.os }}
cc: ${{ matrix.cc }}
reloc: ${{ matrix.reloc }}
cc: clang
reloc: true # Test with relocated installation
runs-on: ubuntu-24.04
suite: ${{ matrix.suite }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
# Ubuntu 24.04 clang
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-0}
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-1}
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-2}
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: dist-vlt-3}
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: vltmt-0}
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: vltmt-1}
- {os: ubuntu-24.04, cc: clang, reloc: 0, suite: vltmt-2}
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
test-2204-gcc:
name: Test | ${{ matrix.os }} | ${{ matrix.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
name: Test | 22.04 | gcc | ${{ matrix.suite }}
needs: build-2204-gcc
uses: ./.github/workflows/reusable-test.yml
with:
archive: ${{ needs.build-2204-gcc.outputs.archive }}
os: ${{ matrix.os }}
cc: ${{ matrix.cc }}
reloc: ${{ matrix.reloc }}
cc: gcc
runs-on: ubuntu-22.04
suite: ${{ matrix.suite }}
dev-gcov: 0
strategy:
fail-fast: false
matrix:
include:
# Ubuntu 22.04 gcc
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-0}
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-1}
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-2}
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: dist-vlt-3}
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-0}
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-1}
- {os: ubuntu-22.04, cc: gcc, reloc: 0, suite: vltmt-2}
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1, vltmt-2]
lint-py:
name: Lint Python
+1 -1
View File
@@ -16,5 +16,5 @@ jobs:
name: "'docs/CONTRIBUTORS' was signed"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- run: test_regress/t/t_dist_contributors.py
+26 -53
View File
@@ -13,6 +13,7 @@ on:
permissions:
contents: read
actions: read
defaults:
run:
@@ -38,15 +39,13 @@ jobs:
(github.event_name == 'workflow_dispatch')
uses: ./.github/workflows/reusable-build.yml
with:
cc: gcc
dev-gcov: true
runs-on: ubuntu-24.04
# For pull requests, build the head of the pull request branch, not the
# merge commit, otherwise patch coverage would include the changes
# between the root of the pull request and the target branch
sha: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
os: ubuntu-24.04
os-name: linux
cc: gcc
dev-asan: 0
dev-gcov: 1
test:
name: Test | ${{ matrix.test }}${{ matrix.num }}
@@ -54,11 +53,10 @@ jobs:
uses: ./.github/workflows/reusable-test.yml
with:
archive: ${{ needs.build.outputs.archive }}
os: ubuntu-24.04
cc: gcc
reloc: 0
dev-gcov: true
runs-on: ubuntu-24.04
suite: ${{ matrix.test }}${{ matrix.num }}
dev-gcov: 1
strategy:
fail-fast: false
matrix:
@@ -74,7 +72,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Download code coverage data
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
@@ -106,13 +104,6 @@ jobs:
if: ${{ contains(needs.*.result, 'success') && !cancelled() }}
runs-on: ubuntu-24.04
steps:
- name: Install dependencies
run: |
echo 'set man-db/auto-update false' | sudo debconf-communicate >/dev/null
sudo dpkg-reconfigure man-db
sudo apt install lcov || \
sudo apt install lcov
- name: Download repository archive
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
@@ -121,9 +112,14 @@ jobs:
- name: Unpack repository archive
run: |
tar -x -z -f ${{ needs.build.outputs.archive }}
tar --zstd -x -f ${{ needs.build.outputs.archive }}
ls -lsha
- name: Install dependencies
uses: ./repo/.github/actions/install-deps
with:
stage: coverage
- name: Download code coverage data
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
@@ -136,53 +132,30 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
run: |
ls -lsha obj_coverage
# Combine reports from test jobs
nodist/fastcov.py -C obj_coverage/verilator-*.info --lcov -o obj_coverage/verilator.info
# For a PR, report patch coverage against the merge-base between the head of the PR and the target branch
ci/ci-coverage-report.bash \
${{ github.run_id }} \
"${{ github.event_name == 'pull_request' && github.event.number || '' }}" \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}"
# Create the report artifact. Note there is no report if the patch is empty.
mkdir ../report-artifact
[ ! -d obj_coverage/report ] || mv obj_coverage/report ../report-artifact/
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
COVERAGE_BASE=$(git rev-parse --short $(git merge-base ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}))
make coverage-report COVERAGE_BASE=${COVERAGE_BASE} |& tee ${{ github.workspace }}/make-coverage-report.log
else
make coverage-report
fi
# Remove data files
rm -f obj_coverage/verilator*.info
# Some extra work for PRs only
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
# Save PR number in report
echo ${{ github.event.number }} > obj_coverage/pr-number.txt
# Generate notification comment content
mkdir -p notification
echo ${{ github.event.number }} > notification/pr-number.txt
NUM=$(gh run view ${{ github.run_id }} --json number --jq ".number")
URL=$(gh run view ${{ github.run_id }} --json url --jq ".url")
echo "Patch coverage from PR workflow [#$NUM]($URL) (code coverage of lines changed relative to ${COVERAGE_BASE}):" > notification/body.txt
if [[ ! -f obj_coverage/empty-patch ]]; then
echo "<pre>" >> notification/body.txt
grep -E "(lines|branches)\.*:" ${{ github.workspace }}/make-coverage-report.log | sed "s/\.*:/:/" >> notification/body.txt || true
echo "</pre>" >> notification/body.txt
echo "Report: [${{ github.run_id }}](https://${{ github.repository_owner }}.github.io/verilator/coverage-reports/${{ github.run_id }}/index.html)" >> notification/body.txt
echo "" >> notification/body.txt
echo "Please get to 100% line coverage, and understand all branches; see https://github.com/verilator/verilator/blob/master/docs/internals.rst#code-coverage-results" >> notification/body.txt
else
echo "Patch contains no code changes" >> notification/body.txt
fi
cat notification/body.txt
echo ${{ github.event.number }} > ../report-artifact/pr-number.txt
fi
- name: Upload report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
path: repo/obj_coverage
path: report-artifact
name: coverage-report
- name: Upload notification
- name: Upload PR notification
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: ./repo/.github/actions/upload-pr-notification
with:
path: repo/notification
name: pr-notification
key: coverage
# Create GitHub issue for failed scheduled jobs
# This should always be the last job (we want an issue if anything breaks)
+6 -6
View File
@@ -39,7 +39,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Extract context variables
run: |
@@ -54,7 +54,7 @@ jobs:
- name: Docker meta
id: docker_meta
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
with:
images: |
${{ vars.DOCKER_HUB_NAMESPACE }}/${{ env.image_name }}
@@ -64,21 +64,21 @@ jobs:
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
- name: Set up QEMU
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4
with:
buildkitd-flags: --debug
- name: Login to Docker Hub
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build and Push to Docker
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
with:
context: ${{ env.build_context }}
+23 -11
View File
@@ -11,35 +11,47 @@ on:
permissions:
contents: write
defaults:
run:
working-directory: repo
jobs:
format:
runs-on: ubuntu-24.04
name: Ubuntu 24.04 | format
env:
CI_OS_NAME: linux
CI_RUNS_ON: ubuntu-24.04
CI_COMMIT: ${{ github.sha }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
path: repo
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install packages for build
env:
CI_BUILD_STAGE_NAME: build
uses: ./repo/.github/actions/install-deps
with:
stage: format
- name: Configure git identity
run: |
sudo apt install clang-format-18 || \
sudo apt install clang-format-18
git config --global user.email "[email protected]"
git config --global user.name "github action"
- name: Format code
- name: Configure
run: |
autoconf
./configure
make venv
- name: Set up Python venv
uses: ./repo/.github/actions/setup-venv
with:
save: ${{ github.ref == 'refs/heads/master' }}
- name: Format code
run: |
source .venv/bin/activate
make -j 4 format CLANGFORMAT=clang-format-18
git status
- name: Push
run: |-
if [ -n "$(git status --porcelain)" ]; then
+21 -7
View File
@@ -7,7 +7,7 @@ name: Pages
on:
push:
branches: [master]
paths: ["ci/**", ".github/workflows"]
paths: ["ci/**", ".github/workflows/**"]
workflow_dispatch:
workflow_run:
workflows: ["Code coverage", "RTLMeter"]
@@ -22,8 +22,11 @@ permissions:
# Allow only one concurrent deployment, skipping runs queued between the run
# in-progress and latest queued. However, do NOT cancel in-progress runs as we
# want to allow these deployments to complete.
# A skipped-upstream run does no work (see the build job's if:), so put it in its
# own throwaway group: otherwise, as the newest queued run, it would cancel a
# pending real run and then deploy nothing.
concurrency:
group: "pages"
group: ${{ (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'skipped') && format('pages-skip-{0}', github.run_id) || 'pages' }}
cancel-in-progress: false
defaults:
@@ -34,19 +37,21 @@ jobs:
build:
name: Build content
runs-on: ubuntu-24.04
# A skipped upstream run (e.g. Code coverage / RTLMeter on a branch push)
# still fires workflow_run; don't rebuild and redeploy for it. deploy and
# notify need this job, so they cascade-skip too.
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion != 'skipped' }}
outputs:
pr-run-ids: ${{ steps.build.outputs.pr-run-ids }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Build pages
id: build
env:
GH_TOKEN: ${{ github.token }}
run: |
./ci/ci-pages.bash
ls -lsha
tree -L 3 pages
- name: Upload pages artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
with:
@@ -58,9 +63,16 @@ jobs:
runs-on: ubuntu-24.04
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
url: ${{ steps.deploy-2.outputs.page_url || steps.deploy-1.outputs.page_url }}
steps:
# GitHub's Pages backend intermittently fails a deployment mid-sync, retry
- name: Deploy to GitHub Pages
id: deploy-1
continue-on-error: true
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
- name: Deploy to GitHub Pages (retry)
id: deploy-2
if: steps.deploy-1.outcome == 'failure'
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
notify:
@@ -70,7 +82,7 @@ jobs:
if: ${{ github.repository == 'verilator/verilator' }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
# Use the Verilator CI app to post the comment
- name: Generate access token
id: generate-token
@@ -83,5 +95,7 @@ jobs:
- name: Comment on PR
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
# Slug of the app the above token belongs to, see 'ci-pages-notify.bash'
GH_AUTHOR: ${{ steps.generate-token.outputs.app-slug }}
PR_RUN_IDS: ${{ needs.build.outputs.pr-run-ids }}
run: ./ci/ci-pages-notify.bash
+28
View File
@@ -0,0 +1,28 @@
---
# Build and push verilator docker image when tags are pushed to the repository.
# The following variable(s) must be configured in the github repository:
# DOCKER_HUB_NAMESPACE: docker hub namespace.
# The following secrets must be configured in the github repository:
# DOCKER_HUB_USER: user name for logging into docker hub
# DOCKER_HUB_ACCESS_TOKEN: docker hub access token.
name: Maintainance - PR conflict check
on:
workflow_dispatch:
push:
pull_request_target:
types: [synchronize]
permissions: {}
jobs:
main:
runs-on: ubuntu-26.04
permissions:
contents: read
pull-requests: write
steps:
- name: check if prs are dirty
uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # 3.1.0
with:
dirtyLabel: "pr-blocked: needs-rebase"
repoToken: "${{ secrets.GITHUB_TOKEN }}"
+73 -40
View File
@@ -7,37 +7,47 @@ name: reusable-build
on:
workflow_call:
inputs:
cc:
description: "Compiler to use: 'gcc' or 'clang'"
required: true
type: string
ccwarn:
description: "Build Verilator with warnings treated as errors (--enable-ccwarn)"
required: false
default: true
type: boolean
dev-asan:
description: "Build Verilator with the address sanitizer (--enable-dev-asan)"
required: false
default: false
type: boolean
dev-gcov:
description: "Build Verilator with gcov instrumentation (--enable-dev-gcov)"
required: false
default: false
type: boolean
install:
description: "Archive the Verilator installation, not the repo tree"
required: false
default: false
type: boolean
runs-on:
description: "Runner to build on, e.g. ubuntu-24.04"
required: true
type: string
sha:
description: "Commit SHA to build"
required: true
type: string
os: # e.g. ubuntu-24.04
required: true
type: string
cc: # 'clang' or 'gcc'
required: true
type: string
os-name: # 'linux' or 'osx'
required: true
type: string
dev-asan:
required: true
type: number
dev-gcov:
required: true
type: number
outputs:
archive:
description: "Name of the built repository archive artifact"
description: "Name of the built archive artifact"
value: ${{ jobs.build.outputs.archive }}
env:
CI_OS_NAME: ${{ inputs.os-name }}
CCACHE_COMPRESS: 1
CACHE_BASE_KEY: build-${{ inputs.runs-on }}-${{ inputs.cc }}${{ inputs.ccwarn && '-ccwarn' || '' }}${{ inputs.dev-asan && '-asan' || '' }}${{ inputs.dev-gcov && '-gcov' || '' }}
CCACHE_COMPILERCHECK: content
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_LIMIT_MULTIPLE: 0.95
INSTALL_DIR: ${{ github.workspace }}/install
RELOC_DIR: ${{ github.workspace }}/relloc
defaults:
run:
@@ -48,26 +58,28 @@ jobs:
build:
name: Build
runs-on: ${{ inputs.os }}
runs-on: ${{ inputs.runs-on }}
outputs:
archive: ${{ steps.create-archive.outputs.archive }}
env:
CI_BUILD_STAGE_NAME: build
CI_DEV_ASAN: ${{ inputs.dev-asan }}
CI_DEV_GCOV: ${{ inputs.dev-gcov }}
CI_RUNS_ON: ${{ inputs.os }}
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
CACHE_BASE_KEY: build-${{ inputs.os }}-${{ inputs.cc }}
CCACHE_MAXSIZE: 1000M # Per build matrix entry (* 5 = 5000M in total)
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
path: repo
ref: ${{ inputs.sha }}
fetch-depth: ${{ inputs.dev-gcov && '0' || '1' }} # Coverage flow needs full history
# Coverage needs full history; install needs it for the 'git describe'
# behind 'verilator --version'
fetch-depth: ${{ (inputs.install || inputs.dev-gcov) && '0' || '1' }}
- name: Configure ccache
run: |
# ccache is unreliable on macOS, and does nothing with 'gcc --coverage'
if [ "${{ startsWith(inputs.runs-on, 'macos') }}" = true ] || [ "${{ inputs.dev-gcov }}" = true ]; then
echo "CCACHE_DISABLE=1" >> "$GITHUB_ENV"
fi
- name: Cache $CCACHE_DIR
if: ${{ env.CCACHE_DISABLE != '1' }}
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
env:
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
@@ -75,25 +87,46 @@ jobs:
path: ${{ env.CCACHE_DIR }}
key: ${{ env.CACHE_KEY }}-${{ inputs.sha }}
restore-keys: |
${{ env.CACHE_KEY }}-
${{ env.CACHE_KEY }}
- name: Install packages for build
run: ./ci/ci-install.bash
uses: ./repo/.github/actions/install-deps
with:
stage: build
- name: Build
run: ./ci/ci-script.bash
run: |
./ci/ci-build.bash \
--prefix ${{ github.workspace }}/install \
--compiler ${{ inputs.cc }} \
--light-debug \
${{ inputs.ccwarn && '--ccwarn' || '' }} \
${{ inputs.dev-asan && '--asan' || '' }} \
${{ inputs.dev-gcov && '--gcov' || '' }}
- name: Create repository archive
- name: Install
if: ${{ inputs.install }}
run: make install
- name: Create archive
id: create-archive
working-directory: ${{ github.workspace }}
run: |
# Name of the archive must be unique based on the build parameters
ARCHIVE=verilator-${{ inputs.sha }}-${{ inputs.os }}-${{ inputs.cc }}-${{ inputs.dev-asan }}-${{ inputs.dev-gcov }}.tar.gz
tar --posix -c -z -f $ARCHIVE repo
# Archive name, unique per build; flavour tags mark non-default builds
ARCHIVE="verilator-${{ inputs.sha }}-${{ inputs.runs-on }}-${{ inputs.cc }}"
ARCHIVE="$ARCHIVE${{ inputs.ccwarn && '-ccwarn' || '' }}"
ARCHIVE="$ARCHIVE${{ inputs.dev-asan && '-asan' || '' }}"
ARCHIVE="$ARCHIVE${{ inputs.dev-gcov && '-gcov' || '' }}"
ARCHIVE="$ARCHIVE.tar.zst"
# zstd compresses faster than gzip and decompresses far faster in the many downstream jobs
ZSTD_NBTHREADS=0 tar --posix --zstd -c -f "$ARCHIVE" ${{ inputs.install && 'install' || 'repo' }}
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
- name: Upload repository archive
- name: Upload archive
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
path: ${{ github.workspace }}/${{ steps.create-archive.outputs.archive }}
name: ${{ steps.create-archive.outputs.archive }}
overwrite: true
# Archive is already zstd-compressed; skip upload-artifact's zip pass
compression-level: 0
+9 -16
View File
@@ -7,14 +7,6 @@ name: reusable-lint-py
on:
workflow_call:
env:
CI_OS_NAME: linux
CI_BUILD_STAGE_NAME: build
CI_RUNS_ON: ubuntu-22.04
CCACHE_COMPRESS: 1
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_LIMIT_MULTIPLE: 0.95
defaults:
run:
shell: bash
@@ -27,23 +19,24 @@ jobs:
name: Sub-lint | Python
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
path: repo
- name: Install packages for build
run: ./ci/ci-install.bash
- name: Install dependencies
uses: ./repo/.github/actions/install-deps
with:
stage: lint-py
- name: Configure
run: |
autoconf
./configure --enable-longtests --enable-ccwarn
- name: Install python dependencies
run: |
sudo apt install python3-clang || \
sudo apt install python3-clang
make venv
- name: Set up Python venv
uses: ./repo/.github/actions/setup-venv
with:
save: ${{ github.ref == 'refs/heads/master' }}
- name: Lint
run: |-
@@ -1,96 +0,0 @@
---
# DESCRIPTION: Github actions config
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: reusable-rtlmeter-build
on:
workflow_call:
inputs:
runs-on:
description: "Runner to use, e.g.: ubuntu-24.04"
type: string
required: true
cc:
description: "Compiler to use: 'gcc' or 'clang'"
type: string
required: true
sha:
description: "Git SHA to build"
type: string
required: true
outputs:
archive:
description: "Name of the built installation archive artifact"
value: ${{ jobs.build.outputs.archive }}
defaults:
run:
shell: bash
env:
CCACHE_DIR: ${{ github.workspace }}/ccache
CCACHE_MAXSIZE: 512M
jobs:
build:
name: Build
runs-on: ${{ inputs.runs-on }}
outputs:
archive: ${{ steps.create-archive.outputs.archive }}
steps:
- name: Install dependencies
run: |
echo "path-exclude /usr/share/doc/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
echo "path-exclude /usr/share/man/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
sudo apt update || \
sudo apt update
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev || \
sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev
- name: Use saved ccache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
with:
path: ccache
key: rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}-${{ github.run_id }}
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}
rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
path: repo
ref: ${{ inputs.sha }}
fetch-depth: 0 # Required for 'git describe' used for 'verilator --version'
- name: Configure
working-directory: repo
run: |
autoconf
./configure --prefix=${{ github.workspace }}/install CXX=${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
- name: Make
working-directory: repo
run: make -j $(nproc)
- name: Install
working-directory: repo
run: make install
- name: Tar up installation
id: create-archive
run: |
SHA=$(git -C repo rev-parse HEAD)
ARCHIVE=verilator-$SHA-rtlmeter-${{ inputs.runs-on }}-${{ inputs.cc }}.tar.gz
tar --posix -c -z -f $ARCHIVE install
echo "archive=$ARCHIVE" >> $GITHUB_OUTPUT
- name: Upload Verilator installation archive
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
path: ${{ steps.create-archive.outputs.archive }}
name: ${{ steps.create-archive.outputs.archive }}
overwrite: true
+18 -23
View File
@@ -20,11 +20,11 @@ on:
type: string
required: true
verilator-archive-new:
description: "Name of the installation archive artifact from reusable-rtlmeter-build, new version"
description: "Name of the installation archive artifact from reusable-build, new version"
type: string
required: true
verilator-archive-old:
description: "Name of the installation archive artifact from reusable-rtlmeter-build, old version"
description: "Name of the installation archive artifact from reusable-build, old version"
type: string
required: false
default: ""
@@ -53,8 +53,8 @@ defaults:
shell: bash
env:
CCACHE_DIR: ${{ github.workspace }}/ccache
CCACHE_MAXSIZE: 512M
# RTLMeter measures compile/execute times, so caching must stay off to keep
# timings representative; ccache is still on PATH but acts as a pass-through
CCACHE_DISABLE: 1
jobs:
@@ -62,18 +62,21 @@ jobs:
runs-on: ${{ inputs.runs-on }}
name: Run
steps:
- name: Checkout Verilator CI scripts
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
path: repo
sparse-checkout: |
.github/actions
ci
- name: Install dependencies
run: |
echo "path-exclude /usr/share/doc/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
echo "path-exclude /usr/share/man/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
sudo apt update || \
sudo apt update
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev || \
sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev
uses: ./repo/.github/actions/install-deps
with:
stage: rtlmeter-run
- name: Checkout RTLMeter
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: "verilator/rtlmeter"
path: rtlmeter
@@ -82,14 +85,6 @@ jobs:
working-directory: rtlmeter
run: make venv
- name: Use saved ccache
if: ${{ env.CCACHE_DISABLE == 0 }}
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
with:
path: ${{ env.CCACHE_DIR }}
key: rtlmeter-run-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.cases }}-${{ inputs.compileArgs }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: rtlmeter-run-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.cases }}-${{ inputs.compileArgs }}
########################################################################
# Run with new Verilator
########################################################################
@@ -101,7 +96,7 @@ jobs:
- name: Unpack Verilator installation archive - new
run: |
tar -x -z -f ${{ inputs.verilator-archive-new }}
tar --zstd -x -f ${{ inputs.verilator-archive-new }}
mv install verilator-new
- name: Compile cases - new
@@ -164,7 +159,7 @@ jobs:
- name: Unpack Verilator installation archive - old
if: ${{ inputs.verilator-archive-old != '' }}
run: |
tar -x -z -f ${{ inputs.verilator-archive-old }}
tar --zstd -x -f ${{ inputs.verilator-archive-old }}
mv install verilator-old
- name: Compile cases - old
+61 -38
View File
@@ -11,29 +11,37 @@ on:
description: "Name of the repository archive artifact from reusable-build"
required: true
type: string
os: # e.g. ubuntu-24.04
required: true
type: string
cc: # gcc or clang
required: true
type: string
reloc: # 0 or 1
required: true
type: number
suite: # e.g. dist-vlt-0
cc:
description: "Compiler to use: 'gcc' or 'clang'"
required: true
type: string
dev-gcov:
description: "Collect gcov coverage data from the test run"
required: false
default: false
type: boolean
reloc:
description: "Relocate the installation before testing"
required: false
default: false
type: boolean
runs-on:
description: "Runner to test on, e.g. ubuntu-24.04"
required: true
type: number
type: string
suite:
description: "Test suite to run, e.g. dist-vlt-0"
required: true
type: string
permissions:
contents: read
actions: read
env:
CI_OS_NAME: linux
CCACHE_COMPRESS: 1
CCACHE_COMPILERCHECK: content
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_LIMIT_MULTIPLE: 0.95
INSTALL_DIR: ${{ github.workspace }}/install
RELOC_DIR: ${{ github.workspace }}/relloc
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
defaults:
run:
@@ -43,15 +51,8 @@ defaults:
jobs:
test:
runs-on: ${{ inputs.os }}
runs-on: ${{ inputs.runs-on }}
name: Test
env:
CI_BUILD_STAGE_NAME: test
CI_RUNS_ON: ${{ inputs.os }}
CI_RELOC: ${{inputs.reloc }}
CXX: ${{ inputs.cc == 'clang' && 'clang++' || 'g++' }}
CACHE_BASE_KEY: test-${{ inputs.os }}-${{ inputs.cc }}-${{inputs.reloc }}-${{ inputs.suite }}
CCACHE_MAXSIZE: 100M # Per build per suite (* 5 * 5 = 2500M in total)
steps:
- name: Download repository archive
@@ -63,32 +64,44 @@ jobs:
- name: Unpack repository archive
working-directory: ${{ github.workspace }}
run: |
tar -x -z -f ${{ inputs.archive }}
tar --zstd -x -f ${{ inputs.archive }}
ls -lsha
- name: Cache $CCACHE_DIR
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
env:
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache2
- name: Configure ccache
run: |
# ccache is unreliable on macOS, and does nothing with 'gcc --coverage'
if [ "${{ startsWith(inputs.runs-on, 'macos') }}" = true ] || [ "${{ inputs.dev-gcov }}" = true ]; then
echo "CCACHE_DISABLE=1" >> "$GITHUB_ENV"
fi
# Test-job ccache is stored as an artifact, not actions/cache
- name: Restore ccache
id: ccache
if: ${{ env.CCACHE_DISABLE != '1' }}
uses: ./repo/.github/actions/artifact-cache
with:
mode: restore
path: ${{ env.CCACHE_DIR }}
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
restore-keys: |
${{ env.CACHE_KEY }}-
key: ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.suite }}
- name: Install test dependencies
run: |
./ci/ci-install.bash
make venv
uses: ./repo/.github/actions/install-deps
with:
stage: test
- name: Set up Python venv
uses: ./repo/.github/actions/setup-venv
with:
save: ${{ github.ref == 'refs/heads/master' }}
- name: Test
id: run-test
continue-on-error: true
env:
TESTS: ${{ inputs.suite }}
run: |
source .venv/bin/activate
./ci/ci-script.bash
./ci/ci-test.bash \
--suite ${{ inputs.suite }} \
${{ inputs.reloc && format('--reloc {0}/reloc', github.workspace) || '' }}
- name: Combine code coverage data
if: ${{ inputs.dev-gcov }}
@@ -104,6 +117,16 @@ jobs:
path: ${{ github.workspace }}/repo/obj_coverage/verilator-${{ inputs.suite }}.info
name: code-coverage-${{ inputs.suite }}
- name: Save ccache
if: ${{ env.CCACHE_DISABLE != '1' && !cancelled() }}
uses: ./repo/.github/actions/artifact-cache
with:
mode: save
path: ${{ env.CCACHE_DIR }}
key: ${{ steps.ccache.outputs.key }}
# Keep master's cache around longer; PR/branch caches churn faster
retention-days: ${{ github.ref == 'refs/heads/master' && 7 || 3 }}
- name: Fail job if a test failed
if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }}
run: |-
+25 -21
View File
@@ -45,7 +45,7 @@ jobs:
cases: ${{ steps.cases.outputs.cases }}
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Startup
id: start
@@ -65,39 +65,47 @@ jobs:
build-gcc-new:
name: Build New Verilator - GCC
needs: start
uses: ./.github/workflows/reusable-rtlmeter-build.yml
uses: ./.github/workflows/reusable-build.yml
with:
runs-on: ubuntu-24.04
cc: gcc
ccwarn: false
install: true
runs-on: ubuntu-24.04
sha: ${{ github.sha }}
build-clang-new:
name: Build New Verilator - Clang
needs: start
uses: ./.github/workflows/reusable-rtlmeter-build.yml
uses: ./.github/workflows/reusable-build.yml
with:
runs-on: ubuntu-24.04
cc: clang
ccwarn: false
install: true
runs-on: ubuntu-24.04
sha: ${{ github.sha }}
build-gcc-old:
name: Build Old Verilator - GCC
needs: start
if: ${{ needs.start.outputs.old-sha != '' }}
uses: ./.github/workflows/reusable-rtlmeter-build.yml
uses: ./.github/workflows/reusable-build.yml
with:
runs-on: ubuntu-24.04
cc: gcc
ccwarn: false
install: true
runs-on: ubuntu-24.04
sha: ${{ needs.start.outputs.old-sha }}
build-clang-old:
name: Build Old Verilator - Clang
needs: start
if: ${{ needs.start.outputs.old-sha != '' }}
uses: ./.github/workflows/reusable-rtlmeter-build.yml
uses: ./.github/workflows/reusable-build.yml
with:
runs-on: ubuntu-24.04
cc: clang
ccwarn: false
install: true
runs-on: ubuntu-24.04
sha: ${{ needs.start.outputs.old-sha }}
run-gcc:
@@ -208,7 +216,7 @@ jobs:
run: echo "tags=$(jq -r 'keys | map(sub("^run-"; "")) | join(" ")' <<< '${{ toJSON(needs) }}')" >> "$GITHUB_OUTPUT"
- name: Checkout RTLMeter
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: "verilator/rtlmeter"
path: rtlmeter
@@ -298,7 +306,7 @@ jobs:
repositories: verilator-rtlmeter-results
permission-contents: write
- name: Checkout verilator-rtlmeter-results
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: "verilator/verilator-rtlmeter-results"
token: ${{ steps.generate-token.outputs.token }}
@@ -331,7 +339,7 @@ jobs:
actions: read
steps:
- name: Checkout RTLMeter
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: "verilator/rtlmeter"
path: rtlmeter
@@ -341,7 +349,7 @@ jobs:
run: make venv
- name: Checkout Verilator
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
path: verilator
@@ -361,10 +369,6 @@ jobs:
mkdir ../report-artifact
mv rtlmeter-report/report ../report-artifact/
echo ${{ github.event.number }} > ../report-artifact/pr-number.txt
# Create the notification artifact
mkdir ../notification-artifact
mv rtlmeter-report/notification.txt ../notification-artifact/body.txt
echo ${{ github.event.number }} > ../notification-artifact/pr-number.txt
- name: Upload report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
@@ -372,11 +376,11 @@ jobs:
path: report-artifact
name: rtlmeter-report
- name: Upload notification
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
- name: Upload PR notification
uses: ./verilator/.github/actions/upload-pr-notification
with:
path: notification-artifact
name: pr-notification
path: verilator/rtlmeter-report/notification
key: rtlmeter
- name: Report status
run: exit ${{ steps.report.outputs.status }}
+6
View File
@@ -96,9 +96,15 @@ ______________________________________________________________________
- [ ] Ran the full regression on at least one OS before submitting. Partial runs
are fine during development, but the submitted PR is expected to pass every
test.
- [ ] Validated the code changes against recommendations in the AGENTS.md files.
- [ ] Did not edit `docs/CONTRIBUTORS` (humans only) or `Changes` (maintainer
updates it near release).
## Drafting a PR
- [ ] When creating a pull request message, omit itemized lists of changed
files and changes, since reviewers already have access to the diff.
## Pick the right diagnostic (and its required test)
The API you choose determines which test must accompany the change.
+4 -3
View File
@@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project(
Verilator
VERSION 5.050
VERSION 5.052
HOMEPAGE_URL https://verilator.org
LANGUAGES CXX
)
@@ -155,11 +155,12 @@ install(
foreach(
program
verilator
verilator_gantt
verilator_ccache_report
verilator_coverage
verilator_difftree
verilator_profcfunc
verilator_gantt
verilator_includer
verilator_profcfunc
)
install(PROGRAMS bin/${program} TYPE BIN)
endforeach()
+177 -1
View File
@@ -10,6 +10,182 @@ The changes in each Verilator version are described below. The
contributors that suggested or implemented a given issue are shown in []. Thanks!
Verilator 5.052 2026-09-05
==========================
**Important:**
* Verilator now supports UVM 2020-3.2; see (#1538) for limitations.
**Other:**
* Add comments as a branch description in coverage .info files (#7843). [Eryk Szpotanski]
* Add --enable-light-debug configure option (#7886). [Geza Lore, Testorrent USA, Inc.]
* Add user-provided DPI-C function declarations (#7626) (#7893). [Jakub Michalski]
* Add error on static virtual functions (#7932). [Igor Zaworski, Antmicro Ltd.]
* Add error when scalar is passed to array task argument (#7948). [Pawel Klopotek]
* Add MULTIDRIVENPROC warning for signals driven by multiple plain always blocks (#7968). [Aisha]
* Add FSM arc and state coverage to verilator_coverage .info output (#7972) (#7973). [Igor Zaworski, Antmicro Ltd.]
* Add SIMILARNAME warning when variables have names that differ in case (#7992) (#8020) (#8186) (#8226) (#8233). [Paul Campbell]
* Add Unigen2 constrained randomization algorithm (#8042). [Jakub Wasilewski, Antmicro Ltd.]
* Add `+verilator+assert+lock` to ignore RTL assert control statements (#8086). [Sumanth Kadiyala]
* Add NEVERMATCH warning when an SVA sequence can never match (#8176) (#8183). [Artur Bieniek, Antmicro Ltd.]
* Remove and deprecate `--assert-unroll-limit`. (#8096). [Artur Bieniek, Antmicro Ltd.]
* Support embedded covergroup member references (#7749) (#8015). [Marco Bartoli]
* Support a sequence used as an event control (#7797) (#7846). [Yilou Wang]
* Support `[\*N:$]` consecutive repetition (#7940). [Artur Bieniek, Antmicro Ltd.]
* Support dynamic containers in unique constraints (#7947). [Adam Kostrzewski, Antmicro Ltd.]
* Support delayed tristate gates (#7960) (#7961). [Patrick Creighton]
* Support `default clocking` (#7984). [Kamil Danecki, Antmicro Ltd.]
* Support embedded covergroup clocking events (#8028). [Marco Bartoli]
* Support `weak`/`strong` keywords in property expressions (#8054). [Artur Bieniek, Antmicro Ltd.]
* Support class handle covergroup arguments (#8071). [Marco Bartoli]
* Support `soft foreach` constraints (#8174). [Kamil Danecki, Antmicro Ltd.]
* Support overlong `within` sequences as never-matching (#8177). [Artur Bieniek, Antmicro Ltd.]
* Support covergroup/function/task (ref/virtual/array) interface arguments (#8180). [Marco Bartoli]
* Support covergroup events with reference members or ref arguments (#8201). [Marco Bartoli]
* Support s_until/s_until_with in property expression (#8220). [Artur Bieniek, Antmicro Ltd.]
* Support solve before with associative array (#8232). [Kamil Danecki, Antmicro Ltd.]
* Optimize random initialization. [Geza Lore, Testorrent USA, Inc.]
* Optimize more always blocks in DFG (#7775). [Geza Lore, Testorrent USA, Inc.]
* Optimize assertion NFAs using bit-vector ring buffers (#7885). [Artur Bieniek, Antmicro Ltd.]
* Optimize circular logic in DFG (#7902). [Geza Lore, Testorrent USA, Inc.]
* Optimize VPI symbol registration and scope construction (#7936). [Nick Brereton]
* Optimize bounded always properties using ring buffers (#8061) (#8092). [Artur Bieniek, Antmicro Ltd.]
* Optimize VL_WORDS_I/VL_BYTES_I (#8095). [Geza Lore, Testorrent USA, Inc.]
* Optimize consecutive and goto repetitions using ring buffers (#8096). [Artur Bieniek, Antmicro Ltd.]
* Optimize temporary insertion for single bit replicates in DFG (#8110). [Geza Lore, Testorrent USA, Inc.]
* Optimize mtask coarsening in multi-threaded scheduling (#8120). [Geza Lore, Testorrent USA, Inc.]
* Optimize NFA delay-ring edge traversal, drop complexity to linear (#8145). [Artur Bieniek, Antmicro Ltd.]
* Optimize associative constant patterns to fixed point (#8147) (#8209). [Geza Lore, Testorrent USA, Inc.]
* Fix procedural tristate assignment overrides (#6785) (#8130). [Eddie Rydell]
* Fix $finish continuing event loop (#7267) (#7950). [Artur Bieniek, Antmicro Ltd.]
* Fix continuing on wait(0) (#7372) (#8231). [Szymon Gizler, Antmicro Ltd.]
* Fix $display accepting streaming concat arguments (#7663) (#7890). [Jaeuk Lee]
* Fix typedef clocking input sampling (#7688 partial) (#8204). [Marco Bartoli]
* Fix DFG misoptimizing bound checks (#7755). [Jakub Michalski]
* Fix unique0 case side effects (#7787). [Pawel Klopotek]
* Fix mid-window disable iff (#7792) (#7869). [Yilou Wang]
* Fix cleaning purity cache after assertions. [Geza Lore, Testorrent USA, Inc.]
* Fix uncaught type error leading to invalid C++ output (#7814). [Geza Lore, Testorrent USA, Inc.]
* Fix internal error for coverpoints that reference a covergroup formal parameter (#7853 partial) (#7889). [Matthew Ballance]
* Fix immediate disable of fork join branches (#7856) (#7931). [Marco Bartoli]
* Fix self-disable of named blocks with forks (Part of #7857) (#7996) (#8006). [Marco Bartoli]
* Fix clang++ ambiguous overload of '==' operator (#7863). [Pawel Kojma, Antmicro Ltd.]
* Fix heap-use-after-free in `VlRNG::VlRNG()` (#7865). [Dragon-Git]
* Fix mixed-width inside and dist range bounds failing randomization (#7875). [Yilou Wang]
* Fix solve-before over array variables failing randomization (#7876). [Yilou Wang]
* Fix scoped randomize with array members under rand_mode (#7877). [Yilou Wang]
* Fix dist constraint on a frozen variable failing randomization (#7878). [Yilou Wang]
* Fix range delays with parameter bounds (#7882). [Artur Bieniek, Antmicro Ltd.]
* Fix --coverage on labeled inline assert/cover property (#7898) (#7904). [Patrick Creighton]
* Fix memory leak in VerilatedFst::close() (#7899). [Jakub Michalski]
* Fix spurious FSM COVERIGN on comparisons (#7900) (#7908). [Yogish Sekhar]
* Fix release of forced port nets (#7901) (#7907). [Artur Bieniek, Antmicro Ltd.]
* Fix independent force of multiply instantiated signals (#7905). [Artur Bieniek, Antmicro Ltd.]
* Fix scheduling of variables written in non-inlined functions in suspendable processes (#7909). [Igor Zaworski, Antmicro Ltd.]
* Fix randomization of unreferenced rand members of unpacked structs (#7910) (#7911). [Philip Axer]
* Fix VL_TO_STRING function for array of structs (#7912). [Kornel Uriasz, Antmicro Ltd.]
* Fix queues falling into wrong template spec (#7914). [Adam Kostrzewski, Antmicro Ltd.]
* Fix type resolution when a local name shadows a type name (#7915). [Pawel Klopotek]
* Fix crash streaming an unpacked array of unpacked structs (#7917). [Nick Brereton]
* Fix streaming concat as output-port lvalue into unpacked struct (#7918). [Nick Brereton]
* Fix dropping variable writes across opaque calls (#7921) (#7933). [Philip Axer]
* Fix dynamic array handling in solve...before (#7922). [Kornel Uriasz, Antmicro Ltd.]
* Fix unlinked error with function call in derived class parameter (#7923). [Mateusz Gancarz, Antmicro Ltd.]
* Fix scheduling of variables written in non-inlined functions in suspendable processes (#7924). [Igor Zaworski, Antmicro Ltd.]
* Fix class parameter resolution (#7935).
* Fix FSM detection of coverage-only branches (#7941) (#7942). [Patrick Creighton]
* Fix use-after-free in V3LinkDotIfaceCapture (#7943). [Nick Brereton]
* Fix sampled-value clocks in repeated assertions (#7944). [Artur Bieniek, Antmicro Ltd.]
* Fix queued $finish/$stop request thread ordering (#7946 prep) (#7952). [Yilou Wang]
* Fix NFA assertion scheduling to fix outcome counts (#7946). [Yilou Wang]
* Fix DFG nested shifts with overflowing shift amount (#7955) (#7977).
* Fix timing controls in interface tasks called via virtual interface (#7959). [Yilou Wang]
* Fix lambda parameter types in queue min and max (#7962). [Bartosz Skorowski]
* Fix solve-before dropping all soft constraints in randomize() (#7963). [Yilou Wang]
* Fix generic interface param resolution in module/cell parameterization (#7970) (#7971). [David Garau]
* Fix lost writes when select width exceeds variable width (#7975). [Bartosz Skorowski]
* Fix $fgets being mis-optimized away (#7976). [G-A. Kamendje]
* Fix SYNCASYNCNET false positive with changed non-edge logic (#7980). [Oron Port]
* Fix variable scope in unique on dynamic array (#7981). [Kornel Uriasz, Antmicro Ltd.]
* Fix table optimization causing not contextually convertible to bool error (#7983). [Jakub Michalski]
* Fix wait fork trigger temporary split across generated functions (#7985) (#7986). [Marco Brambilla]
* Fix randc cycling in a class that also uses solve...before (#7991 partial) (#8055). [Yilou Wang]
* Fix solver session stability to avoid breaking randomize (#7991 partial). [Yilou Wang]
* Fix simulation hang when the solver stops answering (#7991 partial) (#8129) (#8136). [Yilou Wang]
* Fix randomize() with a user variable named 'item' (#7993) (#7994).
* Fix UNSATCONSTR reporting constraint indices above 9 (#7995). [Yilou Wang]
* Fix MULTIDRIVEN/MULTIDRIVENPROC warning suppression via lint_off (#8000). [Gilberto Abram]
* Fix force not substituted into an unpacked array index (#8002).
* Fix dist weights ignored when an item is not a literal (#8003).
* Fix error when forcing an array element read at a run-time index (#8004).
* Fix FSM coverage on empty reset branches (#8005) (#8064). [Yogish Sekhar]
* Fix missing verilator_coverage in cmake install (#8008) (#8011).
* Fix dist inside a foreach nested in a constraint if (#8016).
* Fix gcov dump on warnings discarding later coverage counts (#8017). [Yilou Wang]
* Fix unique constraint crash, guards, and size (#8018).
* Fix Linux peak memory stat to use VmHWM (#8022) (#8070). [Tyrone Marhguy]
* Fix assignment pattern key constant expressions (#8029) (#8030). [Josep Sans]
* Fix skipping optimization for unpacked data types (#8031). [Kornel Uriasz, Antmicro Ltd.]
* Fix gate deduplication with function arguments (#8038). [JOTEGO]
* Fix conditional expression with parameterized classes (#8039). [Pawel Klopotek, Antmicro Ltd.]
* Fix array size references in constraints (#8040). [Kornel Uriasz, Antmicro Ltd.]
* Fix mid-range consecutive repetition rejects (#8041). [Artur Bieniek, Antmicro Ltd.]
* Fix write access to dynamic arrays (#8043). [Bartosz Skorowski, Antmicro Ltd.]
* Fix nondeterminism in trace stage (#8046). [Sumanth Kadiyala]
* Fix force helpers in multiply instantiated modules (#8047). [Artur Bieniek, Antmicro Ltd.]
* Fix pin to non-existent port with PINNOTFOUND suppressed (#8051) (#8052). [Nikolai Kumar]
* Fix wildcard equality against a 4-state constant in an assertion (#8056) (#8057). [Nikolai Kumar]
* Fix untyped datatype error on sampling functions with property arguments (#8060). [Artur Bieniek, Antmicro Ltd.]
* Fix VPI cbValueChange for 1-bit select (#8063). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix NFA assertion crash and mis-counted property if/case (#8074). [Yilou Wang]
* Fix skipping non-constrained enum constraining when inside object of other class (#8075). [Kornel Uriasz, Antmicro Ltd.]
* Fix use-after-free of captured interface typedef reference during parameter cloning (#8076). [em2machine]
* Fix unintended side-effect insertion on associative array read (#8077). [Adam Kostrzewski, Antmicro Ltd.]
* Fix undefined symbol solver error (#8080). [Igor Zaworski, Antmicro Ltd.]
* Fix unpacked array element force with procedural assign (#8084) (#8085). [Nikolai Kumar]
* Fix reading another parameter resolves to 0 in cloned modules (#8086) (#8069). [em2machine]
* Fix hierarchical class scope resolution (#8094). [Artur Bieniek, Antmicro Ltd.]
* Fix segmentation fault on parameters (#8102) (#8149). [Sumanth Kadiyala]
* Fix the clang compilation using precompiled headers (#8105). [Demin Han]
* Fix invalid typedef (#8106). [Adam Kostrzewski, Antmicro Ltd.]
* Fix R/W references to random builtin function seeds (#8112). [Geza Lore, Testorrent USA, Inc.]
* Fix invalid C++ on cast (#8113). [Pawel Klopotek, Antmicro Ltd.]
* Fix COVERIGN on SVA goto repetition (#8118). [Artur Bieniek, Antmicro Ltd.]
* Fix unordered data hazards in multi-threaded scheduling (#8133). [Geza Lore, Testorrent USA, Inc.]
* Fix public table offsets larger than 32 bits (#8135). [Sunimali Rathnayake]
* Fix 'get-value' for empty constrained arrays (#8138). [Kornel Uriasz, Antmicro Ltd.]
* Fix streaming concatenation uninitialized read-modify-write access (#8143). [Geza Lore, Testorrent USA, Inc.]
* Fix unpacked array passing to logic task argument error (#8148). [Pawel Klopotek, Antmicro Ltd.]
* Fix $past in final blocks on a sampling-tick finish (#8153). [Yilou Wang]
* Fix array reference in reduction constraints (#8158). [Kornel Uriasz, Antmicro Ltd.]
* Fix VPI force data corruption (#8160). [Jonathan Drolet]
* Fix sampled-value functions output on the first cycle (#8162). [Artur Bieniek, Antmicro Ltd.]
* Fix null pointer dereference in process destruction (#8171). [Andrii, Antmicro Ltd.]
* Fix invalid conversion of queue (#8172). [Andrii, Antmicro Ltd.]
* Fix crash on forward-referencing parameters (#8175). [Geza Lore, Testorrent USA, Inc.]
* Fix crash on empty `#()` parameter list on class instance (#8182). [Geza Lore, Testorrent USA, Inc.]
* Fix crash on multiple missing type parameters (#8184). [Geza Lore, Testorrent USA, Inc.]
* Fix SVA bound limit guard, leave headroom for alignment, respect --max-num-width (#8192). [Artur Bieniek, Antmicro Ltd.]
* Fix internal error on an enum literal inside a `with` lambda body (#8190) (#8193). [Aditya Shevade]
* Fix internal error on backslash-quoted strings (#8198). [Henry Hsieh]
* Fix backslash behavior in string literals (#8211). [Pawel Klopotek, Antmicro Ltd.]
* Fix task inout pin resizing error (#8212). [Pawel Klopotek, Antmicro Ltd.]
* Fix code generation for postponed region (#8218). [Geza Lore, Testorrent USA, Inc.]
* Fix method calls to be a reorder barrier (#8222). [Matthew Ballance]
* Fix errors on stream connected to instance port (#8234). [Geza Lore, Testorrent USA, Inc.]
* Fix folding of short circuiting operators guarding member access (#8238). [Geza Lore, Testorrent USA, Inc.]
* Fix MULTIDRIVENPROC on static loop induction variables (#8242). [Geza Lore, Testorrent USA, Inc.]
* Fix multiply undefined behavior on signed int overflow (#8247) (#8248). [Paul Rigge]
* Fix initial statements with gate optimization (#8250). [Prithayan Barua]
* Fix segmentation fault on context destruction (#8265).
* Fix mis-randomizing an unpacked struct member when not rand/randc (#8277) (#8278). [Aditya Shevade]
* Fix std::randomize() with-clause array index arguments (#8279) (#8280). [Aditya Shevade]
* Fix incorrect reference to sync/async in SYNCASYNCNET warning (#8281). [Andrii, Antmicro Ltd.]
* Fix use-after-free of captured interface typedef in deleted generate branch (#8287). [Nick Brereton]
Verilator 5.050 2026-07-01
==========================
@@ -5738,7 +5914,7 @@ Verilator 3.104 2003-04-30
**Major:**
* Indicate direction of ports with VL_IN and VL_OUT.
* Allow $c32, etc, to specify width of the $c statement for VCS.
* Allow $c32, etc, to specify width of the $c statement.
* Numerous performance improvements, worth about 25%
**Minor:**
+17 -4
View File
@@ -94,6 +94,7 @@ CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
CFG_WITH_DEV_GCOV = @CFG_WITH_DEV_GCOV@
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
CFG_WITH_SOLVER = @CFG_WITH_SOLVER@
GCOV = @GCOV@
PACKAGE_VERSION = @PACKAGE_VERSION@
#### End of system configuration section. ####
@@ -466,9 +467,11 @@ analyzer-include:
# Bash/sh files
BASH_FILES = \
ci/ci-coverage-report.bash \
ci/ci-install.bash \
ci/ci-pages-notify.bash \
ci/ci-pages.bash \
ci/ci-rtlmeter-report.bash \
ci/ci-script.bash \
ci/docker/run/hooks/post_push \
ci/docker/run/verilator-docker \
@@ -536,6 +539,7 @@ PY_PROGRAMS = \
src/vlcovgen \
test_regress/*.py \
test_regress/t/*.pf \
test_regress/t/randomize_solver_tamper.py \
# Python files, subject to format but not lint
PY_FILES = \
@@ -695,20 +699,28 @@ COVERAGE_DIR := obj_coverage
ifeq ($(CFG_WITH_DEV_GCOV),yes)
# Figure out base and head refs for coverage report
COVERAGE_REF_BASE := $(if $(COVERAGE_BASE),$(shell git rev-parse --short $(COVERAGE_BASE)))
COVERAGE_REF_BASE := $(if $(COVERAGE_BASE),$(shell git rev-parse --short $(COVERAGE_BASE) 2>/dev/null))
ifneq ($(COVERAGE_BASE),)
ifeq ($(COVERAGE_REF_BASE),)
$(error COVERAGE_BASE='$(COVERAGE_BASE)' is not a valid git revision)
endif
endif
COVERAGE_REF_HEAD := $(shell git rev-parse --short HEAD)
override undefine COVERAGE_BASE # Use the above variabels instead
# 'fastcov' setup
FASTCOV := nodist/fastcov.py
FASTCOV_OPT := -j $(shell nproc)
FASTCOV_OPT += --gcov $(GCOV)
FASTCOV_OPT += --lcov
FASTCOV_OPT += --process-gcno
FASTCOV_OPT += --branch-coverage
FASTCOV_OPT += --dump-statistic
# Files matching the following glob patterns will be excluded from coverage
FASTCOV_OPT += --exclude-glob
FASTCOV_OPT += '/usr/*'
FASTCOV_OPT += '/usr/include/*'
FASTCOV_OPT += '/usr/lib*/*'
FASTCOV_OPT += '/usr/share/*'
FASTCOV_OPT += '*examples/*'
FASTCOV_OPT += '*include/fstcpp/*'
FASTCOV_OPT += '*src/obj_dbg/*'
@@ -749,6 +761,7 @@ GENHTML_OPT += --missed
GENHTML_OPT += --rc branch_coverage=1
GENHTML_OPT += --rc genhtml_hi_limit=100
GENHTML_OPT += --ignore-errors negative
GENHTML_OPT += --ignore-errors inconsistent
ifeq ($(COVERAGE_REF_BASE),)
GENHTML_OPT += --header-title "Code coverage for Verilator $(shell git describe --dirty)"
else
@@ -780,7 +793,7 @@ $(COVERAGE_DIR)/verilator-patch.info: $(COVERAGE_DIR)/verilator.info
@echo "####################################################################"
rm -f $(COVERAGE_DIR)/empty-patch
git diff $(COVERAGE_REF_BASE) -- include src > $(COVERAGE_DIR)/filter.patch
[ -s $(COVERAGE_DIR)/filter.patch ]] || touch $(COVERAGE_DIR)/empty-patch
[ -s $(COVERAGE_DIR)/filter.patch ] || touch $(COVERAGE_DIR)/empty-patch
$(FASTCOV) -C $^ --lcov -o $@ --diff-filter $(COVERAGE_DIR)/filter.patch
# Build coverage report
@@ -789,7 +802,7 @@ $(COVERAGE_DIR)/report/index.html: $(COVERAGE_DIR)/verilator$(if $(COVERAGE_REF_
@echo "# genhtml: Generating coverage report"
@echo "####################################################################"
@rm -rf $(COVERAGE_DIR)/report
[ -f $(COVERAGE_DIR)/empty-patch ]] || /usr/bin/time -f "That took %E" \
[ -f $(COVERAGE_DIR)/empty-patch ] || /usr/bin/time -f "That took %E" \
$(GENHTML) $(GENHTML_OPT) --output-directory $(COVERAGE_DIR)/report $^ || true
@# Uncommitted changes not tracked, force rebuild on next run if patch coverage
@$(if $(COVERAGE_REF_BASE),mv $(COVERAGE_DIR)/verilator-patch.info $(COVERAGE_DIR)/verilator-patch-last.info)
+5 -3
View File
@@ -4,7 +4,9 @@
SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|badge1| |badge2| |badge3| |badge4| |badge5| |badge7| |badge8|
|badge1| |badge2| |badge3| |badge4| |badge5|
|badge7| |badge8|
.. |badge1| image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
:target: https://verilator.org
@@ -21,8 +23,8 @@
.. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator
:target: https://hub.docker.com/r/verilator/verilator
.. |badge7| image:: https://github.com/verilator/verilator/workflows/build/badge.svg
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
.. |badge7| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/build-test.yml?branch=master&label=regressions
:target: https://github.com/verilator/verilator/actions/workflows/build-test.yml
.. |badge8| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/rtlmeter.yml?branch=master&event=schedule&label=benchmarks
:target: https://verilator.github.io/verilator-rtlmeter-results
+1
View File
@@ -636,6 +636,7 @@ description of these arguments.
=for VL_SPHINX_EXTRACT "_build/gen/args_verilated.rst"
+verilator+assert+lock Lock assertion status changes at startup
+verilator+coverage+file+<filename> Set coverage output filename
+verilator+debug Enable debugging
+verilator+debugi+<value> Enable debugging at a level
+7 -2
View File
@@ -51,12 +51,17 @@ def profcfunc(filename: str) -> None:
# Find modules
verilated_mods = {}
for func in funcs:
match = re.search(r'(.*)::eval(_step)?\(', func)
# Match only the model entry points: 'eval', 'eval_step', 'eval_end_step',
# and the 'evalXxx' region entry points, but not e.g. a user's '::evaluate'
match = re.match(r'([A-Za-z_][A-Za-z_0-9]*)::eval(_step|_end_step|[A-Z][A-Za-z_0-9]*)?\(',
func)
if match:
prefix = match.group(1)
if prefix.startswith('Verilated'): # Run-time library, not a model
continue
if Args.debug:
print("-got _eval %s prefix=%s" % (func, prefix))
verilated_mods[prefix] = re.compile(r'^' + prefix)
verilated_mods[prefix] = re.compile(r'^' + re.escape(prefix))
# pprint(verilated_mods)
# Sort by Verilog name
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
# DESCRIPTION: Verilator: CI build job script
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
# Executed in the 'build' stage.
################################################################################
# Destructive to the checkout (reconfigures and rebuilds); never run locally
if [ "$GITHUB_ACTIONS" != "true" ]; then
echo "ERROR: $(basename "$0") must only be run in GitHub Actions CI" >&2
exit 1
fi
set -e
set -x
source "$(dirname "$0")/ci-common.bash"
################################################################################
# Parse arguments
OPT_ASAN=0
OPT_CCWARN=0
OPT_GCOV=0
OPT_LIGHT_DEBUG=0
OPT_COMPILER=
OPT_PREFIX=
while [ $# -gt 0 ]; do
case "$1" in
--asan) OPT_ASAN=1 ;;
--ccwarn) OPT_CCWARN=1 ;;
--compiler)
[ $# -ge 2 ] || fatal "--compiler requires an argument"
OPT_COMPILER="$2"
shift
;;
--gcov) OPT_GCOV=1 ;;
--light-debug) OPT_LIGHT_DEBUG=1 ;;
--prefix)
[ $# -ge 2 ] || fatal "--prefix requires an argument"
OPT_PREFIX="$2"
shift
;;
*) fatal "Unknown option: '$1'" ;;
esac
shift
done
[ -n "$OPT_COMPILER" ] || fatal "--compiler is required"
[ -n "$OPT_PREFIX" ] || fatal "--prefix is required"
# Map the compiler name to the executable name configure CXX expects
case "$OPT_COMPILER" in
clang) CXX=clang++ ;;
gcc) CXX=g++ ;;
*) fatal "Unknown compiler: '$OPT_COMPILER'" ;;
esac
################################################################################
# Configure
CONFIGURE_ARGS="--prefix=$OPT_PREFIX --enable-longtests"
if [ "$OPT_CCWARN" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-ccwarn"
fi
if [ "$OPT_ASAN" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-dev-asan"
CXX="$CXX -DVL_LEAK_CHECKS"
# Replaces VNUser's union with a std::variant to validate type usage.
# Only takes effect when the build is C++17 or newer.
CXX="$CXX -DVL_USER_TYPE_CHECKS"
fi
if [ "$OPT_GCOV" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-dev-gcov"
fi
if [ "$OPT_LIGHT_DEBUG" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-light-debug"
fi
autoconf
./configure $CONFIGURE_ARGS CXX="$CXX"
################################################################################
# Build
ccache -z
BUILD_START=$SECONDS
"$MAKE" -j "$NPROC" -k
ccache -svv
ccache --evict-older-than "$((SECONDS - BUILD_START + 60))s"
ccache -svv
+35
View File
@@ -0,0 +1,35 @@
# DESCRIPTION: Verilator: CI common definitions, sourced by the stage scripts
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
# Common definitions sourced by the CI stage scripts; not executed directly.
################################################################################
fatal() {
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
}
case "$(uname -s)" in
Linux)
HOST_OS=linux
MAKE=make
NPROC=$(nproc)
# Distro id/version; subshell keeps os-release out of our namespace
if [ -r /etc/os-release ]; then
DISTRO_ID=$(. /etc/os-release; echo "$ID")
DISTRO_VERSION=$(. /etc/os-release; echo "$VERSION_ID")
fi
;;
Darwin)
HOST_OS=macOS
MAKE=make
NPROC=$(sysctl -n hw.logicalcpu)
;;
*)
fatal "Unknown host OS: '$(uname -s)'"
;;
esac
export MAKE
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env bash
# DESCRIPTION: Verilator: CI script for 'coverage.yml' results
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# This script combines the code coverage data gathered by the test jobs into
# the HTML report published on GitHub Pages, and for a pull request, builds the
# content of the response comment posted on it.
# Developer note: You should be able to run this script in your local checkout
# if you have GitHub CLI (command 'gh') setup, authenticated ('gh auth login'),
# and have set a default repository ('gh repo set-default').
set -eo pipefail
# Trace when running in the CI
[ "$GITHUB_ACTIONS" != "true" ] || set -x
# Arguments:
# 1. run ID
# 2. number of the pull request, or empty for a non-pull-request run
# 3. base SHA of the pull request (pull requests only)
# 4. head SHA of the pull request (pull requests only)
RUN_ID=$1
PR_NUMBER=$2
PR_BASE_SHA=$3
PR_HEAD_SHA=$4
# Coverage data and report directory, uploaded as the 'coverage-report' artifact
COVERAGE_DIR=obj_coverage
ls -lsha ${COVERAGE_DIR}
# Combine the reports from the test jobs
nodist/fastcov.py -C ${COVERAGE_DIR}/verilator-*.info --lcov -o ${COVERAGE_DIR}/verilator.info
# Create the report. For a pull request, report patch coverage against the
# merge-base between the head of the pull request and the target branch. The
# summary quoted in the notification below is only in the log of 'make'.
MAKE_LOG=make-coverage-report.log
if [ -n "${PR_NUMBER}" ]; then
COVERAGE_BASE=$(git rev-parse --short $(git merge-base ${PR_BASE_SHA} ${PR_HEAD_SHA}))
make coverage-report COVERAGE_BASE=${COVERAGE_BASE} |& tee ${MAKE_LOG}
else
make coverage-report
fi
# Remove the data files, only the HTML report is published
rm -f ${COVERAGE_DIR}/verilator*.info
# The rest is for pull requests only
if [ -z "${PR_NUMBER}" ]; then
exit 0
fi
# Get some metadata about the run
RUN_NUM=$(gh run view ${RUN_ID} --json number --jq ".number")
RUN_URL=$(gh run view ${RUN_ID} --json url --jq ".url")
# Repository owner and name of the default repository, used to build the
# GitHub Pages URL of the report. The owner is lowercased, as required for the
# '<owner>.github.io' Pages domain.
PAGES_OWNER=$(gh repo view --json owner --jq '.owner.login' | tr '[:upper:]' '[:lower:]')
PAGES_NAME=$(gh repo view --json name --jq '.name')
REPORT_URL=https://${PAGES_OWNER}.github.io/${PAGES_NAME}/coverage-reports/${RUN_ID}/index.html
###############################################################################
# Create the PR notification
###############################################################################
NOTIFICATION_DIR=notification
mkdir -p ${NOTIFICATION_DIR}
cat > ${NOTIFICATION_DIR}/body.txt <<NOTIFICATION_TEMPLATE
Patch coverage from PR workflow [#${RUN_NUM}](${RUN_URL}) (code coverage of lines changed relative to ${COVERAGE_BASE}):
NOTIFICATION_TEMPLATE
if [ -f ${COVERAGE_DIR}/empty-patch ]; then
# Patch is empty
cat >> ${NOTIFICATION_DIR}/body.txt <<SUMMARY_TEMPLATE
Patch contains no code changes
SUMMARY_TEMPLATE
echo "Workflow [#${RUN_NUM}](${RUN_URL}): patch contains no code changes" > ${NOTIFICATION_DIR}/hist.txt
else
# Patch contains code changes
cat >> ${NOTIFICATION_DIR}/body.txt <<SUMMARY_TEMPLATE
<pre>
$(grep -E "(lines|branches)\.*:" ${MAKE_LOG} | sed "s/\.*:/:/" || true)
</pre>
Report: [${RUN_ID}](${REPORT_URL})
Please get to 100% line coverage, and understand all branches; see the [developer docs](https://github.com/verilator/verilator/blob/master/docs/internals.rst#code-coverage-results)
SUMMARY_TEMPLATE
echo "Workflow [#${RUN_NUM}](${RUN_URL}) report: [${RUN_ID}](${REPORT_URL})" > ${NOTIFICATION_DIR}/hist.txt
fi
+161 -91
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# DESCRIPTION: Verilator: CI dependency install script
#
# SPDX-FileCopyrightText: 2020 Geza Lore
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
@@ -10,141 +10,211 @@
# required by the particular build stage.
################################################################################
# Installs system packages with sudo; never run locally
if [ "$GITHUB_ACTIONS" != "true" ]; then
echo "ERROR: $(basename "$0") must only be run in GitHub Actions CI" >&2
exit 1
fi
set -e
set -x
cd $(dirname "$0")/..
# Avoid occasional cpan failures "Issued certificate has expired."
export PERL_LWP_SSL_VERIFY_HOSTNAME=0
echo "check_certificate = off" >> ~/.wgetrc
source "$(dirname "$0")/ci-common.bash"
fatal() {
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
################################################################################
# Parse arguments
# Which stage to install dependencies for
STAGE="$1"
################################################################################
# Install dependencies
if [ "$HOST_OS" = "macOS" ]; then
# The macos runner image ships an untrusted third-party tap we don't use;
# untap it so brew stops emitting a tap-trust warning. Force + '|| true' since
# untap fails if a formula was installed from it, which is harmless here.
brew untap --force aws/tap || true
fi
# Run 'sudo apt-get', with timeout, retrying if failed
apt-get-retry() {
local _attempt
for _attempt in 1 2 3; do
[ "$_attempt" -eq 1 ] || sleep $((15 * _attempt))
sudo timeout --kill-after=30s 5m env DEBIAN_FRONTEND=noninteractive \
apt-get -o DPkg::Lock::Timeout=60 "$@" && return 0
done
fatal "'apt-get $*' failed on all attempts"
}
if [ "$CI_OS_NAME" = "linux" ]; then
MAKE=make
elif [ "$CI_OS_NAME" = "osx" ]; then
MAKE=make
elif [ "$CI_OS_NAME" = "freebsd" ]; then
MAKE=gmake
else
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
fi
if [ "$CI_OS_NAME" = "linux" ]; then
# Avoid slow "processing triggers for man db"
echo "path-exclude /usr/share/doc/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
echo "path-exclude /usr/share/man/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc
fi
# Run 'brew', retrying if failed
brew-retry() {
local _attempt
for _attempt in 1 2 3; do
[ "$_attempt" -eq 1 ] || sleep $((15 * _attempt))
brew "$@" && return 0
done
fatal "'brew $*' failed on all attempts"
}
install-wavediff() {
source ci/docker/buildenv/wavetools.conf
local _base_url="https://github.com/hudson-trading/wavetools/releases/download/${WAVETOOLS_VERSION}"
local _platform
if [ "$CI_OS_NAME" = "linux" ]; then
if [ "$HOST_OS" = "linux" ]; then
_platform="linux-x86_64"
elif [ "$CI_OS_NAME" = "osx" ]; then
elif [ "$HOST_OS" = "macOS" ]; then
_platform="macos-arm64"
elif [ "$CI_OS_NAME" = "windows" ]; then
_platform="windows-x86_64"
else
echo "WARNING: No wavetools binary available for CI_OS_NAME=$CI_OS_NAME, skipping"
echo "WARNING: No wavetools binary available for HOST_OS=$HOST_OS, skipping"
return 0
fi
local _tmpdir
_tmpdir=$(mktemp -d)
local _archive="wavetools-${WAVETOOLS_VERSION}-${_platform}"
if [ "$CI_OS_NAME" = "windows" ]; then
wget -q -O "${_tmpdir}/${_archive}.zip" "${_base_url}/${_archive}.zip"
unzip -o "${_tmpdir}/${_archive}.zip" -d "${_tmpdir}"
else
wget -q -O "${_tmpdir}/${_archive}.tar.gz" "${_base_url}/${_archive}.tar.gz"
tar -xzf "${_tmpdir}/${_archive}.tar.gz" -C "${_tmpdir}"
fi
wget -q -O "${_tmpdir}/${_archive}.tar.gz" "${_base_url}/${_archive}.tar.gz"
tar -xzf "${_tmpdir}/${_archive}.tar.gz" -C "${_tmpdir}"
sudo cp "${_tmpdir}/${_archive}/wavediff" /usr/local/bin/wavediff
rm -rf "${_tmpdir}"
}
if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
if [ "$STAGE" = "build" ]; then
##############################################################################
# Dependencies of jobs in the 'build' stage, i.e.: packages required to
# build Verilator
if [ "$CI_OS_NAME" = "linux" ]; then
sudo apt-get update ||
sudo apt-get update
sudo apt-get install --yes ccache help2man libfl-dev ||
sudo apt-get install --yes ccache help2man libfl-dev
if [[ ! "$CI_RUNS_ON" =~ "ubuntu-22.04" ]]; then
# Some conflict of libunwind verison on 22.04, can live without it for now
sudo apt-get install --yes libjemalloc-dev ||
sudo apt-get install --yes libjemalloc-dev
fi
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
sudo apt-get install --yes libsystemc libsystemc-dev ||
sudo apt-get install --yes libsystemc libsystemc-dev
if [ "$HOST_OS" = "linux" ]; then
if [ "$DISTRO_ID" = "ubuntu" ]; then
PACKAGES=(
bear
ccache
help2man
libfl-dev
libsystemc-dev
mold
)
# libunwind conflict on 22.04, can live without libjemalloc there
if [ "$DISTRO_VERSION" != "22.04" ]; then
PACKAGES+=(libjemalloc-dev)
fi
apt-get-retry update
apt-get-retry install --yes "${PACKAGES[@]}"
fi
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
sudo apt-get install --yes bear mold ||
sudo apt-get install --yes bear mold
fi
elif [ "$CI_OS_NAME" = "osx" ]; then
brew update ||
brew update
brew install ccache perl gperftools autoconf bison flex help2man ||
brew install ccache perl gperftools autoconf bison flex help2man
elif [ "$CI_OS_NAME" = "freebsd" ]; then
sudo pkg install -y autoconf bison ccache gmake perl5
elif [ "$HOST_OS" = "macOS" ]; then
PACKAGES=(
autoconf
bison
ccache
flex
gperftools
help2man
perl
)
brew-retry update
brew-retry install "${PACKAGES[@]}"
else
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
fatal "Unknown HOST_OS: '$HOST_OS'"
fi
if [ -n "$CCACHE_DIR" ]; then
mkdir -p "$CCACHE_DIR"
fi
elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
elif [ "$STAGE" = "test" ]; then
##############################################################################
# Dependencies of jobs in the 'test' stage, i.e.: packages required to
# run the tests
if [ "$CI_OS_NAME" = "linux" ]; then
sudo apt-get update ||
sudo apt-get update
# libfl-dev needed for internal coverage's test runs
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3 ||
sudo apt-get install --yes gdb gtkwave lcov libfl-dev ccache jq z3
# Required for test_regress/t/t_dist_attributes.py
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
sudo apt-get install --yes python3-clang mold ||
sudo apt-get install --yes python3-clang mold
if [ "$HOST_OS" = "linux" ]; then
if [ "$DISTRO_ID" = "ubuntu" ]; then
PACKAGES=(
ccache
gdb
jq
lcov
libfl-dev
libsystemc-dev
mold
python3-clang
z3
)
apt-get-retry update
apt-get-retry install --yes "${PACKAGES[@]}"
fi
if [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-26.04" ]]; then
if [[ ! "$CI_RUNS_ON" =~ "-riscv" ]]; then
sudo apt-get install --yes libsystemc libsystemc-dev ||
sudo apt-get install --yes libsystemc libsystemc-dev
fi
fi
elif [ "$CI_OS_NAME" = "osx" ]; then
brew update
# brew cask install gtkwave # fst2vcd hangs at launch, so don't bother
brew install ccache perl jq z3
elif [ "$CI_OS_NAME" = "freebsd" ]; then
# fst2vcd fails with "Could not open '<input file>', exiting."
sudo pkg install -y ccache gmake perl5 python3 jq z3
elif [ "$HOST_OS" = "macOS" ]; then
PACKAGES=(
ccache
jq
perl
z3
)
brew-retry update
brew-retry install "${PACKAGES[@]}"
else
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
fatal "Unknown HOST_OS: '$HOST_OS'"
fi
# Common installs
install-wavediff
# Workaround -fsanitize=address crash
sudo sysctl -w vm.mmap_rnd_bits=28
elif [ "$STAGE" = "lint-py" ]; then
##############################################################################
# Dependencies for nodist/clang_check_attributes
if [ "$HOST_OS" = "linux" ] && [ "$DISTRO_ID" = "ubuntu" ]; then
PACKAGES=(
python3-clang # Not run, but importers are linted
)
apt-get-retry update
apt-get-retry install --yes "${PACKAGES[@]}"
fi
elif [ "$STAGE" = "format" ]; then
##############################################################################
# Dependencies of 'make format'
if [ "$HOST_OS" = "linux" ] && [ "$DISTRO_ID" = "ubuntu" ]; then
PACKAGES=(
clang-format-18 # Version pinned, so all of CI formats alike
)
apt-get-retry update
apt-get-retry install --yes "${PACKAGES[@]}"
fi
elif [ "$STAGE" = "coverage" ]; then
##############################################################################
# Dependencies of the coverage report job, which only collates what the
# 'test' stage jobs measured
if [ "$HOST_OS" = "linux" ] && [ "$DISTRO_ID" = "ubuntu" ]; then
PACKAGES=(
lcov
)
apt-get-retry update
apt-get-retry install --yes "${PACKAGES[@]}"
fi
elif [ "$STAGE" = "rtlmeter-run" ]; then
##############################################################################
# Dependencies of the RTLMeter jobs, which compile and run designs with an
# already built Verilator, so they need its runtime prerequisites only
if [ "$HOST_OS" = "linux" ] && [ "$DISTRO_ID" = "ubuntu" ]; then
PACKAGES=(
ccache
libfl-dev
libjemalloc-dev
libsystemc-dev
mold
)
apt-get-retry update
apt-get-retry install --yes "${PACKAGES[@]}"
fi
else
##############################################################################
# Unknown build stage
fatal "Unknown CI_BUILD_STAGE_NAME: '$CI_BUILD_STAGE_NAME'"
fatal "Unknown stage '$STAGE'"
fi
# Report where the tools we may have installed live (ok if some are missing)
set +x
echo "Tools:"
for bin in autoconf bear bison ccache flex gdb help2man jq lcov mold perl wavediff z3; do
echo -n " $bin: "
which "$bin" || echo "Not found"
done
+139 -14
View File
@@ -6,37 +6,162 @@
# Notify PRs via comment that their workflow reports are available
# Get the current repo URL - might differ on a fork
readonly REPO_URL=$(gh repo view --json url --jq .url)
# Note this deliberately does not 'set -e'. A run that cannot be notified, say
# because its pull request was locked, must not hold up the notification of all
# the others, so each failure below moves on to the next run instead. Its
# artifact is then left in place, so it is retried on the next run of 'pages.yml'.
# The account we post as, which is how we tell our own earlier notifications
# from anybody else's comments. 'pages.yml' sets this to the slug of the app the
# token belongs to. Set it to your own login when running this by hand. The 'jq'
# filter below picks it up from the environment.
if [ -z "${GH_AUTHOR:-}" ]; then
echo "GH_AUTHOR must hold the account we post as" >&2
exit 1
fi
echo "Posting as '${GH_AUTHOR}'"
# Marker line separating the report from the history of the older reports
readonly HISTORY_MARKER='<!-- Verilator CI notification history -->'
# Opening line of the HTML comment stashing the history entry of the report
# itself. It is not shown in its own history, only inherited by the ones
# superseding it, hence it is kept out of sight until then.
readonly NEXTHIST_MARKER='<!-- Verilator CI notification entry'
# Create artifacts root directory
ARTIFACTS_ROOT=artifacts
mkdir -p ${ARTIFACTS_ROOT}
for RUN_ID in ${PR_RUN_IDS//,/ }; do
echo "@@@ Processing run ${RUN_ID}"
# Create workflow artifacts directory
ARTIFACTS_DIR=${ARTIFACTS_ROOT}/${RUN_ID}
mkdir -p ${ARTIFACTS_DIR}
# Download artifact of this run, if exists
gh run download ${RUN_ID} --name pr-notification --dir ${ARTIFACTS_DIR} || true
ls -lsha ${ARTIFACTS_DIR}
gh run download ${RUN_ID} --name pr-notification --dir ${ARTIFACTS_DIR} &> /dev/null || true
# Move on if no notification is required
if [ ! -f ${ARTIFACTS_DIR}/pr-number.txt ]; then
echo "No notification found"
continue
fi
echo "Posting notification found"
cat ${ARTIFACTS_DIR}/body.txt
gh pr comment $(cat ${ARTIFACTS_DIR}/pr-number.txt) --body-file ${ARTIFACTS_DIR}/body.txt
PR_NUMBER=$(cat ${ARTIFACTS_DIR}/pr-number.txt)
echo "@@@ Run ${RUN_ID}: notifying PR #${PR_NUMBER}"
# Get the artifact ID
ARTIFACT_ID=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id')
# Everything below is created and validated by the 'upload-pr-notification'
# action. Move on if this artifact was not made by it, e.g. it is left over
# from an earlier version of the CI, so it does not hold up the other runs.
for f in body.txt key.txt hist.txt; do
if [ ! -f ${ARTIFACTS_DIR}/${f} ]; then
echo "Notification has no '${f}'" >&2
continue 2 # Note: continues the enclosing 'RUN_ID' loop
fi
done
# Delete it, so we only notify once
gh api --method DELETE "repos/{owner}/{repo}/actions/artifacts/${ARTIFACT_ID}"
# 'key.txt' holds the key naming the source of the notification. It goes on the
# first line of the comment as an HTML comment, so it is invisible when
# rendered, and is used below to find the stale notifications with the same
# key, which are then superseded by the new one and deleted.
COMMENT_MARKER="<!-- Verilator CI notification: $(cat ${ARTIFACTS_DIR}/key.txt) -->"
# Find the stale notifications from the same source on this PR. That is, our
# own comments with the same marker on their first line. Oldest first, as
# returned by the API. An app posts as '<slug>[bot]', so drop that suffix to
# compare against the slug. Move on if they cannot be listed, as without them
# we would post a duplicate instead of superseding them.
if ! COMMENT_MARKER="${COMMENT_MARKER}" \
gh api --paginate "repos/{owner}/{repo}/issues/${PR_NUMBER}/comments" \
--jq '.[]
| select((.user.login | rtrimstr("[bot]")) == env.GH_AUTHOR)
| select((.body | split("\n") | .[0] | rtrimstr("\r")) == env.COMMENT_MARKER)
| {id, body}' > ${ARTIFACTS_DIR}/stale.json; then
echo "Failed to list the comments of PR #${PR_NUMBER}" >&2
continue
fi
# Inherit the history of the newest stale notification. There should only ever
# be one, and it holds the whole history anyway, as each notification inherits
# the history of the one it superseded. Any older ones are only deleted below.
STALE_COUNT=$(wc -l < ${ARTIFACTS_DIR}/stale.json)
if [ ${STALE_COUNT} -gt 1 ]; then
echo "Found ${STALE_COUNT} stale notifications, only the newest is inherited from"
fi
HISTORY=${ARTIFACTS_DIR}/history.txt
touch ${HISTORY}
if [ ${STALE_COUNT} -gt 0 ]; then
tail -n 1 ${ARTIFACTS_DIR}/stale.json | jq -r '.body' | tr -d '\r' > ${ARTIFACTS_DIR}/stale-body.txt
# Its own entry, stashed in an HTML comment
awk -v marker="${NEXTHIST_MARKER}" '
$0 == marker { inside = 1; next }
!inside { next }
$0 == "-->" { exit }
NF { print }
' ${ARTIFACTS_DIR}/stale-body.txt >> ${HISTORY}
# Followed by its history, without the enclosing '<details>' markup
awk -v marker="${HISTORY_MARKER}" '
$0 == marker { inside = 1; next }
!inside { next }
$0 == "<details>" || /^<summary>/ { next }
$0 == "</details>" { exit }
NF { print }
' ${ARTIFACTS_DIR}/stale-body.txt >> ${HISTORY}
fi
# Assemble the comment. Starts with the marker and the report.
COMMENT=${ARTIFACTS_DIR}/comment.txt
cat > ${COMMENT} <<COMMENT_TEMPLATE
${COMMENT_MARKER}
$(cat ${ARTIFACTS_DIR}/body.txt)
COMMENT_TEMPLATE
# Then the history inherited from the notifications this one supersedes, so
# the older reports remain reachable from the only remaining comment.
if [ -s ${HISTORY} ]; then
echo "History holds $(wc -l < ${HISTORY}) entries"
cat >> ${COMMENT} <<HISTORY_TEMPLATE
${HISTORY_MARKER}
<details>
<summary>PR history</summary>
$(cat ${HISTORY})
</details>
HISTORY_TEMPLATE
fi
# Finally this report's own history entry, stashed out of sight until a later
# notification inherits it.
cat >> ${COMMENT} <<NEXTHIST_TEMPLATE
${NEXTHIST_MARKER}
$(cat ${ARTIFACTS_DIR}/hist.txt)
-->
NEXTHIST_TEMPLATE
# Post it. Move on if this failed, without deleting anything below, otherwise
# the history held by the stale notifications would be lost.
if ! jq -Rs '{body: .}' ${COMMENT} \
| gh api --method POST "repos/{owner}/{repo}/issues/${PR_NUMBER}/comments" \
--input - > ${ARTIFACTS_DIR}/posted.json; then
echo "Failed to post this notification on PR #${PR_NUMBER}:" >&2
cat ${COMMENT} >&2
continue
fi
echo "Posted $(jq -r '.html_url' ${ARTIFACTS_DIR}/posted.json)"
# Delete the stale notifications, so only the new one remains
for COMMENT_ID in $(jq -r '.id' ${ARTIFACTS_DIR}/stale.json); do
echo "Deleting stale comment ${COMMENT_ID}"
gh api --method DELETE "repos/{owner}/{repo}/issues/comments/${COMMENT_ID}"
done
# Get the artifact IDs. Note there can be more than one artifact named
# 'pr-notification' for a single run, as the artifacts endpoint lists
# artifacts across all run attempts, and a re-run uploads a new one while
# keeping the previous attempt's artifact.
ARTIFACT_IDS=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id')
# Delete them all, so we only notify once
for ARTIFACT_ID in ${ARTIFACT_IDS}; do
gh api --method DELETE "repos/{owner}/{repo}/actions/artifacts/${ARTIFACT_ID}"
done
done
+6 -22
View File
@@ -35,13 +35,10 @@ compile_coverage_reports() {
# Gather all coverage workflow runs within the time window
gh run list -w coverage.yml --limit 1000 --created ">=${OLDEST}" --json "databaseId,event,status,conclusion,createdAt,number" > recentRuns.json
echo @@@ Recent runs:
jq "." recentRuns.json
# Select completd runs that were not cancelled or skipped, sort by descending run number
jq 'sort_by(-.number) | map(select(.status == "completed" and (.conclusion == "success" or .conclusion == "failure")))' recentRuns.json > completedRuns.json
echo @@@ Completed with success or failure:
jq "." completedRuns.json
echo "@@@ $(jq length completedRuns.json) of $(jq length recentRuns.json) runs since ${OLDEST} completed with success or failure"
# Create artifacts root directory
local ARTIFACTS_ROOT=artifacts-coverage
@@ -64,11 +61,8 @@ compile_coverage_reports() {
# For each worfklow run that was triggered by this event type
for RUN_ID in $(jq ".[] | select(.event == \"${EVENT}\") |.databaseId" completedRuns.json); do
echo "@@@ Processing run ${RUN_ID}"
# Extract the info of this run
jq ".[] | select(.databaseId == $RUN_ID)" completedRuns.json > workflow.json
jq "." workflow.json
# Record run ID of PR job
[[ $EVENT != "pull_request" ]] || PR_RUN_IDS="$PR_RUN_IDS $RUN_ID"
@@ -78,15 +72,13 @@ compile_coverage_reports() {
mkdir -p ${ARTIFACTS_DIR}
# Download artifacts of this run, if exists
gh run download ${RUN_ID} --name coverage-report --dir ${ARTIFACTS_DIR} || true
ls -lsha ${ARTIFACTS_DIR}
gh run download ${RUN_ID} --name coverage-report --dir ${ARTIFACTS_DIR} &> /dev/null || true
# Move on if no coverage report is available
if [ ! -d ${ARTIFACTS_DIR}/report ]; then
echo "No coverage report found"
continue
fi
echo "Coverage report found"
echo "@@@ Run ${RUN_ID}: coverage report found"
# Emit section header
if [[ -n $EMIT_SECTION_HEADER ]]; then
@@ -159,13 +151,10 @@ compile_rtlmeter_reports() {
# Gather all RTLMeter workflow runs within the time window
gh run list -w rtlmeter.yml --limit 1000 --created ">=${OLDEST}" --json "databaseId,event,status,conclusion,createdAt,number" > recentRuns.json
echo @@@ Recent runs:
jq "." recentRuns.json
# Select completd runs that were not cancelled or skipped, sort by descending run number
jq 'sort_by(-.number) | map(select(.status == "completed" and (.conclusion == "success" or .conclusion == "failure")))' recentRuns.json > completedRuns.json
echo @@@ Completed with success or failure:
jq "." completedRuns.json
echo "@@@ $(jq length completedRuns.json) of $(jq length recentRuns.json) runs since ${OLDEST} completed with success or failure"
# Create artifacts root directory
local ARTIFACTS_ROOT=artifacts-rtlmeter
@@ -188,11 +177,8 @@ compile_rtlmeter_reports() {
# For each worfklow run that was triggered by this event type
for RUN_ID in $(jq ".[] | select(.event == \"${EVENT}\") |.databaseId" completedRuns.json); do
echo "@@@ Processing run ${RUN_ID}"
# Extract the info of this run
jq ".[] | select(.databaseId == $RUN_ID)" completedRuns.json > workflow.json
jq "." workflow.json
# Record run ID of PR job
[[ $EVENT != "pull_request" ]] || PR_RUN_IDS="$PR_RUN_IDS $RUN_ID"
@@ -202,15 +188,13 @@ compile_rtlmeter_reports() {
mkdir -p ${ARTIFACTS_DIR}
# Download artifacts of this run, if exists
gh run download ${RUN_ID} --name rtlmeter-report --dir ${ARTIFACTS_DIR} || true
ls -lsha ${ARTIFACTS_DIR}
gh run download ${RUN_ID} --name rtlmeter-report --dir ${ARTIFACTS_DIR} &> /dev/null || true
# Move on if no RTLMeter report is available
if [ ! -d ${ARTIFACTS_DIR}/report ]; then
echo "No RTLMeter report found"
continue
fi
echo "RTLMeter report found"
echo "@@@ Run ${RUN_ID}: RTLMeter report found"
# Emit section header
if [[ -n $EMIT_SECTION_HEADER ]]; then
+12 -4
View File
@@ -25,6 +25,8 @@ CASES = {
("BlackParrot:2x2:*", 14),
("BlackParrot:4x4:*", 25),
("Caliptra:default:*", 19),
("HummingbirdV2-E203:default:*", 3),
("HummingbirdV2-E203:nodelay:*", 3),
("NVDLA:*", 29),
("OpenPiton:1x1:*", 4),
("OpenPiton:2x2:*", 7),
@@ -57,13 +59,15 @@ CASES = {
("BlackParrot:2x2:*", 9),
("BlackParrot:4x4:*", 12),
("Caliptra:default:*", 16),
("HummingbirdV2-E203:default:*", 10),
("HummingbirdV2-E203:nodelay:*", 10),
("NVDLA:*", 28),
("OpenPiton:1x1:*", 5),
("OpenPiton:2x2:*", 6),
("OpenPiton:4x4:*", 8),
("OpenTitan:*", 10),
("Servant:qerv:*", 17),
("Servant:serv:*", 15),
("Servant:qerv:phil", 17),
("Servant:serv:phil", 15),
("VeeR-EH1:asic:*", 6),
("VeeR-EH1:default:*", 8),
("VeeR-EH1:hiperf:*", 5),
@@ -107,6 +111,8 @@ CASES_PR = {
("BlackParrot:4x4:*", 25),
("Caliptra:default:*", 19),
("NVDLA:*", 29),
("HummingbirdV2-E203:default:*", 3),
("HummingbirdV2-E203:nodelay:*", 3),
("OpenPiton:1x1:*", 4),
("OpenPiton:4x4:*", 10),
("OpenTitan:*", 16),
@@ -137,11 +143,13 @@ CASES_PR = {
("BlackParrot:4x4:*", 12),
("Caliptra:default:*", 16),
("NVDLA:*", 28),
("HummingbirdV2-E203:default:*", 10),
("HummingbirdV2-E203:nodelay:*", 10),
("OpenPiton:1x1:*", 5),
("OpenPiton:4x4:*", 8),
("OpenTitan:*", 10),
("Servant:qerv:*", 17),
("Servant:serv:*", 15),
("Servant:qerv:phil", 17),
("Servant:serv:phil", 15),
("VeeR-EH1:asic:*", 6),
("VeeR-EH1:default:*", 8),
("VeeR-EH1:hiperf:*", 5),
+16 -4
View File
@@ -88,9 +88,16 @@ venv/bin/python3 $SCRIPT_DIR/ci-rtlmeter-report.py ${SUMMARY_ARGS[@]} > $TMP_DIR
# Print it
cat $TMP_DIR/summary.txt
# Create notification comment content
NOTIFICATION=$TMP_DIR/notification.txt
cat > $NOTIFICATION <<NOTIFICATION_TEMPLATE
REPORT_URL=https://${PAGES_OWNER}.github.io/${PAGES_NAME}/rtlmeter-reports/${RUN_ID}/index.html
###############################################################################
# Create the PR notification
###############################################################################
NOTIFICATION_DIR=$TMP_DIR/notification
mkdir -p $NOTIFICATION_DIR
cat > ${NOTIFICATION_DIR}/body.txt <<NOTIFICATION_TEMPLATE
Performance metrics for PR workflow [#$RUN_NUM]($RUN_URL), comparing:
- target branch commit [${REF_SHA:0:9}](https://github.com/${PAGES_OWNER}/${PAGES_NAME}/commit/${REF_SHA}) (A)
- with PR merge commit [${RUN_SHA:0:9}](https://github.com/${PAGES_OWNER}/${PAGES_NAME}/commit/${RUN_SHA}) (B)
@@ -106,10 +113,15 @@ The reported numbers are the geometric means of the ratios of the metrics over a
less than 1 is a regression, greater than 1 is an improvement.
The jobs run on variable and noisy runners, so some variance is expected between runs.
Detailed report: [${RUN_ID}](https://${PAGES_OWNER}.github.io/${PAGES_NAME}/rtlmeter-reports/${RUN_ID}/index.html)
Detailed report: [${RUN_ID}](${REPORT_URL})
NOTIFICATION_TEMPLATE
echo "Workflow [#$RUN_NUM]($RUN_URL) report: [${RUN_ID}](${REPORT_URL})" > ${NOTIFICATION_DIR}/hist.txt
###############################################################################
# Create detailed report
###############################################################################
REPORT=$TMP_DIR/body.html
cat > $REPORT <<SUMMARY_TEMPLATE
<h3>Summary of all runs</h3>
-208
View File
@@ -1,208 +0,0 @@
#!/usr/bin/env bash
# DESCRIPTION: Verilator: CI main job script
#
# SPDX-FileCopyrightText: 2020 Geza Lore
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
# This is the main script executed in the 'script' phase by all jobs. We use a
# single script to keep the CI setting simple. We pass job parameters via
# environment variables using 'env' keys.
################################################################################
set -e
set -x
fatal() {
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
}
if [ "$CI_OS_NAME" = "linux" ]; then
export MAKE=make
NPROC=$(nproc)
elif [ "$CI_OS_NAME" = "osx" ]; then
export MAKE=make
NPROC=$(sysctl -n hw.logicalcpu)
# Disable ccache, doesn't always work in GitHub Actions
export OBJCACHE=
elif [ "$CI_OS_NAME" = "freebsd" ]; then
export MAKE=gmake
NPROC=$(sysctl -n hw.ncpu)
else
fatal "Unknown CI_OS_NAME: '$CI_OS_NAME'"
fi
NPROC=$(expr $NPROC '+' 1)
if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
##############################################################################
# Build verilator
autoconf
CONFIGURE_ARGS="--enable-longtests --enable-ccwarn"
if [ "$CI_DEV_ASAN" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-dev-asan"
CXX="$CXX -DVL_LEAK_CHECKS"
fi
if [ "$CI_DEV_GCOV" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-dev-gcov"
fi
./configure $CONFIGURE_ARGS --prefix="$INSTALL_DIR"
ccache -z
"$MAKE" -j "$NPROC" -k
# 22.04: ccache -s -v
ccache -s
if [ "$CI_OS_NAME" = "osx" ]; then
file bin/verilator_bin
file bin/verilator_bin_dbg
md5 bin/verilator_bin
md5 bin/verilator_bin_dbg
stat bin/verilator_bin
stat bin/verilator_bin_dbg
fi
elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
##############################################################################
# Run tests
export VERILATOR_TEST_NO_CONTRIBUTORS=1 # Separate workflow check
export VERILATOR_TEST_NO_LINT_PY=1 # Separate workflow check
if [ "$CI_OS_NAME" = "osx" ]; then
export VERILATOR_TEST_NO_GDB=1 # Pain to get GDB to work on OS X
# TODO below may no longer be required as configure checks for -pg
export VERILATOR_TEST_NO_GPROF=1 # Apple Clang has no -pg
# export PATH="/Applications/gtkwave.app/Contents/Resources/bin:$PATH" # fst2vcd
file bin/verilator_bin
file bin/verilator_bin_dbg
md5 bin/verilator_bin
md5 bin/verilator_bin_dbg
stat bin/verilator_bin
stat bin/verilator_bin_dbg
# For some reason, the dbg exe is corrupted by this point ('file' reports
# it as data rather than a Mach-O). Unclear if this is an OS X issue or
# CI's. Remove the file and re-link...
rm bin/verilator_bin_dbg
"$MAKE" -j "$NPROC" -k
elif [ "$CI_OS_NAME" = "freebsd" ]; then
export VERILATOR_TEST_NO_GDB=1 # Disable for now, ideally should run
# TODO below may no longer be required as configure checks for -pg
export VERILATOR_TEST_NO_GPROF=1 # gprof is a bit different on FreeBSD, disable
fi
TEST_REGRESS=test_regress
if [ "$CI_RELOC" == 1 ]; then
# Testing that the installation is relocatable.
"$MAKE" install
mkdir -p "$RELOC_DIR"
mv "$INSTALL_DIR" "$RELOC_DIR/relocated-install"
export VERILATOR_ROOT="$RELOC_DIR/relocated-install/share/verilator"
TEST_REGRESS="$RELOC_DIR/test_regress"
mv test_regress "$TEST_REGRESS"
NODIST="$RELOC_DIR/nodist"
mv nodist "$NODIST"
# Feeling brave?
find . -delete
ls -la .
fi
# Run the specified test
ccache -z
case $TESTS in
dist-vlt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=0/4
;;
dist-vlt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=1/4
;;
dist-vlt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=2/4
;;
dist-vlt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=3/4
;;
vltmt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=0/3
;;
vltmt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=1/3
;;
vltmt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=2/3
;;
coverage-dist)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist"
;;
coverage-vlt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=0/10
;;
coverage-vlt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=1/10
;;
coverage-vlt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=2/10
;;
coverage-vlt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=3/10
;;
coverage-vlt-4)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=4/10
;;
coverage-vlt-5)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=5/10
;;
coverage-vlt-6)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=6/10
;;
coverage-vlt-7)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=7/10
;;
coverage-vlt-8)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=8/10
;;
coverage-vlt-9)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=9/10
;;
coverage-vltmt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=0/10
;;
coverage-vltmt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=1/10
;;
coverage-vltmt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=2/10
;;
coverage-vltmt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=3/10
;;
coverage-vltmt-4)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=4/10
;;
coverage-vltmt-5)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=5/10
;;
coverage-vltmt-6)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=6/10
;;
coverage-vltmt-7)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=7/10
;;
coverage-vltmt-8)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=8/10
;;
coverage-vltmt-9)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=9/10
;;
*)
fatal "Unknown TESTS: $TESTS"
;;
esac
# To see load average (1 minute, 5 minute, 15 minute)
uptime
# 22.04: ccache -s -v
ccache -s
else
##############################################################################
# Unknown build stage
fatal "Unknown CI_BUILD_STAGE_NAME: '$CI_BUILD_STAGE_NAME'"
fi
+169
View File
@@ -0,0 +1,169 @@
#!/usr/bin/env bash
# DESCRIPTION: Verilator: CI test job script
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
# Executed in the 'test' stage.
################################################################################
# Destructive to the checkout (wipes most of it); never run locally
if [ "$GITHUB_ACTIONS" != "true" ]; then
echo "ERROR: $(basename "$0") must only be run in GitHub Actions CI" >&2
exit 1
fi
set -e
set -x
source "$(dirname "$0")/ci-common.bash"
################################################################################
# Parse arguments
OPT_RELOC=
OPT_SUITE=
while [ $# -gt 0 ]; do
case "$1" in
--reloc)
[ $# -ge 2 ] || fatal "--reloc requires an argument"
OPT_RELOC="$2"
shift
;;
--suite)
[ $# -ge 2 ] || fatal "--suite requires an argument"
OPT_SUITE="$2"
shift
;;
*) fatal "Unknown option: '$1'" ;;
esac
shift
done
[ -n "$OPT_SUITE" ] || fatal "--suite is required"
################################################################################
# Run tests
export VERILATOR_TEST_NO_CONTRIBUTORS=1 # Separate workflow check
export VERILATOR_TEST_NO_LINT_PY=1 # Separate workflow check
if [ "$HOST_OS" = "macOS" ]; then
export VERILATOR_TEST_NO_GDB=1 # No working GDB on macOS
fi
TEST_REGRESS=test_regress
if [ -n "$OPT_RELOC" ]; then
# Testing that the installation is relocatable.
"$MAKE" install
# Install prefix, as configured into the Makefile at build time
INSTALL_DIR=$(sed -n 's/^prefix = //p' Makefile)
mkdir -p "$OPT_RELOC"
mv "$INSTALL_DIR" "$OPT_RELOC/relocated-install"
export VERILATOR_ROOT="$OPT_RELOC/relocated-install/share/verilator"
TEST_REGRESS="$OPT_RELOC/test_regress"
mv test_regress "$TEST_REGRESS"
NODIST="$OPT_RELOC/nodist"
mv nodist "$NODIST"
# Delete everything else, but keep the CI infrastructure
find . -mindepth 1 -maxdepth 1 ! -name .github ! -name ci -exec rm -rf {} +
ls -la .
fi
# Run the specified suite
ccache -z
TEST_START=$SECONDS
case $OPT_SUITE in
dist-vlt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=0/4
;;
dist-vlt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=1/4
;;
dist-vlt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=2/4
;;
dist-vlt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt --driver-clean" DRIVER_HASHSET=--hashset=3/4
;;
vltmt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=0/3
;;
vltmt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=1/3
;;
vltmt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt --driver-clean" DRIVER_HASHSET=--hashset=2/3
;;
coverage-dist)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist"
;;
coverage-vlt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=0/10
;;
coverage-vlt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=1/10
;;
coverage-vlt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=2/10
;;
coverage-vlt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=3/10
;;
coverage-vlt-4)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=4/10
;;
coverage-vlt-5)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=5/10
;;
coverage-vlt-6)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=6/10
;;
coverage-vlt-7)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=7/10
;;
coverage-vlt-8)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=8/10
;;
coverage-vlt-9)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vlt" DRIVER_HASHSET=--hashset=9/10
;;
coverage-vltmt-0)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=0/10
;;
coverage-vltmt-1)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=1/10
;;
coverage-vltmt-2)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=2/10
;;
coverage-vltmt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=3/10
;;
coverage-vltmt-4)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=4/10
;;
coverage-vltmt-5)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=5/10
;;
coverage-vltmt-6)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=6/10
;;
coverage-vltmt-7)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=7/10
;;
coverage-vltmt-8)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=8/10
;;
coverage-vltmt-9)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--vltmt" DRIVER_HASHSET=--hashset=9/10
;;
*)
fatal "Unknown suite: $OPT_SUITE"
;;
esac
ccache -svv
ccache --evict-older-than "$((SECONDS - TEST_START + 60))s"
ccache -svv
uptime # To see load average
+20 -6
View File
@@ -6,19 +6,33 @@
Set-PSDebug -Trace 1
if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
$NPROC = $env:NUMBER_OF_PROCESSORS
# Absolute path for the win_flex_bison install; CMake reads this from the environment
$env:WIN_FLEX_BISON = "$(Resolve-Path ..)/win_flex_bison"
if (-Not (Test-Path $env:WIN_FLEX_BISON/win_bison.exe)) {
git clone --depth 1 https://github.com/lexxmark/winflexbison
cd winflexbison
mkdir build
cd build
cmake .. --install-prefix $PWD/../../../.ccache
cmake --build . --config Release -j 3
cmake --install . --prefix $PWD/../../../.ccache
cmake .. --install-prefix $env:WIN_FLEX_BISON
cmake --build . --config Release -j $NPROC
cmake --install . --prefix $env:WIN_FLEX_BISON
cd ../..
}
# Enter the MSVC developer shell so cl/link are on PATH for the Ninja generator
$VsPath = & "${env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe" -latest -products * -property installationPath
Import-Module "$VsPath/Common7/Tools/Microsoft.VisualStudio.DevShell.dll"
Enter-VsDevShell -VsInstallPath $VsPath -SkipAutomaticLocation -DevCmdArguments "-arch=x64 -host_arch=x64"
mkdir build
cd build
cmake .. --install-prefix $PWD/../install
cmake --build . --config Release -j 3
# Ninja saturates all cores; the MSBuild generator only parallelizes across
# projects and Verilator is a single target, so it compiles serially. /Od skips
# optimization: this job only checks that Verilator builds and can verilate an
# example, so an optimized binary is not needed and the codegen time dominates.
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release --install-prefix $PWD/../install "-DCMAKE_CXX_FLAGS_RELEASE=/Od /DNDEBUG"
cmake --build .
cmake --install . --prefix $PWD/../install
+6 -2
View File
@@ -7,13 +7,17 @@
Set-PSDebug -Trace 1
$NPROC = $env:NUMBER_OF_PROCESSORS
cd install
$Env:VERILATOR_ROOT=$PWD
cd examples/cmake_tracing_c
mkdir build
cd build
cmake ..
cmake --build . --config Release -j 3
# /Od skips optimization; this only checks the example verilates and builds, so
# an optimized binary is not needed (see ci-win-compile.ps1).
cmake .. "-DCMAKE_CXX_FLAGS_RELEASE=/Od /DNDEBUG"
cmake --build . --config Release -j $NPROC
# TODO put this back in, see issue# 5163
# Release/example.exe
+64 -7
View File
@@ -12,7 +12,7 @@
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.050 2026-07-01],
AC_INIT([Verilator],[5.052 2026-09-05],
[https://verilator.org],
[verilator],[https://verilator.org])
@@ -129,6 +129,21 @@ AC_ARG_ENABLE([dev-gcov],
AC_SUBST(CFG_WITH_DEV_GCOV)
AC_MSG_RESULT($CFG_WITH_DEV_GCOV)
# Flag to reduce the debug info in the debug executable to minimize its size
AC_MSG_CHECKING(whether to reduce debug info in the debug executable)
AC_ARG_ENABLE([light-debug],
[AS_HELP_STRING([--enable-light-debug],
[Reduce the amount of debug information in the debug
Verilator executable to minimize its size. Enable
slight optimization. Enough for backtraces only.])],
[case "${enableval}" in
yes) CFG_WITH_LIGHT_DEBUG=yes ;;
no) CFG_WITH_LIGHT_DEBUG=no ;;
*) AC_MSG_ERROR([bad value '${enableval}' for --enable-light-debug]) ;;
esac],
CFG_WITH_LIGHT_DEBUG=no)
AC_MSG_RESULT($CFG_WITH_LIGHT_DEBUG)
# Special Substitutions - CFG_WITH_DEFENV
AC_MSG_CHECKING(whether to use hardcoded paths)
AC_ARG_ENABLE([defenv],
@@ -432,6 +447,7 @@ AC_DEFUN([_MY_LDLIBS_CHECK_SET],
# Add the coverage flags early as they influence later checks.
if test "$CFG_WITH_DEV_GCOV" = "yes"; then
CFG_GCOV_CXX="$CXX"
_MY_CXX_CHECK_OPT(CXX,--coverage)
# Otherwise inline may not show as uncovered
# If we use this then e.g. verilated.h functions properly show up
@@ -453,8 +469,32 @@ if test "$CFG_WITH_DEV_GCOV" = "yes"; then
_MY_CXX_CHECK_OPT(CXX,-fprofile-abs-path)
# Define so compiled code can know
_MY_CXX_CHECK_OPT(CXX,-DVL_GCOV)
AC_DEFINE([HAVE_DEV_GCOV],[1],[Defined if compiled with code coverage collection for gcov])]
AC_DEFINE([HAVE_DEV_GCOV],[1],[Defined if compiled with code coverage collection for gcov])
if test "x$GCOV" = "x"; then GCOV=gcov; fi
AC_MSG_CHECKING([whether $GCOV reads coverage data from $CFG_GCOV_CXX])
CFG_GCOV_DIR=conftest.gcov.dir
rm -rf $CFG_GCOV_DIR
AS_MKDIR_P([$CFG_GCOV_DIR])
echo 'int main() { return 0; }' > $CFG_GCOV_DIR/conftest_gcov.cpp
CFG_GCOV_RES=no
if (cd $CFG_GCOV_DIR \
&& $CFG_GCOV_CXX --coverage -o conftest_gcov conftest_gcov.cpp \
&& ./conftest_gcov) >/dev/null 2>&1; then
(cd $CFG_GCOV_DIR && $GCOV conftest_gcov.gcda) >/dev/null 2>&1
if test -s $CFG_GCOV_DIR/conftest_gcov.cpp.gcov; then CFG_GCOV_RES=yes; fi
else
CFG_GCOV_RES=untested
fi
rm -rf $CFG_GCOV_DIR
AC_MSG_RESULT($CFG_GCOV_RES)
if test "$CFG_GCOV_RES" = "no"; then
AC_MSG_ERROR(['$GCOV' cannot read coverage data written by '$CFG_GCOV_CXX'.
Coverage collection would silently produce no data.
Pass a matching tool, e.g. GCOV=gcov-<version>
or configure without --enable-dev-gcov.])
fi
fi
AC_SUBST(GCOV)
AC_SUBST(HAVE_DEV_GCOV)
# Compiler flags to enable profiling
@@ -492,6 +532,11 @@ _MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES,-std=gnu++20)
AC_SUBST(CFG_CXXFLAGS_COROUTINES)
AC_SUBST(HAVE_COROUTINES)
# Check if the C++ compiler supports ThreadSanitizer
_MY_CXX_CHECK_IFELSE(-fsanitize=thread,
[AC_DEFINE([HAVE_TSAN],[1],[Defined if ThreadSanitizer is supported by $CXX])])
AC_SUBST(HAVE_TSAN)
# Flags for compiling Verilator internals including parser always
if test "$CFG_WITH_DEV_ASAN" = "yes"; then
_MY_CXX_CHECK_IFELSE(-fsanitize=address -DVL_ASAN,
@@ -518,15 +563,27 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_PARSER,-Wno-unused)
AC_SUBST(CFG_CXXFLAGS_PARSER)
# Flags for compiling the debug version of Verilator (in addition to above CFG_CXXFLAGS_SRC)
if test "$CFG_WITH_DEV_GCOV" = "no"; then # Do not optimize for the coverage build
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-Og)
if test "$CFG_WITH_LIGHT_DEBUG" = "yes"; then
# Slight optimization and minimal compressed debug info. This is enough for
# --gdb/--gdbbt backtraces, but omits the bulk of the debug info, which
# significantly reduces object file sizes. For CI or release builds.
if test "$CFG_WITH_DEV_GCOV" = "no"; then # Do not optimize for the coverage build
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-Og)
fi
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-ggdb1)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-fdebug-info-for-profiling) # For fully-qualified names with clang
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-gz)
else
# Full debug: no optimization, full debug info, for development.
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-O0)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-ggdb)
fi
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-ggdb)
_MY_CXX_CHECK_OPT(CFG_CXXFLAGS_DBG,-gz)
AC_SUBST(CFG_CXXFLAGS_DBG)
# Flags for linking the debug version of Verilator (in addition to above CFG_LDFLAGS_SRC)
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_DBG,-gz)
if test "$CFG_WITH_LIGHT_DEBUG" = "yes"; then
_MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_DBG,-gz)
fi
AC_SUBST(CFG_LDFLAGS_DBG)
# Flags for compiling the optimized version of Verilator (in addition to above CFG_CXXFLAGS_SRC)
+36 -13
View File
@@ -7,13 +7,16 @@ contribution terms including the AI policy in ``docs/CONTRIBUTING.rst``.
Please see the Verilator manual for 200+ additional contributors. Thanks to all.
24bit-xjkp
404allen404
Adam Bagley
Adam Kostrzewski
Aditya Shevade
Adrian Sampson
Adrien Le Masle
أحمد المحمودي (Ahmed El-Mahmoudy)
Aidan McNay
Aisha Salimgereyeva
Aleksander Kiryk
Alex Chadwick
Alex Solomatnikov
@@ -26,16 +29,20 @@ Andrei Kostovski
Andrew Miloradovsky
Andrew Nolte
Andrew Voznytsa
Andrii Andrieiev
anonkey
Anthony Donlon
Anthony Moore
apocelipes
Arkadiusz Kozdra
Arthur Rosa
Artur Bieniek
AUDIY
Aylon Chaim Porat
Bartosz Skorowski
Bartłomiej Chmiel
Benjamin Collier
BRDR LIFE
Brian Li
Cameron Kirk
Cameron Waite
@@ -53,27 +60,36 @@ Dan Ruelas-Petrisko
Daniel Bates
Danny Oler
Dave Sargeant
David Garau
David Horton
David Ledger
David Metz
David Stanford
David Turner
dependabot[bot]
Dercury
Demin Han
Diego Roux
Dominick Grochowina
Don Williamson
Dragon-Git
Drew Ranck
Drew Taussig
Driss Hafdi
Edgar E. Iglesias
Eddie Rydell
em2machine
emmettifelts
Eric Mejdrich
Eric Müller
Eric Rippey
Eunseo Song
Eryk Szpotański
Ethan Sifferman
Eunseo Song
Eyck Jentzsch
Fabian Keßler-Schulz
Fan Shupei
february cozzocrea
Felix Neumärker
Felix Yan
Frans Skarman
@@ -108,6 +124,8 @@ Ilya Barkov
Iru Cai
Ivan Vnučec
Iztok Jeras
JAEUK LEE
Jaeuk Lee (이재욱)
Jake Merdich
Jakub Michalski
Jakub Wasilewski
@@ -122,6 +140,8 @@ Jamie Iles
Jan Van Winkel
Jean Berniolles
Jean-Nicolas Strauss
Jeffrey Song
jeffrey.song
Jens Yuechao Liu
Jeremy Bennett
Jesse Taube
@@ -147,6 +167,7 @@ Josep Sans
Joseph Nwabueze
Josh Redford
Joshua Leahy
JOTEGO (Jose Tejada)
Julian Carrier
Julian Daube
Julie Schwartz
@@ -183,6 +204,7 @@ Maarten De Braekeleer
Maciej Sobkowski
Marcel Chang
Marco Bartoli
Marco Brambilla
Marco Widmer
Mariusz Glebocki
Markus Krause
@@ -208,6 +230,7 @@ Mladen Slijepcevic
Morten Borup Petersen
Mostafa Gamal
Moubarak Jeje
Muzaffer Kal
Nandu Raj
Natan Kreimer
Nathan Graybeal
@@ -218,8 +241,11 @@ Nikolai Kumar
Nikolay Puzanov
Nolan Poe
Oleh Maksymenko
Patrick Creighton
Patrick Stewart
Paul Bowen-Huggett
Paul Campbell
Paul Rigge
Paul Swirhun
Paul Wright
Pawel Jewstafjew
@@ -262,15 +288,19 @@ Sergey Chusov
Sergey Fedorov
Sergi Granell
Seth Pellegrino
Shashvat Prabhu
Shogo Yamazaki
Shou-Li Hsu
spomatasmd
Srinivasan Venkataramanan
Stefan Wallentowitz
Stephen Henry
Steven Hugg
Stuart Morris
sumpster
Szymon Gizler
Sören Tempel
Sumanth Kadiyala
Teng Huang
Thomas Aldrian
Thomas Brown
@@ -291,6 +321,7 @@ Tracy Narine
Trung Nguyen
Tudor Timi
Tymoteusz Blazejczyk
Tyrone Marhguy
Udaya Raj Subedi
Udi Finkelstein
Unai Martinez-Corral
@@ -308,9 +339,11 @@ Wolfgang Mayerwieser
Xi Zhang
Yan Xu
Yangyu Chen
Yilin Li
Yilou Wang
Yinan Xu
Yoda Lee
Yogish Sekhar
Yossi Nivin
Yu-Sheng Lin
Yuri Victorovich
@@ -321,17 +354,7 @@ Zhen Yan
Zhou Shen
Zhouyi Shen
Zixi Li
apocelipes
dependabot[bot]
february cozzocrea
sumpster
em2machine
emmettifelts
Zubin Jain
Àlex Torregrosa
Ícaro Lima
Yogish Sekhar
24bit-xjkp
Zubin Jain
Muzaffer Kal
Yilin Li
Shashvat Prabhu
Sunimali Rathnayake
+1 -1
View File
@@ -2,4 +2,4 @@
.. code-block::
-V{t#,#} 'stl' region trigger index 0 is active: @([hybrid] a)
%Error-DIDNOTCONVERGE: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after '--converge-limit' of 10000 tries
%Error-DIDNOTCONVERGE: Settle region did not converge after '--converge-limit' of 10000 tries
+1 -1
View File
@@ -1,4 +1,4 @@
.. comment: generated by t_lint_didnotconverge_nodbg_bad
.. code-block::
%Error-DIDNOTCONVERGE: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after '--converge-limit' of 10000 tries
%Error-DIDNOTCONVERGE: Settle region did not converge after '--converge-limit' of 10000 tries
+6
View File
@@ -0,0 +1,6 @@
.. comment: generated by t_lint_multidriven_proc_bad
.. code-block:: sv
:linenos:
always @(posedge clk) q <= d;
always @(posedge clk) q <= ~d;
+13
View File
@@ -0,0 +1,13 @@
.. comment: generated by t_lint_multidriven_proc_bad
.. code-block:: sv
:linenos:
integer i;
always @(posedge clk) begin
for (i = 0; i < 4; i = i + 1) q1[i] <= d[i];
end
always @(posedge clk) begin
for (i = 0; i < 4; i = i + 1) q2[i] <= ~d[i];
end
+8
View File
@@ -0,0 +1,8 @@
.. comment: generated by t_lint_multidriven_proc_bad
.. code-block::
%Warning-MULTIDRIVENPROC: example.v:1:25 Variable written to in always block also written by another always block: 'q'
: ... note: In instance 't.u1'
example.v:1:25
54 | always @(posedge clk) q <= ~d;
| ^
+6
View File
@@ -0,0 +1,6 @@
.. comment: generated by t_lint_similarname_bad
.. code-block:: sv
:linenos:
reg i;
wire I;
+9
View File
@@ -0,0 +1,9 @@
.. comment: generated by t_lint_similarname_bad
.. code-block::
%Warning-SIMILARNAME: example.v:1:8 Declaration overlaps another with different case: 'I'
13 | wire I;
| ^
example.v:1:7 ... Location of original declaration
12 | reg i;
| ^
+13 -9
View File
@@ -92,13 +92,17 @@ model.
Refer to ``examples/make_tracing_c`` in the distribution for a detailed
commented example.
Top level IO signals are read and written as members of the model. You call
the model's ``eval()`` method to evaluate the model. When the simulation is
complete call the model's ``final()`` method to execute any SystemVerilog
final blocks, and complete any assertions. If using :vlopt:`--timing`,
there are two additional functions for checking if there are any events
pending in the simulation due to delays, and for retrieving the simulation
time of the next delayed event. See :ref:`Evaluation Loop`.
Top level IO signals are read and written as members of the model. All
inputs must be sanitized, that is have no bits set above those
corresponding to the width of the Verilog construct;
:vlopt:`--runtime-debug` will assert this is correct.
Call the model's ``eval()`` method to evaluate the model. When the
simulation is complete call the model's ``final()`` method to execute any
SystemVerilog final blocks, and complete any assertions. If using
:vlopt:`--timing`, there are two additional functions for checking if there
are any events pending in the simulation due to delays, and for retrieving
the simulation time of the next delayed event. See :ref:`Evaluation Loop`.
Connecting to SystemC
@@ -179,7 +183,7 @@ DPI Example
In the SYSTEMC example above, if you wanted to import C++ functions into
Verilog, put in our.v:
.. code-block:: sv
.. code-block::
import "DPI-C" function int add (input int a, input int b);
@@ -211,7 +215,7 @@ Verilator extends the DPI format to allow using the same scheme to
efficiently add system functions. Use a dollar-sign prefixed system
function name for the import, but note it must be escaped.
.. code-block:: sv
.. code-block::
import "DPI-C" function integer \$myRand;
+8 -1
View File
@@ -21,6 +21,13 @@ Summary:
Options:
.. option:: +verilator+assert+lock
Only allow command line options to disable / enable assertions.
Disables RTL from changing assertion handling via ``$asserton``,
``$assertoff``, and ``$assertcontrol``. Also prevents ``VerilatedContext*``
assertion control functions from updating assertion handling.
.. option:: +verilator+coverage+file+<filename>
When a model was Verilated using :vlopt:`--coverage`, sets the filename
@@ -114,7 +121,7 @@ Options:
When a model was Verilated using :vlopt:`--x-initial unique
<--x-initial>`, sets the simulation runtime initialization technique. 0
= Reset to zeros. 1 = Reset to all-ones. 2 = Randomize. See
:ref:`Unknown States`.
:ref:`Unknown States`. Default is 0.
.. option:: +verilator+seed+<value>
+11 -1
View File
@@ -116,6 +116,10 @@ Summary:
.. option:: --assert-unroll-limit <iterations>
Deprecated and has no effect (ignored).
In versions before 5.052:
Rarely needed. Specifies the maximum repetition or range count Verilator
will unroll inside an SVA concurrent assertion (e.g. ``[*N]``, ``[->M:N]``,
``always[lo:hi]``). Beyond this, the assertion is rejected with an error
@@ -715,6 +719,10 @@ Summary:
.. option:: -fno-dfg-break-cycles
Deprecated and has no effect (ignored).
In versions before 5.052:
Rarely needed. Disable breaking combinational cycles during DFG.
.. option:: -fno-dfg-peephole
@@ -936,7 +944,7 @@ Summary:
:file:`*.mk` files.
Feature may be one of the following: COROUTINES, DEV_ASAN, DEV_GCOV,
SYSTEMC.
SYSTEMC, TSAN.
.. option:: --getenv <variable>
@@ -1219,6 +1227,8 @@ Summary:
.. option:: --max-num-width <value>
Set the maximum number literal width (e.g., in 1024'd22 the 1024).
This also limits SVA cycle delay and temporal bound values.
Defaults to 64K.
.. option:: --Mdir <directory>
+1 -1
View File
@@ -133,7 +133,7 @@ verilator_coverage Arguments
.. option:: --filter-type <regex>
Skips records of coverage types that matches with <regex>
Keeps records of coverage types that matches with <regex>
Possible values are `toggle`, `line`, `branch`, `expr`, `covergroup`,
`user`, `fsm_state`, `fsm_arc` and a wildcard with `\*` or `?`. The
default value is `\*`.
+19
View File
@@ -294,6 +294,25 @@ or "`ifdef`"'s may break other tools.
(if appropriate :vlopt:`--coverage` flags are passed) after being
disabled earlier with :option:`/*verilator&32;coverage_off*/`.
.. option:: /*verilator&32;dpi_c_decl "<C function declaration>"*/
Specifies C function declaration that will be emitted for given DPI-C
function into the Verilator-generated __Dpi.h header, replacing the declaration
that Verilator would build by default using the Verilog function signature.
This enables use of C functions with types not specified in the
standard. For example, it enables use of functions that return ``char*``:
.. code-block:: sv
module t;
import "DPI-C" function string getenv(input string arg) /*verilator dpi_c_decl "char* getenv(const char*)"*/;
initial begin
$display("%s", getenv("HOME"));
end
endmodule
.. option:: /*verilator&32;fargs <arguments>*/
For Verilator developers only. When a source file containing these `fargs`
+1 -1
View File
@@ -66,7 +66,7 @@ The information in this report is:
.. describe:: "allocated 123 MB"
Total memory used during simulation in megabytes.
Peak resident memory used during simulation in megabytes.
.. _benchmarking & optimization:
+2 -2
View File
@@ -623,5 +623,5 @@ The information in this report is:
.. describe:: "allocated 123 MB"
Total memory used during build by Verilator executable (excludes
:vlopt:`--build` compiler's usage) in megabytes.
Peak resident memory used by the Verilator executable during build
(excludes :vlopt:`--build` compiler's usage) in megabytes.
+82
View File
@@ -1469,11 +1469,54 @@ List Of Warnings
q <= d;
end
A further case is when a signal named as a clocking block ``output`` is
also driven by a continuous assignment, or is named as an ``output`` of a
second clocking block. The clocking block drives the signal, so the design
and the testbench contend for it and the synchronous drive may be silently
lost. Declare the clocking block ``input`` if the intent is only to
observe the signal.
Ignoring this warning may hide clock domain crossing, timing, or
portability bugs. It may also cause longer simulation runtimes due to
reduced optimizations.
.. option:: MULTIDRIVENPROC
Warns that the whole of a variable is driven by more than one plain
``always`` block. Unlike the :option:`MULTIDRIVEN` cases, plain
``always`` blocks carry no ``always_comb``/``always_ff`` intent, so this
is legal SystemVerilog rather than an IEEE 1800 violation. It is,
however, typically a synthesis error: hardware cannot have a signal
driven by two separate sequential blocks, so the design usually will not
behave as the RTL simulation suggests.
Disabled by default as this is a code-style warning; it will simulate
correctly.
Faulty example:
.. include:: ../../docs/gen/ex_MULTIDRIVENPROC_faulty.rst
Results in:
.. include:: ../../docs/gen/ex_MULTIDRIVENPROC_msg.rst
Also warns when a signal named as a clocking block ``output`` is driven
by a plain ``always`` block. Driving a signal from both a clocking block
and a plain ``always`` block is a deliberate idiom in some testbenches,
so it is reported as MULTIDRIVENPROC rather than under the on-by-default
:option:`MULTIDRIVEN`.
To fix, drive the signal from a single ``always`` block, or use
``always_ff``/``always_comb`` if the intent is a single specialized
process.
Does not warn for static variables used as loop induction variables:
.. include:: ../../docs/gen/ex_MULTIDRIVENPROC_loopidx.rst
.. option:: MULTITOP
.. TODO better example
@@ -1517,6 +1560,19 @@ List Of Warnings
(neither :vlopt:`--timing` nor :vlopt:`--no-timing` option was provided).
.. option:: NEVERMATCH
Warns that an SVA sequence is statically known to never match. Such a
sequence is legal SystemVerilog and is fully supported, the warning
only highlights a potential problem. For example, using the sequence as
the antecedent of an implication causes the implication to pass
vacuously. For an intersect sequence, ensure its operands can match
over a common length (IEEE 1800-2023 16.9.6).
Ignoring this warning will only suppress the lint check; it will
simulate correctly.
.. option:: NEWERSTD
Warns that a feature requires a newer standard of Verilog or SystemVerilog
@@ -2089,6 +2145,32 @@ List Of Warnings
simulators.
.. option:: SIMILARNAME
Warns that an entity name only differs from another in lexical case.
Faulty example:
.. include:: ../../docs/gen/ex_SIMILARNAME_faulty.rst
Results in:
.. include:: ../../docs/gen/ex_SIMILARNAME_msg.rst
Only declarations that can reach a downstream VLSI tool as a name are
checked, that is nets, variables, instances, named blocks (``begin``,
``fork``, and generate blocks), functions and tasks. All of these can form
part of a flattened signal or scope name. Other declarations, such as
parameters, localparams, genvars and typedefs, are elaborated away, and so
are not checked.
Disabled by default as this is a code-style warning; it will simulate
correctly.
This is a warning as some downstream VLSI tools do
not distinguish net and gate names with the same case.
.. option:: SPECIFYIGN
Warns that Verilator does not support certain constructs in
+87 -71
View File
@@ -425,8 +425,9 @@ logic.
To achieve this, we invoke ``V3Order::order`` on all of the combinational
and hybrid logic, and iterate the resulting evaluation function until no
more hybrid logic is triggered. This yields the `_eval_settle` function,
which is invoked at the beginning of simulation after the `_eval_initial`.
more hybrid logic is triggered. This yields the `_eval_stl` function, which
the runtime event loop iterates at the beginning of simulation, after the
`_eval_initial`.
Partitioning logic for correct NBA updates
@@ -500,51 +501,66 @@ and clock signals on separate evaluations, as was necessary with earlier
versions of Verilator).
Constructing the top level `_eval` function
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Constructing the region evaluation functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To construct the top level `_eval` function, which updates the state of the
circuit to the end of the current time step, we invoke ``V3Order::order``
separately on the 'ico', 'act' and 'nba' logic, which yields the
`_eval_ico`, `_eval_act`, and `_eval_nba` functions. We then put these all
together with the corresponding functions that compute the respective
trigger expressions into the top level `_eval` function, which on the high
level has the form:
To update the state of the circuit to the end of the current time step, we
invoke ``V3Order::order`` separately on the 'ico', 'act' and 'nba' logic,
which yields the `_eval_body__ico`, `_eval_body__act`, and
`_eval_body__nba` functions. Each of these is then combined with the
function computing the respective trigger expressions into a single entry
point per region, `_eval_ico`, `_eval_act` and `_eval_nba`. A region entry
point evaluates one iteration of its region, and returns whether it did any
work, meaning the region has not converged yet and must be evaluated again.
On the high level, `_eval_act` has the form:
.. code-block:: C++
void _eval() {
// Update combinational logic dependent on top level inputs ('ico' region)
while (true) {
_eval__triggers__ico();
// If no 'ico' region trigger is active
if (!ico_triggers.any()) break;
_eval_ico();
}
// Iterate 'act' and 'nba' regions together
while (true) {
// Iterate 'act' region, this computes all derived clocks updaed in the
// Active scheduling region, but does not commit any NBAs that executed
// in 'act' region logic.
while (true) {
_eval__triggers__act();
// If no 'act' region trigger is active
if (!act_triggers.any()) break;
// Remember what 'act' triggers were active, 'nba' uses the same
latch_act_triggers_for_nba();
_eval_act();
}
// If no 'nba' region trigger is active
if (!nba_triggers.any()) break;
// Evaluate all other Active region logic, and commit NBAs
_eval_nba();
}
bool _eval_act() {
_eval_triggers_vec__act();
// Remember what 'act' triggers were active, 'nba' uses the same
latch_act_triggers_for_nba();
const bool execute = act_triggers.any();
// Compute all derived clocks updated in the Active scheduling region,
// but do not commit any NBAs that executed in 'act' region logic.
if (execute) _eval_body__act();
return execute;
}
The loops iterating these regions are not generated. They live in the
runtime library, in ``VerilatedEvalLoop``, and the model exposes its region
entry points to it as virtual methods of ``VerilatedModel``. The generated
model holds a ``VerilatedEvalLoop`` as a member, which its ``eval_step``
runs, and which on the high level has the form:
.. code-block:: C++
void VerilatedEvalLoop::evalImpl() {
// Update combinational logic dependent on top level inputs ('ico' region)
uint32_t icoIterCount = 0;
do {
if (++icoIterCount > m_convergeLimit) didNotConverge("Input combinational");
} while (m_model.evalIco(icoIterCount == 1));
// Iterate 'act' and 'nba' regions together
uint32_t nbaIterCount = 0;
do {
if (++nbaIterCount > m_convergeLimit) didNotConverge("NBA");
// Iterate the 'act' region to convergence
uint32_t actIterCount = 0;
do {
if (++actIterCount > m_convergeLimit) didNotConverge("Active");
} while (m_model.evalAct());
// Evaluate all other Active region logic, and commit NBAs
} while (m_model.evalNba());
}
The remaining regions ('inact', 'obs' and 'react') nest around these in the
same manner, each iterating the loops of all regions that precede it in the
SystemVerilog scheduling order. The runtime event loop calls every region
unconditionally, so scheduling emits an entry point for each one even when
the design has no logic in it. Such an entry point immediately returns
false, ending its loop after a single iteration.
Timing
------
@@ -730,7 +746,8 @@ they wouldn't be evaluated and next coroutine after resumption would fire
the event `a` then it is impossible to get to know whether await or fire on
event `a` was called first - which is necessary to know.
There are two functions for managing timing logic called by ``_eval()``:
There are two functions for managing timing logic called by the 'act'
region:
- ``_timing_ready()``, which commits all coroutines whose triggers were not
set in the current iteration,
@@ -751,36 +768,27 @@ Thanks to this separation a coroutine:
(``test_regress/t/t_event_control_double_lost.v``) - which is possible
when triggers are not evaluated right before awaiting.
All coroutines are committed and resumed in the 'act' eval loop. With
timing features enabled, the ``_eval()`` function takes this form:
All coroutines are committed and resumed in the 'act' region. With timing
features enabled, the 'act' region entry point takes this form:
::
void _eval() {
while (true) {
_eval__triggers__ico();
if (!ico_triggers.any()) break;
_eval_ico();
}
while (true) {
while (true) {
_eval__triggers__act();
// Commit all non-triggered coroutines
_timing_commit();
if (!act_triggers.any()) break;
latch_act_triggers_for_nba();
// Resume all triggered coroutines
_timing_resume();
_eval_act();
}
if (!nba_triggers.any()) break;
_eval_nba();
bool _eval_act() {
_eval_triggers_vec__act();
// Commit all non-triggered coroutines
_timing_commit();
const bool execute = act_triggers.any();
if (execute) {
latch_act_triggers_for_nba();
// Resume all triggered coroutines
_timing_resume();
_eval_body__act();
}
return execute;
}
Forks
@@ -1082,9 +1090,17 @@ solver gets a setup query, then the definition of variables, then all the
constraints (SMT assertions) about the variables. Since the solver has no
information about the class' PRNG state, if the problem is satisfiable, the
solution space is further constrained by adding extra random constraints,
and querying the values satisfying the problem statement. The constraint is
currently constructed as fixing a simple xor of randomly chosen bits of the
variables being randomized.
and querying the values satisfying the problem statement. The constraints
are currently built by the UniGen2 algorithm ("On Parallel Scalable Uniform
SAT Witness Generation", Chakraborty et al.). It first works out how many
random xor equations are needed to split the solution space into cells of a
size worth enumerating, then draws one cell and enumerates it, which gives
a near-uniform sample of the whole space. Both the estimate and the
leftover solutions are cached, so the following calls are answered without
asking the solver again. Cases outside its assumptions, such as ``randc``
variables or soft constraints, fall back to pinning every free bit to a
random value, or, when arrays are involved, to asserting four random xor
equations in a row.
The runtime classes used for handling the randomization are defined in
``verilated_random.h`` and ``verilated_random.cpp``.
+11
View File
@@ -32,6 +32,7 @@ Bagri
Balboni
Baltazar
Bardsley
Barua
Benchmarking
Berman
Bhagavatula
@@ -198,6 +199,7 @@ Holme
Homebrew
Hornung
Hossell
Hsieh
Hsu
Huggett
Hutt
@@ -389,6 +391,7 @@ Preprocess
Pretet
Pretl
Pringlemeir
Prithayan
Priyadharshini
Pullup
Pulver
@@ -477,6 +480,7 @@ Syms
Synopsys
SystemC
SystemVerilog
Szpotanski
Takatsukasa
Tambe
Tarik
@@ -512,6 +516,7 @@ Ueno
Uints
Undefine
Undefines
Unigen
Unsized
Uppal
Urbach
@@ -537,6 +542,7 @@ Verilog
Vighnesh
Viktor
Vilp
VlRNG
VlWide
Vlip
Vm
@@ -644,6 +650,7 @@ casez
casted
castro
cb
cbValueChange
ccache
ccall
cdc
@@ -707,6 +714,7 @@ de
dearray
deassign
debugi
deduplication
defenv
defname
defparam
@@ -947,6 +955,7 @@ misconnected
misconversion
misdetecting
misoptimized
misoptimizing
missized
mk
mno
@@ -995,6 +1004,7 @@ noprivate
noreturn
notif
nullptr
num
numactl
onehot
ooo
@@ -1174,6 +1184,7 @@ tcmalloc
tcmalloc
tenghtt
testbench
testbenches
threadsafe
threashold
timeInc
+1 -1
View File
@@ -90,7 +90,7 @@ static inline constexpr unsigned bitPerEncodedBit(EncodingType type) {
[[maybe_unused]]
static const char* kEncodedBitToCharTable = (
"01" // Binary
"xzhu" // Verilog
"zxhu" // Verilog
"wl-? " // Vhdl (padded with ' ')
);
+124 -53
View File
@@ -423,18 +423,26 @@ public:
void emitValueChange(uint64_t current_time_index, const uint32_t *val, EncodingType encoding) {
auto wh = emitValueChangeCommonPart(current_time_index, encoding);
for (unsigned i = 0; i < bitPerEncodedBit(encoding); ++i) {
// C++17: replace this with if constexpr
if (sizeof(T) == 8) {
// C++17: replace this with if constexpr
if (sizeof(T) == 8) {
if (encoding == EncodingType::VERILOG) {
uint64_t v = val[2]; // high bits
v <<= 32;
v |= val[0]; // low bits
wh.template write<uint64_t>(v);
v = val[3]; // high bits
v <<= 32;
v |= val[1]; // low bits
wh.template write<uint64_t>(v);
} else {
uint64_t v = val[1]; // high bits
v <<= 32;
v |= val[0]; // low bits
wh.template write<uint64_t>(v);
val += 2;
} else {
wh.template write<T>(val[0]);
val += 1;
}
} else {
wh.template write<T>(val[0]);
if (encoding == EncodingType::VERILOG) wh.template write<T>(val[1]);
}
}
@@ -517,7 +525,7 @@ public:
} else {
unsigned val = 0;
for (unsigned i = 0; i < num_element; ++i) {
val |= rh.peek<T>(i);
val |= rh.peek<T>(i) << i;
}
uint64_t delta_time_index = time_index - prev_time_index;
prev_time_index = time_index;
@@ -525,8 +533,8 @@ public:
// clang-format off
case 0: delta_time_index = (delta_time_index<<2) | (0<<1) | 0; break; // '0'
case 1: delta_time_index = (delta_time_index<<2) | (1<<1) | 0; break; // '1'
case 2: delta_time_index = (delta_time_index<<4) | (0<<1) | 1; break; // 'X'
case 3: delta_time_index = (delta_time_index<<4) | (1<<1) | 1; break; // 'Z'
case 2: delta_time_index = (delta_time_index<<4) | (1<<1) | 1; break; // 'Z'
case 3: delta_time_index = (delta_time_index<<4) | (0<<1) | 1; break; // 'X'
// Not supporting VHDL now
// LCOV_EXCL_START
case 4: delta_time_index = (delta_time_index<<4) | (2<<1) | 1; break; // 'H'
@@ -556,13 +564,29 @@ public:
if (first) {
first = false;
} else {
FST_CHECK(enc == EncodingType::BINARY); // TODO
const bool has_non_binary = enc != EncodingType::BINARY;
const uint64_t delta_time_index = time_index - prev_time_index;
prev_time_index = time_index;
h //
.writeLEB128((delta_time_index << 1) | has_non_binary)
.writeUIntPartialForValueChange(rh.peek<T>(), bitwidth);
switch (enc) {
case EncodingType::BINARY: {
h //
.writeLEB128(delta_time_index << 1)
.writeUIntPartialForValueChange(rh.peek<T>(), bitwidth);
} break;
case EncodingType::VERILOG: {
h.writeLEB128((delta_time_index << 1) | 1);
const T val = rh.peek<T>();
const T xz = rh.peek<T>(1);
for (int j = bitwidth; j > 0;) {
--j;
h.writeUIntBE(
kEncodedBitToCharTable[(((xz >> j) << 1) & 2) | ((val >> j) & 1)]
);
}
} break;
[[unlikely]] case EncodingType::VHDL: {
FST_FAIL_STRING("VHDL format is unsupported with wide values");
} break;
}
}
rh.skip(num_byte);
}
@@ -573,16 +597,26 @@ public:
class VariableInfoLongInt {
VariableInfo &info;
unsigned num_words() const { return (info.bitwidth() + 63) / 64; }
unsigned num_words32() const { return (info.bitwidth() + 31) / 32; }
public:
VariableInfoLongInt(VariableInfo &info_) : info(info_) {}
public:
size_t computeBytesNeededNoHeader(EncodingType encoding) const {
switch (encoding) {
case EncodingType::BINARY:
return num_words() * sizeof(uint64_t);
case EncodingType::VERILOG:
return num_words32() * sizeof(uint32_t) * 2;
[[unlikely]] case EncodingType::VHDL:
FST_FAIL_STRING("VHDL format is unsupported with wide values");
}
FST_UNREACHABLE;
}
size_t computeBytesNeeded(EncodingType encoding) const {
return (
kEmitTimeIndexAndEncodingSize +
num_words() * sizeof(uint64_t) * bitPerEncodedBit(encoding)
);
return kEmitTimeIndexAndEncodingSize + computeBytesNeededNoHeader(encoding);
}
EmitWriterHelper emitValueChangeCommonPart(uint64_t current_time_index, EncodingType encoding) {
@@ -600,13 +634,12 @@ public:
public:
void construct() {
const size_t nw = num_words();
const size_t nw = num_words32();
info.resize(computeBytesNeeded(EncodingType::VERILOG));
EmitWriterHelper wh(info.data_ptr());
wh //
.writeTimeIndexAndEncoding(0, EncodingType::VERILOG)
.fill(uint64_t(0), nw)
.fill(uint64_t(-1), nw);
.fill(static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) << 32, nw);
}
void emitValueChange(uint64_t current_time_index, const uint64_t val) {
@@ -616,12 +649,12 @@ public:
}
void emitValueChange(uint64_t current_time_index, const uint32_t *val, EncodingType encoding) {
const unsigned nw32 = (info.bitwidth() + 31) / 32;
const unsigned bpb = bitPerEncodedBit(encoding);
const unsigned nw32 = num_words32();
auto wh = emitValueChangeCommonPart(current_time_index, encoding);
for (unsigned i = 0; i < bpb; ++i) {
switch (encoding) {
case EncodingType::BINARY: {
for (unsigned j = 0; j < nw32 / 2; ++j) {
uint64_t v = val[1]; // high bits
v <<= 32;
@@ -634,13 +667,25 @@ public:
wh.write(v);
val += 1;
}
} break;
case EncodingType::VERILOG: {
for (unsigned j = 0; j < nw32; ++j) {
uint64_t v = val[1]; // high bits
v <<= 32;
v |= val[0]; // low bits
wh.write(v);
val += 2;
}
} break;
[[unlikely]] case EncodingType::VHDL:
FST_FAIL_STRING("VHDL format is unsupported with wide values");
}
}
void emitValueChange(uint64_t current_time_index, const uint64_t *val, EncodingType encoding) {
const unsigned nw_encoded = num_words() * bitPerEncodedBit(encoding);
auto wh = emitValueChangeCommonPart(current_time_index, encoding);
wh.write(val, nw_encoded);
FST_CHECK(encoding == EncodingType::BINARY);
wh.write(val, num_words());
}
void dumpInitialBits(std::vector<uint8_t> &buf) const {
@@ -663,15 +708,16 @@ public:
break;
}
case EncodingType::VERILOG: {
for (unsigned word_index = nw; word_index-- > 0;) {
const uint64_t v0 = rh.peek<uint64_t>(nw * 0 + word_index);
const uint64_t v1 = rh.peek<uint64_t>(nw * 1 + word_index);
for (unsigned word_index = num_words32(); word_index-- > 0;) {
const uint64_t val = rh.peek<uint64_t>(word_index);
const uint32_t aval = static_cast<uint32_t>(val);
const uint32_t bval = static_cast<uint32_t>(val >> 32);
const unsigned num_bit =
(word_index * 64 + 64 > info.bitwidth()) ? (info.bitwidth() % 64) : 64;
(word_index * 32 + 32 > info.bitwidth()) ? (info.bitwidth() % 32) : 32;
for (unsigned bit_index = num_bit; bit_index-- > 0;) {
const bool b0 = ((v0 >> bit_index) & uint64_t(1));
const bool b1 = ((v1 >> bit_index) & uint64_t(1));
const char c = kEncodedBitToCharTable[(b1 << 1) | b0];
const bool a = ((aval >> bit_index) & 1);
const bool b = ((bval >> bit_index) & 1);
const char c = kEncodedBitToCharTable[(b << 1) | a];
buf.push_back(c);
}
}
@@ -717,33 +763,58 @@ public:
FST_DCHECK_GT(tail, rh.ptr);
const auto time_index = rh.read<uint64_t>();
const auto enc = rh.read<EncodingType>();
const auto num_element = bitPerEncodedBit(enc);
const auto num_byte = num_element * nw * sizeof(uint64_t);
const auto num_byte = computeBytesNeededNoHeader(enc);
if (first) {
// Note: [0] is initial value, which is already dumped in dumpInitialBits()
first = false;
} else {
FST_CHECK(enc == EncodingType::BINARY); // TODO
const bool has_non_binary = enc != EncodingType::BINARY;
const uint64_t delta_time_index = time_index - prev_time_index;
prev_time_index = time_index;
h.writeLEB128((delta_time_index << 1) | has_non_binary);
if (bitwidth % 64 != 0) {
const unsigned remaining = bitwidth % 64;
uint64_t hi64 = rh.peek<uint64_t>(nw - 1);
// write from nw-1 to 1
for (unsigned j = nw - 1; j > 0; --j) {
uint64_t lo64 = rh.peek<uint64_t>(j - 1);
h.writeUIntBE((hi64 << (64 - remaining)) | (lo64 >> remaining));
hi64 = lo64;
switch (enc) {
case EncodingType::BINARY: {
h.writeLEB128((delta_time_index << 1));
if (bitwidth % 64 != 0) {
const unsigned remaining = bitwidth % 64;
uint64_t hi64 = rh.peek<uint64_t>(nw - 1);
// write from nw-1 to 1
for (unsigned j = nw - 1; j > 0; --j) {
uint64_t lo64 = rh.peek<uint64_t>(j - 1);
h.writeUIntBE((hi64 << (64 - remaining)) | (lo64 >> remaining));
hi64 = lo64;
}
// write 0
h.writeUIntPartialForValueChange(hi64, remaining);
} else {
// write from nw-1 to 0
for (unsigned j = nw; j-- > 0;) {
h.writeUIntBE(rh.peek<uint64_t>(j));
}
}
// write 0
h.writeUIntPartialForValueChange(hi64, remaining);
} else {
// write from nw-1 to 0
for (unsigned j = nw; j-- > 0;) {
h.writeUIntBE(rh.peek<uint64_t>(j));
} break;
case EncodingType::VERILOG: {
h.writeLEB128((delta_time_index << 1) | 1);
const int fullWords = (bitwidth / 32);
if (int j = bitwidth % 32) {
const uint64_t val = rh.peek<uint64_t>(fullWords);
while (j > 0) {
--j;
const uint64_t v = val >> j;
h.writeUIntBE(kEncodedBitToCharTable[((v >> 31) & 2) | (v & 1)]);
}
}
for (size_t i = fullWords; i > 0;) {
--i;
const uint64_t val = rh.peek<uint64_t>(i);
for (int j = 32; j > 0;) {
--j;
const uint64_t v = val >> j;
h.writeUIntBE(kEncodedBitToCharTable[((v >> 31) & 2) | (v & 1)]);
}
}
} break;
[[unlikely]] case EncodingType::VHDL: {
FST_FAIL_STRING("VHDL format is unsupported with wide values");
} break;
}
}
rh.skip(num_byte);
+36 -7
View File
@@ -268,26 +268,55 @@ void Writer::emitValueChange(Handle handle, const char *val) {
// For normal integer handles, const char* is "01xz..." (1B per bit)
const uint32_t bitwidth{var_info.bitwidth()};
const bool hasXZ = // Detects A-Z and a-z but not 0-9 and NOT `-` `?`
(std::accumulate(val, val + bitwidth, 0, [](int a, char b) { return a | b; }) & (1 << 6)) !=
0;
FST_DCHECK_NE(bitwidth, 0);
val += bitwidth;
const unsigned num_words{(bitwidth + 63) / 64};
m_packed_value_buffer_.assign(num_words, 0);
m_packed_value_buffer_.assign(num_words << (hasXZ ? 1 : 0), 0);
for (unsigned i = 0; i < num_words; ++i) {
const char *start{val - std::min((i + 1) * 64, bitwidth)};
const char *end{val - 64 * i};
m_packed_value_buffer_[i] = 0;
for (const char *p = start; p < end; ++p) {
// No checking for invalid characters, follow original C implementation
m_packed_value_buffer_[i] <<= 1;
m_packed_value_buffer_[i] |= static_cast<uint64_t>(*p - '0');
if (hasXZ) {
const size_t j = i << 1;
m_packed_value_buffer_[j] <<= 1;
m_packed_value_buffer_[j | 1] <<= 1;
switch (*p) {
case '0':
break;
case '1': {
m_packed_value_buffer_[i] |= 1;
} break;
case 'X':
case 'x': {
m_packed_value_buffer_[i] |= 1;
} // FALLTHROUGH
case 'Z':
case 'z': {
m_packed_value_buffer_[j | 1] |= 1;
} break;
[[unlikely]] default: { FST_FAIL_STRING("Unexpected char"); } break;
}
} else {
m_packed_value_buffer_[i] <<= 1;
m_packed_value_buffer_[i] |= static_cast<uint64_t>(*p - '0');
}
}
}
if (bitwidth <= 64) {
if (bitwidth <= 64 && !hasXZ) {
emitValueChange(handle, m_packed_value_buffer_.front());
} else {
emitValueChange(handle, m_packed_value_buffer_.data(), EncodingType::BINARY);
emitValueChange(
handle,
m_packed_value_buffer_.data(),
hasXZ ? EncodingType::VERILOG : EncodingType::BINARY
);
}
}
@@ -605,7 +634,7 @@ void detail::ValueChangeData::writeEncodedPositions(
}
// encode as signed (value << 1) | 1 and write as signed LEB128
h.writeLEB128Signed((value_to_encode << 1) | 1);
h.writeLEB128Signed((static_cast<uint64_t>(value_to_encode) << 1) | 1);
++i;
}
@@ -695,7 +724,7 @@ void Writer::flushValueChangeDataConstPart_(
(void)count;
return std::make_pair(positions, memory_usage);
}();
const std::vector<int64_t> positions{p_tmp2.first};
const std::vector<int64_t> positions{std::move(p_tmp2.first)};
const size_t memory_usage{p_tmp2.second};
// 4. Position Section
+202 -32
View File
@@ -243,20 +243,32 @@ void vl_warn(const char* filename, int linenum, const char* hier, const char* ms
// Wrapper to call certain functions via messages when multithreaded
void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE {
VerilatedContext* const contextp = Verilated::threadContextp();
contextp->finishPendingInc();
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
vl_finish(filename, linenum, hier);
contextp->finishPendingDec();
}});
}
void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE {
// Classify now, so a queued request is pending from the moment it is posted
VerilatedContext* const contextp = Verilated::threadContextp();
const bool stop = contextp->stopRequestReserve(maybe);
if (stop) contextp->finishPendingInc();
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
vl_stop_maybe(filename, linenum, hier, maybe);
contextp->stopRequestRelease();
if (stop) contextp->finishPendingDec();
}});
}
void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
VerilatedContext* const contextp = Verilated::threadContextp();
contextp->finishPendingInc();
VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { //
vl_fatal(filename, linenum, hier, msg);
contextp->finishPendingDec();
}});
}
@@ -563,9 +575,10 @@ IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE {
}
IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
if (Verilated::threadContextp()->randReset() == 0) return 0;
const int randReset = Verilated::threadContextp()->randReset();
if (randReset == 0) return 0;
IData data = ~0;
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
if (randReset != 1) { // if 2, randomize
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
data = rng.rand64();
}
@@ -574,9 +587,10 @@ IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT
}
QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
if (Verilated::threadContextp()->randReset() == 0) return 0;
const int randReset = Verilated::threadContextp()->randReset();
if (randReset == 0) return 0;
QData data = ~0ULL;
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
if (randReset != 1) { // if 2, randomize
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
data = rng.rand64();
}
@@ -586,10 +600,17 @@ QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT
WDataOutP VL_SCOPED_RAND_RESET_W(int obits, WDataOutP outwp, uint64_t scopeHash,
uint64_t salt) VL_MT_UNSAFE {
if (Verilated::threadContextp()->randReset() != 2) { return VL_RAND_RESET_W(obits, outwp); }
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = rng.rand64();
outwp[VL_WORDS_I(obits) - 1] = rng.rand64() & VL_MASK_E(obits);
const int words = VL_WORDS_I(obits);
const int randReset = Verilated::threadContextp()->randReset();
if (randReset == 0) {
VL_MEMSET_ZERO_W(outwp, words);
} else if (randReset == 1) {
VL_MEMSET_ONES_W(outwp, words);
} else {
VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
for (int i = 0; i < words; ++i) outwp[i] = rng.rand64();
}
outwp[words - 1] &= VL_MASK_E(obits);
return outwp;
}
@@ -614,30 +635,14 @@ WDataOutP VL_SCOPED_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp, uint64_t sco
}
IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
if (Verilated::threadContextp()->randReset() == 0) return 0;
const int randReset = Verilated::threadContextp()->randReset();
if (randReset == 0) return 0;
IData data = ~0;
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
data = VL_RANDOM_I();
}
if (randReset != 1) data = VL_RANDOM_I(); // if 2, randomize
data &= VL_MASK_I(obits);
return data;
}
QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
if (Verilated::threadContextp()->randReset() == 0) return 0;
QData data = ~0ULL;
if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize
data = VL_RANDOM_Q();
}
data &= VL_MASK_Q(obits);
return data;
}
WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = VL_RAND_RESET_I(32);
outwp[VL_WORDS_I(obits) - 1] = VL_RAND_RESET_I(32) & VL_MASK_E(obits);
return outwp;
}
WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
// Not inlined to speed up compilation of slowpath code
return VL_ZERO_W(obits, outwp);
@@ -3067,9 +3072,11 @@ VerilatedContext::VerilatedContext()
// Must declare here not in interface, as otherwise forward declarations not known
VerilatedContext::~VerilatedContext() {
Verilated::threadContextp(this); // In unlikely case some other destructor needs it
checkMagic(this);
m_magic = 0x1; // Arbitrary but 0x1 is what Verilator src uses for a deleted pointer
logRestoreOutput();
Verilated::threadContextp(nullptr);
}
void VerilatedContext::checkMagic(const VerilatedContext* contextp) {
@@ -3087,6 +3094,7 @@ VerilatedContext::Serialized::Serialized() {
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
if (assertCtlsLocked()) return;
// Set all assert and directive types when true, clear otherwise.
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
}
@@ -3105,16 +3113,22 @@ uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
}
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
if (assertCtlsLocked()) return;
m_s.m_assertOn |= assertOnMask(types, directives);
}
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
if (assertCtlsLocked()) return;
m_s.m_assertOn &= ~assertOnMask(types, directives);
}
bool VerilatedContext::assertCtlsLocked() const VL_MT_SAFE { return m_ns.m_assertCtlsLocked; }
void VerilatedContext::assertCtlsLocked(bool flag) VL_MT_SAFE { m_ns.m_assertCtlsLocked = flag; }
void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
// IEEE 1800-2023 Table 20-5 control_type. Lock freezes the On/Off state of the
// selected bits until Unlock; On/Off/Kill leave locked bits unchanged.
// +verilator+assert+lock freezes everything, including Lock/Unlock itself.
if (assertCtlsLocked()) return;
const uint32_t mask = assertOnMask(types, directives);
const uint32_t lockedMask = mask & ~m_s.m_assertLock;
switch (controlType) {
@@ -3286,6 +3300,15 @@ void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_s.m_gotFinish = flag;
}
bool VerilatedContext::stopRequestReserve(bool maybe) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
const int reserved = ++m_ns.m_stopReserved;
return !maybe || m_s.m_errorCount + reserved >= m_s.m_errorLimit;
}
void VerilatedContext::stopRequestRelease() VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
--m_ns.m_stopReserved;
}
bool VerilatedContext::executingFinal() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_executingFinal;
@@ -3556,7 +3579,9 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) {
std::string str;
uint64_t u64;
if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
if (arg == "+verilator+assert+lock") {
assertCtlsLocked(true);
} else if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
coverageFilename(str);
} else if (arg == "+verilator+debug") {
Verilated::debug(4);
@@ -3575,7 +3600,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
logFilename(str);
logOutputToFile(false /* append */);
} else if (arg == "+verilator+noassert") {
assertOn(false);
// Set directly on to avoid conflicts with +verilator+assert+lock
m_s.m_assertOn = 0;
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+start+", u64)) {
profExecStart(u64);
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+window+", u64, 1)) {
@@ -3891,7 +3917,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
// When running internal code coverage (gcc --coverage, as opposed to
// verilator --coverage), dump coverage data to properly cover failing
// tests.
VL_GCOV_DUMP();
VL_GCOV_DUMP_RESET();
}
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { addCbExit(cb, datap); }
@@ -4012,6 +4038,106 @@ void VerilatedImp::versionDump() VL_MT_SAFE {
VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
}
//===========================================================================
// VerilatedEvalLoop:: Methods
void VerilatedEvalLoop::didNotConverge(const char* namep,
void (VerilatedModel::*dumpTriggersp)()) {
if (dumpTriggersp) (m_model.*dumpTriggersp)();
const std::string msg = "DIDNOTCONVERGE: "s + namep
+ " region did not converge after '--converge-limit' of "
+ std::to_string(m_convergeLimit) + " tries";
VL_FATAL_MT("", 0, "", msg.c_str());
VL_UNREACHABLE; // VL_FATAL_MT does not return
}
template <bool Profiling>
void VerilatedEvalLoop::evalImpl() {
VL_DEBUG_IF(VL_DBG_MSGF("+ Eval\n"););
if VL_CONSTEXPR_CXX17 (Profiling) {
// Advance the profiling window
if (VL_UNLIKELY(m_profTopLevel)) m_profilerp->configure();
m_profilerp->sectionPush("eval");
}
m_model.evalBegin();
// Initialization on first time step only
if (VL_UNLIKELY(!m_model.m_didInit)) {
VL_DEBUG_IF(VL_DBG_MSGF("+ Initial\n"););
// Static initializers
m_model.evalStatic();
// Initial blocks
m_model.evalInitial();
// The 'Settle' region, iterated until it converges
uint32_t stlIterCount = 0;
do {
checkConvergence(++stlIterCount, "Settle", &VerilatedModel::dumpTriggersStl);
} while (m_model.evalStl(stlIterCount == 1));
m_model.m_didInit = true;
}
// Sampled values are collected before anything can read them
m_model.evalSample();
// The 'Input combinational' region updates combinational logic driven from primary inputs
{
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPush("loop ico");
uint32_t icoIterCount = 0;
do {
checkConvergence(++icoIterCount, "Input combinational",
&VerilatedModel::dumpTriggersIco);
} while (m_model.evalIco(icoIterCount == 1));
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // loop ico
}
// The remaining regions are nested: each iteration of a region's loop
// re-runs the loops of all regions that precede it in the scheduling order.
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPush("loop react");
uint32_t reactIterCount = 0;
do {
checkConvergence(++reactIterCount, "Reactive", &VerilatedModel::dumpTriggersReact);
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPush("loop obs");
uint32_t obsIterCount = 0;
do {
checkConvergence(++obsIterCount, "Observed", &VerilatedModel::dumpTriggersObs);
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPush("loop nba");
uint32_t nbaIterCount = 0;
do {
checkConvergence(++nbaIterCount, "NBA", &VerilatedModel::dumpTriggersNba);
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPush("loop inact");
uint32_t inactIterCount = 0;
do {
checkConvergence(++inactIterCount, "Inactive");
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPush("loop act");
uint32_t actIterCount = 0;
do {
checkConvergence(++actIterCount, "Active",
&VerilatedModel::dumpTriggersAct);
} while (m_model.evalAct());
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // loop act
} while (m_model.evalInact());
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // loop inact
} while (m_model.evalNba());
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // loop nba
} while (m_model.evalObs());
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // loop obs
} while (m_model.evalReact());
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // loop react
// The 'Postponed' region runs once, at the end of the time step
m_model.evalPostponed();
m_model.evalEnd();
if VL_CONSTEXPR_CXX17 (Profiling) m_profilerp->sectionPop(); // eval
}
// Template instantiations
template void VerilatedEvalLoop::evalImpl<false>();
template void VerilatedEvalLoop::evalImpl<true>();
//===========================================================================
// VerilatedModel:: Methods
@@ -4078,12 +4204,12 @@ VerilatedScope::VerilatedScope(VerilatedSyms* symsp, const char* suffixp, const
, m_defnamep{defnamep}
, m_timeunit{timeunit}
, m_type{type} {
Verilated::threadContextp()->impp()->scopeInsert(this);
contextp()->impp()->scopeInsert(this);
}
VerilatedScope::~VerilatedScope() {
// Memory cleanup - not called during normal operation
Verilated::threadContextp()->impp()->scopeErase(this);
contextp()->impp()->scopeErase(this);
VL_DO_DANGLING(delete[] m_namep, m_namep);
VL_DO_DANGLING(delete[] m_callbacksp, m_callbacksp);
VL_DO_DANGLING(delete m_varsp, m_varsp);
@@ -4142,6 +4268,50 @@ VerilatedVar* VerilatedScope::varInsert(const char* namep, void* datap, bool isP
return &(m_varsp->find(namep)->second);
}
void VerilatedScope::varsInsertFromTable(const VlVarTableEntry* entp, size_t n,
void* basep) VL_MT_UNSAFE {
// Table-driven equivalent of a run of varInsert()/varInsertSized() calls; see VlVarTableEntry.
if (!m_varsp) m_varsp = new VerilatedVarNameMap;
uint8_t* const base = static_cast<uint8_t*>(basep);
for (size_t i = 0; i < n; ++i) {
const VlVarTableEntry& e = entp[i];
void* const datap = base + e.byteOffset;
const VerilatedVarFlags vlflags = static_cast<VerilatedVarFlags>(e.vlflags);
VerilatedVar var{e.namep, datap, e.vltype, vlflags, e.udims, e.pdims, /*isParam=*/false};
for (int d = 0; d < e.udims; ++d) {
var.m_unpacked[d].m_left = e.dims[2 * d];
var.m_unpacked[d].m_right = e.dims[2 * d + 1];
}
for (int d = 0; d < e.pdims; ++d) {
var.m_packed[d].m_left = e.dims[2 * (e.udims + d)];
var.m_packed[d].m_right = e.dims[2 * (e.udims + d) + 1];
}
// Recompute the flattened DPI packed range now dims are known (see
// VerilatedVarProps::initPacked)
if (e.pdims == 1) {
var.m_packedDpi = var.m_packed.front();
} else if (e.pdims > 1) {
int packedSize = 1;
for (int d = 0; d < e.pdims; ++d) packedSize *= var.m_packed[d].elements();
var.m_packedDpi = VerilatedRange{packedSize - 1, 0};
}
m_varsp->emplace(e.namep, std::move(var));
}
}
void VerilatedScope::scopesConstructFromTable(const VlScopeTableEntry* entp, size_t n,
VerilatedSyms* symsp) VL_MT_UNSAFE {
// Table-driven equivalent of a run of 'new VerilatedScope{...}' statements; see
// VlScopeTableEntry. The generated Syms class derives VerilatedSyms as its sole primary
// base at offset 0, so symsp doubles as the base for the offsetof-baked member addresses.
uint8_t* const base = reinterpret_cast<uint8_t*>(symsp);
for (size_t i = 0; i < n; ++i) {
const VlScopeTableEntry& e = entp[i];
VerilatedScope** const slotp = reinterpret_cast<VerilatedScope**>(base + e.ptrOffset);
*slotp = new VerilatedScope{symsp, e.namep, e.identp, e.defnamep, e.timeunit, e.type};
}
}
VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, int udims,
uint32_t entSize...) VL_MT_UNSAFE {
+162
View File
@@ -160,6 +160,21 @@ enum VerilatedVarFlags : uint32_t {
VLVF_NET = (1 << 15) // Net object
};
// One VPI-visible variable, consumed by VerilatedScope::varsInsertFromTable();
// replaces per-variable varInsert() calls, which compiles faster at scale.
struct VlVarTableEntry final {
static constexpr int kMaxDims = 3; // Max packed+unpacked dims a table row holds
const char* namep; // VPI-facing (protected) variable name, string literal
size_t byteOffset; // offsetof of storage member from module instance base
VerilatedVarType vltype;
uint32_t vlflags; // Direction + flags (VLVD_*/VLVF_*)
uint8_t udims; // udims + pdims <= kMaxDims
uint8_t pdims;
// (left,right) pairs: unpacked dims first, then packed; int32_t since large
// unpacked memories exceed int16 range
int32_t dims[kMaxDims * 2];
};
// IEEE 1800-2023 Table 20-6
enum class VerilatedAssertType : uint8_t {
ASSERT_TYPE_CONCURRENT = (1 << 0),
@@ -286,6 +301,8 @@ public:
#endif
};
class VlExecutionProfilerBase;
//=========================================================================
/// Base class of a Verilator generated (Verilated) model.
///
@@ -298,6 +315,8 @@ class VerilatedModel VL_NOT_FINAL {
VerilatedContext& m_context; // The VerilatedContext this model is instantiated under
protected:
bool m_didInit = false; // Time 0 initialization has run
explicit VerilatedModel(VerilatedContext& context);
virtual ~VerilatedModel() = default;
@@ -316,8 +335,82 @@ private:
// The following are for use by Verilator internals only
template <typename, typename>
friend class VerilatedTrace;
friend class VerilatedEvalLoop;
// Run-time trace configuration requested by this model
virtual std::unique_ptr<VerilatedTraceConfig> traceConfig() const;
// Entry points called by VerilatedEvalLoop
virtual void evalBegin() = 0;
virtual void evalEnd() = 0;
virtual void evalStatic() = 0;
virtual void evalInitial() = 0;
virtual void evalSample() = 0;
virtual bool evalStl(bool firstIteration) = 0;
virtual bool evalIco(bool firstIteration) = 0;
virtual bool evalAct() = 0;
virtual bool evalInact() = 0;
virtual bool evalNba() = 0;
virtual bool evalObs() = 0;
virtual bool evalReact() = 0;
virtual void evalPostponed() = 0;
virtual void dumpTriggersStl() = 0;
virtual void dumpTriggersIco() = 0;
virtual void dumpTriggersAct() = 0;
virtual void dumpTriggersNba() = 0;
virtual void dumpTriggersObs() = 0;
virtual void dumpTriggersReact() = 0;
// Runs 'final' blocks at the end of the simulation
virtual void evalFinal() = 0;
};
//=========================================================================
/// Evaluation loop calling a VerilatedModel's entry points
class VerilatedEvalLoop final {
VL_UNCOPYABLE(VerilatedEvalLoop);
// MEMBERS
VerilatedModel& m_model; // The model this loop evaluates
const uint32_t m_convergeLimit; // --converge-limit from compiler command line
// Where to record --prof-exec sections, or null if not profiling
VlExecutionProfilerBase* m_profilerp = nullptr;
// Whether this is the top level model during profiling
bool m_profTopLevel = false;
public:
// CONSTRUCTORS
VerilatedEvalLoop(VerilatedModel& model, uint32_t convergeLimit)
: m_model{model}
, m_convergeLimit{convergeLimit} {}
// METHODS
// Evaluate a single time step of the SV scheduling model
void eval() {
if (VL_UNLIKELY(m_profilerp)) {
evalImpl<true>();
} else {
evalImpl<false>();
}
}
// Set --prof-exec profiler
void profiler(VlExecutionProfilerBase* profilerp, bool topLevel) {
m_profilerp = profilerp;
m_profTopLevel = topLevel;
}
private:
// Evaluate a time step, recording --prof-exec sections iff 'Profiling'.
template <bool Profiling>
void evalImpl();
// Check the iteration convergence
void checkConvergence(uint32_t iterCount, const char* namep,
void (VerilatedModel::*dumpTriggersp)() = nullptr) {
if (VL_UNLIKELY(iterCount > m_convergeLimit)) didNotConverge(namep, dumpTriggersp);
}
// Dump the region's triggers, if it has any, then report non-convergence and abort
void didNotConverge(const char* namep, void (VerilatedModel::*dumpTriggersp)());
};
//=========================================================================
@@ -342,6 +435,23 @@ public:
virtual ~VerilatedVirtualBase() = default;
};
//===========================================================================
// Internal: Base of the '--prof-exec' execution profiler
//
// Implemented by VlExecutionProfiler, see verilated_profiler.h. Declared here
// so the evaluation loop can drive the profiler without naming it, as it is
// only linked when Verilated with --prof-exec.
class VlExecutionProfilerBase VL_NOT_FINAL : public VerilatedVirtualBase {
public:
// Mark the beginning of a section of execution
virtual void sectionPush(const char* namep) = 0;
// Mark the end of the innermost open section
virtual void sectionPop() = 0;
// Advance the profiling window at the start of a time step
virtual void configure() = 0;
};
//===========================================================================
/// Verilator simulation context
///
@@ -372,6 +482,8 @@ private:
static uint32_t assertOnMask(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_PURE;
static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1;
// No termination request has stamped m_finishPendingTime yet
static constexpr uint64_t TIME_UNSET = ~0ULL;
protected:
// TYPES
@@ -430,6 +542,12 @@ protected:
struct NonSerialized final { // Non-serialized information
// These are reloaded from on command-line settings, so do not need to persist
// Fast path
// A worker queues $finish before the main thread callback can set m_gotFinish.
std::atomic<uint32_t> m_finishPending{0}; // Number of queued $finish callbacks
std::atomic<uint64_t> m_finishPendingTime{TIME_UNSET}; // Time of the first callback
std::atomic<bool> m_assertCtlsLocked{
false}; // When true, all assertion-control updates are ignored
int m_stopReserved = 0; // Posted $stop requests not yet executed
bool m_executingFinal = false; // Running generated final() code
uint64_t m_profExecStart = 1; // +prof+exec+start time
uint32_t m_profExecWindow = 2; // +prof+exec+window size
@@ -510,6 +628,12 @@ public:
/// Clear enabled status for given assertion types
void assertOnClear(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
/// Return if assertion-control updates are locked. When locked, RTL assert
// control statements ($asserton/$assertoff/$assertcontrol) are ignored, as
// are updates from the C++ API.
bool assertCtlsLocked() const VL_MT_SAFE;
/// Lock/unlock assertion-control updates.
void assertCtlsLocked(bool flag) VL_MT_SAFE;
/// Apply assertion control for given control, assertion, and directive types
void assertCtl(uint32_t controlType, VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
@@ -669,6 +793,27 @@ public:
// METHODS - public but for internal use only
// Internal: Track $finish/$stop callbacks queued by worker threads
bool finishPending() const VL_MT_SAFE { return m_ns.m_finishPending.load() != 0; }
void finishPendingInc() VL_MT_SAFE {
++m_ns.m_finishPending;
uint64_t unset = TIME_UNSET;
m_ns.m_finishPendingTime.compare_exchange_strong(unset, time());
}
void finishPendingDec() VL_MT_SAFE {
const uint32_t previous = m_ns.m_finishPending.fetch_sub(1);
assert(previous > 0);
if (previous == 1 && !gotFinish()) m_ns.m_finishPendingTime = TIME_UNSET;
}
// Internal: Time of the first termination request, else the current time
uint64_t finishPendingTime() const VL_MT_SAFE {
const uint64_t stamped = m_ns.m_finishPendingTime.load();
return stamped == TIME_UNSET ? time() : stamped;
}
// Internal: Reserve a posted $stop, returning true if it reaches the termination limit
bool stopRequestReserve(bool maybe) VL_MT_SAFE;
void stopRequestRelease() VL_MT_SAFE;
// Internal: access to implementation class
VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast<VerilatedContextImp*>(this); }
const VerilatedContextImp* impp() const VL_MT_SAFE {
@@ -756,6 +901,8 @@ public: // But for internal use only
// Verilator scope information class
// Used for internal VPI implementation, and introspection into scopes
struct VlScopeTableEntry; // Defined below VerilatedScope; used by scopesConstructFromTable()
class VerilatedScope final {
public:
enum Type : uint8_t {
@@ -792,6 +939,9 @@ public: // But internals only - called from verilated modules, VerilatedSyms
void* forceReadSignalData, const char* forceReadSignalName,
std::pair<VerilatedVar*, VerilatedVar*> forceControlSignals,
int udims, int pdims...) VL_MT_UNSAFE;
void varsInsertFromTable(const VlVarTableEntry* entp, size_t n, void* basep) VL_MT_UNSAFE;
static void scopesConstructFromTable(const VlScopeTableEntry* entp, size_t n,
VerilatedSyms* symsp) VL_MT_UNSAFE;
// ACCESSORS
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; }
@@ -805,6 +955,18 @@ public: // But internals only - called from verilated modules, VerilatedSyms
static void* exportFindNullError(int funcnum) VL_MT_SAFE;
static void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE;
Type type() const { return m_type; }
VerilatedContext* contextp() const { return m_symsp->_vm_contextp__; }
};
// One scope, consumed by VerilatedScope::scopesConstructFromTable(); replaces
// per-scope 'new VerilatedScope{...}' statements, which compiles faster at scale.
struct VlScopeTableEntry final {
size_t ptrOffset; // offsetof of the target __Vscopep_* member within the Syms object
const char* namep; // Scope suffix name (protected), string literal
const char* identp; // Identifier with escapes removed (protected)
const char* defnamep; // Definition name (SCOPE_MODULE only), else "<null>"
int8_t timeunit; // Timeunit in negative power-of-10
VerilatedScope::Type type;
};
class VerilatedHierarchy final {
+1 -1
View File
@@ -43,7 +43,7 @@ CFG_CXXFLAGS_WEXTRA = @CFG_CXXFLAGS_WEXTRA@
# Compiler flags that enable coroutine support
CFG_CXXFLAGS_COROUTINES = @CFG_CXXFLAGS_COROUTINES@
# Compiler flags when creating a precompiled header
CFG_CXXFLAGS_PCH = -x c++-header
CFG_CXXFLAGS_PCH = -c -x c++-header
# Compiler option to put in front of filename to read precompiled header
CFG_CXXFLAGS_PCH_I = @CFG_CXXFLAGS_PCH_I@
# Compiler's filename prefix for precompiled headers, .gch if clang, empty if GCC
+12 -12
View File
@@ -75,24 +75,24 @@ class VerilatedCovImp;
ccontextp->_insertp("hier", name, __VA_ARGS__); \
} while (false)
static inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData,
const IData oldData) {
inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData,
const IData oldData) {
const IData chgData = newData ^ oldData;
for (int i = 0; i < width; ++i) {
*(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1;
}
}
static inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const QData newData,
const QData oldData) {
inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const QData newData,
const QData oldData) {
const QData chgData = newData ^ oldData;
for (int i = 0; i < width; ++i) {
*(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1;
}
}
static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData,
WDataInP oldData) {
inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData,
WDataInP oldData) {
for (int i = 0; i < VL_WORDS_I(width); ++i) {
const EData chgData = newData[i] ^ oldData[i];
if (chgData) {
@@ -104,8 +104,8 @@ static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WData
}
}
static inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic<uint32_t>* covp,
const IData newData, const IData oldData) VL_MT_SAFE {
inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic<uint32_t>* covp,
const IData newData, const IData oldData) VL_MT_SAFE {
const IData chgData = newData ^ oldData;
for (int i = 0; i < width; ++i) {
if (VL_BITISSET_I(chgData, i)) {
@@ -114,8 +114,8 @@ static inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic<uint32_t>
}
}
static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic<uint32_t>* covp,
const QData newData, const QData oldData) VL_MT_SAFE {
inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic<uint32_t>* covp,
const QData newData, const QData oldData) VL_MT_SAFE {
const QData chgData = newData ^ oldData;
for (int i = 0; i < width; ++i) {
if (VL_BITISSET_Q(chgData, i)) {
@@ -124,8 +124,8 @@ static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic<uint32_t>
}
}
static inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic<uint32_t>* covp,
WDataInP newData, WDataInP oldData) VL_MT_SAFE {
inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic<uint32_t>* covp, WDataInP newData,
WDataInP oldData) VL_MT_SAFE {
for (int i = 0; i < VL_WORDS_I(width); ++i) {
const EData chgData = newData[i] ^ oldData[i];
if (chgData) {
+12 -8
View File
@@ -41,9 +41,13 @@ enum class VlCovBinKind : uint8_t {
//=============================================================================
// VlCoverpointIf
/// Read-side view of a coverpoint. The writer queries bins by index; the
/// implementor computes names/kinds on demand. Bounded bin count, so random
/// access by index is the primary usage.
/// Read-side view of a coverpoint -- a named, index-addressable set of bins
/// with a coverage fraction. A cross is also a coverpoint from this view: its
/// auto cross bins (one per element of the Cartesian product of the feeding
/// coverpoints' Normal bins) are all Normal, and their names are built on
/// demand by concatenating the feeding coverpoints' bin names. The writer
/// queries bins by index; the implementor computes names/kinds on demand.
/// Bounded bin count, so random access by index is the primary usage.
class VlCoverpointIf VL_NOT_FINAL {
public:
@@ -51,11 +55,11 @@ public:
virtual ~VlCoverpointIf() = default;
// METHODS
// All bins, across every set; index range [0, binCount())
virtual int binCount() const = 0;
// Bin name in declaration order (e.g. "myBin" or "b[3]")
virtual std::string binName(int i) const = 0;
virtual VlCovBinKind binKind(int i) const = 0;
// All bins, across every set; index range [0, binCount()).
virtual uint32_t binCount() const = 0;
// Bin name in declaration order (e.g. "myBin" or "b[3]"); for a cross,
// the concatenated cross bin name (e.g. "b1_x_b2_x_b3")
virtual std::string binName(uint32_t i) const = 0;
// Bins covered / effective total (Normal set only) for the coverage calc
virtual void coverageParts(double& covered, double& total) const = 0;
};
+119 -10
View File
@@ -23,34 +23,57 @@
#include "verilated_covergroup.h"
#include "verilated.h"
// This file is compiled whenever covergroups are used, with or without
// "verilator --coverage" (see V3Global::verilatedCppFiles). Bin counts are
// members of the covergroup objects themselves, so sampling, bin naming, and
// coverage queries such as get_inst_coverage() all work with no coverage
// database present. VL_COVER_INSERT does not copy a count; it hands the
// database the address of a counter to read at write time. Only that
// publication step needs verilated_cov.cpp, which is compiled solely under
// --coverage, so only the registerBins() bodies are gated on VM_COVERAGE.
#if VM_COVERAGE
#include "verilated_cov.h"
#endif
void VlCoverpoint::init(const char* hier, uint32_t atLeast, int nBins) {
void VlCoverpoint::init(const char* hier, uint32_t atLeast, uint32_t nBins) {
m_hier = hier;
m_atLeast = atLeast;
m_total = nBins;
m_counts.assign(nBins, 0);
m_crossIdx.assign(nBins, -1);
m_crossToBin.clear();
}
void VlCoverpoint::addNamer(VlCovBinKind set, int count, VlCovBinNaming naming, const char* name,
const char* file, int line, int col) {
void VlCoverpoint::addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming,
const char* name, const char* file, int line, int col) {
m_namers.emplace_back(set, count, m_nextBase, naming, name, file, line, col);
if (set == VlCovBinKind::KIND_NORMAL) {
// Assign each Normal bin a cross index, and record the inverse map.
for (uint32_t b = m_nextBase; b < m_nextBase + count; ++b) {
m_crossIdx[b] = static_cast<int>(m_crossToBin.size());
m_crossToBin.push_back(b);
}
m_normal += count;
}
m_nextBase += count;
if (set == VlCovBinKind::KIND_NORMAL) m_normal += count;
}
const VlCovNamer& VlCoverpoint::namerFor(int i) const {
// Namers are appended in ascending, contiguous index order covering [0, m_total),
// and i is always a valid bin index, so the matching namer always exists.
std::string VlCoverpoint::normalBinName(uint32_t crossIdx) const {
// Build the bin name based on the bin index
return binName(m_crossToBin[crossIdx]);
}
const VlCovNamer& VlCoverpoint::namerFor(uint32_t i) const {
// Namers are appended in ascending order covering [0, m_total),
for (const VlCovNamer& nm : m_namers) {
if (i < nm.base() + nm.count()) return nm;
}
VL_UNREACHABLE;
VL_UNREACHABLE; // LCOV_EXCL_LINE
}
std::string VlCoverpoint::binName(int i) const {
std::string VlCoverpoint::binName(uint32_t i) const {
const VlCovNamer& nm = namerFor(i);
std::string name = nm.name();
if (nm.naming() == VlCovBinNaming::Array) name += '[' + std::to_string(i - nm.base()) + ']';
@@ -59,7 +82,7 @@ std::string VlCoverpoint::binName(int i) const {
#if VM_COVERAGE
void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* page) {
for (int i = 0; i < binCount(); ++i) {
for (uint32_t i = 0; i < binCount(); ++i) {
const VlCovNamer& nm = namerFor(i);
const VlCovBinKind kind = binKind(i);
const std::string binp = binName(i);
@@ -81,3 +104,89 @@ void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* pa
}
}
#endif // VM_COVERAGE
//=============================================================================
// VlCoverCross
void VlCoverCross::init(const char* hier, uint32_t dims, VlCoverpoint* const* cps,
const char* file, int line, int col) {
m_hier = hier;
m_file = file;
m_line = line;
m_col = col;
m_dims = dims;
m_cps.assign(cps, cps + dims);
m_cpBinCounts.resize(dims);
// Accumulate in 64 bits so the overflow check itself cannot overflow.
uint64_t product = 1;
for (uint32_t d = 0; d < dims; ++d) {
m_cpBinCounts[d] = cps[d]->normalBinCount();
product *= m_cpBinCounts[d];
if (VL_UNLIKELY(product > UINT32_MAX)) { // LCOV_EXCL_START
VL_FATAL_MT(file, line, "", "Cross has too many auto bins to represent");
} // LCOV_EXCL_STOP
}
m_numAutoBins = static_cast<uint32_t>(product);
// stride[d] = product of the Normal bin counts of all dimensions after d.
// Counts down with an offset so the unsigned index never wraps below zero.
m_stride.assign(dims, 1);
for (uint32_t d = dims; d > 1; --d) m_stride[d - 2] = m_stride[d - 1] * m_cpBinCounts[d - 1];
m_flatCounts.assign(m_numAutoBins, 0);
}
void VlCoverCross::iterateProduct(VlCoverpoint* const* cps, uint32_t dim, uint32_t baseIdx) {
const uint32_t hits = cps[dim]->hitCount();
const uint32_t* const list = cps[dim]->hitList();
const bool last = (dim == m_dims - 1);
const uint32_t stride = m_stride[dim];
for (uint32_t hit = 0; hit < hits; ++hit) {
const uint32_t idx = baseIdx + list[hit] * stride;
if (last) {
incrementTuple(idx);
} else {
iterateProduct(cps, dim + 1, idx);
}
}
}
void VlCoverCross::sample(VlCoverpoint* const* cps) {
// Fast path: if any dimension had no Normal-bin hit, the cross cannot hit.
for (uint32_t d = 0; d < m_dims; ++d) {
if (cps[d]->hitCount() == 0) return;
}
iterateProduct(cps, 0, 0);
}
std::string VlCoverCross::binName(uint32_t flat) const {
// Built on demand by concatenating each coverpoint's own bin name.
std::string name;
for (uint32_t d = 0; d < m_dims; ++d) {
const uint32_t crossIdx = (flat / m_stride[d]) % m_cpBinCounts[d];
if (d > 0) name += "_x_";
name += m_cps[d]->normalBinName(crossIdx);
}
return name;
}
#if VM_COVERAGE
void VlCoverCross::registerBins(VerilatedCovContext* covcontextp, const char* page) {
// Register every auto cross bin (zero-count bins included), so the report
// shows the full Cartesian product of cross bins. Names are built on the fly.
const std::string lineStr = std::to_string(m_line);
const std::string colStr = std::to_string(m_col);
for (uint32_t flat = 0; flat < binCount(); ++flat) {
const std::string bin = binName(flat); // "b1_x_b2_x_..."
// cross_bins metadata: the same components joined by ',' (not read by the report)
std::string crossBins;
for (uint32_t d = 0; d < m_dims; ++d) {
const uint32_t crossIdx = (flat / m_stride[d]) % m_cpBinCounts[d];
if (d > 0) crossBins += ",";
crossBins += m_cps[d]->normalBinName(crossIdx);
}
const std::string full = m_hier + "." + bin;
VL_COVER_INSERT(covcontextp, full.c_str(), &m_flatCounts[flat], "page", page, "filename",
m_file, "lineno", lineStr.c_str(), "column", colStr.c_str(), "bin",
bin.c_str(), "cross", "1", "cross_bins", crossBins.c_str());
}
}
#endif // VM_COVERAGE
+136 -23
View File
@@ -22,6 +22,9 @@
/// it in the constructor (init + add*Namer), increments bins from sample(),
/// and registers via registerBins().
///
/// Collection and coverage queries are always available; only registerBins(),
/// which publishes bin counters to the coverage database, requires VM_COVERAGE.
///
//=============================================================================
#ifndef VERILATOR_VERILATED_COVERGROUP_H_
@@ -49,8 +52,8 @@ enum class VlCovBinNaming : uint8_t {
class VlCovNamer final {
// MEMBERS
VlCovBinKind m_set; // which set the bins belong to
int m_count; // bins this namer covers (1 for Single)
int m_base; // first bin index (declaration order), assigned on append
uint32_t m_count; // bins this namer covers (1 for Single)
uint32_t m_base; // first bin index (declaration order), assigned on append
VlCovBinNaming m_naming; // how bin names are built
const char* m_name; // bin name (Single) or array base name (Array)
const char* m_file; // declaration file
@@ -59,8 +62,8 @@ class VlCovNamer final {
public:
// CONSTRUCTORS
VlCovNamer(VlCovBinKind set, int count, int base, VlCovBinNaming naming, const char* name,
const char* file, int line, int col)
VlCovNamer(VlCovBinKind set, uint32_t count, uint32_t base, VlCovBinNaming naming,
const char* name, const char* file, int line, int col)
: m_set{set}
, m_count{count}
, m_base{base}
@@ -72,8 +75,8 @@ public:
// METHODS
VlCovBinKind set() const { return m_set; }
int count() const { return m_count; }
int base() const { return m_base; }
uint32_t count() const { return m_count; }
uint32_t base() const { return m_base; }
VlCovBinNaming naming() const { return m_naming; }
const char* name() const { return m_name; }
const char* file() const { return m_file; }
@@ -87,19 +90,30 @@ public:
/// bin's set/name come from the owning namer. coverage() is computed on demand
/// by scanning bin counts, keeping the sample() hot path a plain counter bump.
class VlCoverpoint final : public VlCoverpointIf {
// MEMBERS
// Base coverpoint runtime (read side + collection logic, no hit-list storage).
// VlCoverpointT<MaxHits> adds the inline hit-list array and the incrementBin write
// path; the cross holds VlCoverpoint* and reads via hitCount()/hitList().
class VlCoverpoint VL_NOT_FINAL : public VlCoverpointIf {
protected:
// MEMBERS (protected so VlCoverpointT::incrementBin can update them)
std::string m_hier; // "covergroup.coverpoint"
uint32_t m_atLeast = 1; // option.at_least (coverpoint-wide)
int m_total = 0; // bins across all sets
int m_normal = 0; // Normal bins (coverage denominator)
int m_nextBase = 0; // running append cursor
uint32_t m_total = 0; // bins across all sets
uint32_t m_normal = 0; // Normal bins (coverage denominator)
uint32_t m_nextBase = 0; // running append cursor
std::vector<uint32_t> m_counts; // [m_total], one per bin
std::vector<VlCovNamer> m_namers; // appended in declaration order
// [m_total] full bin idx -> cross idx (Normal-only), -1 otherwise. The only
// signed index here: -1 marks a non-Normal bin, which incrementBin filters on.
std::vector<int> m_crossIdx;
// [m_normal] inverse of m_crossIdx: cross idx -> full bin idx, appended in cross-index order
std::vector<uint32_t> m_crossToBin;
uint32_t m_hitCount = 0; // entries valid in the hit list this sample
private:
// PRIVATE METHODS
const VlCovNamer& namerFor(int i) const; // obtain the bin-specific name producer
void addNamer(VlCovBinKind set, int count, VlCovBinNaming naming, const char* name,
const VlCovNamer& namerFor(uint32_t i) const; // obtain the bin-specific name producer
void addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming, const char* name,
const char* file, int line, int col);
public:
@@ -108,31 +122,44 @@ public:
// METHODS
// ---- configuration (from generated constructor) ----
void init(const char* hier, uint32_t atLeast, int nBins);
void init(const char* hier, uint32_t atLeast, uint32_t nBins);
void addSingleNamer(VlCovBinKind set, const char* name, const char* file, int line, int col) {
addNamer(set, 1, VlCovBinNaming::Single, name, file, line, col);
}
void addArrayNamer(VlCovBinKind set, int count, const char* name, const char* file, int line,
int col) {
void addArrayNamer(VlCovBinKind set, uint32_t count, const char* name, const char* file,
int line, int col) {
addNamer(set, count, VlCovBinNaming::Array, name, file, line, col);
}
void registerBins(VerilatedCovContext* covcontextp, const char* page);
// ---- hot path (from generated sample()) ----
void incrementBin(int i) { ++m_counts[i]; } // Normal bin: count only
void recordHit(int i) { ++m_counts[i]; } // Ignore/Illegal/Default: count only
// Clear the hit list at the start of each sample() for a cross-fed coverpoint.
void clearHitList() { m_hitCount = 0; }
// Ignore/Illegal/Default: count only; never propagates to cross coverage.
void recordHit(uint32_t i) { ++m_counts[i]; }
// incrementBin (Normal bin: count + hit-list append) lives in VlCoverpointT<MaxHits>,
// where MaxHits is the gen-time max per-sample bin overlap.
// ---- cross support (read by VlCoverCross) ----
uint32_t hitCount() const { return m_hitCount; }
virtual const uint32_t* hitList() const = 0; // provided by VlCoverpointT
uint32_t normalBinCount() const { return m_normal; } // cross dimension size (Normal bins)
std::string normalBinName(uint32_t crossIdx) const; // name of the crossIdx-th Normal bin
// ---- VlCoverpointIf ----
int binCount() const override { return m_total; }
std::string binName(int i) const override;
VlCovBinKind binKind(int i) const override { return namerFor(i).set(); }
uint32_t binCount() const override { return m_total; }
std::string binName(uint32_t i) const override;
// Deliberately not on VlCoverpointIf: only registerBins() needs it, via the
// concrete coverpoint. A cross has all-Normal bins and exposes no kind, so the
// interface omits it; add it back only if a writer needs it polymorphically.
VlCovBinKind binKind(uint32_t i) const { return namerFor(i).set(); }
void coverageParts(double& covered, double& total) const override {
// Count Normal bins that reached option.at_least on demand, so the hot
// path (incrementBin) stays a plain counter bump.
int numCovered = 0;
uint32_t numCovered = 0;
for (const VlCovNamer& nm : m_namers) {
if (nm.set() != VlCovBinKind::KIND_NORMAL) continue;
for (int i = nm.base(); i < nm.base() + nm.count(); ++i) {
for (uint32_t i = nm.base(); i < nm.base() + nm.count(); ++i) {
if (m_counts[i] >= m_atLeast) ++numCovered;
}
}
@@ -141,4 +168,90 @@ public:
}
};
//=============================================================================
// VlCoverpointT
/// Concrete coverpoint with an inline hit-list array sized to MaxHits -- the
/// gen-time maximum number of Normal bins one sample value can match (1 for the
/// common non-overlapping case). The bound is a compile-time constant, so for
/// MaxHits == 1 incrementBin collapses to a single store. Generated code holds
/// the coverpoint as VlCoverpointT<K> and calls incrementBin via the concrete
/// type; the cross reads it polymorphically through VlCoverpoint*.
template <uint32_t MaxHits>
class VlCoverpointT final : public VlCoverpoint {
// MEMBERS
uint32_t m_hits[MaxHits]; // cross indices of Normal bins hit this sample
public:
// CONSTRUCTORS
VlCoverpointT() = default;
// METHODS
// Normal bin: bump count and append the bin's cross index to the hit list.
// m_hitCount can never exceed MaxHits (the gen-time overlap bound), so no hit
// is ever dropped; the bound check is a compile-time-folded safety net.
void incrementBin(uint32_t i) {
++m_counts[i];
// m_crossIdx is signed only to carry the -1 "not a Normal bin" marker;
// the >= 0 test below is what makes every stored hit index unsigned-safe.
const int cx = m_crossIdx[i];
if (cx >= 0 && m_hitCount < MaxHits) m_hits[m_hitCount++] = static_cast<uint32_t>(cx);
}
const uint32_t* hitList() const override { return m_hits; }
};
//=============================================================================
// VlCoverCross
/// Per-instance auto cross runtime. Holds flat uint32_t[] storage over the
/// Cartesian product of the feeding coverpoints' Normal bins. Each sample()
/// walks the coverpoint hit lists (O(hits), not O(product)). Bin names are
/// built on demand from the coverpoints, so no per-bin name is stored.
class VlCoverCross final : public VlCoverpointIf {
// MEMBERS
std::string m_hier; // "covergroup.cross"
const char* m_file = nullptr; // Cross declaration file (registration metadata)
int m_line = 0; // Cross declaration line
int m_col = 0; // Cross declaration column
uint32_t m_dims = 0; // Number of feeding coverpoints
// Cross bin indexes are unsigned, like the coverpoint bin indexes they are
// built from. init() fatals if the product would exceed UINT32_MAX, so every
// index computed here provably fits. That bound is far beyond anything
// storable anyway: m_flatCounts alone would need 16GB.
uint32_t m_numAutoBins = 0; // Product of per-dim Normal bin counts
uint32_t m_numCovered = 0; // Distinct bins hit >= 1 (maintained incrementally)
std::vector<uint32_t> m_cpBinCounts; // [m_dims] Normal bin count per dimension
std::vector<uint32_t> m_stride; // [m_dims] Flat-index stride per dimension
std::vector<uint32_t> m_flatCounts; // [m_numAutoBins] Per-bin hit counts
std::vector<VlCoverpoint*> m_cps; // Feeding coverpoints (the only name source)
// PRIVATE METHODS
void iterateProduct(VlCoverpoint* const* cps, uint32_t dim, uint32_t baseIdx);
void incrementTuple(uint32_t idx) {
if (m_flatCounts[idx]++ == 0) ++m_numCovered;
}
public:
// CONSTRUCTORS
VlCoverCross() = default;
// METHODS
// ---- configuration (from generated constructor, after coverpoints init'd) ----
void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
int line, int col);
void registerBins(VerilatedCovContext* covcontextp, const char* page);
// ---- hot path (from generated sample(), after all coverpoints sampled) ----
void sample(VlCoverpoint* const* cps);
// ---- VlCoverpointIf ----
// A cross is a coverpoint whose bins are the auto cross bins (all Normal).
uint32_t binCount() const override { return m_numAutoBins; }
std::string binName(uint32_t flat) const override;
void coverageParts(double& covered, double& total) const override {
covered = m_numCovered;
total = m_numAutoBins;
}
};
#endif // Guard
+16 -16
View File
@@ -37,68 +37,68 @@
// SETTING OPERATORS
// Convert svBitVecVal to Verilator internal data
static inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE {
inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE {
const int words = VL_WORDS_I(obits);
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
}
static inline void VL_SET_Q_SVBV(int obits, QData& out, const svBitVecVal* lwp) VL_MT_SAFE {
inline void VL_SET_Q_SVBV(int obits, QData& out, const svBitVecVal* lwp) VL_MT_SAFE {
out = VL_MASK_Q(obits) & VL_SET_QII(lwp[1], lwp[0]);
}
static inline void VL_SET_I_SVBV(int obits, IData& out, const svBitVecVal* lwp) VL_MT_SAFE {
inline void VL_SET_I_SVBV(int obits, IData& out, const svBitVecVal* lwp) VL_MT_SAFE {
out = VL_MASK_I(obits) & lwp[0];
}
static inline void VL_SET_S_SVBV(int obits, SData& out, const svBitVecVal* lwp) VL_MT_SAFE {
inline void VL_SET_S_SVBV(int obits, SData& out, const svBitVecVal* lwp) VL_MT_SAFE {
out = VL_MASK_I(obits) & lwp[0];
}
static inline void VL_SET_C_SVBV(int obits, CData& out, const svBitVecVal* lwp) VL_MT_SAFE {
inline void VL_SET_C_SVBV(int obits, CData& out, const svBitVecVal* lwp) VL_MT_SAFE {
out = VL_MASK_I(obits) & lwp[0];
}
// Convert Verilator internal data to svBitVecVal
static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
const int words = VL_WORDS_I(obits);
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
}
static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, const IData ld) VL_MT_SAFE { owp[0] = ld; }
static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE {
inline void VL_SET_SVBV_I(int, svBitVecVal* owp, const IData ld) VL_MT_SAFE { owp[0] = ld; }
inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE {
VL_SET_WQ(WDataOutP::external(owp), ld);
}
// Convert svLogicVecVal to Verilator internal data
// Note these functions ignore X/Z in svLogicVecVal
static inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE {
inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE {
const int words = VL_WORDS_I(obits);
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i].aval;
owp[words - 1] = lwp[words - 1].aval & VL_MASK_I(obits);
}
static inline void VL_SET_Q_SVLV(int obits, QData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
inline void VL_SET_Q_SVLV(int obits, QData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
out = VL_MASK_Q(obits) & VL_SET_QII(lwp[1].aval, lwp[0].aval);
}
static inline void VL_SET_I_SVLV(int obits, IData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
inline void VL_SET_I_SVLV(int obits, IData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
out = VL_MASK_I(obits) & lwp[0].aval;
}
static inline void VL_SET_S_SVLV(int obits, SData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
inline void VL_SET_S_SVLV(int obits, SData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
out = VL_MASK_I(obits) & lwp[0].aval;
}
static inline void VL_SET_C_SVLV(int obits, CData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
inline void VL_SET_C_SVLV(int obits, CData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
out = VL_MASK_I(obits) & lwp[0].aval;
}
// Convert Verilator internal data to svLogicVecVal
// Note these functions never create X/Z in svLogicVecVal
static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
const int words = VL_WORDS_I(obits);
for (int i = 0; i < words; ++i) owp[i].bval = 0;
for (int i = 0; i < words - 1; ++i) owp[i].aval = lwp[i];
owp[words - 1].aval = lwp[words - 1] & VL_MASK_I(obits);
}
static inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, const IData ld) VL_MT_SAFE {
inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, const IData ld) VL_MT_SAFE {
owp[0].aval = ld;
owp[0].bval = 0;
}
static inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, const QData ld) VL_MT_SAFE {
inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, const QData ld) VL_MT_SAFE {
VlWide<2> lwp;
VL_SET_WQ(lwp, ld);
owp[0].aval = lwp[0];
+4 -2
View File
@@ -98,8 +98,10 @@ void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
Super::closeBase();
emitTimeChangeMaybe();
if (m_fst) m_fst->close(); // LCOV_EXCL_BR_LINE
m_fst = nullptr;
if (m_fst) {
m_fst->close();
VL_DO_CLEAR(delete m_fst, m_fst = nullptr);
}
}
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
+406 -465
View File
File diff suppressed because it is too large Load Diff
+22 -51
View File
@@ -90,23 +90,26 @@ static double _vl_dbase_chi_square(IData& seedr, int32_t deg_of_free) VL_MT_SAFE
return x;
}
static IData _vl_round_to_int32(double r) VL_PURE {
int32_t i;
if (r >= 0) {
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
} else {
r = -r;
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
i = -i;
}
return static_cast<IData>(i);
}
IData VL_DIST_CHI_SQUARE(IData& seedr, IData udf) VL_MT_SAFE {
const int32_t df = static_cast<int32_t>(udf);
if (VL_UNLIKELY(df <= 0)) {
// Chi_square distribution must have positive degree of freedom
return 0;
}
double r = _vl_dbase_chi_square(seedr, df);
int32_t i;
if (r >= 0) {
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
} else {
r = -r; // LCOV_EXCL_LINE
i = static_cast<int32_t>(
r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding // LCOV_EXCL_LINE
i = -i; // LCOV_EXCL_LINE
}
return static_cast<IData>(i);
const double r = _vl_dbase_chi_square(seedr, df);
return _vl_round_to_int32(r);
}
IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE {
@@ -120,16 +123,8 @@ IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE {
for (int32_t i = 1; i <= k; ++i) x = x * _vl_dbase_uniform(seedr, 0, 1);
const double a = static_cast<double>(mean);
const double b = static_cast<double>(k);
double r = -a * log(x) / b;
int32_t i;
if (r >= 0) {
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
} else {
r = -r;
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
i = -i;
}
return static_cast<IData>(i);
const double r = -a * log(x) / b;
return _vl_round_to_int32(r);
}
IData VL_DIST_EXPONENTIAL(IData& seedr, IData umean) VL_MT_SAFE {
@@ -139,31 +134,15 @@ IData VL_DIST_EXPONENTIAL(IData& seedr, IData umean) VL_MT_SAFE {
return 0;
}
int32_t i;
double r = _vl_dbase_exponential(seedr, mean);
if (r >= 0) {
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
} else {
r = -r; // LCOV_EXCL_LINE
i = static_cast<int32_t>(
r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding // LCOV_EXCL_LINE
i = -i; // LCOV_EXCL_LINE
}
return static_cast<IData>(i);
const double r = _vl_dbase_exponential(seedr, mean);
return _vl_round_to_int32(r);
}
IData VL_DIST_NORMAL(IData& seedr, IData umean, IData usd) VL_MT_SAFE {
const int32_t mean = static_cast<int32_t>(umean);
const int32_t sd = static_cast<int32_t>(usd);
double r = _vl_dbase_normal(seedr, mean, sd);
int32_t i;
if (r >= 0) {
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
} else {
r = -r;
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
i = -i;
}
return static_cast<IData>(i);
const double r = _vl_dbase_normal(seedr, mean, sd);
return _vl_round_to_int32(r);
}
IData VL_DIST_POISSON(IData& seedr, IData umean) VL_MT_SAFE {
@@ -192,16 +171,8 @@ IData VL_DIST_T(IData& seedr, IData udf) VL_MT_SAFE {
const double chi2 = _vl_dbase_chi_square(seedr, df);
const double div = chi2 / static_cast<double>(df);
const double root = std::sqrt(div);
double r = _vl_dbase_normal(seedr, 0, 1) / root;
int32_t i;
if (r >= 0) {
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
} else {
r = -r;
i = static_cast<int32_t>(r + 0.5); // cppcheck-suppress bugprone-incorrect-rounding
i = -i;
}
return static_cast<IData>(i);
const double r = _vl_dbase_normal(seedr, 0, 1) / root;
return _vl_round_to_int32(r);
}
IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE {
+10 -3
View File
@@ -149,7 +149,7 @@ static_assert(std::is_trivially_destructible<VlExecutionRecord>::value,
//=============================================================================
// VlExecutionProfiler is for collecting profiling data about model execution
class VlExecutionProfiler final : public VerilatedVirtualBase {
class VlExecutionProfiler final : public VlExecutionProfilerBase {
// CONSTANTS
// In order to try to avoid dynamic memory allocations during the actual profiling phase,
@@ -193,8 +193,15 @@ public:
t_trace.emplace_back();
return t_trace.back();
}
// Configure profiler (called in beginning of 'eval')
void configure();
// Record the beginning/end of a section, for the run-time library
void sectionPush(const char* namep) override {
if (VL_UNLIKELY(m_enabled)) addRecord().sectionPush(namep);
}
void sectionPop() override {
if (VL_UNLIKELY(m_enabled)) addRecord().sectionPop();
}
// Advance the profiling window, called by the run-time evaluation loop
void configure() override;
// Setup profiling on a particular thread;
void setupThread(uint32_t threadId);
// Clear all profiling data
+1132 -466
View File
File diff suppressed because it is too large Load Diff
+158 -32
View File
@@ -79,10 +79,14 @@ public:
virtual void* datap(int /*idx*/) const { return m_datap; }
std::uint32_t randModeIdx() const { return m_randModeIdx; }
bool randModeIdxNone() const { return randModeIdx() == std::numeric_limits<unsigned>::max(); }
bool set(const std::string& idx, const std::string& val) const;
void set(const std::string& idx, const std::string& val) const;
virtual void emitGetValue(std::ostream& s) const;
virtual void emitExtract(std::ostream& s, int i) const;
virtual void emitType(std::ostream& s) const;
// Emit the expression referring to element j as a whole (the full var
// for scalars, "(select arr idx...)" for array element j). Used by BSAT
// to query/re-assert one whole element's value, not a single bit.
virtual void emitElement(std::ostream& s, int j) const;
// Emit the current runtime value as an SMT bit-vector literal (#b...).
// Used by randomize(null) to pin a var to its existing value.
virtual void emitConcreteValue(std::ostream& s) const;
@@ -104,13 +108,32 @@ public:
count_cache[base_name] = count;
return count;
}
bool hasMatchingElements(const ArrayInfoMap& arr_vars, const std::string& base_name) const {
return arr_vars.find(base_name + "0") != arr_vars.end();
}
};
// SMT key width per associative-array level (string = 128, integral = 8 * sizeof).
template <typename T>
struct VlRandomAssocKeyWidths final {
static void push(std::vector<size_t>&) {}
};
template <typename T_Key, typename T_Value>
struct VlRandomAssocKeyWidths<VlAssocArray<T_Key, T_Value>> final {
static void push(std::vector<size_t>& widths) {
widths.push_back(std::is_same<T_Key, std::string>::value ? 128 : sizeof(T_Key) * 8);
VlRandomAssocKeyWidths<T_Value>::push(widths);
}
};
template <typename T>
class VlRandomArrayVarTemplate final : public VlRandomVar {
// Static key widths per level for the empty-array declaration fallback
const std::vector<size_t> m_fallbackIdxWidths;
public:
VlRandomArrayVarTemplate(const std::string& name, int width, void* datap, int dimension,
std::uint32_t randModeIdx)
: VlRandomVar{name, width, datap, dimension, randModeIdx} {}
std::uint32_t randModeIdx, const std::vector<size_t>& idxWidths = {})
: VlRandomVar{name, width, datap, dimension, randModeIdx}
, m_fallbackIdxWidths{idxWidths} {}
void* datap(int idx) const override {
const std::string indexed_name = name() + std::to_string(idx);
const auto it = m_arrVarsRefp->find(indexed_name);
@@ -175,7 +198,13 @@ public:
}
} else {
if (dimension() > 0) {
for (int i = 0; i < dimension(); ++i) s << "(Array (_ BitVec 32) ";
// Empty array: declare from the static key widths, not a 32-bit default.
for (int i = 0; i < dimension(); ++i) {
const size_t idxWidth = i < static_cast<int>(m_fallbackIdxWidths.size())
? m_fallbackIdxWidths[i]
: 32;
s << "(Array (_ BitVec " << idxWidth << ") ";
}
s << "(_ BitVec " << width() << ")";
for (int i = 0; i < dimension(); ++i) s << ")";
} else {
@@ -188,6 +217,20 @@ public:
const int elementCounts = countMatchingElements(*m_arrVarsRefp, name());
return width() * elementCounts;
}
void emitElement(std::ostream& s, int j) const override {
const std::string indexed_name = name() + std::to_string(j);
const auto it = m_arrVarsRefp->find(indexed_name);
// Callers take j from totalWidth(), which counts these same keys, so the element
// should always be there
if (VL_UNCOVERABLE(it == m_arrVarsRefp->end())) {
// LCOV_EXCL_START
VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
return;
// LCOV_EXCL_STOP
}
s << ' ';
emitSelect(s, it->second->m_indices, it->second->m_idxWidths);
} // LCOV_EXCL_BR_LINE
void emitExtract(std::ostream& s, int i) const override {
const int j = i / width();
i = i % width();
@@ -205,6 +248,8 @@ public:
}
};
class VlSolverSession;
//=============================================================================
// Object holding constraints and variable references.
class VlRandomizer VL_NOT_FINAL {
@@ -217,8 +262,7 @@ class VlRandomizer VL_NOT_FINAL {
std::set<std::string> m_disabledVars; // Variables with rand_mode off (skip write-back)
// variables
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
std::vector<std::string> m_unique_arrays;
std::map<std::string, uint32_t> m_unique_array_sizes;
std::vector<std::string> m_unique_arrays; // Arrays whose elements must be distinct
const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state;
const VlQueue<CData>* m_static_randmodep = nullptr; // Static rand_mode state (shared)
std::unordered_set<std::string> m_staticVars; // Names of static rand vars
@@ -230,17 +274,88 @@ class VlRandomizer VL_NOT_FINAL {
std::vector<std::pair<std::string, std::string>>
m_solveBefore; // Solve-before ordering pairs (beforeVar, afterVar)
bool m_checkOnly = false; // Set for randomize(null)
bool isFrozenVar(const std::string& name,
const VlRandomVar& var) const; // true if this var is currently frozen
bool hasFrozenVar() const; // true if any var is currently rand_mode(0)-frozen
// PRIVATE METHODS
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
bool parseSolution(std::iostream& os, bool log = false);
bool checkSat(std::iostream& os);
// Fetch the model and write it into the registered variables.
bool applyModel(VlSolverSession& sess);
bool parseModel(std::istream& is, size_t requested);
// Assert the maximal compatible soft-constraint set onto the open session.
void relaxSoftConstraints(VlSolverSession& sess);
// Indices of the "a<N>" literals named by (get-unsat-assumptions).
std::vector<int> readUnsatAssumptions(std::iostream& os);
std::vector<int> readUnsatAssumptions(VlSolverSession& sess);
void reportUnsatSetup(VlSolverSession& sess, const std::vector<std::string>& uniqueExprs);
void reportUnsatCore(VlSolverSession& sess);
void emitRandcExclusions(std::ostream& os) const; // Emit randc exclusion constraints
void recordRandcValues(); // Record solved randc values for future exclusion
size_t hashConstraints() const;
bool nextPhased(VlRNG& rngr); // Phased solving for solve...before
size_t hashConstraints(const std::vector<std::string>& extras) const;
bool nextRandomize(VlRNGReseeds& rngr, bool checkOnly);
// "(distinct ...)" expression per unique-constrained array
std::vector<std::string> buildUniqueExprs() const;
void emitDefines(std::ostream& os) const;
void emitDeclares(std::ostream& os, bool pinCurrent) const;
void emitAsserts(std::ostream& os, const std::vector<std::string>& extras, bool named) const;
bool nextFlat(VlRNG& rngr, VlSolverSession& sess, const std::vector<std::string>& uniqueExprs);
// --- UniGen2 (a near-uniform constrained randomization sampler) fields ---
// Implementation based on the following paper:
// "On Parallel Scalable Uniform SAT Witness Generation", Chakraborty et al.
using Witness = std::map<std::string, std::map<int, std::string>>; // One sampled solution
// Sample one random solution. False if a sample couldn't be produced
bool unigen2(VlRNG& rngr, VlSolverSession& sess, const std::vector<std::string>& uniqueExprs);
// Find out how finely to cut the solution space, and what cell size to accept
bool estimateParameters(VlSolverSession& sess, VlRNG& rngr);
// Collect up to `bound` different solutions of whatever is asserted right now.
// diversifyRngp adds a randomization step that spreads the solutions apart
int bsat(VlSolverSession& sess, size_t bound, std::vector<Witness>& witnesses,
VlRNG* diversifyRngp = nullptr);
// Add `bits` random XOR equations, which cut the solution space into cells holding
// roughly 1/2**bits of all the solutions
void unigenXors(std::iostream& os, VlRNG& rngr, int bits);
// Draw a cell and refill the batch of solutions from it
bool generateSamples(VlSolverSession& sess, VlRNG& rngr);
// Copy one solution into the SV variables
void writeBackWitness(const Witness& witness);
// UniGen2: sampling state
struct Unigen2State final {
std::vector<Witness> loThreshWitnesses; // Consumable batch of solutions: loThresh random
// picks out of the cell BSAT enumerated
std::vector<std::pair<std::string, int>> bsatOrder; // (var, element) query order
bool isLargeSpace = false; // Large solution space (more than 61 * 2^10 solutions)
// Below properties are what estimateParameters worked out, kept until the constraints
// change so the expensive search is not repeated on every call.
bool paramsValid = false; // Whether the values below are set
size_t paramHash = 0; // Constraint set they were computed for
int hashBits = 0; // How many XOR equations to cut the space with
int loThresh = 0; // Smallest usable cell, and how many samples to take
int hiThresh = 0; // First cell size counted as too big
uint64_t rngReseeds = 0; // rngr.reseeds() as of the last call
int lastSuccessI = -1; // Hash-bit count that worked last time, -1 if none
};
Unigen2State m_ug2;
void solveDiversity(VlRNG& rngr, VlSolverSession& sess);
void solveDiversityPins(VlRNG& rngr, VlSolverSession& sess);
void solveDiversityXor(VlRNG& rngr, VlSolverSession& sess);
// Layers of solve...before variables in dependency order
bool buildSolveLayers(std::vector<std::vector<std::string>>& layersr);
const char* phasedLogic() const;
bool nextPhased(VlRNG& rngr, VlSolverSession& sess,
const std::vector<std::string>& uniqueExprs);
bool solvePhases(VlRNG& rngr, VlSolverSession& sess,
const std::vector<std::vector<std::string>>& layers,
const std::vector<std::string>& uniqueExprs, bool& exhaustedr);
bool solvePhaseValues(VlSolverSession& sess, VlRNG& rngr,
const std::vector<std::string>& layerVars,
std::map<std::string, std::string>& solvedValuesr);
bool readPhaseValues(VlSolverSession& sess, std::map<std::string, std::string>& solvedValuesr);
bool parsePhaseValues(std::istream& is, std::map<std::string, std::string>& solvedValuesr);
public:
// CONSTRUCTORS
@@ -249,10 +364,10 @@ public:
// METHODS
// Finds the next solution satisfying the constraints
bool next(VlRNG& rngr);
bool next(VlRNGReseeds& rngr);
// Validate the constraints against the current runtime values of every
// registered rand variable without picking new ones.
bool next_check_only(VlRNG& rngr);
bool next_check_only(VlRNGReseeds& rngr);
// --- Process the key for associative array ---
@@ -377,12 +492,12 @@ public:
}
// Register queue of non-struct types
template <typename T>
template <typename T, size_t N_MaxSize>
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
write_var(VlQueue<T>& var, int width, const char* name, int dimension,
write_var(VlQueue<T, N_MaxSize>& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (m_vars.find(name) == m_vars.end()) {
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T>>>(
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T, N_MaxSize>>>(
name, width, &var, dimension, randmodeIdx);
}
if (dimension > 0) {
@@ -394,9 +509,9 @@ public:
}
// Register queue of structs
template <typename T>
template <typename T, size_t N_MaxSize>
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
write_var(VlQueue<T>& var, int width, const char* name, int dimension,
write_var(VlQueue<T, N_MaxSize>& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
}
@@ -433,9 +548,11 @@ public:
write_var(VlAssocArray<T_Key, T_Value>& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (m_vars.find(name) == m_vars.end()) {
std::vector<size_t> keyWidths;
VlRandomAssocKeyWidths<VlAssocArray<T_Key, T_Value>>::push(keyWidths);
m_vars[name]
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
name, width, &var, dimension, randmodeIdx);
name, width, &var, dimension, randmodeIdx, keyWidths);
}
if (dimension > 0) {
m_index = 0;
@@ -465,11 +582,10 @@ public:
++m_index;
}
// This is the "Sender" API for the generated code
void rand_unique(const std::string& name, uint32_t size) {
m_unique_arrays.push_back(name);
m_unique_array_sizes[name] = size;
}
// This is the "Sender" API for the generated code.
// The elements to make distinct are taken from the array element table at
// solve time, so a container resized by the solver is handled correctly.
void rand_unique(const std::string& name) { m_unique_arrays.push_back(name); }
// Recursively record all elements in an unpacked array
template <typename T, std::size_t N_Depth>
@@ -488,8 +604,8 @@ public:
}
// Recursively record all elements in a queue
template <typename T>
void record_arr_table(VlQueue<T>& var, const std::string& name, int dimension,
template <typename T, size_t N_MaxSize>
void record_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
std::vector<IData> indices, std::vector<size_t> idxWidths) {
if ((dimension > 0) && (var.size() != 0)) {
idxWidths.push_back(32);
@@ -522,7 +638,8 @@ public:
idxWidths.push_back(idx_width);
indices.insert(indices.end(), integral_index.begin(), integral_index.end());
record_arr_table(var.at(key), indexed_name, dimension - 1, indices, idxWidths);
record_arr_table(var.atWrite(key), indexed_name, dimension - 1, indices,
idxWidths);
// Cleanup indices and widths
idxWidths.pop_back();
@@ -562,8 +679,8 @@ public:
}
// Recursively process VlQueue of structs
template <typename T>
void record_struct_arr(VlQueue<T>& var, const std::string& name, int dimension,
template <typename T, size_t N_MaxSize>
void record_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
std::vector<IData> indices, std::vector<size_t> idxWidths) {
if ((dimension > 0) && (var.size() != 0)) {
idxWidths.push_back(32);
@@ -596,7 +713,7 @@ public:
std::string result = oss.str();
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
record_struct_arr(var.at(key), name + "." + result, dimension - 1, indices,
record_struct_arr(var.atWrite(key), name + "." + result, dimension - 1, indices,
idxWidths);
}
}
@@ -666,7 +783,7 @@ public:
// Light wrapper for RNG used by std::randomize() to support scope-level randomization.
class VlStdRandomizer final : public VlRandomizer {
// MEMBERS
VlRNG m_rng; // Random number generator
VlRNGReseeds m_rng; // Random number generator
public:
// CONSTRUCTORS
@@ -724,11 +841,20 @@ public:
bool basicStdRandomization(VlAssocArray<T_Key, T_Value>& value, size_t width) {
T_Key key;
for (int exists = value.first(key); exists; exists = value.next(key)) {
basicStdRandomization(value.at(key), width);
basicStdRandomization(value.atWrite(key), width);
}
return true;
}
bool next() { return VlRandomizer::next(m_rng); }
};
//======================================================================
//Helper method for dynamic array handling in SMT expressions
inline std::string vlToSolverHex(const IData& value) {
std::ostringstream oss;
oss << std::hex << std::setfill('0') << std::setw(8) << value;
return oss.str();
}
#endif // Guard
+1 -1
View File
@@ -327,7 +327,7 @@ VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VlAssocArray<T_Key, T
T_Value value;
os >> index;
os >> value;
rhs.at(index) = value;
rhs.atWrite(index) = value;
}
return os;
}
+2 -1
View File
@@ -196,7 +196,8 @@ package std;
static task killQueue(ref process processQueue[$]);
`ifdef VERILATOR_TIMING
repeat (processQueue.size()) begin
processQueue.pop_front().kill();
process p = processQueue.pop_front();
if (p) p.kill();
end
`endif
endtask
+9 -11
View File
@@ -284,13 +284,13 @@ void VlDynamicTriggerScheduler::dump() const {
//======================================================================
// VlForkSync:: Methods
void VlProcess::forkSyncOnKill(VlForkSyncState* forkSyncp) {
void VlProcess::forkSyncOnKill(std::shared_ptr<VlForkSyncState> forkSyncp) {
m_forkSyncOnKillp = forkSyncp;
m_forkSyncOnKillDone = false;
}
void VlProcess::forkSyncOnKillClear(VlForkSyncState* forkSyncp) {
if (m_forkSyncOnKillp != forkSyncp) return;
if (m_forkSyncOnKillp.get() != forkSyncp) return;
m_forkSyncOnKillp = nullptr;
m_forkSyncOnKillDone = false;
}
@@ -301,19 +301,15 @@ void VlProcess::state(int s) {
m_forkSyncOnKillDone = true;
m_state = s;
m_forkSyncOnKillp->done();
m_forkSyncOnKillp = nullptr;
return;
}
m_state = s;
}
VlForkSyncState::~VlForkSyncState() {
for (const VlProcessRef& processp : m_onKillProcessps) processp->forkSyncOnKillClear(this);
}
void VlForkSync::onKill(VlProcessRef process) {
if (!process) return;
m_state->m_onKillProcessps.emplace_back(process);
process->forkSyncOnKill(m_state.get());
process->forkSyncOnKill(m_state);
}
void VlForkSyncState::done(const char* filename, int lineno) {
@@ -337,16 +333,18 @@ void VlForkSyncState::done(const char* filename, int lineno) {
}
//======================================================================
// VlCoroutine:: Methods
// VlPromise:: Methods
VlCoroutine::VlPromise::~VlPromise() {
VlCoroutine VlPromise::get_return_object() { return {this}; }
VlPromise::~VlPromise() {
// Indicate to the return object that the coroutine has finished or been destroyed
if (m_corop) m_corop->m_promisep = nullptr;
// If there is a continuation, destroy it
if (m_continuation) m_continuation.destroy();
}
std::suspend_never VlCoroutine::VlPromise::final_suspend() noexcept {
std::suspend_never VlPromise::final_suspend() noexcept {
// Indicate to the return object that the coroutine has finished
if (m_corop) {
m_corop->m_promisep = nullptr;
+69 -44
View File
@@ -99,11 +99,34 @@ public:
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
// suspended, another VlCoroutineHandle was created to manage it.
class VlCoroutine;
struct VlPromise final {
std::coroutine_handle<VlPromise>
m_continuation; // Coroutine to resume after this one finishes
VlCoroutine* m_corop = nullptr; // Pointer to the coroutine return object
bool m_forever = false; // Coroutine suspended forever
~VlPromise();
VlCoroutine get_return_object();
// Never suspend at the start of the coroutine
std::suspend_never initial_suspend() const { return {}; }
// Never suspend at the end of the coroutine (thanks to this, the coroutine will clean up
// after itself)
std::suspend_never final_suspend() noexcept;
void unhandled_exception() const { std::abort(); } // LCOV_EXCL_LINE
void return_void() const {}
};
class VlCoroutineHandle final {
VL_UNCOPYABLE(VlCoroutineHandle);
// MEMBERS
std::coroutine_handle<> m_coro; // The wrapped coroutine handle
std::coroutine_handle<VlPromise> m_coro; // The wrapped coroutine handle
VlProcessRef m_process; // Data of the suspended process, null if not needed
VlFileLineDebug m_fileline;
@@ -117,7 +140,8 @@ public:
, m_process{process} {
if (m_process) m_process->state(VlProcess::WAITING);
}
VlCoroutineHandle(std::coroutine_handle<> coro, VlProcessRef process, VlFileLineDebug fileline)
VlCoroutineHandle(std::coroutine_handle<VlPromise> coro, VlProcessRef process,
VlFileLineDebug fileline)
: m_coro{coro}
, m_process{process}
, m_fileline{fileline} {
@@ -135,10 +159,11 @@ public:
// Usually these coroutines should get resumed; we only need to clean up if we destroy a
// model with some coroutines suspended
if (VL_UNLIKELY(m_coro)) {
m_coro.destroy();
if (m_process && m_process->state() != VlProcess::KILLED) {
if (m_process && m_process->state() != VlProcess::KILLED
&& !m_coro.promise().m_forever) {
m_process->state(VlProcess::FINISHED);
}
m_coro.destroy();
}
}
// METHODS
@@ -173,7 +198,13 @@ class VlDelayScheduler final {
std::vector<VlCoroutineHandle> m_zeroDelayed; // Coroutines waiting for #0
// Coroutines that waited for #0 and are being resumed now. As member to avoid reallocations
std::vector<VlCoroutineHandle> m_zeroDelayesSwap;
std::vector<VlCoroutineHandle>
m_forevered; // Coroutines, that called wait(0), and are to be freed.
// Their callers are freed recursively through m_continuation.
//
// NOTE: it would be possible to clean up forevered coroutines immedietely without storing
// them, but it involves a lot of nasty edge cases. Defering cleanup to end of eval step
// simplifies logic a lot by ensuring that whole "coroutine stack" was already suspended.
public:
// CONSTRUCTORS
explicit VlDelayScheduler(VerilatedContext& context)
@@ -195,6 +226,7 @@ public:
}
// Are there coroutines to resume in the inactive region after a #0 delay?
bool awaitingZeroDelay() const { return !m_context.gotFinish() && !m_zeroDelayed.empty(); }
void cleanupForevered() { m_forevered.clear(); };
#ifdef VL_DEBUG
void dump() const;
#endif
@@ -210,7 +242,7 @@ public:
const VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
void await_suspend(std::coroutine_handle<VlPromise> coro) {
// Both active delays and fork..join_none #0 are resumed out of the time queue.
if (phase != VlDelayPhase::INACTIVE) {
queue.emplace(delay, VlCoroutineHandle{coro, process, fileline});
@@ -233,6 +265,26 @@ public:
m_zeroDelayed, m_context.time() + delay,
phase, VlFileLineDebug{filename, lineno}};
}
// Helper awaitable func for suspending coroutines forever.
// Used for constant wait statements.
auto waitForever(VlProcessRef process, const char* filename = VL_UNKNOWN, int lineno = 0) {
VL_DEBUG_IF(
VL_DBG_MSGF(" Awaiting join of fork at: %s:%d\n", filename, lineno););
struct Awaitable final {
VlProcessRef process; // Data of the suspended process, null if not needed
std::vector<VlCoroutineHandle>& forevered;
VlFileLineDebug fileline;
bool await_ready() { return false; }
void await_suspend(std::coroutine_handle<VlPromise> coro) {
coro.promise().m_forever = true;
forevered.emplace_back(VlCoroutineHandle{coro, process, fileline});
}
void await_resume() const {} // LCOV_EXCL_LINE
};
return Awaitable{process, m_forevered, VlFileLineDebug{filename, lineno}};
}
};
//=============================================================================
@@ -280,7 +332,7 @@ public:
VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
void await_suspend(std::coroutine_handle<VlPromise> coro) {
suspended.emplace_back(coro, process, fileline);
}
void await_resume() const {}
@@ -327,7 +379,7 @@ class VlDynamicTriggerScheduler final {
VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
void await_suspend(std::coroutine_handle<VlPromise> coro) {
suspended.emplace_back(coro, process, fileline);
}
void await_resume() const {}
@@ -371,16 +423,6 @@ public:
}
};
//=============================================================================
// VlForever is a helper awaitable type for suspending coroutines forever. Used for constant
// wait statements.
struct VlForever final {
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) const { coro.destroy(); }
void await_resume() const {}
};
//=============================================================================
// VlForkSync is used to manage fork..join and fork..join_any constructs.
@@ -393,11 +435,9 @@ public:
size_t m_pendingDones = 0; // done() calls seen before init() (e.g. early killed branch)
bool m_inDone = false; // Guard against re-entrant resume recursion from nested kills
bool m_resumePending = false; // Join reached zero again while inside done()
std::vector<VlProcessRef> m_onKillProcessps; // Branches registered for kill hooks
VlForkSyncState() // Construct with a null coroutine handle
: m_susp{VlProcessRef{}} {}
~VlForkSyncState();
void done(const char* filename = VL_UNKNOWN, int lineno = 0);
};
@@ -431,7 +471,7 @@ public:
VlFileLineDebug fileline;
bool await_ready() { return state->m_counter == 0; } // Suspend if join still exists
void await_suspend(std::coroutine_handle<> coro) {
void await_suspend(std::coroutine_handle<VlPromise> coro) {
state->m_susp = {coro, process, fileline};
}
void await_resume() const {}
@@ -445,31 +485,10 @@ public:
// Return value of a coroutine. Used for chaining coroutine suspension/resumption.
class VlCoroutine final {
private:
// TYPES
struct VlPromise final {
std::coroutine_handle<> m_continuation; // Coroutine to resume after this one finishes
VlCoroutine* m_corop = nullptr; // Pointer to the coroutine return object
~VlPromise();
VlCoroutine get_return_object() { return {this}; }
// Never suspend at the start of the coroutine
std::suspend_never initial_suspend() const { return {}; }
// Never suspend at the end of the coroutine (thanks to this, the coroutine will clean up
// after itself)
std::suspend_never final_suspend() noexcept;
void unhandled_exception() const { std::abort(); }
void return_void() const {}
};
public:
// MEMBERS
VlPromise* m_promisep; // The promise created for this coroutine
public:
// TYPES
using promise_type = VlPromise; // promise_type has to be public
@@ -495,7 +514,13 @@ public:
// Suspend the awaiter if the coroutine is suspended (the promise exists)
bool await_ready() const noexcept { return !m_promisep; }
// Set the awaiting coroutine as the continuation of the current coroutine
void await_suspend(std::coroutine_handle<> coro) { m_promisep->m_continuation = coro; }
// If the current coroutine is suspended forever, propagate that to the awaiting coroutine
// In that case, m_continuation will be used for cleaning up the whole suspended coroutine
// stack
void await_suspend(std::coroutine_handle<VlPromise> awaiting_coro) {
if (m_promisep->m_forever) awaiting_coro.promise().m_forever = true;
m_promisep->m_continuation = awaiting_coro;
}
void await_resume() const noexcept {}
};
+4 -4
View File
@@ -449,7 +449,7 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::initLib(const std::string& name) VL_MT_
// All of these take a destination pointer where the string will be emitted,
// and a value to convert. There are a couple of variants for efficiency.
static inline void cvtCDataToStr(char* dstp, CData value) {
inline void cvtCDataToStr(char* dstp, CData value) {
#ifdef VL_HAVE_SSE2
// Similar to cvtSDataToStr but only the bottom 8 byte lanes are used
const __m128i a = _mm_cvtsi32_si128(value);
@@ -471,7 +471,7 @@ static inline void cvtCDataToStr(char* dstp, CData value) {
#endif
}
static inline void cvtSDataToStr(char* dstp, SData value) {
inline void cvtSDataToStr(char* dstp, SData value) {
#ifdef VL_HAVE_SSE2
// We want each bit in the 16-bit input value to end up in a byte lane
// within the 128-bit XMM register. Note that x86 is little-endian and we
@@ -507,7 +507,7 @@ static inline void cvtSDataToStr(char* dstp, SData value) {
#endif
}
static inline void cvtIDataToStr(char* dstp, IData value) {
inline void cvtIDataToStr(char* dstp, IData value) {
#ifdef VL_HAVE_AVX2
// Similar to cvtSDataToStr but the bottom 16-bits are processed in the
// top half of the YMM registers
@@ -526,7 +526,7 @@ static inline void cvtIDataToStr(char* dstp, IData value) {
#endif
}
static inline void cvtQDataToStr(char* dstp, QData value) {
inline void cvtQDataToStr(char* dstp, QData value) {
cvtIDataToStr(dstp, value >> 32);
cvtIDataToStr(dstp + 32, value);
}
+46 -14
View File
@@ -214,7 +214,7 @@ public:
};
static_assert(sizeof(WDataInP) == sizeof(EData*), "WDataInP should be a single pointer");
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
template <std::size_t N_Words>
bool VlWide<N_Words>::operator<(const VlWide<N_Words>& rhs) const VL_PURE {
@@ -276,7 +276,7 @@ constexpr IData VL_CLOG2_CE_Q(QData lhs) VL_PURE {
// Random
// Random Number Generator with internal state
class VlRNG final {
class VlRNG VL_NOT_FINAL {
std::array<uint64_t, 2> m_state;
public:
@@ -295,6 +295,22 @@ public:
static VlRNG& vl_thread_rng() VL_MT_SAFE;
};
// VlRNG that also counts how often it was reseeded, for randomize() to notice.
class VlRNGReseeds final : public VlRNG {
uint64_t m_reseeds = 0; // Times the state was set from outside
public:
void srandom(uint64_t n) VL_MT_UNSAFE {
VlRNG::srandom(n);
++m_reseeds;
}
void set_randstate(const std::string& state) VL_MT_UNSAFE {
VlRNG::set_randstate(state);
++m_reseeds;
}
uint64_t reseeds() const VL_MT_UNSAFE { return m_reseeds; }
};
//===================================================================
// Metadata of processes
using VlProcessRef = std::shared_ptr<VlProcess>;
@@ -306,7 +322,7 @@ class VlProcess final {
int m_state; // Current state of the process
VlProcessRef m_parentp = nullptr; // Parent process, if exists
std::set<VlProcess*> m_children; // Active child processes
VlForkSyncState* m_forkSyncOnKillp
std::shared_ptr<VlForkSyncState> m_forkSyncOnKillp
= nullptr; // Optional fork..join counter to decrement on kill
bool m_forkSyncOnKillDone = false; // Ensure on-kill callback fires only once
VlRNG m_rng; // Per-process RNG (IEEE 1800-2023 18.14)
@@ -337,6 +353,7 @@ public:
~VlProcess() {
if (m_parentp) m_parentp->detach(this);
if (t_currentp == this) t_currentp = m_parentp.get();
}
void attach(VlProcess* childp) { m_children.insert(childp); }
@@ -347,13 +364,14 @@ public:
void disable() {
state(KILLED);
disableFork();
m_forkSyncOnKillp = nullptr;
}
void disableFork() {
// childp->disable() may resume coroutines and mutate m_children
const std::set<VlProcess*> children = m_children;
for (VlProcess* childp : children) childp->disable();
}
void forkSyncOnKill(VlForkSyncState* forkSyncp);
void forkSyncOnKill(std::shared_ptr<VlForkSyncState> forkSyncp);
void forkSyncOnKillClear(VlForkSyncState* forkSyncp);
bool completed() const { return state() == FINISHED || state() == KILLED; }
bool completedFork() const {
@@ -931,7 +949,7 @@ public:
VlQueue min(T_Func with_func) const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::min_element(m_deque.cbegin(), m_deque.cend(),
[&with_func](const IData& a, const IData& b) {
[&with_func](const T_Value& a, const T_Value& b) {
return with_func(0, a) < with_func(0, b);
});
return VlQueue::consV(*it);
@@ -945,7 +963,7 @@ public:
VlQueue max(T_Func with_func) const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::max_element(m_deque.cbegin(), m_deque.cend(),
[&with_func](const IData& a, const IData& b) {
[&with_func](const T_Value& a, const T_Value& b) {
return with_func(0, a) < with_func(0, b);
});
return VlQueue::consV(*it);
@@ -1119,9 +1137,7 @@ public:
return 1;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(const T_Key& index) {
T_Value& atWrite(const T_Key& index) {
const auto it = m_map.find(index);
if (it == m_map.end()) {
std::pair<typename Map::iterator, bool> pit = m_map.emplace(index, m_defaultValue);
@@ -1137,7 +1153,7 @@ public:
}
// Setting as a chained operation
VlAssocArray& set(const T_Key& index, const T_Value& value) {
at(index) = value;
atWrite(index) = value;
return *this;
}
VlAssocArray& setDefault(const T_Value& value) {
@@ -1394,7 +1410,7 @@ void VL_READMEM_N(bool hex, int bits, const std::string& filename,
QData addr;
std::string data;
if (rmem.get(addr /*ref*/, data /*ref*/)) {
rmem.setData(&(obj.at(addr)), data);
rmem.setData(&(obj.atWrite(addr)), data);
} else {
break;
}
@@ -2069,8 +2085,24 @@ struct VlNull final {
operator T*() const {
return nullptr;
}
template <class T>
bool operator==(T* rhs) const {
return !rhs;
}
template <class T>
bool operator==(const T* rhs) const {
return !rhs;
}
};
inline bool operator==(const void* ptr, VlNull) { return !ptr; }
template <class T>
inline bool operator==(T* lhs, VlNull) {
return !lhs;
}
template <class T>
inline bool operator==(const T* lhs, VlNull) {
return !lhs;
}
//===================================================================
// Verilog class reference container
@@ -2223,7 +2255,7 @@ public:
};
template <typename T_Lhs, typename T_Out>
static inline bool VL_CAST_DYNAMIC(VlClassRef<T_Lhs> in, VlClassRef<T_Out>& outr) {
inline bool VL_CAST_DYNAMIC(VlClassRef<T_Lhs> in, VlClassRef<T_Out>& outr) {
if (!in) {
outr = VlNull{};
return true;
@@ -2237,7 +2269,7 @@ static inline bool VL_CAST_DYNAMIC(VlClassRef<T_Lhs> in, VlClassRef<T_Out>& outr
}
template <typename T_Lhs>
static inline bool VL_CAST_DYNAMIC(VlNull, VlClassRef<T_Lhs>& outr) {
inline bool VL_CAST_DYNAMIC(VlNull, VlClassRef<T_Lhs>& outr) {
outr = VlNull{};
return true;
}
+119 -41
View File
@@ -539,7 +539,7 @@ public:
for (auto idx : index()) m_fullname += "[" + std::to_string(idx) + "]";
return m_fullname.c_str();
}
void* prevDatap() const { return m_prevDatap; }
uint8_t* prevDatap() const { return m_prevDatap; }
void* varDatap() const override { return m_varDatap; }
void createPrevDatap() {
if (VL_UNLIKELY(!m_prevDatap)) {
@@ -1027,6 +1027,26 @@ struct VerilatedVpiTimedCbsCmp final {
class VerilatedVpiError;
void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset);
// Information about how to access packed array data.
// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word
// boundaries, in which case m_maskHi != 0.
template <typename T>
struct VarAccessInfo final {
T* m_datap; // Typed pointer to packed array base address
size_t m_bitOffset; // Data start location (bit offset)
size_t m_wordOffset; // Data start location (word offset, VLVT_WDATA only)
T m_maskLo; // Access mask for m_datap[m_wordOffset]
T m_maskHi; // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only)
};
template <typename T>
VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount,
size_t addOffset);
template <typename T>
T vl_vpi_get_word_gen(VarAccessInfo<T> info);
template <typename T>
void vl_vpi_put_word_gen(VarAccessInfo<T> info, T word);
class VerilatedVpiImp final {
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maximum callback reason
using VpioCbList = std::list<VerilatedVpiCbHolder>;
@@ -1169,6 +1189,72 @@ public:
s().m_cbCallList.clear();
return called;
}
template <typename T>
static bool valueDiffersFromPrev(VerilatedVpioVar* varop) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p size=%d\n",
varop->fullname(), *(static_cast<CData*>(varop->varDatap())),
*(varop->prevDatap()), varop->varDatap(), varop->prevDatap(),
varop->entSize()););
if (varop->bitSize() == 1) {
T* const prevDatap = reinterpret_cast<T*>(
varop->prevDatap()); // Was malloced when we added the callback
const VarAccessInfo<T> currInfo
= vl_vpi_var_access_info<T>(varop, varop->bitSize(), 0);
VarAccessInfo<T> prevInfo = currInfo;
prevInfo.m_datap = prevDatap;
return vl_vpi_get_word_gen(currInfo) != vl_vpi_get_word_gen(prevInfo);
}
return std::memcmp(varop->prevDatap(), varop->varDatap(), varop->entSize()) != 0;
}
static bool valueDiffersFromPrev(VerilatedVpioVar* varop) {
switch (varop->varp()->vltype()) {
case VLVT_UINT8: return valueDiffersFromPrev<CData>(varop);
case VLVT_UINT16: return valueDiffersFromPrev<SData>(varop);
case VLVT_UINT32: return valueDiffersFromPrev<IData>(varop);
case VLVT_UINT64: return valueDiffersFromPrev<QData>(varop);
case VLVT_WDATA:
return valueDiffersFromPrev<EData>(varop);
// LCOV_EXCL_START - Would require earlier type check to not catch that
default:
const std::string msg
= "Unsupported type (" + std::to_string(varop->varp()->vltype()) + ")";
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
return true;
// LCOV_EXCL_STOP
}
}
template <typename T>
static void updatePrev(const VerilatedVpioVar* const varop) {
if (varop->bitSize() == 1) {
const VarAccessInfo<T> currInfo
= vl_vpi_var_access_info<T>(varop, varop->bitSize(), 0);
VarAccessInfo<T> prevInfo = currInfo;
T* const prevDatap = reinterpret_cast<T*>(varop->prevDatap());
prevInfo.m_datap = prevDatap;
const T currWord = vl_vpi_get_word_gen(currInfo);
vl_vpi_put_word_gen(prevInfo, currWord);
assert(std::memcmp(varop->prevDatap(), varop->varDatap(), varop->entSize()) == 0);
} else {
std::memcpy(varop->prevDatap(), varop->varDatap(), varop->entSize());
}
}
static void updatePrev(const VerilatedVpioVar* const varop) {
switch (varop->varp()->vltype()) {
case VLVT_UINT8: updatePrev<CData>(varop); break;
case VLVT_UINT16: updatePrev<SData>(varop); break;
case VLVT_UINT32: updatePrev<IData>(varop); break;
case VLVT_UINT64: updatePrev<QData>(varop); break;
case VLVT_WDATA:
updatePrev<EData>(varop);
break;
// LCOV_EXCL_START - Would require earlier type check to not catch that
default:
const std::string msg
= "Unsupported type (" + std::to_string(varop->varp()->vltype()) + ")";
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
// LCOV_EXCL_STOP
}
}
static bool callValueCbs() VL_MT_UNSAFE_ONE {
assertOneCheck();
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
@@ -1187,15 +1273,10 @@ public:
VerilatedVpiCbHolder& ho = *it++;
VerilatedVpioVar* const varop
= reinterpret_cast<VerilatedVpioVar*>(ho.cb_datap()->obj);
void* const newDatap = varop->varDatap();
void* const prevDatap = varop->prevDatap(); // Was malloced when we added the callback
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
varop->fullname(), *(static_cast<CData*>(newDatap)),
*(static_cast<CData*>(prevDatap)), newDatap, prevDatap););
if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
if (valueDiffersFromPrev(varop)) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
ho.id(), varop->fullname(),
*(static_cast<CData*>(newDatap))););
*(static_cast<CData*>(varop->varDatap()))););
update.insert(varop);
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
(ho.cb_rtnp())(ho.cb_datap());
@@ -1203,9 +1284,7 @@ public:
}
if (was_last) break;
}
for (const VerilatedVpioVar* const ip : update) {
std::memcpy(ip->prevDatap(), ip->varDatap(), ip->entSize());
}
for (const VerilatedVpioVar* const varop : update) updatePrev(varop);
return called;
}
static void dumpCbs() VL_MT_UNSAFE_ONE;
@@ -2410,7 +2489,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
return vop->castVpiHandle();
}
default:
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported callback type %s", __func__,
VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported callback type '%s'", __func__,
VerilatedVpiError::strFromVpiCallbackReason(reason));
return nullptr;
}
@@ -2856,7 +2935,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
}
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
"%s: Unsupported vpiHandle '%p' for type '%s', nothing will be returned",
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
return nullptr;
}
@@ -2870,7 +2949,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
}
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
"%s: Unsupported vpiHandle '%p' for type '%s', nothing will be returned",
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
return nullptr;
}
@@ -3031,8 +3110,9 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
[[fallthrough]];
}
default:
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
__func__, VerilatedVpiError::strFromVpiProp(property));
VL_VPI_ERROR_(__FILE__, __LINE__,
"%s: Unsupported property '%s', nothing will be returned", __func__,
VerilatedVpiError::strFromVpiProp(property));
return vpiUndefined;
}
}
@@ -3195,18 +3275,6 @@ static void vl_strprintf(std::string& buffer, char const* fmt, ...) {
va_end(args);
}
// Information about how to access packed array data.
// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word
// boundaries, in which case m_maskHi != 0.
template <typename T>
struct VarAccessInfo final {
T* m_datap; // Typed pointer to packed array base address
size_t m_bitOffset; // Data start location (bit offset)
size_t m_wordOffset; // Data start location (word offset, VLVT_WDATA only)
T m_maskLo; // Access mask for m_datap[m_wordOffset]
T m_maskHi; // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only)
};
template <typename T>
VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount,
size_t addOffset) {
@@ -3262,21 +3330,25 @@ VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t
}
template <typename T>
T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
T vl_vpi_get_word_gen(VarAccessInfo<T> info) {
const size_t wordBits = sizeof(T) * 8;
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
if (info.m_maskHi)
if (info.m_maskHi) {
return ((info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset)
| ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi)
<< (wordBits - info.m_bitOffset));
}
return (info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset;
}
template <typename T>
void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) {
const size_t wordBits = sizeof(T) * 8;
T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
return vl_vpi_get_word_gen(info);
}
template <typename T>
void vl_vpi_put_word_gen(VarAccessInfo<T> info, T word) {
const size_t wordBits = sizeof(T) * 8;
if (info.m_maskHi) {
info.m_datap[info.m_wordOffset + 1]
= (info.m_datap[info.m_wordOffset + 1] & ~info.m_maskHi)
@@ -3287,6 +3359,12 @@ void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, s
| ((word << info.m_bitOffset) & info.m_maskLo);
}
template <typename T>
void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) {
const VarAccessInfo<T> info = vl_vpi_var_access_info<T>(vop, bitCount, addOffset);
vl_vpi_put_word_gen(info, word);
}
// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds
// addOffset: additional read bitoffset
QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
@@ -3500,7 +3578,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
return;
}
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle '%p'", __func__, object);
}
vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/,
@@ -3841,7 +3919,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
__func__, vop->fullname());
return nullptr;
}
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle '%p'", __func__, object);
return nullptr;
}
@@ -4236,13 +4314,13 @@ void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (VL_UNLIKELY(!vop)) {
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle '%p'", __func__, object);
return;
}
if (vop->type() != vpiRegArray) {
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
VerilatedVpiError::strFromVpiObjType(vop->type()));
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type '%s' for '%s'", __func__,
VerilatedVpiError::strFromVpiObjType(vop->type()), vop->name());
return;
}
@@ -4418,13 +4496,13 @@ void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (VL_UNLIKELY(!vop)) {
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle '%p'", __func__, object);
return;
}
if (vop->type() != vpiRegArray) {
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
VerilatedVpiError::strFromVpiObjType(vop->type()));
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle type '%s' for '%s'", __func__,
VerilatedVpiError::strFromVpiObjType(vop->type()), vop->name());
return;
}
+17 -4
View File
@@ -332,10 +332,19 @@
#ifdef VL_GCOV
extern "C" void __gcov_dump();
extern "C" void __gcov_reset();
// Dump internal code coverage data before e.g. std::abort()
# define VL_GCOV_DUMP() __gcov_dump()
// Dump, then re-arm dumping; dumping is one-shot, so without the reset a dump
// on a nonfatal path would silently discard everything counted after it
# define VL_GCOV_DUMP_RESET() \
do { \
__gcov_dump(); \
__gcov_reset(); \
} while (false)
#else
# define VL_GCOV_DUMP()
# define VL_GCOV_DUMP_RESET()
#endif
//=========================================================================
@@ -368,6 +377,7 @@ extern "C" void __gcov_dump();
#define __STDC_FORMAT_MACROS
// Now that C++ requires these standard types the vl types are deprecated
#include <cstddef> // offsetof (used by generated VlVarTableEntry tables)
#include <cstdint>
#include <cinttypes>
#include <cmath>
@@ -460,6 +470,7 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// Integer size macros
#define VL_BYTESIZE 8 ///< Bits in a CData / byte
#define VL_BYTESIZE_LOG2 3 ///< log2(VL_BYTESIZE)
#define VL_SHORTSIZE 16 ///< Bits in a SData / short
#define VL_IDATASIZE 32 ///< Bits in an IData / word
#define VL_QUADSIZE 64 ///< Bits in a QData / quadword
@@ -472,9 +483,9 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#endif
/// Return number of bytes argument-number of bits needs (1 bit=1 byte)
#define VL_BYTES_I(nbits) (((nbits) + (VL_BYTESIZE - 1)) / VL_BYTESIZE)
#define VL_BYTES_I(nbits) (((nbits) + (VL_BYTESIZE - 1)) >> VL_BYTESIZE_LOG2)
/// Return Words/EDatas in argument-number of bits needs (1 bit=1 word)
#define VL_WORDS_I(nbits) (((nbits) + (VL_EDATASIZE - 1)) / VL_EDATASIZE)
#define VL_WORDS_I(nbits) (((nbits) + (VL_EDATASIZE - 1)) >> VL_EDATASIZE_LOG2)
// Number of Words/EDatas a quad requires
#define VL_WQ_WORDS_E VL_WORDS_I(VL_QUADSIZE)
@@ -536,10 +547,10 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// #defines, to avoid requiring math.h on all compile runs
#ifdef _MSC_VER
static inline double VL_TRUNC(double n) {
inline double VL_TRUNC(double n) {
return (n < 0) ? std::ceil(n) : std::floor(n);
}
static inline double VL_ROUND(double n) {
inline double VL_ROUND(double n) {
return (n < 0) ? std::ceil(n-0.5) : std::floor(n + 0.5);
}
#else
@@ -608,6 +619,8 @@ static inline double VL_ROUND(double n) {
# define VL_CPU_RELAX() asm volatile("lr 0,0" ::: "memory")
#elif defined(__sparc__)
# define VL_CPU_RELAX() asm volatile("rd %%ccr, %%g0" ::: "memory")
#elif defined(__wasm__) // Wasm has no spin-wait hint, and no inline asm
# define VL_CPU_RELAX()
#elif defined(VL_IGNORE_UNKNOWN_ARCH)
# define VL_CPU_RELAX()
#else
+7 -5
View File
@@ -65,7 +65,7 @@ double DeltaCpuTime::gettime() VL_MT_SAFE {
| static_cast<uint64_t>(lpUserTime.dwHighDateTime) << 32ULL)
* 1e-7;
}
#else
#elif !defined(__wasi__) // WASI has no CLOCK_PROCESS_CPUTIME_ID
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
timespec ts;
if (0 == clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)) // MT-Safe // LCOV_EXCL_BR_LINE
@@ -169,17 +169,19 @@ void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
}
#else
// Highly unportable. Sorry
// Use VmHWM (peak resident), matching Windows PeakWorkingSetSize and macOS resident_size_max.
// VmHWM excludes pages swapped out before the peak; /proc has no peak-(RSS+Swap) counter.
std::ifstream is{"/proc/self/status"};
if (!is) return;
std::string line;
uint64_t vmPeak = 0;
uint64_t vmHwm = 0;
uint64_t vmRss = 0;
uint64_t vmSwap = 0;
std::string field;
while (std::getline(is, line)) {
if (line.rfind("VmPeak:", 0) == 0) {
if (line.rfind("VmHWM:", 0) == 0) {
std::stringstream ss{line};
ss >> field >> vmPeak;
ss >> field >> vmHwm;
} else if (line.rfind("VmRSS:", 0) == 0) {
std::stringstream ss{line};
ss >> field >> vmRss;
@@ -188,7 +190,7 @@ void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
ss >> field >> vmSwap;
}
}
peakr = vmPeak * 1024;
peakr = vmHwm * 1024;
currentr = (vmRss + vmSwap) * 1024;
#endif
}
+2
View File
@@ -12,6 +12,8 @@
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: CC0-1.0
# These flags tested against verible-v0.0-4080-ga0a8d8eb
verible-verilog-format \
--inplace \
--wrap_end_else_clauses \
+1 -1
View File
@@ -33,4 +33,4 @@ sphinxcontrib-spelling==8.0.2
yamlfix==1.19.1
yapf==0.43.0
git+https://github.com/antmicro/astsee.git
git+https://github.com/antmicro/astsee.git@222480a8ec13b312809ea4acc08c81b4c0f4da2f
+7 -2
View File
@@ -62,6 +62,7 @@ set(HEADERS
V3Cast.h
V3Cfg.h
V3Class.h
V3ClassGraph.h
V3Clean.h
V3Clock.h
V3Combine.h
@@ -149,6 +150,7 @@ set(HEADERS
V3OrderGraph.h
V3OrderInternal.h
V3OrderMoveGraph.h
V3OrderMTaskGraph.h
V3Os.h
V3PairingHeap.h
V3Param.h
@@ -156,6 +158,7 @@ set(HEADERS
V3ParseImp.h
V3PchAstMT.h
V3PchAstNoMT.h
V3PoolAllocator.h
V3PreExpr.h
V3PreLex.h
V3PreProc.h
@@ -170,7 +173,6 @@ set(HEADERS
V3Sampled.h
V3Sched.h
V3Scope.h
V3Scoreboard.h
V3SenExprBuilder.h
V3SenTree.h
V3Simulate.h
@@ -229,6 +231,7 @@ set(COMMON_SOURCES
V3CfgBuilder.cpp
V3CfgLiveVariables.cpp
V3Class.cpp
V3ClassGraph.cpp
V3Clean.cpp
V3Clock.cpp
V3Combine.cpp
@@ -319,6 +322,9 @@ set(COMMON_SOURCES
V3Order.cpp
V3OrderGraphBuilder.cpp
V3OrderMoveGraph.cpp
V3OrderMTaskContraction.cpp
V3OrderMTaskFixHazards.cpp
V3OrderMTaskGraph.cpp
V3OrderParallel.cpp
V3OrderProcessDomains.cpp
V3OrderSerial.cpp
@@ -346,7 +352,6 @@ set(COMMON_SOURCES
V3SchedUtil.cpp
V3SchedVirtIface.cpp
V3Scope.cpp
V3Scoreboard.cpp
V3Slice.cpp
V3Split.cpp
V3SplitVar.cpp
+7 -1
View File
@@ -105,6 +105,9 @@ CPPFLAGS += -I. -I$(bldsrc) -I$(srcdir) -I$(incdir) -I../../include
#CPPFLAGS += -DVL_ALLOC_RANDOM_CHECKS # To allow --debug-new-random
#CPPFLAGS += -DVL_LEAK_CHECKS # If running valgrind or other hunting tool
CPPFLAGS += -MP # Only works on recent GCC versions
# Replaces VNUser's union with a std::variant to validate int vs void* usage
# Ignored unless the build is C++17 or newer
#CPPFLAGS += -DVL_USER_TYPE_CHECKS
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
CPPFLAGS += -W -Wall $(CFG_CXXFLAGS_WEXTRA) $(CFG_CXXFLAGS_SRC) -Werror
#CPPFLAGS += -pedantic-errors
@@ -244,6 +247,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3CfgBuilder.o \
V3CfgLiveVariables.o \
V3Class.o \
V3ClassGraph.o \
V3Clean.o \
V3Clock.o \
V3Combine.o \
@@ -309,6 +313,9 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Order.o \
V3OrderGraphBuilder.o \
V3OrderMoveGraph.o \
V3OrderMTaskContraction.o \
V3OrderMTaskFixHazards.o \
V3OrderMTaskGraph.o \
V3OrderParallel.o \
V3OrderProcessDomains.o \
V3OrderSerial.o \
@@ -330,7 +337,6 @@ RAW_OBJS_PCH_ASTNOMT = \
V3SchedUtil.o \
V3SchedVirtIface.o \
V3Scope.o \
V3Scoreboard.o \
V3Slice.o \
V3Split.o \
V3SplitVar.o \
+31 -3
View File
@@ -34,6 +34,7 @@
#include "V3Graph.h"
#include <unordered_map>
#include <unordered_set>
VL_DEFINE_DEBUG_FUNCTIONS;
@@ -670,6 +671,32 @@ public:
//######################################################################
// Pass 2: inject automatic sample() calls for covergroup instances
class CovergroupEventBindVisitor final : public VNVisitor {
AstVarScope* const m_instancep; // Variable scope for the covergroup instance being sampled
std::unordered_set<const AstVar*> m_memberps; // Non-static variables in the covergroup class
void visit(AstVarRef* nodep) override {
if (!m_memberps.count(nodep->varp())) return;
FileLine* const fl = nodep->fileline();
AstMemberSel* const selp
= new AstMemberSel{fl, new AstVarRef{fl, m_instancep, VAccess::READ}, nodep->varp()};
selp->access(nodep->access());
nodep->replaceWith(selp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
CovergroupEventBindVisitor(AstSenTree* eventp, AstClass* classp, AstVarScope* instancep)
: m_instancep{instancep} {
classp->foreachMember([&](AstClass* const, AstVar* const varp) {
if (!varp->isStatic()) m_memberps.emplace(varp);
});
iterate(eventp);
}
~CovergroupEventBindVisitor() override = default;
};
class CovergroupInjectVisitor final : public VNVisitor {
// NODE STATE (set by CovergroupCollectVisitor, consumed here)
// AstClass::user1p() -> AstCFunc*. The sample() CFunc for this covergroup class
@@ -719,10 +746,11 @@ class CovergroupInjectVisitor final : public VNVisitor {
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.
// Clone the sensitivity for this active block. References to covergroup members need
// to select through this particular instance; all other VarRefs retain the VarScopes
// resolved by V3Scope.
AstSenTree* senTreep = eventp->cloneTree(false);
CovergroupEventBindVisitor{senTreep, classp, nodep};
// Get or create the AstActive node for this sensitivity
// senTreep is a template used by getActive() which clones it into the AstActive;
+3 -1
View File
@@ -36,11 +36,13 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Active class functions
class ActiveTopVisitor final : public VNVisitor {
// NODE STATE
// AstVarScope::user1() // bool. Processed
// STATE
SenTreeFinder m_finder; // Find global sentree's / add them under the AstTopScope
// METHODS
static bool isInitial(AstNode* nodep) {
const VNUser1InUse user1InUse;
// Return true if no variables that read.
+254 -36
View File
@@ -20,6 +20,7 @@
#include "V3AstUserAllocator.h"
#include "V3Stats.h"
#include "V3UniqueNames.h"
VL_DEFINE_DEBUG_FUNCTIONS;
@@ -89,6 +90,58 @@ public:
explicit DefaultDisablePropagateVisitor(AstNetlist* nodep) { iterate(nodep); }
};
// Lower a sequence used as an event control ('@seq', IEEE 1800-2023 9.4.2.4) into a
// synthesized event fired by an internal 'cover sequence' on each end-of-match
class SeqEventLowerVisitor final : public VNVisitor {
// STATE
AstNodeModule* m_modp = nullptr; // Current module
V3UniqueNames m_names{"__VseqEvent"}; // Synthesized event names
// VISITORS
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
m_modp = nodep;
iterateChildren(nodep);
}
void visit(AstSenItem* nodep) override {
AstFuncRef* const funcrefp = VN_CAST(nodep->sensp(), FuncRef);
if (funcrefp && VN_IS(funcrefp->taskp(), Sequence)) {
FileLine* const flp = nodep->fileline();
AstVar* const eventp = new AstVar{flp, VVarType::MODULETEMP, m_names.get(nodep),
m_modp->findBasicDType(VBasicDTypeKwd::EVENT)};
eventp->lifetime(VLifetime::STATIC_EXPLICIT);
m_modp->addStmtsp(eventp);
v3Global.setHasEvents();
funcrefp->unlinkFrBack();
nodep->sensp(new AstVarRef{flp, eventp, VAccess::READ});
const bool automaticActual = funcrefp->exists([](const AstNodeVarRef* refp) {
return refp->varp() && refp->varp()->lifetime().isAutomatic();
});
if (automaticActual) {
nodep->v3error("Arguments to a sequence used as an event control must be"
" static (IEEE 1800-2023 9.4.2.4)");
VN_AS(funcrefp->taskp(), Sequence)->isReferenced(false);
VL_DO_DANGLING(pushDeletep(funcrefp), funcrefp);
return;
}
AstFireEvent* const firep
= new AstFireEvent{flp, new AstVarRef{flp, eventp, VAccess::WRITE}, false};
AstCover* const coverp
= new AstCover{flp, new AstPropSpec{flp, nullptr, nullptr, funcrefp}, firep,
VAssertType::CONCURRENT};
coverp->isCoverSeq(true);
coverp->isSeqEvent(true);
m_modp->addStmtsp(coverp);
return;
}
iterateChildren(nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
explicit SeqEventLowerVisitor(AstNetlist* nodep) { iterate(nodep); }
};
} // namespace
void V3AssertCommon::collectDefaultDisable(AstNetlist* nodep) {
@@ -96,6 +149,11 @@ void V3AssertCommon::collectDefaultDisable(AstNetlist* nodep) {
{ DefaultDisablePropagateVisitor{nodep}; }
}
void V3AssertCommon::lowerSequenceEvents(AstNetlist* nodep) {
{ SeqEventLowerVisitor{nodep}; }
V3Global::dumpCheckGlobalTree("assertseqevent", 0, dumpTreeEitherLevel() >= 3);
}
//######################################################################
// AssertDeFutureVisitor
// If any AstFuture, then move all non-future varrefs to be one cycle behind,
@@ -188,6 +246,41 @@ public:
~AssertDeFutureVisitor() = default;
};
// Prepare one default sampled expression.
class DefaultSampledValueVisitor final : public VNVisitor {
// METHODS
static AstConst* newTypeDefault(AstNodeExpr* nodep) {
AstConst* const constp
= new AstConst{nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()};
if (nodep->dtypep()->isFourstate()) constp->num().setAllBitsX();
constp->dtypeFrom(nodep);
return constp;
}
// VISITORS
void visit(AstCMethodHard* nodep) override {
if (nodep->method() == VCMethod::EVENT_IS_TRIGGERED) {
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else {
iterateChildren(nodep);
}
}
void visit(AstNodeVarRef* nodep) override {
AstVar* const varp = nodep->varp();
if (varp->isParam()
|| (!varp->isNet() && varp->lifetime().isStatic() && varp->hasUserInit())) {
return;
}
nodep->replaceWith(newTypeDefault(nodep));
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
explicit DefaultSampledValueVisitor(AstNode* nodep) { iterate(nodep); }
};
//######################################################################
// AssertVisitor
@@ -228,9 +321,17 @@ class AssertVisitor final : public VNVisitor {
AstNode* m_failsp = nullptr; // Current fail statement
AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion
AstFinal* m_finalp = nullptr; // Current final block
// Map from (expression, senTree) to AstAlways that computes delayed values of the expression
VDouble0 m_statLiftedCaseExprs; // Count of purified case expressions
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
V3UniqueNames m_caseTempNames{"__VCase"};
// Maps from (expression, senTree) to the AstAlways that computes its delayed values.
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
m_modExpr2Sen2DelayedAlwaysp;
// Source sampled-value history is initialized, unlike internal property-lowering history.
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
m_modExpr2Sen2DefaultDelayedAlwaysp;
// Map from delayed-value AstAlways to its last-sampling-tick time variable
std::unordered_map<const AstAlways*, AstVar*> m_delayedAlways2TickTimep;
// METHODS
static string assertCtlGetCall(const char* query, VAssertType type,
@@ -326,8 +427,7 @@ class AssertVisitor final : public VNVisitor {
}
}
AstSampled* newSampledExpr(AstNodeExpr* nodep) {
AstSampled* const sampledp = new AstSampled{nodep->fileline(), nodep, nodep->dtypep()};
return sampledp;
return new AstSampled{nodep->fileline(), nodep, nodep->dtypep(), true};
}
AstVarRef* newMonitorNumVarRefp(const AstNode* nodep, VAccess access) {
if (!m_monitorNumVarp) {
@@ -437,12 +537,18 @@ class AssertVisitor final : public VNVisitor {
return bodysp;
}
AstVar* createDelayedVar(const std::string& name, AstAlways* alwaysp, AstNodeExpr* exprp) {
AstVar* createDelayedVar(const std::string& name, AstAlways* alwaysp, AstNodeExpr* exprp,
AstNodeExpr* initp) {
FileLine* const flp = exprp->fileline();
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, exprp->dtypep()};
// TODO: this lifetime seems nonsene (can't have NBAs to automatics), but is as before
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
m_modp->addStmtsp(varp);
// Before enough clock ticks, $past returns the expression's default sampled value.
if (initp) {
m_modp->addStmtsp(new AstInitialStatic{
flp, new AstAssign{flp, new AstVarRef{flp, varp, VAccess::WRITE}, initp}});
}
++m_statPastVars;
// Actually set the delayed value
AstNodeExpr* const lhsp = new AstVarRef{flp, varp, VAccess::WRITE};
@@ -455,8 +561,10 @@ class AssertVisitor final : public VNVisitor {
return varp;
}
AstAlways* getDelayedAlways(AstNodeExpr* exprp, AstSenTree* senTreep) {
AstAlways*& alwayspr = m_modExpr2Sen2DelayedAlwaysp[*exprp][*senTreep];
AstAlways* getDelayedAlways(AstNodeExpr* exprp, AstNodeExpr* initp, AstSenTree* senTreep) {
auto& expr2Sen2Alwaysr
= initp ? m_modExpr2Sen2DefaultDelayedAlwaysp : m_modExpr2Sen2DelayedAlwaysp;
AstAlways*& alwayspr = expr2Sen2Alwaysr[*exprp][*senTreep];
if (!alwayspr) {
FileLine* const flp = exprp->fileline();
// Create the always block that computes the delayed values
@@ -464,36 +572,64 @@ class AssertVisitor final : public VNVisitor {
m_modp->addStmtsp(alwayspr);
// Create the once-delayed variable
const std::string name = "_Vpast_" + cvtToStr(m_modPastNum++) + "_1";
AstVar* const varp = createDelayedVar(name, alwayspr, exprp);
AstVar* const varp = createDelayedVar(name, alwayspr, exprp, initp);
// Add it to delayed variable vector
m_delayed(alwayspr).emplace_back(varp);
} else {
// Reusing exiting, not needed
VL_DO_DANGLING(pushDeletep(exprp), exprp);
if (initp) VL_DO_DANGLING(pushDeletep(initp), initp);
VL_DO_DANGLING(pushDeletep(senTreep), senTreep);
}
return alwayspr;
}
AstNodeExpr* getPastValue(AstNodeExpr* exprp, AstSenTree* senTreep, uint32_t ticks) {
AstNodeExpr* getPastValue(AstNodeExpr* exprp, AstNodeExpr* initp, AstSenTree* senTreep,
uint32_t ticks) {
UASSERT_OBJ(ticks > 0, exprp, "Delay must be > 0");
AstAlways* const alwaysp = getDelayedAlways(exprp, senTreep);
FileLine* const flp = exprp->fileline();
AstAlways* const alwaysp
= getDelayedAlways(exprp, initp ? initp->cloneTreePure(false) : nullptr, senTreep);
AstNodeExpr* const resultp = pastValueRef(alwaysp, flp, ticks, initp);
if (initp) VL_DO_DANGLING(pushDeletep(initp), initp);
return resultp;
}
AstNodeExpr* pastValueRef(AstAlways* alwaysp, FileLine* flp, uint32_t ticks,
AstNodeExpr* initp) {
std::vector<AstVar*>& delayedr = m_delayed(alwaysp);
// Ensure the required delay exists
while (delayedr.size() < ticks) {
AstVar* const firstp = delayedr.front();
FileLine* const flp = firstp->fileline();
FileLine* const varFlp = firstp->fileline();
// Create once more delayed value
std::string name = firstp->name();
name.resize(name.size() - 1);
name += std::to_string(delayedr.size() + 1);
AstNodeExpr* const prevp = new AstVarRef{flp, delayedr.back(), VAccess::READ};
AstVar* const varp = createDelayedVar(name, alwaysp, prevp);
AstNodeExpr* const prevp = new AstVarRef{varFlp, delayedr.back(), VAccess::READ};
AstVar* const varp = createDelayedVar(name, alwaysp, prevp,
initp ? initp->cloneTreePure(false) : nullptr);
// Add it to delayed variable vector
delayedr.emplace_back(varp);
}
// Return a reference to the appropriately delayed variable
return new AstVarRef{exprp->fileline(), delayedr.at(ticks - 1), VAccess::READ};
return new AstVarRef{flp, delayedr.at(ticks - 1), VAccess::READ};
}
// Time of the pipeline's last sampling tick, for end-of-simulation readers
AstVar* getPastTickTimeVar(AstAlways* alwaysp) {
AstVar*& varpr = m_delayedAlways2TickTimep[alwaysp];
if (!varpr) {
FileLine* const flp = alwaysp->fileline();
varpr = new AstVar{flp, VVarType::MODULETEMP,
"_Vpast_" + cvtToStr(m_modPastNum++) + "_t",
m_modp->findUInt64DType()};
m_modp->addStmtsp(varpr);
AstNodeExpr* const timep = new AstCExpr{flp, "vlSymsp->_vm_contextp__->time()", 64};
alwaysp->addStmtsp(
new AstAssign{flp, new AstVarRef{flp, varpr, VAccess::WRITE}, timep});
}
return varpr;
}
void visitAssertionIterate(AstNodeCoverOrAssert* nodep, AstNode* failsp) {
@@ -542,14 +678,19 @@ class AssertVisitor final : public VNVisitor {
bool selfDestruct = false;
bool passspGated = false;
if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
const AstCover* const coverp = VN_CAST(nodep, Cover);
// A sequence event control is not an assertion directive; no assertion control
const bool seqEvent = coverp && coverp->isSeqEvent();
if (coverp) {
++m_statCover;
if (!v3Global.opt.coverageUser()) {
if (seqEvent) {
// Keep the event-fire action, with no coverage bucket
} else if (!v3Global.opt.coverageUser()) {
selfDestruct = true;
} else {
// V3Coverage assigned us a bucket to increment.
AstCoverInc* const covincp = VN_AS(snodep->coverincsp(), CoverInc);
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
AstCoverInc* const covincp = VN_AS(coverp->coverincsp(), CoverInc);
UASSERT_OBJ(covincp, coverp, "Missing AstCoverInc under assertion");
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
if (message != "") covincp->declp()->comment(message);
if (passsp) {
@@ -584,26 +725,51 @@ class AssertVisitor final : public VNVisitor {
AstNode* propExprp;
AstNodeExpr* disablep = nullptr;
AstNodeExpr* matchCountp = nullptr;
if (AstPropSpec* const specp = VN_CAST(nodep->propp(), PropSpec)) {
propExprp = specp->propp()->unlinkFrBack();
if (specp->disablep()) disablep = specp->disablep()->unlinkFrBack();
matchCountp = specp->matchCountp();
} else {
propExprp = nodep->propp()->unlinkFrBack();
}
FileLine* const flp = nodep->fileline();
bool passspAlreadyGated = false;
if (passsp && VN_IS(passsp, If)) passspAlreadyGated = VN_AS(passsp, If)->user1();
if (passsp && !passspGated && !passspAlreadyGated && !VN_IS(propExprp, PExpr)) {
if (passsp && !passspGated && !passspAlreadyGated && !VN_IS(propExprp, PExpr)
&& !seqEvent) {
passsp = newIfAssertPassOn(passsp, nodep->directive(), nodep->userType(),
/*vacuous=*/false);
}
if (failsp && !VN_IS(propExprp, PExpr)) {
failsp = newIfAssertFailOn(failsp, nodep->directive(), nodep->userType());
}
if (coverp && matchCountp && passsp) {
// Convert the match count into a loop that decrements a temporary variable until it
// reaches zero.
matchCountp->unlinkFrBack();
AstVar* const remainingp = new AstVar{
flp, VVarType::BLOCKTEMP, "__VnfaRemainingMatchCount", matchCountp->dtypep()};
remainingp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
AstBegin* const replayp = new AstBegin{flp, "", remainingp, true};
replayp->addStmtsp(
new AstAssign{flp, new AstVarRef{flp, remainingp, VAccess::WRITE}, matchCountp});
AstLoop* const loopp = new AstLoop{flp};
loopp->addStmtsp(
new AstLoopTest{flp, loopp, new AstVarRef{flp, remainingp, VAccess::READ}});
loopp->addStmtsp(passsp);
loopp->addStmtsp(
new AstAssign{flp, new AstVarRef{flp, remainingp, VAccess::WRITE},
new AstSub{flp, new AstVarRef{flp, remainingp, VAccess::READ},
new AstConst{flp, AstConst::WidthedValue{},
remainingp->dtypep()->width(), 1}}});
replayp->addStmtsp(loopp);
passsp = replayp;
}
AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
// Add assertOn check last, for better combining
bodysp = newIfAssertOn(bodysp, nodep->directive(), nodep->userType());
if (!seqEvent) bodysp = newIfAssertOn(bodysp, nodep->directive(), nodep->userType());
if (sentreep) bodysp = new AstAlways{flp, VAlwaysKwd::ALWAYS, sentreep, bodysp};
if (passsp && !passsp->backp()) VL_DO_DANGLING(pushDeletep(passsp), passsp);
@@ -755,6 +921,28 @@ class AssertVisitor final : public VNVisitor {
//========== Case assertions
void visit(AstCase* nodep) override {
// Introduce temporary variable for AstCase if needed - it is done here and not in V3Case
// because this phase is before V3Scope and V3Case is not. Doing it before V3Scope ensures
// that V3Scope will take care of a scope creation
// We also need to do it before V3Begin, co that pragmas like `unique0` also work correctly
if (!nodep->exprp()->isPure()) {
++m_statLiftedCaseExprs;
FileLine* const fl = nodep->exprp()->fileline();
AstVar* const varp = new AstVar{fl, VVarType::BLOCKTEMP, m_caseTempNames.get(nodep),
nodep->exprp()->dtypep()};
AstNodeExpr* const origp = nodep->exprp()->unlinkFrBack();
nodep->addHereThisAsNext(
new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, origp});
nodep->exprp(new AstVarRef{fl, varp, VAccess::READ});
if (m_ftaskp) {
varp->funcLocal(true);
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
m_ftaskp->stmtsp()->addHereThisAsNext(varp);
} else {
m_modp->stmtsp()->addHereThisAsNext(varp);
}
VIsCached::clearCacheTree();
}
iterateChildren(nodep);
if (!nodep->user1SetOnce()) {
bool has_default = false;
@@ -878,6 +1066,11 @@ class AssertVisitor final : public VNVisitor {
//========== Past
void visit(AstPast* nodep) override {
if (!nodep->propertyTiming()) {
nodep->initp(nodep->exprp()->cloneTreePure(false));
{ DefaultSampledValueVisitor{nodep->initp()}; }
}
AstNodeExpr* const initp = nodep->initp() ? nodep->initp()->unlinkFrBack() : nullptr;
iterateChildren(nodep);
uint32_t ticks = 1;
if (nodep->ticksp()) {
@@ -887,7 +1080,27 @@ class AssertVisitor final : public VNVisitor {
}
UASSERT_OBJ(ticks >= 1, nodep, "0 tick should have been checked in V3Width");
AstNodeExpr* const exprp = newSampledExpr(nodep->exprp()->unlinkFrBack());
AstNodeExpr* inp = getPastValue(exprp, nodep->sentreep()->unlinkFrBack(), ticks);
AstSenTree* const senTreep = nodep->sentreep()->unlinkFrBack();
AstNodeExpr* inp = nullptr;
// Deliberately emitted for every ftask $past; calls outside final take the normal stage
if (VN_IS(m_procedurep, Final) || m_ftaskp) {
// A sampling-tick finish reads one stage deeper (IEEE 1800-2023 16.9.3).
FileLine* const flp = nodep->fileline();
AstAlways* const alwaysp
= getDelayedAlways(exprp, initp ? initp->cloneTreePure(false) : nullptr, senTreep);
AstNodeExpr* const betweenTicksp = pastValueRef(alwaysp, flp, ticks, initp);
AstNodeExpr* const onTickp = pastValueRef(alwaysp, flp, ticks + 1, initp);
if (initp) VL_DO_DANGLING(pushDeletep(initp), initp);
AstNodeExpr* const onTickCondp = new AstLogAnd{
flp, new AstCExpr{flp, "vlSymsp->_vm_contextp__->executingFinal()", 1},
new AstEq{flp, new AstVarRef{flp, getPastTickTimeVar(alwaysp), VAccess::READ},
new AstCExpr{flp, "vlSymsp->_vm_contextp__->finishPendingTime()", 64}}};
AstCond* const condp = new AstCond{flp, onTickCondp, onTickp, betweenTicksp};
condp->dtypeFrom(onTickp);
inp = condp;
} else {
inp = getPastValue(exprp, initp, senTreep, ticks);
}
nodep->replaceWith(inp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
@@ -983,11 +1196,7 @@ class AssertVisitor final : public VNVisitor {
= new AstSenItem{fl, VEdgeType::ET_CHANGED,
// Clone so get VarRef or VarXRef as needed
varrefp->cloneTree(false)};
if (!monSenItemsp) {
monSenItemsp = senItemp;
} else {
monSenItemsp->addNext(senItemp);
}
monSenItemsp = AstNode::addNextNull(monSenItemsp, senItemp);
});
}
@@ -1047,9 +1256,9 @@ class AssertVisitor final : public VNVisitor {
bool assertTypeConst = true;
if (!nodep->assertTypesp()) {
nodep->ctlAssertTypes(VAssertType{ALL_ASSERT_TYPES});
nodep->assertTypes(VAssertType{ALL_ASSERT_TYPES});
} else if (const AstConst* const assertTypesp = VN_CAST(nodep->assertTypesp(), Const)) {
nodep->ctlAssertTypes(VAssertType{assertTypesp->toSInt()});
nodep->assertTypes(VAssertType{assertTypesp->toSInt()});
} else {
assertTypeConst = false;
}
@@ -1069,9 +1278,9 @@ class AssertVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
if (assertTypeConst && nodep->ctlAssertTypes() != ALL_ASSERT_TYPES
&& nodep->ctlAssertTypes().containsAny(VAssertType::UNIQUE | VAssertType::UNIQUE0
| VAssertType::PRIORITY)) {
if (assertTypeConst && nodep->assertTypes() != ALL_ASSERT_TYPES
&& nodep->assertTypes().containsAny(VAssertType::UNIQUE | VAssertType::UNIQUE0
| VAssertType::PRIORITY)) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assert control assertion_type");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
@@ -1079,11 +1288,11 @@ class AssertVisitor final : public VNVisitor {
bool directiveTypeConst = true;
if (!nodep->directiveTypesp()) {
nodep->ctlDirectiveTypes(VAssertDirectiveType::ASSERT | VAssertDirectiveType::ASSUME
| VAssertDirectiveType::COVER);
nodep->directiveTypes(VAssertDirectiveType::ASSERT | VAssertDirectiveType::ASSUME
| VAssertDirectiveType::COVER);
} else if (const AstConst* const directiveTypesp
= VN_CAST(nodep->directiveTypesp(), Const)) {
nodep->ctlDirectiveTypes(VAssertDirectiveType{directiveTypesp->toSInt()});
nodep->directiveTypes(VAssertDirectiveType{directiveTypesp->toSInt()});
} else {
directiveTypeConst = false;
}
@@ -1105,7 +1314,7 @@ class AssertVisitor final : public VNVisitor {
} else {
newp->add(std::to_string(ALL_ASSERT_TYPES));
}
newp->add(", " + std::to_string(nodep->ctlDirectiveTypes()) + ");\n");
newp->add(", " + std::to_string(nodep->directiveTypes()) + ");\n");
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
@@ -1127,12 +1336,13 @@ class AssertVisitor final : public VNVisitor {
VL_RESTORER(m_modp);
VL_RESTORER(m_modPastNum);
VL_RESTORER(m_modStrobeNum);
VL_RESTORER(m_modExpr2Sen2DelayedAlwaysp);
VL_RESTORER(m_finalp);
VL_RESTORER_CLEAR(m_modExpr2Sen2DelayedAlwaysp);
VL_RESTORER_CLEAR(m_modExpr2Sen2DefaultDelayedAlwaysp);
VL_RESTORER_CLEAR(m_delayedAlways2TickTimep);
m_modp = nodep;
m_modPastNum = 0;
m_modStrobeNum = 0;
m_modExpr2Sen2DelayedAlwaysp.clear();
m_finalp = nullptr;
iterateChildren(nodep);
}
@@ -1141,6 +1351,11 @@ class AssertVisitor final : public VNVisitor {
m_procedurep = nodep;
iterateChildren(nodep);
}
void visit(AstNodeFTask* nodep) override {
VL_RESTORER(m_ftaskp);
m_ftaskp = nodep;
iterateChildren(nodep);
}
void visit(AstGenBlock* nodep) override {
// This code is needed rather than a visitor in V3Begin,
// because V3Assert is called before V3Begin
@@ -1180,6 +1395,9 @@ public:
V3Stats::addStat("Assertions, $past variables", m_statPastVars);
V3Stats::addStat("Assertions, assertOn checks combined", m_statAssertOnCombined);
V3Stats::addStat("Assertions, assertOn checks hoisted", m_statAssertOnHoisted);
V3Stats::addStat("Assertions, lifted impure case expressions", m_statLiftedCaseExprs);
// Rewrites can change purity, e.g. by compiling out assertion statements with --no-assert
VIsCached::clearCacheTree();
}
};
+1
View File
@@ -27,6 +27,7 @@
class V3AssertCommon final {
public:
static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED;
static void lowerSequenceEvents(AstNetlist* nodep) VL_MT_DISABLED;
};
class V3Assert final {
+957 -572
View File
File diff suppressed because it is too large Load Diff
+44 -20
View File
@@ -79,6 +79,14 @@ private:
// METHODS
static void checkSamplingFuncDType(AstNodeExpr* nodep, const AstNode* exprp) {
const AstNodeDType* const dtypep = exprp->dtypep()->skipRefp();
if (!dtypep->isIntegralOrPacked()) {
nodep->v3error("Expected numeric type, but got a " << dtypep->prettyDTypeNameQ()
<< " data type");
}
}
AstSenTree* newSenTree(AstNode* nodep, AstSenTree* useTreep = nullptr,
AstNodeCoverOrAssert* cassertp = nullptr) {
// Create sentree based on clocked or default clock
@@ -372,7 +380,7 @@ private:
// #1step means the value that is sampled is always the signal's last value
// before the clock edge (IEEE 1800-2023 14.4)
AstSampled* const sampledp
= new AstSampled{flp, exprp->cloneTreePure(false), exprp->dtypep()};
= new AstSampled{flp, exprp->cloneTreePure(false), exprp->dtypep(), true};
AstAssign* const assignp = new AstAssign{flp, refp, sampledp};
m_clockingp->addNextHere(new AstAlways{
flp, VAlwaysKwd::ALWAYS,
@@ -683,6 +691,7 @@ private:
void visit(AstFalling* nodep) override {
if (nodep->user1SetOnce()) return;
iterateChildren(nodep);
checkSamplingFuncDType(nodep, nodep->exprp());
FileLine* const fl = nodep->fileline();
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
@@ -696,6 +705,7 @@ private:
void visit(AstFell* nodep) override {
if (nodep->user1SetOnce()) return;
iterateChildren(nodep);
checkSamplingFuncDType(nodep, nodep->exprp());
FileLine* const fl = nodep->fileline();
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
@@ -712,11 +722,13 @@ private:
void visit(AstFuture* nodep) override {
if (nodep->user1SetOnce()) return;
iterateChildren(nodep);
checkSamplingFuncDType(nodep, nodep->exprp());
AstSenTree* const sentreep = nodep->sentreep();
if (sentreep) VL_DO_DANGLING(pushDeletep(sentreep->unlinkFrBack()), sentreep);
nodep->sentreep(newSenTree(nodep));
}
void visit(AstPast* nodep) override {
checkSamplingFuncDType(nodep, nodep->exprp());
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
nodep->sentreep(newSenTree(nodep));
@@ -774,6 +786,7 @@ private:
void visit(AstRising* nodep) override {
if (nodep->user1SetOnce()) return;
iterateChildren(nodep);
checkSamplingFuncDType(nodep, nodep->exprp());
FileLine* const fl = nodep->fileline();
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
@@ -787,6 +800,7 @@ private:
void visit(AstRose* nodep) override {
if (nodep->user1SetOnce()) return;
iterateChildren(nodep);
checkSamplingFuncDType(nodep, nodep->exprp());
FileLine* const fl = nodep->fileline();
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
@@ -849,7 +863,8 @@ private:
// Assertion condition check
AstLoop* const loopp = new AstLoop{flp};
AstNodeExpr* const condp = new AstSampled{flp, nodep->exprp()->unlinkFrBack(), nullptr};
AstSampled* const condp
= new AstSampled{flp, nodep->exprp()->unlinkFrBack(), nullptr, true};
loopp->addStmtsp(new AstLoopTest{flp, loopp, new AstLogNot{flp, condp}});
loopp->addStmtsp(new AstEventControl{flp, sentreep, nullptr});
@@ -916,6 +931,7 @@ private:
void visit(AstStable* nodep) override {
if (nodep->user1SetOnce()) return;
iterateChildren(nodep);
checkSamplingFuncDType(nodep, nodep->exprp());
FileLine* const fl = nodep->fileline();
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
AstSenTree* sentreep = nodep->sentreep();
@@ -931,6 +947,7 @@ private:
void visit(AstSteady* nodep) override {
if (nodep->user1SetOnce()) return;
iterateChildren(nodep);
checkSamplingFuncDType(nodep, nodep->exprp());
FileLine* const fl = nodep->fileline();
AstNodeExpr* exprp = nodep->exprp()->unlinkFrBack();
if (exprp->width() > 1) exprp = new AstSel{fl, exprp, 0, 1};
@@ -941,6 +958,10 @@ private:
nodep->replaceWith(exprp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstSampled* nodep) override {
iterateChildren(nodep);
if (!nodep->internal()) checkSamplingFuncDType(nodep, nodep->exprp());
}
// Validate repetition count: must be a non-negative elaboration-time constant.
// Shared by goto [->N] and nonconsecutive [=N] repetition.
@@ -952,7 +973,7 @@ private:
nodep->v3error("Repetition count is not an elaboration-time constant"
" (IEEE 1800-2023 16.9.2)");
VL_DO_DANGLING(pushDeletep(countp), countp);
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalseErroring{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return nullptr;
}
@@ -960,7 +981,7 @@ private:
nodep->v3error("Repetition count must be non-negative"
" (IEEE 1800-2023 16.9.2)");
VL_DO_DANGLING(pushDeletep(countp), countp);
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalseErroring{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return nullptr;
}
@@ -1203,11 +1224,7 @@ private:
AstNode* const nextp = stmtp->nextp();
if (AstAssign* const assignp = VN_CAST(stmtp, Assign)) {
assignp->unlinkFrBack();
if (!matchAssignsp) {
matchAssignsp = assignp;
} else {
matchAssignsp->addNext(assignp);
}
matchAssignsp = AstNode::addNextNull(matchAssignsp, assignp);
}
stmtp = nextp;
}
@@ -1233,11 +1250,7 @@ private:
AstNodeExpr* const assignRhsp = assignp->rhsp()->unlinkFrBack();
AstAssignDly* const dlyp = new AstAssignDly{flp, assignLhsp, assignRhsp};
VL_DO_DANGLING(pushDeletep(assignp), assignp);
if (!matchAssignsp) {
matchAssignsp = dlyp;
} else {
matchAssignsp->addNext(dlyp);
}
matchAssignsp = AstNode::addNextNull(matchAssignsp, dlyp);
}
stmtp = nextp;
}
@@ -1271,7 +1284,8 @@ private:
lhsp
= new AstAnd{flp, new AstNot{flp, m_disablep->cloneTreePure(false)}, lhsp};
}
AstPast* const pastp = new AstPast{flp, lhsp};
AstPast* const pastp
= new AstPast{flp, lhsp, nullptr, nullptr, /* propertyTiming */ true};
pastp->dtypeFrom(lhsp);
pastp->sentreep(newSenTree(nodep));
condp = pastp;
@@ -1295,7 +1309,8 @@ private:
lhsp = new AstAnd{flp, new AstNot{flp, m_disablep->cloneTreePure(false)}, lhsp};
}
AstPast* const pastp = new AstPast{flp, lhsp};
AstPast* const pastp
= new AstPast{flp, lhsp, nullptr, nullptr, /* propertyTiming */ true};
pastp->dtypeFrom(lhsp);
pastp->sentreep(newSenTree(nodep));
AstNodeExpr* const exprp
@@ -1312,14 +1327,21 @@ private:
!m_pexprp, nodep,
"'" << nodep->verilogKwd()
<< "' in complex property expression should have been rejected by V3AssertNfa");
if (nodep->isStrong()
&& (v3Global.opt.timing().isSetFalse() || !v3Global.opt.timing().isSetTrue())) {
nodep->v3warn(E_NOTIMING, nodep->verilogKwd() << " requires --timing");
nodep->replaceWith(new AstConst{flp, AstConst::BitFalse{}});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
if (nodep->isStrong()) {
// p s_until q / p s_until_with q: q must eventually be true. Until then, p must
// be true on every sampled tick. For s_until, check q first: when q is true on
// this tick, p is not required. For s_until_with, p must be true on the q tick too.
AstNodeExpr* const rawLhsp = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* const rawRhsp = nodep->rhsp()->unlinkFrBack();
AstSampled* const lhsp = new AstSampled{flp, rawLhsp, rawLhsp->dtypep()};
AstSampled* const rhsp = new AstSampled{flp, rawRhsp, rawRhsp->dtypep()};
AstSampled* const lhsp = new AstSampled{flp, rawLhsp, rawLhsp->dtypep(), true};
AstSampled* const rhsp = new AstSampled{flp, rawRhsp, rawRhsp->dtypep(), true};
AstNodeExpr* finalCondp = rhsp->cloneTreePure(false);
if (nodep->isOverlapping()) {
finalCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), finalCondp};
@@ -1466,7 +1488,9 @@ private:
iterateAndNextNull(nodep->sensesp());
if (m_senip && m_senip != nodep->sensesp())
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Only one PSL clock allowed per assertion");
if (!nodep->disablep() && m_defaultDisablep) {
const AstCover* const coverp = VN_CAST(nodep->backp(), Cover);
const bool seqEvent = coverp && coverp->isSeqEvent();
if (!nodep->disablep() && m_defaultDisablep && !seqEvent) {
nodep->disablep(m_defaultDisablep->condp()->cloneTreePure(true));
}
m_disablep = nodep->disablep();
@@ -1476,7 +1500,7 @@ private:
if (!VN_AS(nodep->backp(), NodeCoverOrAssert)->immediate()) {
const AstNodeDType* const propDtp = nodep->propp()->dtypep();
nodep->propp(new AstSampled{nodep->fileline(), nodep->propp()->unlinkFrBack(),
propDtp->dtypep()});
propDtp->dtypep(), true});
}
// cover counts non-vacuous matches only (IEEE 1800-2023 16.15.2), so an
// implication antecedent must hold; assert passes vacuously instead.
+77 -34
View File
@@ -49,26 +49,31 @@ bool VNUser4InUse::s_userBusy = false;
int AstNodeDType::s_uniqueNum = 0;
V3AST_VCMETHOD_ITEMDATA_DECL;
//======================================================================
// VCMethod information
VCMethod VCMethod::arrayMethod(const string& name) {
for (const auto& it : s_itemData)
if (it.m_name == name) return it.m_e;
v3fatalSrc("Not a method name known to VCMethod::s_itemData: '" << name << '\'');
for (int i = 0; i < _ENUM_MAX; ++i) {
const VCMethod method{i};
if (name == method.ascii()) return method;
}
v3fatalSrc("Not a method name known to VCMethod: '" << name << '\'');
return VCMethod{};
}
void VCMethod::selfTest() {
int i = 0;
for (const auto& it : s_itemData) {
const VCMethod exp{i};
UASSERT_STATIC(it.m_e == exp,
"VCMethod::s_itemData table rows are out-of-order, starting at row "s
+ cvtToStr(i) + " '" + +it.m_name + '\'');
++i;
}
//######################################################################
// VNUser
std::string VNUser::dumpStr(std::string (*fmtAddrp)(const void*)) const {
#ifdef VL_USER_TYPE_CHECKS
if (const int* const uip = std::get_if<int>(&m_u)) return "#"s + cvtToStr(*uip);
if (void* const* const upp = std::get_if<void*>(&m_u)) return fmtAddrp(*upp);
return "";
#else
// Dumps void* representation
if (!m_u.up) return "";
return fmtAddrp(m_u.up);
#endif
}
//######################################################################
@@ -1384,10 +1389,15 @@ void AstNode::dumpPtrs(std::ostream& os) const {
if (op2p()) os << " op2p=" << cvtToHex(op2p());
if (op3p()) os << " op3p=" << cvtToHex(op3p());
if (op4p()) os << " op4p=" << cvtToHex(op4p());
if (user1p()) os << " user1p=" << cvtToHex(user1p());
if (user2p()) os << " user2p=" << cvtToHex(user2p());
if (user3p()) os << " user3p=" << cvtToHex(user3p());
if (user4p()) os << " user4p=" << cvtToHex(user4p());
const auto dumpUser = [&os](const char* prefix, const VNUser& user) {
const std::string s
= user.dumpStr([](const void* p) -> std::string { return cvtToHex(p); });
if (!s.empty()) os << prefix << s;
};
dumpUser(" user1p=", user1u());
dumpUser(" user2p=", user2u());
dumpUser(" user3p=", user3u());
dumpUser(" user4p=", user4u());
if (m_iterpp) {
os << " iterpp=" << cvtToHex(m_iterpp);
// This may cause address sanitizer failures as iterpp can be stale
@@ -1542,14 +1552,14 @@ string AstNode::instanceStr() const {
return "";
}
void AstNode::v3errorEnd(const std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
// Don't look for instance name when warning is disabled.
// In case of large number of warnings, this can
// take significant amount of time
const string instanceStrExtra
= m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr();
if (!m_fileline) {
V3Error::v3errorEnd(str, instanceStrExtra, nullptr);
V3Error::v3errorEnd(str, "", nullptr);
} else {
// Don't look for instance name when warning is disabled.
// In case of large number of warnings, this can
// take significant amount of time
const string instanceStrExtra
= m_fileline->warnIsOff(V3Error::s().errorCode()) ? "" : instanceStr();
std::ostringstream nsstr;
nsstr << str.str();
if (debug()) {
@@ -1660,7 +1670,7 @@ static const AstNodeDType* computeCastableBase(const AstNodeDType* nodep) {
}
static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDType* fromDtp,
const AstNode* fromConstp) {
const AstNode* fromConstp, const bool checkIfaceArgCompat) {
const VCastable castable = VCastable::UNSUPPORTED;
toDtp = toDtp->skipRefToEnump();
fromDtp = fromDtp->skipRefToEnump();
@@ -1698,22 +1708,52 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp
if (upcast) return VCastable::COMPATIBLE;
if (downcast) return VCastable::DYNAMIC_CLASS;
return VCastable::INCOMPATIBLE;
} else if (const AstIfaceRefDType* const toIfp = VN_CAST(toDtp, IfaceRefDType)) {
} else if (const AstIfaceRefDType* const toIfp
= VN_CAST(checkIfaceArgCompat ? toDtp->elemDTypep(true) : toDtp, IfaceRefDType)) {
// Two interface refs are compatible if they point at the same interface
// module (and modport, if any). Pointer-equality on the dtype isn't
// enough since every cell binding clones the dtype.
const AstIfaceRefDType* const fromIfp = VN_CAST(fromDtp, IfaceRefDType);
if (fromIfp && toIfp->ifaceViaCellp() == fromIfp->ifaceViaCellp()
&& (!toIfp->modportp() || toIfp->modportp() == fromIfp->modportp())) {
// Argument compatibility also requires matching virtualness for ref arguments, while
// input arguments may bind an unqualified or same-modport source to a virtual target.
// Argument compatibility also supports fixed-size arrays of interface references.
const AstIfaceRefDType* const fromIfp
= VN_CAST(checkIfaceArgCompat ? fromDtp->elemDTypep(true) : fromDtp, IfaceRefDType);
if (checkIfaceArgCompat && fromIfp && (toDtp != toIfp || fromDtp != fromIfp)) {
const AstUnpackArrayDType* const toArrayp = VN_CAST(toDtp, UnpackArrayDType);
const AstUnpackArrayDType* const fromArrayp = VN_CAST(fromDtp, UnpackArrayDType);
// IEEE 1800-2023 6.22.2: Equal-sized fixed arrays have equivalent types.
if (!toArrayp || !fromArrayp
|| toArrayp->elementsConst() != fromArrayp->elementsConst()) {
return VCastable::INCOMPATIBLE;
}
return computeCastableImp(toArrayp->subDTypep(), fromArrayp->subDTypep(), nullptr,
checkIfaceArgCompat);
}
if (!fromIfp || toIfp->ifaceViaCellp() != fromIfp->ifaceViaCellp()) {
if (!checkIfaceArgCompat) return castable;
return VCastable::INCOMPATIBLE;
}
const bool sameModport = toIfp->modportp() == fromIfp->modportp();
if (!checkIfaceArgCompat) {
if (!toIfp->modportp() || sameModport) return VCastable::COMPATIBLE;
return castable;
}
if (toIfp->isVirtual() == fromIfp->isVirtual() && sameModport) {
return VCastable::SAMEISH;
}
// An unqualified interface or virtual interface may bind to a modport-qualified
// virtual interface.
if (toIfp->isVirtual() && (!fromIfp->modportp() || sameModport)) {
return VCastable::COMPATIBLE;
}
return VCastable::INCOMPATIBLE;
}
return castable;
}
VCastable AstNode::computeCastable(const AstNodeDType* toDtp, const AstNodeDType* fromDtp,
const AstNode* fromConstp) {
const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp);
const AstNode* fromConstp, const bool checkIfaceArgCompat) {
const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp, checkIfaceArgCompat);
UINFO(9, " castable=" << castable << " for " << toDtp);
UINFO(9, " =?= " << fromDtp);
if (fromConstp) UINFO(9, " const= " << fromConstp);
@@ -1735,12 +1775,15 @@ AstNodeDType* AstNode::getCommonClassTypep(AstNode* node1p, AstNode* node2p) {
if (castable == VCastable::DYNAMIC_CLASS) return node2p->dtypep();
}
AstClassRefDType* classDtypep1 = VN_CAST(node1p->dtypep(), ClassRefDType);
AstClassRefDType* classDtypep1 = VN_CAST(node1p->dtypep()->skipRefp(), ClassRefDType);
while (classDtypep1) {
const VCastable castable = computeCastable(classDtypep1, node2p->dtypep(), node2p);
if (castable == VCastable::COMPATIBLE) return classDtypep1;
const AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr;
AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
if (!extendsp) break;
AstNodeDType* const edtp
= extendsp->dtypep() ? extendsp->dtypep() : extendsp->childDTypep();
classDtypep1 = VN_AS(edtp->skipRefp(), ClassRefDType);
}
return nullptr;
}
+62 -2
View File
@@ -30,6 +30,7 @@
#include "V3Ast__gen_forward_class_decls.h" // From ./astgen
#include <array>
#include <cmath>
#include <cstdint>
#include <functional>
@@ -40,6 +41,14 @@
#include <utility>
#include <vector>
// VL_USER_TYPE_CHECKS requires C++17 for std::variant. Ignore on older standards
#if defined(VL_USER_TYPE_CHECKS) && __cplusplus < 201703L
#undef VL_USER_TYPE_CHECKS
#endif
#ifdef VL_USER_TYPE_CHECKS
#include <variant>
#endif
// clang-format off
#include "V3AstAttr.h"
// clang-format on
@@ -137,12 +146,48 @@ class V3GraphVertex;
class VSymEnt;
class VNUser final {
// Defining VL_USER_TYPE_CHECKS replaces the VNUser union with a std::variant
// which remembers which form of VNUser is being stored to catch mismatched reads.
#ifdef VL_USER_TYPE_CHECKS
// monostate is an unwritten / cleared slot. It can be read as either form
// and yields nullptr/0.
std::variant<std::monostate, int, void*> m_u;
#else
union {
void* up;
int ui;
} m_u;
#endif
public:
#ifdef VL_USER_TYPE_CHECKS
VNUser() = default;
// non-explicit:
// cppcheck-suppress noExplicitConstructor
VNUser(int i) {
// VNUser{0} represents the monostate
if (i) m_u = i;
}
explicit VNUser(void* p) {
// VNUser{nullptr} represents the monostate
if (p) m_u = p;
}
~VNUser() = default;
// Casters
template <typename T>
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE {
if (std::holds_alternative<std::monostate>(m_u)) return nullptr;
void* const* const upp = std::get_if<void*>(&m_u);
UASSERT_STATIC(upp, "AstNode user() slot written as int, read as pointer");
return reinterpret_cast<T>(*upp);
}
int toInt() const {
if (std::holds_alternative<std::monostate>(m_u)) return 0;
const int* const uip = std::get_if<int>(&m_u);
UASSERT_STATIC(uip, "AstNode user() slot written as pointer, read as int");
return *uip;
}
#else
VNUser() = default;
// non-explicit:
// cppcheck-suppress noExplicitConstructor
@@ -157,10 +202,14 @@ public:
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE {
return reinterpret_cast<T>(m_u.up);
}
int toInt() const { return m_u.ui; }
#endif
VSymEnt* toSymEnt() const { return to<VSymEnt*>(); }
AstNode* toNodep() const VL_MT_SAFE { return to<AstNode*>(); }
V3GraphVertex* toGraphVertex() const { return to<V3GraphVertex*>(); }
int toInt() const { return m_u.ui; }
// Render for dumps without asserting on the form held: "" if unset, "#<int>"
// if an int, else the pointer via fmtAddrp
std::string dumpStr(std::string (*fmtAddrp)(const void*)) const;
};
//######################################################################
@@ -441,6 +490,8 @@ class AstNode VL_NOT_FINAL {
static int s_cloneCntGbl; // Count of which userp is set
// This member ordering both allows 64 bit alignment and puts associated data together
// (under VL_USER_TYPE_CHECKS a VNUser is larger than 64 bits, so this packing no
// longer holds; that build trades node size for catching int/pointer confusion)
VNUser m_user1u{0}; // Contains any information the user iteration routine wants
uint32_t m_user1Cnt = 0; // Mark of when userp was set
uint32_t m_user2Cnt = 0; // Mark of when userp was set
@@ -730,6 +781,7 @@ public:
// ACCESSORS for specific types
// Alas these can't be virtual or they break when passed a nullptr
bool isDisableQueuePushSelfStmt();
inline bool isClassHandleValue() const;
inline bool isNull() const;
inline bool isZero() const;
@@ -808,7 +860,7 @@ public:
static AstBasicDType* findInsertSameDType(AstBasicDType* nodep);
static VCastable computeCastable(const AstNodeDType* toDtp, const AstNodeDType* fromDtp,
const AstNode* fromConstp);
const AstNode* fromConstp, bool checkIfaceArgCompat = false);
static AstNodeDType* getCommonClassTypep(AstNode* nodep1, AstNode* nodep2);
// METHODS - dump and error
@@ -1260,6 +1312,14 @@ public:
this->foreach([&count](const AstNode*) { ++count; });
return count;
}
// Return true if and only if the tree rooted at this node has more than 'limit' nodes.
// Traversal terminates as soon as the result is known, so unlike comparing 'nodeCount',
// this is cheap on a large tree.
bool isLargerThan(int limit) const {
int count = 0;
return this->exists([&count, limit](const AstNode*) { return ++count > limit; });
}
};
// Forward declarations of specializations defined in V3Ast.cpp
+277 -251
View File
@@ -299,6 +299,34 @@ constexpr VAssertType::en operator|(VAssertType::en lhs, VAssertType::en rhs) {
// ######################################################################
class VPropStrength final {
public:
enum en : uint8_t {
DEFAULT = 0, // Resolve from assertion/coverage context
WEAK,
STRONG,
};
enum en m_e;
// cppcheck-suppress noExplicitConstructor
constexpr VPropStrength(en _e)
: m_e{_e} {}
const char* ascii() const {
static const char* const names[] = {"default", "weak", "strong"};
return names[m_e];
}
};
constexpr bool operator==(const VPropStrength& lhs, const VPropStrength& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VPropStrength& lhs, VPropStrength::en rhs) {
return lhs.m_e == rhs;
}
constexpr bool operator!=(const VPropStrength& lhs, VPropStrength::en rhs) {
return lhs.m_e != rhs;
}
// ######################################################################
class VAttrType final {
public:
// clang-format off
@@ -779,137 +807,136 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
// ######################################################################
// C++ methods invoked on runtime library data types via AstCMethodHard
// clang-format off
#define FOR_EACH_CMETHOD(macro) \
/* id, method, pure */ \
macro(_NONE, "_none", false) \
macro(ARRAY_AND, "and", true) \
macro(ARRAY_AT, "at", true) \
macro(ARRAY_AT_BACK, "atBack", true) \
macro(ARRAY_AT_WRITE, "atWrite", true) \
macro(ARRAY_FIND, "find", true) \
macro(ARRAY_FIND_FIRST, "find_first", true) \
macro(ARRAY_FIND_FIRST_INDEX, "find_first_index", true) \
macro(ARRAY_FIND_INDEX, "find_index", true) \
macro(ARRAY_FIND_LAST, "find_last", true) \
macro(ARRAY_FIND_LAST_INDEX, "find_last_index", true) \
macro(ARRAY_FIRST, "first", false) \
macro(ARRAY_INSIDE, "inside", true) \
macro(ARRAY_LAST, "last", false) \
macro(ARRAY_MAP, "map", true) \
macro(ARRAY_MAX, "max", true) \
macro(ARRAY_MIN, "min", true) \
macro(ARRAY_NEXT, "next", false) \
macro(ARRAY_OR, "or", true) \
macro(ARRAY_POP_BACK, "pop_back", false) \
macro(ARRAY_POP_FRONT, "pop_front", false) \
macro(ARRAY_PREV, "prev", false) \
macro(ARRAY_PRODUCT, "product", true) \
macro(ARRAY_PUSH_BACK, "push_back", false) \
macro(ARRAY_PUSH_FRONT, "push_front", false) \
macro(ARRAY_REVERSE, "reverse", false) \
macro(ARRAY_RSORT, "rsort", false) \
macro(ARRAY_R_AND, "r_and", true) \
macro(ARRAY_R_OR, "r_or", true) \
macro(ARRAY_R_PRODUCT, "r_product", true) \
macro(ARRAY_R_SUM, "r_sum", true) \
macro(ARRAY_R_XOR, "r_xor", true) \
macro(ARRAY_SHUFFLE, "shuffle", false) \
macro(ARRAY_SORT, "sort", false) \
macro(ARRAY_SUM, "sum", true) \
macro(ARRAY_UNIQUE, "unique", true) \
macro(ARRAY_UNIQUE_INDEX, "unique_index", true) \
macro(ARRAY_XOR, "xor", true) \
macro(ASSOC_CLEAR, "clear", false) \
macro(ASSOC_ERASE, "erase", false) \
macro(ASSOC_EXISTS, "exists", true) \
macro(ASSOC_FIRST, "first", false) \
macro(ASSOC_NEXT, "next", false) \
macro(ASSOC_SIZE, "size", true) \
macro(CLASS_SET_RANDMODE, "set_randmode", false) \
macro(DYN_AT_WRITE_APPEND, "atWriteAppend", false) \
macro(DYN_AT_WRITE_APPEND_BACK, "atWriteAppendBack", false) \
macro(DYN_CLEAR, "clear", false) \
macro(DYN_ERASE, "erase", false) \
macro(DYN_INSERT, "insert", false) \
macro(DYN_POP, "pop", false) \
macro(DYN_POP_FRONT, "pop_front", false) \
macro(DYN_PUSH, "push", false) \
macro(DYN_PUSH_FRONT, "push_front", false) \
macro(DYN_RENEW, "renew", false) \
macro(DYN_RENEW_COPY, "renew_copy", false) \
macro(DYN_RESIZE, "resize", false) \
macro(DYN_SIZE, "size", true) \
macro(DYN_SLICE, "slice", true) \
macro(DYN_SLICE_ASSIGN, "sliceAssign", false) \
macro(DYN_SLICE_ASSIGN_BACK_BACK, "sliceAssignBackBack", false) \
macro(DYN_SLICE_ASSIGN_FRONT_BACK, "sliceAssignFrontBack", false) \
macro(DYN_SLICE_BACK_BACK, "sliceBackBack", true) \
macro(DYN_SLICE_FRONT_BACK, "sliceFrontBack", true) \
macro(EVENT_CLEAR_FIRED, "clearFired", false) \
macro(EVENT_CLEAR_TRIGGERED, "clearTriggered", false) \
macro(EVENT_FIRE, "fire", false) \
macro(EVENT_IS_FIRED, "isFired", true) \
macro(EVENT_IS_TRIGGERED, "isTriggered", true) \
macro(FORCE_ADD, "addForce", false) \
macro(FORCE_READ, "read", true) \
macro(FORCE_READ_INDEX, "readIndex", true) \
macro(FORCE_READ_SEL, "readSel", true) \
macro(FORCE_RELEASE, "release", false) \
macro(FORCE_TOUCH, "touch", false) \
macro(FORK_DONE, "done", false) \
macro(FORK_INIT, "init", false) \
macro(FORK_JOIN, "join", false) \
macro(FORK_ON_KILL, "onKill", false) \
macro(RANDOMIZER_BASIC_STD_RANDOMIZATION, "basicStdRandomization", false) \
macro(RANDOMIZER_CLEARCONSTRAINTS, "clearConstraints", false) \
macro(RANDOMIZER_CLEARALL, "clearAll", false) \
macro(RANDOMIZER_DISABLE_SOFT, "disable_soft", false) \
macro(RANDOMIZER_HARD, "hard", false) \
macro(RANDOMIZER_SOFT, "soft", false) \
macro(RANDOMIZER_UNIQUE, "rand_unique", false) \
macro(RANDOMIZER_MARK_RANDC, "markRandc", false) \
macro(RANDOMIZER_SOLVE_BEFORE, "solveBefore", false) \
macro(RANDOMIZER_PIN_VAR, "pin_var", false) \
macro(RANDOMIZER_WRITE_VAR, "write_var", false) \
macro(RANDOMIZER_SET_VAR_DISABLED, "set_var_disabled", false) \
macro(RANDOMIZER_CLEAR_VAR_DISABLED, "clear_var_disabled", false) \
macro(RANDOMIZER_MARK_VAR_STATIC, "mark_var_static", false) \
macro(RANDOMIZER_SET_STATIC_RANDMODE, "set_static_randmode", false) \
macro(RNG_GET_RANDSTATE, "__Vm_rng.get_randstate", true) \
macro(RNG_SET_RANDSTATE, "__Vm_rng.set_randstate", false) \
macro(SCHED_ANY_TRIGGERED, "anyTriggered", false) \
macro(SCHED_AWAITING_CURRENT_TIME, "awaitingCurrentTime", true) \
macro(SCHED_AWAITING_ZERO_DELAY, "awaitingZeroDelay", true) \
macro(SCHED_READY, "ready", false) \
macro(SCHED_COMMIT, "commit", false) \
macro(SCHED_MOVE_TO_RESUME_QUEUE, "moveToResumeQueue", false) \
macro(SCHED_DELAY, "delay", false) \
macro(SCHED_DO_POST_UPDATES, "doPostUpdates", false) \
macro(SCHED_ENQUEUE, "enqueue", false) \
macro(SCHED_EVALUATE, "evaluate", false) \
macro(SCHED_EVALUATION, "evaluation", false) \
macro(SCHED_POST_UPDATE, "postUpdate", false) \
macro(SCHED_RESUME, "resume", false) \
macro(SCHED_RESUME_ZERO_DELAY, "resumeZeroDelay", false) \
macro(SCHED_RESUMPTION, "resumption", false) \
macro(SCHED_TRIGGER, "trigger", false) \
macro(SCHED_WAIT_FOREVER, "waitForever", false) \
macro(UNPACKED_ASSIGN, "assign", false) \
macro(UNPACKED_FILL, "fill", false) \
macro(UNPACKED_NEQ, "neq", true)
// clang-format on
class VCMethod final {
public:
// Entries in this table need to match below VCMethod::s_itemData[] table
enum en : uint8_t {
_NONE, // Unknown
ARRAY_AND,
ARRAY_AT,
ARRAY_AT_BACK,
ARRAY_AT_WRITE,
ARRAY_FIND,
ARRAY_FIND_FIRST,
ARRAY_FIND_FIRST_INDEX,
ARRAY_FIND_INDEX,
ARRAY_FIND_LAST,
ARRAY_FIND_LAST_INDEX,
ARRAY_FIRST,
ARRAY_INSIDE,
ARRAY_LAST,
ARRAY_MAP,
ARRAY_MAX,
ARRAY_MIN,
ARRAY_NEXT,
ARRAY_OR,
ARRAY_POP_BACK,
ARRAY_POP_FRONT,
ARRAY_PREV,
ARRAY_PRODUCT,
ARRAY_PUSH_BACK,
ARRAY_PUSH_FRONT,
ARRAY_REVERSE,
ARRAY_RSORT,
ARRAY_R_AND,
ARRAY_R_OR,
ARRAY_R_PRODUCT,
ARRAY_R_SUM,
ARRAY_R_XOR,
ARRAY_SHUFFLE,
ARRAY_SORT,
ARRAY_SUM,
ARRAY_UNIQUE,
ARRAY_UNIQUE_INDEX,
ARRAY_XOR,
ASSOC_CLEAR,
ASSOC_ERASE,
ASSOC_EXISTS,
ASSOC_FIRST,
ASSOC_NEXT,
ASSOC_SIZE,
CLASS_SET_RANDMODE,
DYN_AT_WRITE_APPEND,
DYN_AT_WRITE_APPEND_BACK,
DYN_CLEAR,
DYN_ERASE,
DYN_INSERT,
DYN_POP,
DYN_POP_FRONT,
DYN_PUSH,
DYN_PUSH_FRONT,
DYN_RENEW,
DYN_RENEW_COPY,
DYN_RESIZE,
DYN_SIZE,
DYN_SLICE,
DYN_SLICE_ASSIGN,
DYN_SLICE_ASSIGN_BACK_BACK,
DYN_SLICE_ASSIGN_FRONT_BACK,
DYN_SLICE_BACK_BACK,
DYN_SLICE_FRONT_BACK,
EVENT_CLEAR_FIRED,
EVENT_CLEAR_TRIGGERED,
EVENT_FIRE,
EVENT_IS_FIRED,
EVENT_IS_TRIGGERED,
FORCE_ADD,
FORCE_READ,
FORCE_READ_INDEX,
FORCE_READ_SEL,
FORCE_RELEASE,
FORCE_TOUCH,
FORK_DONE,
FORK_INIT,
FORK_JOIN,
FORK_ON_KILL,
RANDOMIZER_BASIC_STD_RANDOMIZATION,
RANDOMIZER_CLEARCONSTRAINTS,
RANDOMIZER_CLEARALL,
RANDOMIZER_DISABLE_SOFT,
RANDOMIZER_HARD,
RANDOMIZER_SOFT,
RANDOMIZER_UNIQUE,
RANDOMIZER_MARK_RANDC,
RANDOMIZER_SOLVE_BEFORE,
RANDOMIZER_PIN_VAR,
RANDOMIZER_WRITE_VAR,
RANDOMIZER_SET_VAR_DISABLED,
RANDOMIZER_CLEAR_VAR_DISABLED,
RANDOMIZER_MARK_VAR_STATIC,
RANDOMIZER_SET_STATIC_RANDMODE,
RNG_GET_RANDSTATE,
RNG_SET_RANDSTATE,
SCHED_ANY_TRIGGERED,
SCHED_AWAITING_CURRENT_TIME,
SCHED_AWAITING_ZERO_DELAY,
SCHED_READY,
SCHED_COMMIT,
SCHED_MOVE_TO_RESUME_QUEUE,
SCHED_DELAY,
SCHED_DO_POST_UPDATES,
SCHED_ENQUEUE,
SCHED_EVALUATE,
SCHED_EVALUATION,
SCHED_POST_UPDATE,
SCHED_RESUME,
SCHED_RESUME_ZERO_DELAY,
SCHED_RESUMPTION,
SCHED_TRIGGER,
UNPACKED_ASSIGN,
UNPACKED_FILL,
UNPACKED_NEQ,
_ENUM_MAX // Leave last
#define VL_CMETHOD_ID(id, method, pure) id,
FOR_EACH_CMETHOD(VL_CMETHOD_ID)
#undef VL_CMETHOD_ID
_ENUM_MAX // Leave last
};
private:
struct Item final {
enum en m_e; // Method's enum mnemonic, for checking
const char* m_name; // Method name, printed into C++
bool m_pure; // Method being called is pure
};
static Item s_itemData[];
public:
enum en m_e;
VCMethod()
: m_e{_NONE} {}
@@ -919,11 +946,26 @@ public:
explicit VCMethod(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const VL_PURE { return s_itemData[m_e].m_name; }
bool isPure() const VL_PURE { return s_itemData[m_e].m_pure; }
const char* ascii() const VL_PURE {
static const char* const values[] = {
#define VL_CMETHOD_NAME(id, method, pure) method,
FOR_EACH_CMETHOD(VL_CMETHOD_NAME)
#undef VL_CMETHOD_NAME
"_ENUM_MAX" //
};
return values[m_e];
}
bool isPure() const VL_PURE {
static const bool values[] = {
#define VL_CMETHOD_PURE(id, method, pure) pure,
FOR_EACH_CMETHOD(VL_CMETHOD_PURE)
#undef VL_CMETHOD_PURE
false //
};
return values[m_e];
}
// Return array method for given name
static VCMethod arrayMethod(const string& name);
static void selfTest();
};
constexpr bool operator==(const VCMethod& lhs, const VCMethod& rhs) { return lhs.m_e == rhs.m_e; }
constexpr bool operator==(const VCMethod& lhs, VCMethod::en rhs) { return lhs.m_e == rhs; }
@@ -932,127 +974,7 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
return os << rhs.ascii();
}
// Entries in this table need to match above VCMethod enum table
//
// {Mnemonic, C++ method, pure}
#define V3AST_VCMETHOD_ITEMDATA_DECL \
VCMethod::Item VCMethod::s_itemData[] \
= {{_NONE, "_none", false}, \
{ARRAY_AND, "and", true}, \
{ARRAY_AT, "at", true}, \
{ARRAY_AT_BACK, "atBack", true}, \
{ARRAY_AT_WRITE, "atWrite", true}, \
{ARRAY_FIND, "find", true}, \
{ARRAY_FIND_FIRST, "find_first", true}, \
{ARRAY_FIND_FIRST_INDEX, "find_first_index", true}, \
{ARRAY_FIND_INDEX, "find_index", true}, \
{ARRAY_FIND_LAST, "find_last", true}, \
{ARRAY_FIND_LAST_INDEX, "find_last_index", true}, \
{ARRAY_FIRST, "first", false}, \
{ARRAY_INSIDE, "inside", true}, \
{ARRAY_LAST, "last", false}, \
{ARRAY_MAP, "map", true}, \
{ARRAY_MAX, "max", true}, \
{ARRAY_MIN, "min", true}, \
{ARRAY_NEXT, "next", false}, \
{ARRAY_OR, "or", true}, \
{ARRAY_POP_BACK, "pop_back", false}, \
{ARRAY_POP_FRONT, "pop_front", false}, \
{ARRAY_PREV, "prev", false}, \
{ARRAY_PRODUCT, "product", true}, \
{ARRAY_PUSH_BACK, "push_back", false}, \
{ARRAY_PUSH_FRONT, "push_front", false}, \
{ARRAY_REVERSE, "reverse", false}, \
{ARRAY_RSORT, "rsort", false}, \
{ARRAY_R_AND, "r_and", true}, \
{ARRAY_R_OR, "r_or", true}, \
{ARRAY_R_PRODUCT, "r_product", true}, \
{ARRAY_R_SUM, "r_sum", true}, \
{ARRAY_R_XOR, "r_xor", true}, \
{ARRAY_SHUFFLE, "shuffle", false}, \
{ARRAY_SORT, "sort", false}, \
{ARRAY_SUM, "sum", true}, \
{ARRAY_UNIQUE, "unique", true}, \
{ARRAY_UNIQUE_INDEX, "unique_index", true}, \
{ARRAY_XOR, "xor", true}, \
{ASSOC_CLEAR, "clear", false}, \
{ASSOC_ERASE, "erase", false}, \
{ASSOC_EXISTS, "exists", true}, \
{ASSOC_FIRST, "first", false}, \
{ASSOC_NEXT, "next", false}, \
{ASSOC_SIZE, "size", true}, \
{CLASS_SET_RANDMODE, "set_randmode", false}, \
{DYN_AT_WRITE_APPEND, "atWriteAppend", false}, \
{DYN_AT_WRITE_APPEND_BACK, "atWriteAppendBack", false}, \
{DYN_CLEAR, "clear", false}, \
{DYN_ERASE, "erase", false}, \
{DYN_INSERT, "insert", false}, \
{DYN_POP, "pop", false}, \
{DYN_POP_FRONT, "pop_front", false}, \
{DYN_PUSH, "push", false}, \
{DYN_PUSH_FRONT, "push_front", false}, \
{DYN_RENEW, "renew", false}, \
{DYN_RENEW_COPY, "renew_copy", false}, \
{DYN_RESIZE, "resize", false}, \
{DYN_SIZE, "size", true}, \
{DYN_SLICE, "slice", true}, \
{DYN_SLICE_ASSIGN, "sliceAssign", false}, \
{DYN_SLICE_ASSIGN_BACK_BACK, "sliceAssignBackBack", false}, \
{DYN_SLICE_ASSIGN_FRONT_BACK, "sliceAssignFrontBack", false}, \
{DYN_SLICE_BACK_BACK, "sliceBackBack", true}, \
{DYN_SLICE_FRONT_BACK, "sliceFrontBack", true}, \
{EVENT_CLEAR_FIRED, "clearFired", false}, \
{EVENT_CLEAR_TRIGGERED, "clearTriggered", false}, \
{EVENT_FIRE, "fire", false}, \
{EVENT_IS_FIRED, "isFired", true}, \
{EVENT_IS_TRIGGERED, "isTriggered", true}, \
{FORCE_ADD, "addForce", false}, \
{FORCE_READ, "read", true}, \
{FORCE_READ_INDEX, "readIndex", true}, \
{FORCE_READ_SEL, "readSel", true}, \
{FORCE_RELEASE, "release", false}, \
{FORCE_TOUCH, "touch", false}, \
{FORK_DONE, "done", false}, \
{FORK_INIT, "init", false}, \
{FORK_JOIN, "join", false}, \
{FORK_ON_KILL, "onKill", false}, \
{RANDOMIZER_BASIC_STD_RANDOMIZATION, "basicStdRandomization", false}, \
{RANDOMIZER_CLEARCONSTRAINTS, "clearConstraints", false}, \
{RANDOMIZER_CLEARALL, "clearAll", false}, \
{RANDOMIZER_DISABLE_SOFT, "disable_soft", false}, \
{RANDOMIZER_HARD, "hard", false}, \
{RANDOMIZER_SOFT, "soft", false}, \
{RANDOMIZER_UNIQUE, "rand_unique", false}, \
{RANDOMIZER_MARK_RANDC, "markRandc", false}, \
{RANDOMIZER_SOLVE_BEFORE, "solveBefore", false}, \
{RANDOMIZER_PIN_VAR, "pin_var", false}, \
{RANDOMIZER_WRITE_VAR, "write_var", false}, \
{RANDOMIZER_SET_VAR_DISABLED, "set_var_disabled", false}, \
{RANDOMIZER_CLEAR_VAR_DISABLED, "clear_var_disabled", false}, \
{RANDOMIZER_MARK_VAR_STATIC, "mark_var_static", false}, \
{RANDOMIZER_SET_STATIC_RANDMODE, "set_static_randmode", false}, \
{RNG_GET_RANDSTATE, "__Vm_rng.get_randstate", true}, \
{RNG_SET_RANDSTATE, "__Vm_rng.set_randstate", false}, \
{SCHED_ANY_TRIGGERED, "anyTriggered", false}, \
{SCHED_AWAITING_CURRENT_TIME, "awaitingCurrentTime", true}, \
{SCHED_AWAITING_ZERO_DELAY, "awaitingZeroDelay", true}, \
{SCHED_READY, "ready", false}, \
{SCHED_COMMIT, "commit", false}, \
{SCHED_MOVE_TO_RESUME_QUEUE, "moveToResumeQueue", false}, \
{SCHED_DELAY, "delay", false}, \
{SCHED_DO_POST_UPDATES, "doPostUpdates", false}, \
{SCHED_ENQUEUE, "enqueue", false}, \
{SCHED_EVALUATE, "evaluate", false}, \
{SCHED_EVALUATION, "evaluation", false}, \
{SCHED_POST_UPDATE, "postUpdate", false}, \
{SCHED_RESUME, "resume", false}, \
{SCHED_RESUME_ZERO_DELAY, "resumeZeroDelay", false}, \
{SCHED_RESUMPTION, "resumption", false}, \
{SCHED_TRIGGER, "trigger", false}, \
{UNPACKED_ASSIGN, "assign", false}, \
{UNPACKED_FILL, "fill", false}, \
{UNPACKED_NEQ, "neq", true}, \
{_ENUM_MAX, "_ENUM_MAX", false}};
#undef FOR_EACH_CMETHOD
// ######################################################################
@@ -1509,6 +1431,110 @@ constexpr bool operator==(VEdgeType::en lhs, const VEdgeType& rhs) { return lhs
// ######################################################################
// Enumeration of the model's evaluation entry points. Fields:
// is iterated, has triggers, takes 'firstIteration' flag, is slow
// clang-format off
#define FOR_EACH_EVAL(macro) \
/* id, iterated, hasTrigs, firstIt, slow */ \
macro(STATIC, false, false, false, true) \
macro(INITIAL, false, false, false, true) \
macro(STL, true, true, true, true) \
macro(SAMPLE, false, false, false, false) \
macro(ICO, true, true, true, false) \
macro(ACT, true, true, false, false) \
macro(INACT, true, false, false, false) \
macro(NBA, true, true, false, false) \
macro(OBS, true, true, false, false) \
macro(REACT, true, true, false, false) \
macro(POSTPONED, false, false, false, false) \
macro(FINAL, false, false, false, true)
// clang-format on
class VEval final {
public:
enum en : uint8_t {
#define VL_EVAL_ID(id, iterated, triggers, first, slow) id,
FOR_EACH_EVAL(VL_EVAL_ID)
#undef VL_EVAL_ID
_ENUM_END
};
enum en m_e;
const char* ascii() const {
static const char* const values[] = {
#define VL_EVAL_NAME(id, iterated, triggers, first, slow) #id,
FOR_EACH_EVAL(VL_EVAL_NAME)
#undef VL_EVAL_NAME
"_ENUM_END" //
};
return values[m_e];
}
bool isIterated() const {
static const bool values[] = {
#define VL_EVAL_IS_ITERATED(id, iterated, triggers, first, slow) iterated,
FOR_EACH_EVAL(VL_EVAL_IS_ITERATED)
#undef VL_EVAL_IS_ITERATED
false //
};
return values[m_e];
}
bool hasTriggers() const {
static const bool values[] = {
#define VL_EVAL_HAS_TRIGGERS(id, iterated, triggers, first, slow) triggers,
FOR_EACH_EVAL(VL_EVAL_HAS_TRIGGERS)
#undef VL_EVAL_HAS_TRIGGERS
false //
};
return values[m_e];
}
bool firstIteration() const {
static const bool values[] = {
#define VL_EVAL_FIRST(id, iterated, triggers, first, slow) first,
FOR_EACH_EVAL(VL_EVAL_FIRST)
#undef VL_EVAL_FIRST
false //
};
return values[m_e];
}
bool slow() const {
static const bool values[] = {
#define VL_EVAL_SLOW(id, iterated, triggers, first, slow) slow,
FOR_EACH_EVAL(VL_EVAL_SLOW)
#undef VL_EVAL_SLOW
false //
};
return values[m_e];
}
// Short name
std::string tag() const { return VString::downcase(ascii()); }
// Name of the generated entry point function
std::string funcName() const { return "_eval_" + tag(); }
// Name of the VerilatedModel virtual method invoking the entry point
std::string evalMethod() const { return "eval" + capitalizedTag(); }
// Name of the function dumping the triggers of this region
std::string dumpTriggersFuncName() const { return "_eval_dump_triggers__" + tag(); }
// Name of the VerilatedModel virtual method dumping this region's triggers
std::string dumpTriggersMethod() const { return "dumpTriggers" + capitalizedTag(); }
// cppcheck-suppress noExplicitConstructor
VEval(en _e)
: m_e{_e} {}
explicit VEval(int _e)
: m_e(static_cast<en>(_e)) {}
operator en() const { return m_e; }
private:
std::string capitalizedTag() const {
const std::string name = ascii();
return name.substr(0, 1) + VString::downcase(name.substr(1));
}
};
#undef FOR_EACH_EVAL
// ######################################################################
class VFwdType final {
public:
enum en : uint8_t { NONE, ENUM, STRUCT, UNION, CLASS, INTERFACE_CLASS, GENERIC_INTERFACE };
+3 -1
View File
@@ -146,6 +146,7 @@ bool AstBasicDType::ascending() const {
bool AstActive::hasClocked() const { return m_sentreep->hasClocked(); }
bool AstActive::hasCombo() const { return m_sentreep->hasCombo(); }
bool AstActive::hasInitial() const { return m_sentreep->hasInitial(); }
bool AstActive::hasStatic() const { return m_sentreep->hasStatic(); }
AstAlways::AstAlways(AstAssignW* assignp)
@@ -160,7 +161,8 @@ AstElabDisplay::AstElabDisplay(FileLine* fl, VDisplayType dispType, AstNodeExpr*
bool AstVar::sameNode(const AstNode* samep) const {
const AstVar* const asamep = VN_DBG_AS(samep, Var);
return m_name == asamep->m_name && varType() == asamep->varType();
return m_name == asamep->m_name && varType() == asamep->varType()
&& covergroupRefMember() == asamep->covergroupRefMember();
}
AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp)
+19 -5
View File
@@ -38,7 +38,6 @@ class AstNodeDType VL_NOT_FINAL : public AstNode {
int m_widthMin
= 0; // (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation
VSigning m_numeric; // (also in AstTypeTable::Key) Node is signed
// Other members
bool m_generic = false; // Simple globally referenced type, don't garbage collect
// Unique number assigned to each dtype during creation for IEEE matching
static int s_uniqueNum;
@@ -584,6 +583,7 @@ class AstClassRefDType final : public AstNodeDType {
//
// @astgen ptr := m_classp : Optional[AstClass] // data type pointed to, BELOW the AstTypedef
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Package hierarchy
bool m_rawPointer = false; // Emit as a non-owning C++ pointer rather than VlClassRef
public:
AstClassRefDType(FileLine* fl, AstClass* classp, AstPin* paramsp)
: ASTGEN_SUPER_ClassRefDType(fl)
@@ -595,7 +595,8 @@ public:
// METHODS
bool sameNode(const AstNode* samep) const override {
const AstClassRefDType* const asamep = VN_DBG_AS(samep, ClassRefDType);
return (m_classp == asamep->m_classp && m_classOrPackagep == asamep->m_classOrPackagep);
return (m_classp == asamep->m_classp && m_classOrPackagep == asamep->m_classOrPackagep
&& m_rawPointer == asamep->m_rawPointer);
}
bool similarDTypeNode(const AstNodeDType* samep) const override;
void dump(std::ostream& str = std::cout) const override;
@@ -613,6 +614,9 @@ public:
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
AstClass* classp() const VL_MT_STABLE { return m_classp; }
void classp(AstClass* nodep) { m_classp = nodep; }
bool rawPointer() const { return m_rawPointer; }
void rawPointer(bool flag) { m_rawPointer = flag; }
static void selfTest();
bool isCompound() const override { return true; }
};
class AstConstDType final : public AstNodeDType {
@@ -868,6 +872,8 @@ public:
dtypep(this);
}
ASTGEN_MEMBERS_AstIfaceGenericDType;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
void dumpSmall(std::ostream& str) const override;
bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; }
@@ -918,7 +924,8 @@ public:
: ASTGEN_SUPER_IfaceRefDType(fl)
, m_modportFileline{modportFl}
, m_cellName{cellName}
, m_ifaceName{ifaceName} {
, m_ifaceName{ifaceName}
, m_modportName{modport} {
addParamsp(paramsp);
}
ASTGEN_MEMBERS_AstIfaceRefDType;
@@ -968,7 +975,7 @@ class AstMemberDType final : public AstNodeDType {
string m_tag; // Holds the string of the verilator tag -- used in JSON output.
int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member
bool m_constrainedRand = false;
// UNSUP: int m_randType; // Randomization type (IEEE)
VRandAttr m_rand; // Randomizability of this member (rand, randc, etc)
public:
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
AstNode* valuep)
@@ -1025,6 +1032,8 @@ public:
}
bool isConstrainedRand() const { return m_constrainedRand; }
void markConstrainedRand(bool flag) { m_constrainedRand = flag; }
VRandAttr rand() const { return m_rand; }
void rand(const VRandAttr flag) { m_rand = flag; }
};
class AstNBACommitQueueDType final : public AstNodeDType {
// @astgen ptr := m_subDTypep : AstNodeDType // Type of the corresponding variable
@@ -1038,7 +1047,8 @@ public:
dtypep(this);
}
ASTGEN_MEMBERS_AstNBACommitQueueDType;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
AstNodeDType* subDTypep() const override VL_MT_STABLE { return m_subDTypep; }
bool partial() const { return m_partial; }
bool sameNode(const AstNode* samep) const override {
@@ -1108,6 +1118,8 @@ public:
ASTGEN_MEMBERS_AstParseTypeDType;
AstNodeDType* dtypep() const VL_MT_STABLE { return nullptr; }
// METHODS
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
int widthAlignBytes() const override { return 0; }
@@ -1452,6 +1464,8 @@ public:
widthFromSub(subDTypep());
}
ASTGEN_MEMBERS_AstUnpackArrayDType;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
bool sameNode(const AstNode* samep) const override {
const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType);
+83 -45
View File
@@ -237,8 +237,9 @@ private:
string m_name; // Name of variable
string m_dotted; // Dotted part of scope the name()ed task/func is under or ""
string m_inlinedDots; // Dotted hierarchy flattened out
bool m_pli = false; // Pli system call ($name)
bool m_containsGenBlock = false; // Contains gen block reference
bool m_pli = false; // Pli system call ($name)
bool m_superReference = false; // Called with super reference
VIsCached m_purity; // Pure state
protected:
@@ -263,10 +264,12 @@ public:
void dotted(const string& name) { m_dotted = name; }
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
bool pli() const { return m_pli; }
void pli(bool flag) { m_pli = flag; }
bool containsGenBlock() const { return m_containsGenBlock; }
void containsGenBlock(const bool flag) { m_containsGenBlock = flag; }
bool pli() const { return m_pli; }
void pli(bool flag) { m_pli = flag; }
bool superReference() const { return m_superReference; }
void superReference(bool flag) { m_superReference = flag; }
bool isPure() override;
bool sameNode(const AstNode* samep) const override {
const AstNodeFTaskRef* const asamep = VN_DBG_AS(samep, NodeFTaskRef);
@@ -276,7 +279,8 @@ public:
&& dotted() == asamep->dotted() //
&& inlinedDots() == asamep->inlinedDots() //
&& pli() == asamep->pli() //
&& containsGenBlock() == asamep->containsGenBlock();
&& containsGenBlock() == asamep->containsGenBlock()
&& superReference() == asamep->superReference();
}
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
string emitC() final override { V3ERROR_NA_RETURN(""); }
@@ -681,9 +685,9 @@ public:
init(text, setwidth);
}
ASTGEN_MEMBERS_AstCExpr;
// METHODS
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
// METHODS
bool cleanOut() const override { return true; }
std::string emitC() override { V3ERROR_NA_RETURN(""); }
std::string emitVerilog() override { V3ERROR_NA_RETURN(""); }
@@ -714,6 +718,8 @@ public:
, m_pure{true} {}
ASTGEN_MEMBERS_AstCExprUser;
// METHODS
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
bool cleanOut() const override { return false; }
std::string emitC() override { V3ERROR_NA_RETURN(""); }
std::string emitVerilog() override { V3ERROR_NA_RETURN(""); }
@@ -905,7 +911,7 @@ public:
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
// There's no classOrPackagep(); use classOrPackageNodep() to get Node,
// or iterating to package with classOrPackageSkipp()
AstNodeModule* classOrPackageSkipp() const;
AstNodeModule* classOrPackageSkipp(const bool doRefs = true) const;
AstNode* classOrPackageNodep() const { return m_classOrPackageNodep; }
void classOrPackageNodep(AstNode* nodep) { m_classOrPackageNodep = nodep; }
void classOrPackagep(AstNodeModule* nodep) {
@@ -1159,6 +1165,16 @@ public:
, m_num(this, 1, 0) { // Need () constructor
dtypeSetBit();
}
// False but created due to reporting earlier error; suppress some later errors
class BitFalseErroring {};
AstConst(FileLine* fl, BitFalseErroring)
: ASTGEN_SUPER_Const(fl)
, m_num(this, 1, 0) { // Need () constructor
dtypeSetBit();
FileLine* const newfl = new FileLine{fileline()};
newfl->erroringOn(true);
fileline(newfl);
}
// Shorthand const 1 (or with argument 0/1), dtype should be a bit of size 1
class BitTrue {};
AstConst(FileLine* fl, BitTrue, bool on = true)
@@ -1358,6 +1374,8 @@ public:
this->weightp(weightp);
}
ASTGEN_MEMBERS_AstDistItem;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string emitVerilog() override { return "%l "s + (m_isWhole ? ":/" : ":=") + " %r"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return false; } // NA
@@ -2022,21 +2040,33 @@ class AstPast final : public AstNodeExpr {
// @astgen op1 := exprp : AstNodeExpr
// @astgen op2 := ticksp : Optional[AstNodeExpr]
// @astgen op3 := sentreep : Optional[AstSenTree]
// @astgen op4 := initp : Optional[AstNodeExpr] // Default sampled expression
// True only for history synthesized as property-evaluation bookkeeping (for example, |=> or
// NFA local-variable capture). Source $past and AstPast nodes lowering IEEE sampled-value
// functions such as $rose/$fell must remain false so they receive default initialization.
bool m_propertyTiming : 1;
public:
AstPast(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* ticksp = nullptr,
AstSenTree* sentreep = nullptr)
: ASTGEN_SUPER_Past(fl) {
AstSenTree* sentreep = nullptr, bool propertyTiming = false)
: ASTGEN_SUPER_Past(fl)
, m_propertyTiming{propertyTiming} {
this->exprp(exprp);
this->ticksp(ticksp);
this->sentreep(sentreep);
}
ASTGEN_MEMBERS_AstPast;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool sameNode(const AstNode* samep) const override {
return m_propertyTiming == VN_DBG_AS(samep, Past)->m_propertyTiming;
}
bool propertyTiming() const { return m_propertyTiming; }
bool isSystemFunc() const override { return true; }
};
class AstPatMember final : public AstNodeExpr {
@@ -2163,6 +2193,8 @@ public:
dtypeSetUInt32();
}
ASTGEN_MEMBERS_AstRand;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string emitVerilog() override {
return seedp() ? (m_urandom ? "%f$urandom(%l)" : "%f$random(%l)")
: (m_urandom ? "%f$urandom()" : "%f$random()");
@@ -2262,24 +2294,25 @@ public:
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
};
class AstSConsRep final : public AstNodeExpr {
// Consecutive repetition [*N], [*N:M], [+], [*] (IEEE 1800-2023 16.9.2)
// Consecutive repetition [*N], [*N:M], [*N:$], [+], [*] (IEEE 1800-2023 16.9.2)
// op1 := exprp -- the repeated expression
// op2 := countp -- min repetition count (N); always a positive constant after V3Width
// op2 := countp -- min repetition count (N); always a non-negative constant after V3Width
// op3 := maxCountp -- max repetition count (M); nullptr when exact or unbounded
//
// Encoding:
// [*N]: countp=N, maxCountp=nullptr, unbounded=false
// [*N:M]: countp=N, maxCountp=M, unbounded=false
// [*N:$]: countp=N, maxCountp=nullptr, unbounded=true
// [+]: countp=1, maxCountp=nullptr, unbounded=true (= [*1:$])
// [*]: countp=0, maxCountp=nullptr, unbounded=true (= [*0:$])
//
// Lowering:
// Exact [*N] standalone: V3AssertPre saturating counter
// All other forms and all SExpr-contained forms: V3AssertProp PExpr loop
// All other forms and all SExpr-contained forms: V3AssertNfa
// @astgen op1 := exprp : AstNodeExpr
// @astgen op2 := countp : AstNodeExpr
// @astgen op3 := maxCountp : Optional[AstNodeExpr]
const bool m_unbounded = false; // True for [+] and [*] (upper bound is $)
const bool m_unbounded = false; // True when the upper bound is $
public:
// Exact [*N]
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
@@ -2287,7 +2320,7 @@ public:
this->exprp(exprp);
this->countp(countp);
}
// Range [*N:M] or unbounded [+]/[*]
// Range [*N:M] or unbounded [*N:$]/[+]/[*]
AstSConsRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp, AstNodeExpr* maxCountp,
bool unbounded)
: ASTGEN_SUPER_SConsRep(fl)
@@ -2554,19 +2587,26 @@ public:
class AstSampled final : public AstNodeExpr {
// Verilog $sampled
// @astgen op1 := exprp : AstNode<AstNodeExpr|AstPropSpec>
bool m_internal : 1; // Internally created, not from a source $sampled
public:
AstSampled(FileLine* fl, AstNode* exprp, AstNodeDType* dtypep)
: ASTGEN_SUPER_Sampled(fl) {
AstSampled(FileLine* fl, AstNode* exprp, AstNodeDType* dtypep, bool internal = false)
: ASTGEN_SUPER_Sampled(fl)
, m_internal{internal} {
this->exprp(exprp);
this->dtypep(dtypep);
}
ASTGEN_MEMBERS_AstSampled;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string emitVerilog() override { return "$sampled(%l)"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return 0; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool sameNode(const AstNode* samep) const override {
return m_internal == VN_DBG_AS(samep, Sampled)->m_internal;
}
bool internal() const { return m_internal; }
bool isSystemFunc() const override { return true; }
};
class AstScopeName final : public AstNodeExpr {
@@ -3063,6 +3103,8 @@ public:
dtypeSetInt();
}
ASTGEN_MEMBERS_AstCompareNN;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
out.opCompareNN(lhs, rhs, m_ignoreCase);
}
@@ -3079,6 +3121,7 @@ public:
bool cleanRhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
bool ignoreCase() const { return m_ignoreCase; }
};
class AstConcat final : public AstNodeBiop {
// If you're looking for {#{}}, see AstReplicate
@@ -3258,6 +3301,7 @@ public:
bool sizeMattersRhs() const override { return false; }
bool isSystemFunc() const override { return true; }
int instrCount() const override { return widthInstrs() * 64; }
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
};
class AstFUngetC final : public AstNodeBiop {
public:
@@ -3978,12 +4022,16 @@ public:
class AstSAnd final : public AstNodeBiop {
// Sequence 'and' (IEEE 1800-2023 16.9.5): both operand sequences must match.
// Operates on match sets, not values. For boolean operands, lowered to AstLogAnd.
const bool m_propertyControl; // Parser-generated property if/case branch conjunction
public:
AstSAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
: ASTGEN_SUPER_SAnd(fl, lhsp, rhsp) {
AstSAnd(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp, bool propertyControl = false)
: ASTGEN_SUPER_SAnd(fl, lhsp, rhsp)
, m_propertyControl{propertyControl} {
dtypeSetBit();
}
ASTGEN_MEMBERS_AstSAnd;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
out.opLogAnd(lhs, rhs);
}
@@ -3997,6 +4045,10 @@ public:
bool sizeMattersRhs() const override { return false; }
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
bool isMultiCycleSva() const override { return true; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_LINE
return m_propertyControl == VN_DBG_AS(samep, SAnd)->m_propertyControl; // LCOV_EXCL_LINE
}
bool propertyControl() const { return m_propertyControl; }
};
class AstSIntersect final : public AstNodeBiop {
// Sequence 'intersect' (IEEE 1800-2023 16.9.6): both operands match with equal length.
@@ -4886,9 +4938,11 @@ public:
bool cleanRhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
bool isGateOptimizable() const override { return false; } // AssocSel creates on miss
bool isGateOptimizable() const override {
return !isLValue(); // AssocSel creates on miss
}
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; } // AssocSel creates on miss
bool isPure() override { return !isLValue(); } // AssocSel creates on miss
bool sameNode(const AstNode* /*samep*/) const override { return true; }
int instrCount() const override { return widthInstrs(); }
};
@@ -5044,19 +5098,11 @@ public:
// === AstNodeFTaskRef ===
class AstFuncRef final : public AstNodeFTaskRef {
// A reference to a function
bool m_superReference = false; // Called with super reference
public:
inline AstFuncRef(FileLine* fl, AstFunc* taskp, AstArg* argsp = nullptr);
AstFuncRef(FileLine* fl, const string& name, AstArg* argsp = nullptr)
: ASTGEN_SUPER_FuncRef(fl, name, argsp) {}
ASTGEN_MEMBERS_AstFuncRef;
bool sameNode(const AstNode* samep) const override {
if (!this->AstNodeFTaskRef::sameNode(samep)) return false;
const AstFuncRef* const asamep = VN_DBG_AS(samep, FuncRef);
return superReference() == asamep->superReference();
}
bool superReference() const { return m_superReference; }
void superReference(bool flag) { m_superReference = flag; }
};
class AstMethodCall final : public AstNodeFTaskRef {
// A reference to a member task (or function)
@@ -5097,7 +5143,6 @@ public:
};
class AstTaskRef final : public AstNodeFTaskRef {
// A reference to a task
bool m_superReference = false; // Called with super reference
public:
inline AstTaskRef(FileLine* fl, AstTask* taskp, AstArg* argsp = nullptr);
AstTaskRef(FileLine* fl, const string& name, AstArg* argsp = nullptr)
@@ -5105,13 +5150,6 @@ public:
dtypeSetVoid();
}
ASTGEN_MEMBERS_AstTaskRef;
bool sameNode(const AstNode* samep) const override {
if (!this->AstNodeFTaskRef::sameNode(samep)) return false;
const AstTaskRef* const asamep = VN_DBG_AS(samep, TaskRef);
return superReference() == asamep->superReference();
}
bool superReference() const { return m_superReference; }
void superReference(bool flag) { m_superReference = flag; }
};
// === AstNodePreSel ===
@@ -5392,17 +5430,17 @@ public:
enum FmtType : int { ATOI = 10, ATOHEX = 16, ATOOCT = 8, ATOBIN = 2, ATOREAL = -1 };
private:
const FmtType m_fmt; // Operation type
const FmtType m_fmtType; // Operation type
public:
AstAtoN(FileLine* fl, AstNodeExpr* lhsp, FmtType fmt)
AstAtoN(FileLine* fl, AstNodeExpr* lhsp, FmtType fmtType)
: ASTGEN_SUPER_AtoN(fl, lhsp)
, m_fmt{fmt} {
fmt == ATOREAL ? dtypeSetDouble() : dtypeSetInteger();
, m_fmtType{fmtType} {
fmtType == ATOREAL ? dtypeSetDouble() : dtypeSetInteger();
}
ASTGEN_MEMBERS_AstAtoN;
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAtoN(lhs, m_fmt); }
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAtoN(lhs, m_fmtType); }
string name() const override VL_MT_STABLE {
switch (m_fmt) {
switch (m_fmtType) {
case ATOI: return "atoi";
case ATOHEX: return "atohex";
case ATOOCT: return "atooct";
@@ -5413,7 +5451,7 @@ public:
}
string emitVerilog() override { return "%l." + name() + "()"; }
string emitC() override {
switch (m_fmt) {
switch (m_fmtType) {
case ATOI: return "VL_ATOI_N(%li, 10)";
case ATOHEX: return "VL_ATOI_N(%li, 16)";
case ATOOCT: return "VL_ATOI_N(%li, 8)";
@@ -5425,7 +5463,7 @@ public:
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
FmtType format() const { return m_fmt; }
FmtType fmtType() const { return m_fmtType; }
};
class AstBitsToRealD final : public AstNodeUniop {
public:
+88 -60
View File
@@ -94,6 +94,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
// @astgen op4 := scopeNamep : Optional[AstScopeName]
string m_name; // Name of task
string m_cname; // Name of task if DPI import
string m_dpiCDecl; // Custom DPI-C function declaration
string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8)
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
bool m_taskPublic : 1; // Public task
@@ -199,6 +200,9 @@ public:
void dpiOpenChild(bool flag) { m_dpiOpenChild = flag; }
bool dpiTask() const { return m_dpiTask; }
void dpiTask(bool flag) { m_dpiTask = flag; }
bool dpiCDeclOverride() const { return !m_dpiCDecl.empty(); }
const string& dpiCDecl() const { return m_dpiCDecl; }
void dpiCDecl(const string& cDecl) { m_dpiCDecl = cDecl; }
bool isConstructor() const { return m_isConstructor; }
void isConstructor(bool flag) { m_isConstructor = flag; }
bool isHideLocal() const { return m_isHideLocal; }
@@ -456,6 +460,7 @@ public:
// METHODS
inline bool hasClocked() const;
inline bool hasCombo() const;
inline bool hasInitial() const;
inline bool hasStatic() const;
};
class AstAlias final : public AstNode {
@@ -504,6 +509,7 @@ class AstCFunc final : public AstNode {
// @astgen op1 := argsp : List[AstVar] // Argument (and return value) variables
// @astgen op2 := varsp : List[AstVar] // Local variables
// @astgen op3 := stmtsp : List[AstNode]
// @astgen op4 := scopeNamep : Optional[AstScopeName] // Scoping context for DPI export
//
// @astgen ptr := m_scopep : Optional[AstScope] // Scope that function is under
string m_name;
@@ -511,6 +517,7 @@ class AstCFunc final : public AstNode {
string m_rtnType; // void, bool, or other return type
string m_argTypes; // Argument types
string m_ifdef; // #ifdef symbol around this function
string m_dpiCDecl; // Custom DPI-C function declaration
VBoolOrUnknown m_isConst; // Function is declared const (*this not changed)
bool m_isStatic : 1; // Function is static (no need for a 'this' pointer)
bool m_isTrace : 1; // Function is related to tracing
@@ -640,6 +647,9 @@ public:
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
bool dpiCDeclOverride() const { return !m_dpiCDecl.empty(); }
const string& dpiCDecl() const { return m_dpiCDecl; }
void dpiCDecl(const string& cDecl) { m_dpiCDecl = cDecl; }
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
void recursive(bool flag) { m_recursive = flag; }
bool recursive() const { return m_recursive; }
@@ -650,20 +660,10 @@ public:
void cost(int cost) { m_cost = cost; }
// Special methods
bool emptyBody() const {
return !keepIfEmpty() && !argsp() && !varsp() && !stmtsp() && !isVirtual()
return !keepIfEmpty() && !argsp() && !varsp() && !stmtsp() && !scopeNamep() && !isVirtual()
&& !dpiImportPrototype();
}
};
class AstCLocalScope final : public AstNode {
// Pack statements into an unnamed scope when generating C++
// @astgen op1 := stmtsp : List[AstNode]
public:
AstCLocalScope(FileLine* fl, AstNode* stmtsp)
: ASTGEN_SUPER_CLocalScope(fl) {
addStmtsp(stmtsp);
}
ASTGEN_MEMBERS_AstCLocalScope;
};
class AstCUse final : public AstNode {
// C++ use of a class or #include; indicates need of forward declaration
// Parents: NODEMODULE
@@ -806,7 +806,7 @@ public:
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
VCoverOptionType optionType() const { return m_optType; }
VCoverOptionType optType() const { return m_optType; }
bool typeOption() const { return m_typeOption; }
};
class AstClassExtends final : public AstNode {
@@ -844,7 +844,7 @@ class AstClocking final : public AstNode {
// @astgen op2 := itemsp : List[AstNode]
// @astgen op3 := eventp : Optional[AstVar]
std::string m_name; // Clocking block name
const bool m_isDefault; // True if default clocking
bool m_isDefault; // True if default clocking
const bool m_isGlobal; // True if global clocking
public:
@@ -865,6 +865,7 @@ public:
bool isDefault() const { return m_isDefault; }
bool isGlobal() const { return m_isGlobal; }
AstVar* ensureEventp(bool childDType = false);
void makeDefault() { m_isDefault = true; }
};
class AstClockingItem final : public AstNode {
// Parents: CLOCKING
@@ -1042,22 +1043,6 @@ public:
void isStatic(bool flag) { m_isStatic = flag; }
bool isStatic() const { return m_isStatic; }
};
class AstConstraintBefore final : public AstNode {
// Constraint solve before item
// @astgen op1 := lhssp : List[AstNodeExpr]
// @astgen op2 := rhssp : List[AstNodeExpr]
public:
AstConstraintBefore(FileLine* fl, AstNodeExpr* lhssp, AstNodeExpr* rhssp)
: ASTGEN_SUPER_ConstraintBefore(fl) {
addLhssp(lhssp);
addRhssp(rhssp);
}
ASTGEN_MEMBERS_AstConstraintBefore;
bool isGateOptimizable() const override { return false; }
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]
@@ -1065,7 +1050,7 @@ class AstCoverBin final : public AstNode {
// @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)
const VCoverBinsType m_binsType; // Bin type (eg AUTO, IGNORE, ILLEGAL)
bool m_isArray = false; // Bin is either an auto-sized array of values or transitions
bool m_isWildcard = false; // Bin uses wildcard matching (independent of ignore/illegal)
@@ -1074,10 +1059,10 @@ public:
bool isWildcard = false)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{isIllegal ? VCoverBinsType::BINS_ILLEGAL
: (isIgnore ? VCoverBinsType::BINS_IGNORE
: (isWildcard ? VCoverBinsType::BINS_WILDCARD
: VCoverBinsType::BINS_USER))}
, m_binsType{isIllegal ? VCoverBinsType::BINS_ILLEGAL
: (isIgnore ? VCoverBinsType::BINS_IGNORE
: (isWildcard ? VCoverBinsType::BINS_WILDCARD
: VCoverBinsType::BINS_USER))}
, m_isWildcard{isWildcard} {
addRangesp(rangesp);
}
@@ -1085,7 +1070,7 @@ public:
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{VCoverBinsType::BINS_AUTO}
, m_binsType{VCoverBinsType::BINS_AUTO}
, m_isArray{true} {
this->arraySizep(arraySizep);
}
@@ -1093,13 +1078,13 @@ public:
AstCoverBin(FileLine* fl, const string& name, VCoverBinsType type)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{type} {}
, m_binsType{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_binsType{type}
, m_isArray{isArrayBin} {
UASSERT(transp, "AstCoverBin transition constructor requires non-null transp");
addTransp(transp);
@@ -1108,7 +1093,7 @@ public:
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; }
VCoverBinsType binsType() const { return m_binsType; }
bool isWildcard() const { return m_isWildcard; }
bool isArray() const { return m_isArray; }
void isArray(bool flag) { m_isArray = flag; }
@@ -1116,18 +1101,18 @@ public:
class AstCoverOption final : public AstNode {
// Coverage-option assignment
// @astgen op1 := valuep : AstNodeExpr
const VCoverOptionType m_type; // Option being assigned
const VCoverOptionType m_optType; // Option being assigned
public:
AstCoverOption(FileLine* fl, VCoverOptionType type, AstNodeExpr* valuep)
AstCoverOption(FileLine* fl, VCoverOptionType optType, AstNodeExpr* valuep)
: ASTGEN_SUPER_CoverOption(fl)
, m_type{type} {
, m_optType{optType} {
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; }
VCoverOptionType optType() const { return m_optType; }
};
class AstCoverTransItem final : public AstNode {
// Represents a single transition item: value or value[*N] or value[->N] or value[=N]
@@ -1222,6 +1207,16 @@ public:
ASTGEN_MEMBERS_AstDefParam;
bool sameNode(const AstNode*) const override { return true; }
};
class AstDefaultClocking final : public AstNode {
std::string m_name; // Clocking block name
public:
AstDefaultClocking(FileLine* fl, const std::string& name)
: ASTGEN_SUPER_DefaultClocking(fl)
, m_name{name} {}
ASTGEN_MEMBERS_AstDefaultClocking;
std::string name() const override VL_MT_STABLE { return m_name; }
};
class AstDefaultDisable final : public AstNode {
// @astgen op1 := condp : AstNodeExpr
@@ -1244,6 +1239,8 @@ public:
, m_name{vname}
, m_cname{cname} {}
ASTGEN_MEMBERS_AstDpiExport;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& name) override { m_name = name; }
string cname() const { return m_cname; }
@@ -1447,8 +1444,6 @@ class AstNetlist final : public AstNode {
// @astgen ptr := m_dollarUnitPkgp : Optional[AstPackage] // $unit
// @astgen ptr := m_stdPackagep : Optional[AstPackage] // SystemVerilog std package
// @astgen ptr := m_stdPackageProcessp : Optional[AstClass] // SystemVerilog std process class
// @astgen ptr := m_evalp : Optional[AstCFunc] // The '_eval' function
// @astgen ptr := m_evalNbap : Optional[AstCFunc] // The '_eval__nba' function
// @astgen ptr := m_dpiExportTriggerp : Optional[AstVarScope] // DPI export trigger variable
// @astgen ptr := m_delaySchedulerp : Optional[AstVar] // Delay scheduler variable
// @astgen ptr := m_nbaEventp : Optional[AstVarScope] // NBA event variable
@@ -1466,6 +1461,10 @@ class AstNetlist final : public AstNode {
// AstConst itself, as AstConst is a very common node and only a small fraction carry this
// name.
std::unordered_map<const AstConst*, string> m_constOrigParamNames;
// The model's evaluation entry point functions
std::array<AstCFunc*, VEval::_ENUM_END> m_evalFuncps{};
// The trigger dump function of each region if exists, otherwise nullptr
std::array<AstCFunc*, VEval::_ENUM_END> m_dumpTriggersFuncps{};
public:
AstNetlist();
@@ -1489,10 +1488,10 @@ public:
void astConstOrigParamNameErase(const AstConst* nodep);
AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; }
AstPackage* dollarUnitPkgAddp();
AstCFunc* evalp() const { return m_evalp; }
void evalp(AstCFunc* funcp) { m_evalp = funcp; }
AstCFunc* evalNbap() const { return m_evalNbap; }
void evalNbap(AstCFunc* funcp) { m_evalNbap = funcp; }
AstCFunc* evalFuncp(VEval eval) const { return m_evalFuncps[eval]; }
void evalFuncp(VEval eval, AstCFunc* funcp) { m_evalFuncps[eval] = funcp; }
AstCFunc* dumpTriggersFuncp(VEval eval) const { return m_dumpTriggersFuncps[eval]; }
void dumpTriggersFuncp(VEval eval, AstCFunc* funcp) { m_dumpTriggersFuncps[eval] = funcp; }
AstVarScope* dpiExportTriggerp() const { return m_dpiExportTriggerp; }
void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; }
AstVar* delaySchedulerp() const { return m_delaySchedulerp; }
@@ -1528,6 +1527,9 @@ public:
const std::string& name = resolvedTopModuleName();
return prettyName(name.empty() ? v3Global.rootp()->topModulep()->name() : name);
}
// Record statistics for eval functions
void addEvalStats(const std::string& phase);
};
class AstPackageExport final : public AstNode {
// A package export declaration
@@ -1679,17 +1681,25 @@ class AstPropSpec final : public AstNode {
// @astgen op1 := sensesp : Optional[AstSenItem]
// @astgen op2 := disablep : Optional[AstNodeExpr]
// @astgen op3 := propp : AstNode
// @astgen op4 := matchCountp : Optional[AstNodeExpr] // Cover sequence matches this tick
VPropStrength m_propStrength = VPropStrength::DEFAULT;
public:
AstPropSpec(FileLine* fl, AstSenItem* sensesp, AstNodeExpr* disablep, AstNode* propp)
: ASTGEN_SUPER_PropSpec(fl) {
AstPropSpec(FileLine* fl, AstSenItem* sensesp, AstNodeExpr* disablep, AstNode* propp,
VPropStrength propStrength = VPropStrength::DEFAULT)
: ASTGEN_SUPER_PropSpec(fl)
, m_propStrength{propStrength} {
this->sensesp(sensesp);
this->disablep(disablep);
this->propp(propp);
}
ASTGEN_MEMBERS_AstPropSpec;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool hasDType() const override VL_MT_SAFE {
return true;
} // Used under Cover, which expects a bool child
VPropStrength propStrength() const { return m_propStrength; }
};
class AstPull final : public AstNode {
// @astgen op1 := lhsp : AstNodeExpr
@@ -1706,7 +1716,9 @@ public:
bool sameNode(const AstNode* samep) const override {
return direction() == VN_DBG_AS(samep, Pull)->direction();
}
uint32_t direction() const { return static_cast<uint32_t>(m_direction); }
bool direction() const { return m_direction; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstScope final : public AstNode {
// A particular usage of a cell
@@ -2091,6 +2103,8 @@ public:
addOFieldsp(oFieldsp2);
}
ASTGEN_MEMBERS_AstUdpTableLine;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
int udpIsCombo() const { return m_udpIsCombo; }
};
class AstUdpTableLineVal final : public AstNode {
@@ -2152,7 +2166,8 @@ class AstVar final : public AstNode {
bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment
bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment
bool m_constPoolEntry : 1; // Constant pool variable
bool m_fileDescr : 1; // File descriptor
bool m_covergroupRefMember : 1; // Persistent covergroup ref/const ref argument
bool m_attrFileDescr : 1; // File descriptor
bool m_gotNansiType : 1; // Linker saw Non-ANSI type declaration
bool m_icoMaybeWritten : 1; // Design might write this input signal - for ico change detect
bool m_isConst : 1; // Table contains constant data
@@ -2173,7 +2188,7 @@ class AstVar final : public AstNode {
bool m_noSubst : 1; // Do not substitute out references
bool m_sampled : 1; // Sampled timing region
bool m_substConstOnly : 1; // Only substitute if constant
bool m_overridenParam : 1; // Overridden parameter by #(...) or defparam
bool m_overriddenParam : 1; // Overridden parameter by #(...) or defparam
bool m_trace : 1; // Trace this variable
bool m_isLatched : 1; // Not assigned in all control paths of combo always
bool m_isForceable : 1; // May be forced/released externally from user C code
@@ -2216,7 +2231,8 @@ class AstVar final : public AstNode {
m_attrFsmResetArc = false;
m_attrFsmArcInclCond = false;
m_constPoolEntry = false;
m_fileDescr = false;
m_covergroupRefMember = false;
m_attrFileDescr = false;
m_gotNansiType = false;
m_icoMaybeWritten = false;
m_isConst = false;
@@ -2237,7 +2253,7 @@ class AstVar final : public AstNode {
m_noSubst = false;
m_sampled = false;
m_substConstOnly = false;
m_overridenParam = false;
m_overriddenParam = false;
m_trace = false;
m_isLatched = false;
m_isForceable = false;
@@ -2356,7 +2372,7 @@ public:
void ansi(bool flag) { m_ansi = flag; }
void declTyped(bool flag) { m_declTyped = flag; }
void sensIfacep(AstIface* nodep) { m_sensIfacep = nodep; }
void attrFileDescr(bool flag) { m_fileDescr = flag; }
void attrFileDescr(bool flag) { m_attrFileDescr = flag; }
void attrScBv(bool flag) { m_attrScBv = flag; }
void attrScBigUint(bool flag) { m_attrScBigUint = flag; }
void attrSFormat(bool flag) { m_attrSFormat = flag; }
@@ -2367,6 +2383,8 @@ public:
void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; }
bool constPoolEntry() const { return m_constPoolEntry; }
void setConstPoolEntry() { m_constPoolEntry = true; }
bool covergroupRefMember() const { return m_covergroupRefMember; }
void covergroupRefMember(bool flag) { m_covergroupRefMember = flag; }
void rand(const VRandAttr flag) { m_rand = flag; }
void usedParam(bool flag) { m_usedParam = flag; }
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
@@ -2420,8 +2438,8 @@ public:
void sampled(bool flag) { m_sampled = flag; }
bool substConstOnly() const { return m_substConstOnly; }
void substConstOnly(bool flag) { m_substConstOnly = flag; }
bool overriddenParam() const { return m_overridenParam; }
void overriddenParam(bool flag) { m_overridenParam = flag; }
bool overriddenParam() const { return m_overriddenParam; }
void overriddenParam(bool flag) { m_overriddenParam = flag; }
void trace(bool flag) { m_trace = flag; }
void isLatched(bool flag) { m_isLatched = flag; }
bool isForceable() const { return m_isForceable; }
@@ -2525,7 +2543,7 @@ public:
bool isPulldown() const { return m_isPulldown; }
bool attrScBv() const { return m_attrScBv; }
bool attrScBigUint() const { return m_attrScBigUint; }
bool attrFileDescr() const { return m_fileDescr; }
bool attrFileDescr() const { return m_attrFileDescr; }
bool attrSFormat() const { return m_attrSFormat; }
bool attrSplitVar() const { return m_attrSplitVar; }
bool attrFsmState() const { return m_attrFsmState; }
@@ -2723,6 +2741,8 @@ public:
AstSequence(FileLine* fl, const string& name, AstNode* stmtp)
: ASTGEN_SUPER_Sequence(fl, name, stmtp) {}
ASTGEN_MEMBERS_AstSequence;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool hasDType() const override VL_MT_SAFE { return true; }
AstNodeFTask* cloneType(const string& name) override {
return new AstSequence{fileline(), name, nullptr};
@@ -2784,11 +2804,14 @@ class AstCoverCross final : public AstNodeFuncCovItem {
// @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only
// @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items;
// // post-LinkParse: empty
// @astgen op4 := iffp : Optional[AstNodeExpr] // Conditional sampling guard
public:
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp)
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp,
AstNodeExpr* iffp = nullptr)
: ASTGEN_SUPER_CoverCross(fl, name) {
UASSERT(itemsp, "AstCoverCross requires at least one coverpoint reference");
addItemsp(itemsp);
this->iffp(iffp);
}
ASTGEN_MEMBERS_AstCoverCross;
void dump(std::ostream& str) const override;
@@ -2889,6 +2912,7 @@ class AstClass final : public AstNodeModule {
// @astgen op4 := extendsp : List[AstClassExtends]
// MEMBERS
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
// @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class
uint32_t m_declTokenNum; // Declaration token number
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?)
@@ -2913,6 +2937,10 @@ public:
bool timescaleMatters() const override { return false; }
AstClassPackage* classOrPackagep() const VL_MT_STABLE { return m_classOrPackagep; }
void classOrPackagep(AstClassPackage* classpackagep) { m_classOrPackagep = classpackagep; }
AstClass* covergroupEnclosingClassp() const VL_MT_STABLE {
return m_covergroupEnclosingClassp;
}
void covergroupEnclosingClassp(AstClass* classp) { m_covergroupEnclosingClassp = classp; }
AstNode* membersp() const VL_MT_STABLE { return stmtsp(); }
void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
bool isCovergroup() const { return m_covergroup; }
+79 -14
View File
@@ -163,6 +163,8 @@ protected:
public:
ASTGEN_MEMBERS_AstNodeIf;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool isGateOptimizable() const override { return false; }
bool isGateDedupable() const override { return true; }
int instrCount() const override { return INSTR_COUNT_BRANCH; }
@@ -190,6 +192,8 @@ public:
this->msbp(msbp);
}
ASTGEN_MEMBERS_AstNodeReadWriteMem;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; }
@@ -262,10 +266,10 @@ public:
bool isOutputter() override { return true; }
VAssertCtlType ctlType() const { return m_ctlType; }
void ctlType(int32_t type) { m_ctlType = VAssertCtlType{type}; }
VAssertType ctlAssertTypes() const { return m_assertTypes; }
void ctlAssertTypes(VAssertType types) { m_assertTypes = types; }
VAssertDirectiveType ctlDirectiveTypes() const { return m_directiveTypes; }
void ctlDirectiveTypes(VAssertDirectiveType types) { m_directiveTypes = types; }
VAssertType assertTypes() const { return m_assertTypes; }
void assertTypes(VAssertType types) { m_assertTypes = types; }
VAssertDirectiveType directiveTypes() const { return m_directiveTypes; }
void directiveTypes(VAssertDirectiveType types) { m_directiveTypes = types; }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
};
@@ -297,6 +301,16 @@ public:
AstSenTree* sentreep() const { return m_sentreep; }
void clearSentreep() { m_sentreep = nullptr; }
};
class AstCLocalScope final : public AstNodeStmt {
// Pack statements into an unnamed scope when generating C++
// @astgen op1 := stmtsp : List[AstNode]
public:
AstCLocalScope(FileLine* fl, AstNode* stmtsp)
: ASTGEN_SUPER_CLocalScope(fl) {
addStmtsp(stmtsp);
}
ASTGEN_MEMBERS_AstCLocalScope;
};
class AstCReturn final : public AstNodeStmt {
// C++ return from a function
// @astgen op1 := lhsp : AstNodeExpr
@@ -382,6 +396,8 @@ public:
, m_fromDollarC{fromDollarC} {}
ASTGEN_MEMBERS_AstCStmtUser;
// METHODS
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool isGateOptimizable() const override { return false; }
bool isOutputter() override { return true; }
bool isPredictOptimizable() const override { return false; }
@@ -447,10 +463,27 @@ public:
, m_name{name}
, m_showAt{showAt} {}
ASTGEN_MEMBERS_AstComment;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; } // * = Text
bool sameNode(const AstNode* samep) const override { return true; } // Ignore name in comments
virtual bool showAt() const { return m_showAt; }
};
class AstConstraintBefore final : public AstNodeStmt {
// Constraint solve before item
// @astgen op1 := lhssp : List[AstNodeExpr]
// @astgen op2 := rhssp : List[AstNodeExpr]
public:
AstConstraintBefore(FileLine* fl, AstNodeExpr* lhssp, AstNodeExpr* rhssp)
: ASTGEN_SUPER_ConstraintBefore(fl) {
addLhssp(lhssp);
addRhssp(rhssp);
}
ASTGEN_MEMBERS_AstConstraintBefore;
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstConstraintExpr final : public AstNodeStmt {
// Constraint expression
// @astgen op1 := exprp : AstNodeExpr
@@ -557,11 +590,11 @@ class AstDelay final : public AstNodeStmt {
// @astgen op3 := rhsp : Optional[AstNodeExpr] // Max bound for cycle range or fall delay
// @astgen op4 := throughoutp : Optional[AstNodeExpr] // Throughout condition (IEEE 16.9.9)
VTimescale m_timeunit; // Delay's time unit
const bool m_isCycle; // True if it is a cycle delay
const bool m_isCycleDelay; // True if it is a cycle delay
public:
AstDelay(FileLine* fl, AstNodeExpr* lhsp, bool isCycle)
AstDelay(FileLine* fl, AstNodeExpr* lhsp, bool isCycleDelay)
: ASTGEN_SUPER_Delay(fl)
, m_isCycle{isCycle} {
, m_isCycleDelay{isCycleDelay} {
this->lhsp(lhsp);
}
ASTGEN_MEMBERS_AstDelay;
@@ -571,11 +604,11 @@ public:
bool sameNode(const AstNode* /*samep*/) const override { return true; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
bool isCycleDelay() const { return m_isCycle; }
bool isRangeDelay() const { return m_isCycle && rhsp() != nullptr; }
bool isCycleDelay() const { return m_isCycleDelay; }
bool isRangeDelay() const { return m_isCycleDelay && rhsp(); }
bool isUnbounded() const { return isRangeDelay() && VN_IS(rhsp(), Unbounded); }
void fallDelay(AstNodeExpr* const fallDelayp) { rhsp(fallDelayp); }
AstNodeExpr* fallDelay() const { return m_isCycle ? nullptr : rhsp(); }
AstNodeExpr* fallDelay() const { return m_isCycleDelay ? nullptr : rhsp(); }
};
class AstDisable final : public AstNodeStmt {
// @astgen op1 := targetRefp : Optional[AstNodeExpr] // Reference to link in V3LinkDot
@@ -598,6 +631,7 @@ public:
explicit AstDisableFork(FileLine* fl)
: ASTGEN_SUPER_DisableFork(fl) {}
ASTGEN_MEMBERS_AstDisableFork;
string verilogKwd() const override { return "disable fork"; }
};
class AstDisplay final : public AstNodeStmt {
// Parents: stmtlist
@@ -667,6 +701,8 @@ public:
bool isPure() override { return false; }
virtual bool cleanOut() const { return true; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
VDumpCtlType ctlType() const { return m_ctlType; }
};
class AstEventControl final : public AstNodeStmt {
@@ -785,6 +821,8 @@ public:
this->operandp(operandp);
}
ASTGEN_MEMBERS_AstFireEvent;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool isDelayed() const { return m_delayed; }
};
class AstInitialAutomaticStmt final : public AstNodeStmt {
@@ -928,6 +966,8 @@ public:
: ASTGEN_SUPER_MonitorOff(fl)
, m_off{off} {}
ASTGEN_MEMBERS_AstMonitorOff;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string verilogKwd() const override { return m_off ? "$monitoroff" : "$monitoron"; }
bool isGateOptimizable() const override { return false; } // Though deleted before opt
bool isPredictOptimizable() const override { return false; } // Though deleted before opt
@@ -940,9 +980,9 @@ public:
bool off() const { return m_off; }
};
class AstPExprClause final : public AstNodeStmt {
const bool m_pass; // True if will be replaced by passing assertion clause, false for
const bool m_pass; // Will be replaced by passing assertion clause, false for
// assertion failure clause
const bool m_vacuous; // True if pass is vacuous
const bool m_vacuous; // Pass is vacuous
public:
ASTGEN_MEMBERS_AstPExprClause;
@@ -950,6 +990,8 @@ public:
: ASTGEN_SUPER_PExprClause(fl)
, m_pass{pass}
, m_vacuous{vacuous} {}
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool pass() const { return m_pass; }
bool vacuous() const { return m_vacuous; }
};
@@ -1406,6 +1448,8 @@ public:
, m_quotedPrefix{quotedPrefix} {}
ASTGEN_MEMBERS_AstTracePushPrefix;
bool sameNode(const AstNode* samep) const override { return false; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string prefix() const { return m_prefix; }
VTracePrefixType prefixType() const { return m_prefixType; }
int left() const { return m_left; }
@@ -1431,6 +1475,7 @@ public:
: ASTGEN_SUPER_WaitFork(fl) {}
ASTGEN_MEMBERS_AstWaitFork;
bool isTimingControl() const override { return true; }
string verilogKwd() const override { return "wait fork"; }
};
// === AstNodeAssign ===
@@ -1568,15 +1613,23 @@ class AstFork final : public AstNodeBlock {
//
// @astgen op3 := forksp : List[AstBegin]
const VJoinType m_joinType; // Join keyword type
bool m_immediateStart = false; // Fork starts before its parent blocks or exits
public:
AstFork(FileLine* fl, VJoinType joinType, const string& name = "")
: ASTGEN_SUPER_Fork(fl, name)
, m_joinType{joinType} {}
ASTGEN_MEMBERS_AstFork;
bool sameNode(const AstNode* samep) const override {
const AstFork* const asamep = VN_DBG_AS(samep, Fork);
return joinType() == asamep->joinType() && immediateStart() == asamep->immediateStart();
}
bool isTimingControl() const override { return !joinType().joinNone(); }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
VJoinType joinType() const { return m_joinType; }
bool immediateStart() const { return m_immediateStart; }
void immediateStart(bool flag) { m_immediateStart = flag; }
};
// === AstNodeCoverOrAssert ===
@@ -1612,6 +1665,8 @@ class AstCover final : public AstNodeCoverOrAssert {
// @astgen op3 := coverincsp: List[AstNode] // Coverage node
bool m_isCoverSeq = false; // 'cover sequence' (IEEE 1800-2023 16.14.3): fires per
// end-of-match, not per property success
bool m_isSeqEvent = false; // Synthesized for a sequence used as an event control
// (IEEE 1800-2023 9.4.2.4)
public:
ASTGEN_MEMBERS_AstCover;
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type,
@@ -1622,6 +1677,8 @@ public:
void dumpJson(std::ostream& str) const override;
bool isCoverSeq() const { return m_isCoverSeq; }
void isCoverSeq(bool flag) { m_isCoverSeq = flag; }
bool isSeqEvent() const { return m_isSeqEvent; }
void isSeqEvent(bool flag) { m_isSeqEvent = flag; }
};
class AstRestrict final : public AstNodeCoverOrAssert {
public:
@@ -1636,10 +1693,16 @@ public:
// === AstNodeForeach ===
class AstConstraintForeach final : public AstNodeForeach {
// Constraint foreach statement
bool m_soft; // is soft foreach, non-standard extension
public:
AstConstraintForeach(FileLine* fl, AstForeachHeader* headerp, AstNode* bodyp)
: ASTGEN_SUPER_ConstraintForeach(fl, headerp, bodyp) {}
AstConstraintForeach(FileLine* fl, AstForeachHeader* headerp, AstNode* bodyp,
bool soft = false)
: ASTGEN_SUPER_ConstraintForeach(fl, headerp, bodyp)
, m_soft{soft} {}
ASTGEN_MEMBERS_AstConstraintForeach;
bool isSoft() const { return m_soft; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstForeach final : public AstNodeForeach {
public:
@@ -1670,6 +1733,8 @@ public:
void unique0Pragma(bool flag) { m_unique0Pragma = flag; }
bool priorityPragma() const { return m_priorityPragma; }
void priorityPragma(bool flag) { m_priorityPragma = flag; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
// === AstNodeReadWriteMem ===
+399 -38
View File
@@ -22,6 +22,8 @@
#include "V3Global.h"
#include "V3Graph.h"
#include "V3Hasher.h"
#include "V3InstrCount.h"
#include "V3Stats.h"
#include "V3String.h"
#include "V3Ast__gen_impl.h" // Generated by 'astgen'
@@ -108,11 +110,27 @@ int AstNodeSel::bitConst() const {
return (constp ? constp->toSInt() : 0);
}
bool AstNode::isDisableQueuePushSelfStmt() {
// Detect LinkJump-generated registration:
// __VprocessQueue_*.push_back(std::process::self())
AstStmtExpr* const stmtExprp = VN_CAST(this, StmtExpr);
if (!stmtExprp) return false;
AstCMethodHard* const methodp = VN_CAST(stmtExprp->exprp(), CMethodHard);
if (!methodp || methodp->name() != "push_back") return false;
AstNode* const basep = AstArraySel::baseFromp(methodp->fromp(), false);
if (AstVarRef* const refp = VN_CAST(basep, VarRef)) return refp->varp()->processQueue();
if (AstMemberSel* const selp = VN_CAST(basep, MemberSel)) {
return selp->varp() && selp->varp()->processQueue();
}
return false;
}
void AstNodeStmt::dump(std::ostream& str) const { this->AstNode::dump(str); }
void AstNodeStmt::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstNodeCCall::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (superReference()) str << " [SUPERREF]";
if (funcp()) {
str << " " << funcp()->name() << " => ";
funcp()->dump(str);
@@ -121,6 +139,7 @@ void AstNodeCCall::dump(std::ostream& str) const {
}
}
void AstNodeCCall::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, superReference);
if (funcp()) dumpJsonStr(str, "funcName", funcp()->name());
dumpJsonGen(str);
}
@@ -420,14 +439,19 @@ void AstCReset::dumpJson(std::ostream& str) const {
void AstCase::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << verilogKwd();
if (fullPragma()) str << " [FULL]";
if (parallelPragma()) str << " [PAR]";
if (priorityPragma()) str << " [PRI]";
if (uniquePragma()) str << " [UNQ]";
if (unique0Pragma()) str << " [UNQ0]";
}
void AstCase::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "kwd", verilogKwd());
dumpJsonBoolIf(str, "full", fullPragma());
dumpJsonBoolIf(str, "parallel", parallelPragma());
dumpJsonBoolIf(str, "priority", priorityPragma());
dumpJsonBoolIf(str, "unique", uniquePragma());
dumpJsonBoolIf(str, "unique0", unique0Pragma());
dumpJsonBoolIf(str, "priority", priorityPragma());
dumpJsonGen(str);
}
@@ -468,6 +492,14 @@ void AstSConsRep::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, unbounded);
dumpJsonGen(str);
} // LCOV_EXCL_STOP
void AstSAnd::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (propertyControl()) str << " [PROPERTY_CONTROL]";
}
void AstSAnd::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, propertyControl);
dumpJsonGen(str);
}
void AstPropAlways::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (isStrong()) str << " [strong]";
@@ -495,6 +527,7 @@ void AstConstraint::dump(std::ostream& str) const {
str << " [PROTO]";
if (isKwdPure()) str << " [KWDPURE]";
if (isStatic()) str << " [STATIC]";
if (baseOverride().isAny()) str << " [" << baseOverride().ascii() << "]";
}
void AstConstraint::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isExternDef);
@@ -579,6 +612,15 @@ const char* AstNetlist::broken() const {
for (const AstVar* const varp : m_deferredParamVarps) {
BROKEN_RTN(!varp || !varp->brokeExists());
}
for (int i = 0; i < VEval::_ENUM_END; ++i) {
const AstCFunc* const funcp = m_evalFuncps[i];
const AstCFunc* const dumpp = m_dumpTriggersFuncps[i];
BROKEN_RTN(funcp && !funcp->brokeExists());
BROKEN_RTN(dumpp && !dumpp->brokeExists());
// Once created, an entry point has a trigger dump iff it has triggers
BROKEN_RTN(funcp && VEval{i}.hasTriggers() && !dumpp);
BROKEN_RTN(dumpp && !VEval{i}.hasTriggers());
}
return nullptr;
}
@@ -694,13 +736,17 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
asRef = asRef || isDpiOpenArray() || (forFunc && (isWritable() || isRef() || isConstRef()));
if (forFunc && (isReadOnly() || constRef) && asRef) ostatic = ostatic + "const ";
string oname;
if (named) {
if (!namespc.empty()) oname += namespc + "::";
oname += VIdProtect::protectIf(name(), protect());
}
if (forFunc && (isReadOnly() || constRef) && asRef) {
if (VN_IS(dtypep()->skipRefp(), IfaceRefDType)) {
return ostatic + dtypep()->cType("", forFunc, false) + " const &" + oname;
}
ostatic += "const ";
}
return ostatic + dtypep()->cType(oname, forFunc, asRef);
}
@@ -1179,7 +1225,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
info.m_type = "VlSampleQueue<" + sub.m_type + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
info.m_type = "VlClassRef<" + EmitCUtil::prefixNameProtect(adtypep) + ">";
const string className = EmitCUtil::prefixNameProtect(adtypep);
info.m_type = adtypep->rawPointer() ? className + "*" : "VlClassRef<" + className + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
info.m_type = EmitCUtil::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
@@ -1401,6 +1448,9 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
} else if (VN_IS(nodep, WildcardSel)) {
nodep = VN_AS(nodep, WildcardSel)->fromp();
continue;
} else if (VN_IS(nodep, CMethodHard)) {
nodep = VN_AS(nodep, CMethodHard)->fromp();
continue;
} else if (overMembers && VN_IS(nodep, MemberSel)) {
nodep = VN_AS(nodep, MemberSel)->fromp();
continue;
@@ -1815,10 +1865,16 @@ void AstNode::dump(std::ostream& str) const {
#endif
<< " {" << fileline()->filenameLetters() << std::dec << fileline()->lastLineno()
<< fileline()->firstColumnLetters() << "}";
if (user1p()) str << " u1=" << nodeAddr(user1p());
if (user2p()) str << " u2=" << nodeAddr(user2p());
if (user3p()) str << " u3=" << nodeAddr(user3p());
if (user4p()) str << " u4=" << nodeAddr(user4p());
const auto dumpUser = [&str](const char* prefix, const VNUser& user) {
const std::string s = user.dumpStr([](const void* p) -> std::string {
return nodeAddr(reinterpret_cast<const AstNode*>(p));
});
if (!s.empty()) str << prefix << s;
};
dumpUser(" u1=", user1u());
dumpUser(" u2=", user2u());
dumpUser(" u3=", user3u());
dumpUser(" u4=", user4u());
if (hasDType()) {
// Final @ so less likely to by accident read it as a nodep
if (dtypep() == this) {
@@ -1830,6 +1886,7 @@ void AstNode::dump(std::ostream& str) const {
} else { // V3Broken will throw an error
if (dtypep()) str << " %Error-dtype-exp=null,got=" << nodeAddr(dtypep());
}
if (fileline()->erroringOn()) str << " [ERRORING]";
if (name() != "") {
if (VN_IS(this, Const)) {
str << " " << name(); // Already quoted
@@ -1933,15 +1990,15 @@ void AstAssertCtl::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (ctlType() != VAssertCtlType::_TO_BE_EVALUATED) {
str << " [" << ctlType().ascii() << "]";
str << " [" << ctlAssertTypes().ascii() << "]";
str << " [" << ctlDirectiveTypes().ascii() << "]";
str << " [" << assertTypes().ascii() << "]";
str << " [" << directiveTypes().ascii() << "]";
}
}
void AstAssertCtl::dumpJson(std::ostream& str) const {
if (ctlType() != VAssertCtlType::_TO_BE_EVALUATED) {
dumpJsonStr(str, "ctlType", ctlType().ascii());
dumpJsonStr(str, "ctlAssertTypes", ctlAssertTypes().ascii());
dumpJsonStr(str, "ctlDirectiveTypes", ctlDirectiveTypes().ascii());
dumpJsonStr(str, "assertTypes", assertTypes().ascii());
dumpJsonStr(str, "directiveTypes", directiveTypes().ascii());
}
dumpJsonGen(str);
}
@@ -1977,6 +2034,18 @@ string AstBasicDType::prettyDTypeName(bool) const {
void AstNodeExpr::dump(std::ostream& str) const { this->AstNode::dump(str); }
void AstNodeExpr::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstPropSpec::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (propStrength() != VPropStrength::DEFAULT) {
str << " [" << VString::upcase(propStrength().ascii()) << "]";
}
}
void AstPropSpec::dumpJson(std::ostream& str) const {
if (propStrength() != VPropStrength::DEFAULT)
dumpJsonStr(str, "strength", propStrength().ascii());
dumpJsonGen(str);
}
AstConst::~AstConst() {
// Only rare constants carry originating parameter-name metadata. For all other AstConst nodes,
// the V3Number bit keeps this destructor from touching AstNetlist's side table. When the bit
@@ -2022,6 +2091,8 @@ bool AstNodeExpr::isLValue() const {
return varrefp->access().isWriteOrRW();
} else if (const AstMemberSel* const memberselp = VN_CAST(this, MemberSel)) {
return memberselp->access().isWriteOrRW();
} else if (const AstStructSel* const structselp = VN_CAST(this, StructSel)) {
return structselp->fromp()->isLValue();
} else if (const AstSel* const selp = VN_CAST(this, Sel)) {
return selp->fromp()->isLValue();
} else if (const AstNodeSel* const nodeSelp = VN_CAST(this, NodeSel)) {
@@ -2080,6 +2151,7 @@ void AstCell::dump(std::ostream& str) const {
}
}
void AstCell::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, modName);
dumpJsonStrFunc(str, origName);
dumpJsonStrFunc(str, verilogName);
dumpJsonBoolFuncIf(str, recursive);
@@ -2110,6 +2182,14 @@ void AstCExpr::dumpJson(std::ostream& str) const {
dumpJsonBoolIf(str, "pure", m_pure);
dumpJsonGen(str);
}
void AstCExprUser::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (m_pure) str << " [PURE]";
}
void AstCExprUser::dumpJson(std::ostream& str) const {
dumpJsonBoolIf(str, "pure", m_pure);
dumpJsonGen(str);
}
bool AstClass::isCacheableChild(const AstNode* nodep) {
return VN_IS(nodep, Var) || VN_IS(nodep, Typedef)
|| (VN_IS(nodep, Constraint) && !VN_AS(nodep, Constraint)->isExternProto())
@@ -2136,26 +2216,32 @@ void AstClass::dump(std::ostream& str) const {
if (isCovergroup()) str << " [CG]";
if (isExtended()) str << " [EXT]";
if (isInterfaceClass()) str << " [IFCCLS]";
if (isVirtual()) str << " [VIRT]";
if (useVirtualPublic()) str << " [VIRPUB]";
if (isPrintedFrom()) str << " [PRINTED]";
if (isVirtual()) str << " [VIRT]";
if (needRNG()) str << " [NRNG]";
if (useVirtualPublic()) str << " [VIRPUB]";
if (baseOverride().isAny()) str << " [" << baseOverride().ascii() << "]";
}
void AstClass::dumpJson(std::ostream& str) const {
// dumpJsonNumFunc(str, declTokenNum); // Not dumped as adding token changes whole file
dumpJsonBoolFuncIf(str, isCovergroup);
dumpJsonBoolFuncIf(str, isExtended);
dumpJsonBoolFuncIf(str, isInterfaceClass);
dumpJsonBoolFuncIf(str, isVirtual);
dumpJsonBoolFuncIf(str, isPrintedFrom);
dumpJsonBoolFuncIf(str, isVirtual);
dumpJsonBoolFuncIf(str, needRNG);
dumpJsonBoolFuncIf(str, useVirtualPublic);
if (baseOverride().isAny()) dumpJsonStr(str, "baseOverride", baseOverride().ascii());
dumpJsonGen(str);
}
void AstClassExtends::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isImplements()) str << " [IMPLEMENTS]";
if (isImplements()) str << " [IMPL]";
if (parameterized()) str << " [PAR]";
}
void AstClassExtends::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isImplements);
dumpJsonBoolFuncIf(str, parameterized);
dumpJsonGen(str);
}
AstClass* AstClassExtends::classOrNullp() const {
@@ -2181,8 +2267,22 @@ void AstClassRefDType::dump(std::ostream& str) const {
} else {
str << " -> UNLINKED";
}
if (rawPointer()) str << " [RAWPTR]";
}
void AstClassRefDType::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, rawPointer);
dumpJsonGen(str);
}
void AstClassRefDType::selfTest() {
FileLine* const fl = new FileLine{FileLine::commandLineFilename()};
AstClassRefDType* const owningp = new AstClassRefDType{fl, nullptr, nullptr};
AstClassRefDType* const rawp = new AstClassRefDType{fl, nullptr, nullptr};
rawp->rawPointer(true);
UASSERT_OBJ(!owningp->sameNode(rawp) && !rawp->sameNode(owningp) && rawp->sameNode(rawp), rawp,
"Raw class pointer must have distinct type identity");
VL_DO_DANGLING(owningp->deleteTree(), owningp);
VL_DO_DANGLING(rawp->deleteTree(), rawp);
}
void AstClassRefDType::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstClassRefDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "class:" << name();
@@ -2241,11 +2341,21 @@ void AstNodeCoverOrAssert::dumpJson(std::ostream& str) const {
void AstCover::dump(std::ostream& str) const {
this->AstNodeCoverOrAssert::dump(str);
if (isCoverSeq()) str << " [COVERSEQ]";
if (isSeqEvent()) str << " [SEQEVENT]";
}
void AstCover::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isCoverSeq);
dumpJsonBoolFuncIf(str, isSeqEvent);
this->AstNodeCoverOrAssert::dumpJson(str);
}
void AstConstraintForeach::dump(std::ostream& str) const {
this->AstNodeForeach::dump(str);
if (isSoft()) str << "[SOFT]";
}
void AstConstraintForeach::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isSoft);
dumpJsonGen(str);
}
void AstClocking::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isDefault()) str << " [DEFAULT]";
@@ -2286,7 +2396,26 @@ void AstDisplay::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
str << " [" << displayType().ascii() << "]";
}
void AstDisplay::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstDisplay::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "displayType", displayType().ascii());
dumpJsonGen(str);
}
void AstDistItem::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (isWhole()) str << " [WHOLE]";
}
void AstDistItem::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isWhole);
dumpJsonGen(str);
}
void AstDumpCtl::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
str << " [" << ctlType().ascii() << "]";
}
void AstDumpCtl::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "ctlType", ctlType().ascii());
dumpJsonGen(str);
}
void AstEnumDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
str << " enum";
@@ -2348,6 +2477,29 @@ void AstGenBlock::dumpJson(std::ostream& str) const {
dumpJsonGen(str);
}
void AstNodeIf::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isBoundsCheck()) str << " [BOUNDS]";
}
void AstNodeIf::dumpJson(std::ostream& str) const {
dumpJsonBoolIf(str, "isBoundsCheck", isBoundsCheck());
dumpJsonGen(str);
}
void AstIf::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << verilogKwd();
if (priorityPragma()) str << " [PRI]";
if (uniquePragma()) str << " [UNQ]";
if (unique0Pragma()) str << " [UNQ0]";
}
void AstIf::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "kwd", verilogKwd());
dumpJsonBoolIf(str, "priority", priorityPragma());
dumpJsonBoolIf(str, "unique", uniquePragma());
dumpJsonBoolIf(str, "unique0", unique0Pragma());
dumpJsonGen(str);
}
void AstIfaceRefDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (isPortDecl()) str << " [PORTDECL]";
@@ -2485,13 +2637,17 @@ const char* AstLoopTest::broken() const {
void AstMemberDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (isConstrainedRand()) str << " [CONSTRAINEDRAND]";
if (isConstrainedRand()) str << " [CSRAND]";
if (rand().isRandomizable()) str << " [" << rand() << "]";
if (lsb()) str << " lsb=" << lsb();
if (name() != "") str << " name=" << name();
if (tag() != "") str << " tag=" << tag();
}
void AstMemberDType::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isConstrainedRand);
if (rand().isRandomizable()) dumpJsonStr(str, "rand", rand().ascii());
if (lsb() != 0) dumpJsonStr(str, "lsb", std::to_string(lsb()));
dumpJsonStrFunc(str, name);
dumpJsonStrFunc(str, tag);
dumpJsonGen(str);
@@ -2584,6 +2740,14 @@ void AstModule::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, hasGenericIface);
dumpJsonGen(str);
}
void AstMonitorOff::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (off()) str << " [OFF]";
}
void AstMonitorOff::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, off);
dumpJsonGen(str);
}
void AstPin::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (modVarp()) {
@@ -2592,11 +2756,13 @@ void AstPin::dump(std::ostream& str) const {
} else {
str << " ->UNLINKED";
}
if (param()) str << " [PAR]";
if (!paramPath().empty()) str << " paramPath=" << paramPath();
if (svDotName()) str << " [.n]";
if (svImplicit()) str << " [.SV]";
}
void AstPin::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, param);
dumpJsonBoolFuncIf(str, svDotName);
dumpJsonBoolFuncIf(str, svImplicit);
if (!paramPath().empty()) dumpJsonStr(str, "paramPath", paramPath());
@@ -2630,7 +2796,14 @@ void AstPrintTimeScale::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "timeunit", timeunit().ascii());
dumpJsonGen(str);
}
void AstPull::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (direction()) str << " [UP]";
}
void AstPull::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, direction);
dumpJsonGen(str);
}
void AstTime::dump(std::ostream& str) const {
this->AstNodeTermop::dump(str);
str << " " << timeunit();
@@ -2655,6 +2828,16 @@ void AstTimeImport::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "timeunit", timeunit().ascii());
dumpJsonGen(str);
}
void AstTracePushPrefix::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (m_quotedPrefix) str << " [QUOTE]";
str << " [" << prefixType().ascii() << "]";
}
void AstTracePushPrefix::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, quotedPrefix);
dumpJsonStr(str, "prefixType", prefixType().ascii());
dumpJsonGen(str);
}
void AstTypedef::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (attrPublic()) str << " [PUBLIC]";
@@ -2680,6 +2863,14 @@ void AstTypedefFwd::dumpJson(std::ostream& str) const {
}
void AstNodeRange::dump(std::ostream& str) const { this->AstNode::dump(str); }
void AstNodeRange::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstRand::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (urandom()) str << " [U]";
}
void AstRand::dumpJson(std::ostream& str) const {
dumpJsonBoolIf(str, "urandom", urandom());
dumpJsonGen(str);
}
void AstRandSequence::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " start=" << start();
@@ -2706,8 +2897,18 @@ void AstRange::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, fromBracket);
dumpJsonGen(str);
}
void AstNodeReadWriteMem::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (isHex()) str << " [HEX]";
}
void AstNodeReadWriteMem::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isHex);
dumpJsonGen(str);
}
void AstParamTypeDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (fwdType() != VFwdType::NONE) str << " [" << fwdType().ascii() << "]";
str << " [" << varType().ascii() << "]";
if (subDTypep()) {
str << " -> ";
subDTypep()->dump(str);
@@ -2715,7 +2916,19 @@ void AstParamTypeDType::dump(std::ostream& str) const {
str << " -> UNLINKED";
}
}
void AstParamTypeDType::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstParamTypeDType::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "fwdType", fwdType().ascii());
dumpJsonStr(str, "varType", varType().ascii());
dumpJsonGen(str);
}
void AstParseTypeDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (fwdType() != VFwdType::NONE) str << " [" << fwdType().ascii() << "]";
}
void AstParseTypeDType::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "fwdType", fwdType().ascii());
dumpJsonGen(str);
}
void AstRefDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (typedefp() || subDTypep()) {
@@ -2752,15 +2965,25 @@ void AstNodeUOrStructDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (packed()) str << " [PACKED]";
if (isFourstate()) str << " [4STATE]";
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
if (isConstrainedRand()) str << " [CSRAND]";
if (emitToString()) str << " [EMITSTR]";
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
}
void AstNodeUOrStructDType::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, packed);
dumpJsonBoolFuncIf(str, isConstrainedRand);
dumpJsonBoolFuncIf(str, isFourstate);
dumpJsonBoolFuncIf(str, emitToString);
dumpJsonGen(str);
}
void AstUdpTableLine::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (udpIsCombo()) str << " [COMBO]";
}
void AstUdpTableLine::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, udpIsCombo);
dumpJsonGen(str);
}
void AstUnionDType::dump(std::ostream& str) const {
this->AstNodeUOrStructDType::dump(str);
if (isSoft()) str << " [soft]";
@@ -2872,11 +3095,21 @@ std::vector<AstUnpackArrayDType*> AstUnpackArrayDType::unpackDimensions() {
}
return dims;
}
void AstNBACommitQueueDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (partial()) str << " [PARTIAL]";
}
void AstNBACommitQueueDType::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, partial);
dumpJsonGen(str);
}
void AstNetlist::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (timescaleSpecified()) str << " [TIMESCALES]";
str << " [" << timeunit() << "/" << timeprecision() << "]";
}
void AstNetlist::dumpJson(std::ostream& str) const {
dumpJsonBoolIf(str, "timescaleSpecified", timescaleSpecified());
dumpJsonStr(str, "timeunit", timeunit().ascii());
dumpJsonStr(str, "timeprecision", timeprecision().ascii());
dumpJsonGen(str);
@@ -2887,13 +3120,13 @@ void AstNetlist::deleteContents() {
m_constPoolp = nullptr;
m_dollarUnitPkgp = nullptr;
m_stdPackagep = nullptr;
m_evalp = nullptr;
m_evalNbap = nullptr;
m_dpiExportTriggerp = nullptr;
m_delaySchedulerp = nullptr;
m_nbaEventp = nullptr;
m_nbaEventTriggerp = nullptr;
m_topScopep = nullptr;
m_evalFuncps.fill(nullptr);
m_dumpTriggersFuncps.fill(nullptr);
if (op1p()) op1p()->unlinkFrBackWithNext()->deleteTree();
if (op2p()) op2p()->unlinkFrBackWithNext()->deleteTree();
if (op3p()) op3p()->unlinkFrBackWithNext()->deleteTree();
@@ -2937,6 +3170,18 @@ AstFuncRef* AstNetlist::stdPackageProcessSelfp(FileLine* flp) const {
processSelfp->classOrPackagep(v3Global.rootp()->stdPackageProcessp());
return processSelfp;
}
void AstNetlist::addEvalStats(const std::string& phase) {
if (!v3Global.opt.stats()) return;
for (int i = 0; i < VEval::_ENUM_END; ++i) {
VEval eval{i};
AstCFunc* const funcp = m_evalFuncps[i];
if (!funcp) continue;
const uint32_t nodes = funcp->nodeCount();
const uint32_t instr = V3InstrCount::count(funcp, false);
V3Stats::addStat("Size of eval, nodes - '" + eval.tag() + "', " + phase, nodes);
V3Stats::addStat("Size of eval, instr - '" + eval.tag() + "', " + phase, instr);
}
}
void AstNodeModule::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " L" << level();
@@ -2959,6 +3204,7 @@ void AstNodeModule::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, origName);
dumpJsonStrFunc(str, verilogName);
dumpJsonNumFunc(str, level);
dumpJsonNumFunc(str, depth);
dumpJsonBoolFuncIf(str, modPublic);
dumpJsonBoolFuncIf(str, inLibrary);
dumpJsonBoolFuncIf(str, ctorVarReset);
@@ -2970,6 +3216,16 @@ void AstNodeModule::dumpJson(std::ostream& str) const {
if (libname() != "work") dumpJsonStr(str, "libname=", libname());
dumpJsonGen(str);
}
void AstPExprClause::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (pass()) str << " [PASS]";
if (vacuous()) str << " [VACUOUS]";
}
void AstPExprClause::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, pass);
dumpJsonBoolFuncIf(str, vacuous);
dumpJsonGen(str);
}
void AstPackageExport::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (packagep()) {
@@ -2978,7 +3234,10 @@ void AstPackageExport::dump(std::ostream& str) const {
str << " ->UNLINKED:" << pkgName();
}
}
void AstPackageExport::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstPackageExport::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, pkgName);
dumpJsonGen(str);
}
void AstPackageImport::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (packagep()) {
@@ -2991,6 +3250,14 @@ void AstPackageImport::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstPackageImport::pkgNameFrom() {
if (packagep()) m_pkgName = packagep()->name();
}
void AstPast::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (propertyTiming()) str << " [PROPERTY_TIMING]";
}
void AstPast::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, propertyTiming);
dumpJsonGen(str);
}
void AstPatMember::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (isConcat()) str << " [CONCAT]";
@@ -3025,13 +3292,23 @@ VFormatAttr AstSFormatArg::formatAttrDefauled(const AstSFormatArg* nodep,
void AstSFormatF::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (exprFormat()) str << " [EXPRFMT]";
if (hidden()) str << " [HID]";
if (optionalFormat()) str << " [OPTFMT]";
}
void AstSFormatF::dumpJson(std::ostream& str) const {
dumpJsonGen(str);
dumpJsonBoolFuncIf(str, exprFormat);
dumpJsonBoolFuncIf(str, hidden);
dumpJsonBoolFuncIf(str, optionalFormat);
}
void AstSampled::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (internal()) str << " [INTERNAL]";
}
void AstSampled::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, internal);
dumpJsonGen(str);
}
void AstSel::dump(std::ostream& str) const {
this->AstNodeBiop::dump(str);
str << " widthConst=" << this->widthConst();
@@ -3122,6 +3399,15 @@ bool AstWildcardArrayDType::similarDTypeNode(const AstNodeDType* samep) const {
const AstWildcardArrayDType* const asamep = VN_DBG_AS(samep, WildcardArrayDType);
return asamep->subDTypep() && subDTypep()->similarDType(asamep->subDTypep());
}
void AstUnpackArrayDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (isCompound()) str << " [COMPOUND]";
str << " " << declRange();
}
void AstUnpackArrayDType::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isCompound);
dumpJsonGen(str);
}
bool AstUnpackArrayDType::similarDTypeNode(const AstNodeDType* samep) const {
const AstUnpackArrayDType* const asamep = VN_DBG_AS(samep, UnpackArrayDType);
return hi() == asamep->hi() && rangep()->sameTree(asamep->rangep())
@@ -3152,6 +3438,14 @@ void AstVoidDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "void";
}
void AstIfaceGenericDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);
if (modportName() != "") str << " mp=" << modportName();
}
void AstIfaceGenericDType::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, modportName);
dumpJsonGen(str);
}
void AstIfaceGenericDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "generic_interface";
@@ -3256,6 +3550,7 @@ int AstVarRef::instrCount() const {
void AstVar::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (constPoolEntry()) str << " [CONSTPOOL]";
if (covergroupRefMember()) str << " [CGREF]";
if (isSc()) str << " [SC]";
if (isPrimaryIO()) str << (isInout() ? " [PIO]" : (isWritable() ? " [PO]" : " [PI]"));
if (isPrimaryClock()) str << " [PCLK]";
@@ -3271,7 +3566,7 @@ void AstVar::dump(std::ostream& str) const {
if (isInternal()) str << " [INTERNAL]";
if (isLatched()) str << " [LATCHED]";
if (isUsedLoopIdx()) str << " [LOOPIDX]";
if (rand().isRandomizable()) str << " [" << rand() << "]";
if (rand().isRandomizable()) str << " [" << rand().ascii() << "]";
if (noCReset()) str << " [!CRST]";
if (noReset()) str << " [!RST]";
if (processQueue()) str << " [PROCQ]";
@@ -3298,6 +3593,7 @@ void AstVar::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, origName);
dumpJsonStrFunc(str, verilogName);
dumpJsonBoolFuncIf(str, constPoolEntry);
dumpJsonBoolFuncIf(str, covergroupRefMember);
dumpJsonBoolFuncIf(str, isSc);
dumpJsonBoolFuncIf(str, isPrimaryIO);
dumpJsonBoolFuncIf(str, isPrimaryClock);
@@ -3335,6 +3631,7 @@ void AstVar::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, hasUserInit);
dumpJsonBoolFuncIf(str, ignorePostWrite);
dumpJsonBoolFuncIf(str, ignoreSchedWrite);
if (rand().isRandomizable()) dumpJsonStr(str, "rand", rand().ascii());
dumpJsonGen(str);
}
void AstScope::dump(std::ostream& str) const {
@@ -3401,7 +3698,7 @@ void AstClassOrPackageRef::dump(std::ostream& str) const {
}
}
void AstClassOrPackageRef::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
AstNodeModule* AstClassOrPackageRef::classOrPackageSkipp() const {
AstNodeModule* AstClassOrPackageRef::classOrPackageSkipp(const bool doRefs) const {
AstNode* foundp = m_classOrPackageNodep;
AstNode* lastp = nullptr;
while (foundp != lastp) {
@@ -3409,11 +3706,12 @@ AstNodeModule* AstClassOrPackageRef::classOrPackageSkipp() const {
if (AstNodeDType* const anodep = VN_CAST(foundp, NodeDType)) {
foundp = anodep->skipRefOrNullp();
}
if (const AstTypedef* const anodep = VN_CAST(foundp, Typedef)) {
foundp = anodep->subDTypep();
}
if (const AstClassRefDType* const anodep = VN_CAST(foundp, ClassRefDType)) {
foundp = anodep->classp();
if (doRefs) {
if (const AstTypedef* const anodep = VN_CAST(foundp, Typedef)) {
foundp = anodep->subDTypep();
} else if (const AstClassRefDType* const anodep = VN_CAST(foundp, ClassRefDType)) {
foundp = anodep->classp();
}
}
}
return VN_CAST(foundp, NodeModule);
@@ -3441,6 +3739,8 @@ void AstNodeFTaskRef::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
if (containsGenBlock()) str << " [GENBLK]";
if (pli()) str << " [PLI]";
if (superReference()) str << " [SUPERREF]";
str << " -> ";
if (dotted() != "") str << ".=" << dotted() << " ";
if (taskp()) {
@@ -3452,6 +3752,8 @@ void AstNodeFTaskRef::dump(std::ostream& str) const {
void AstNodeFTaskRef::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, dotted);
dumpJsonBoolFuncIf(str, containsGenBlock);
dumpJsonBoolFuncIf(str, pli);
dumpJsonBoolFuncIf(str, superReference);
dumpJsonGen(str);
}
void AstNodeFTask::dump(std::ostream& str) const {
@@ -3473,6 +3775,7 @@ void AstNodeFTask::dump(std::ostream& str) const {
if (verilogFunction()) str << " [VFUNC]";
if (needProcess()) str << " [NPRC]";
if ((dpiImport() || dpiExport()) && cname() != name()) str << " [c=" << cname() << "]";
if (baseOverride().isAny()) str << " [" << baseOverride().ascii() << "]";
}
bool AstNodeFTask::isPure() {
if (!m_purity.isCached()) m_purity.set(getPurityRecurse());
@@ -3538,6 +3841,22 @@ void AstBegin::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, needProcess);
dumpJsonGen(str);
}
void AstComment::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (showAt()) str << " [SHOWAT]";
}
void AstComment::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, showAt);
dumpJsonGen(str);
}
void AstCompareNN::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (ignoreCase()) str << " [IGNORECASE]";
}
void AstCompareNN::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, ignoreCase);
dumpJsonGen(str);
}
void AstNodeCoverDecl::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (!page().empty()) str << " page=" << page();
@@ -3597,12 +3916,38 @@ void AstCoverInc::dump(std::ostream& str) const {
}
}
void AstCoverInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
void AstDpiExport::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (cname() != "" && cname() != name()) str << " [c=" << cname() << "]";
}
void AstDpiExport::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "cname", cname());
dumpJsonGen(str);
}
void AstFireEvent::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (isDelayed()) str << " [DLY]";
}
void AstFireEvent::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isDelayed);
dumpJsonGen(str);
}
void AstFork::dump(std::ostream& str) const {
this->AstNodeBlock::dump(str);
str << " [" << joinType() << "]";
if (immediateStart()) str << " [IMMEDIATE]";
}
void AstFork::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "joinType", joinType().ascii());
dumpJsonBoolFuncIf(str, immediateStart);
dumpJsonGen(str);
}
void AstSequence::dump(std::ostream& str) const {
this->AstNodeFTask::dump(str);
if (isReferenced()) str << " [REFED]";
}
void AstSequence::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isReferenced);
dumpJsonGen(str);
}
void AstStop::dump(std::ostream& str) const {
@@ -3615,10 +3960,12 @@ void AstStop::dumpJson(std::ostream& str) const {
}
void AstTraceDecl::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (inDtypeFunc()) str << " [DT]";
if (codeAssigned()) str << " [code=" << code() << "]";
if (dtypeCallp()) str << " [dtypeCallp=" << dtypeCallp() << "]";
}
void AstTraceDecl::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, inDtypeFunc);
dumpJsonNumFunc(str, code);
dumpJsonGen(str);
}
@@ -3685,6 +4032,7 @@ void AstCFunc::dump(std::ostream& str) const {
if (needProcess()) str << " [NPRC]";
if (entryPoint()) str << " [ENTRY]";
if (noLife()) str << " [NOLIFE]";
if (!cname().empty() && cname() != name()) str << " [c=" << cname() << "]";
}
void AstCFunc::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, slow);
@@ -3700,6 +4048,7 @@ void AstCFunc::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, isCoroutine);
dumpJsonBoolFuncIf(str, needProcess);
dumpJsonBoolFuncIf(str, noLife);
dumpJsonStrFunc(str, cname);
dumpJsonGen(str);
// TODO: maybe try to shorten these flags somehow
}
@@ -3746,6 +4095,14 @@ void AstCStmt::dumpJson(std::ostream& str) const {
dumpJsonGen(str);
if (!stmtType().isNone()) dumpJsonStr(str, "stmtType", stmtType().ascii());
}
void AstCStmtUser::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (!fromDollarC()) str << " [$C]";
}
void AstCStmtUser::dumpJson(std::ostream& str) const {
dumpJsonBoolFuncIf(str, fromDollarC);
dumpJsonGen(str);
}
void AstCUse::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " [" << useType() << "]";
@@ -3776,10 +4133,12 @@ string AstCase::pragmaString() const {
}
void AstCgOptionAssign::dump(std::ostream& str) const {
str << " [" << optType().ascii() << "]";
if (typeOption()) str << " [TYPEOPT]";
this->AstNode::dump(str);
}
void AstCgOptionAssign::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "optType", optType().ascii());
dumpJsonBoolFuncIf(str, typeOption);
dumpJsonGen(str);
}
@@ -3793,7 +4152,7 @@ void AstDelay::dumpJson(std::ostream& str) const {
}
const char* AstDisable::broken() const {
BROKEN_RTN((m_targetp && targetRefp()) || ((!m_targetp && !targetRefp())));
BROKEN_RTN(!m_targetp && !targetRefp());
return nullptr;
}
void AstDisable::dump(std::ostream& str) const {
@@ -3861,14 +4220,16 @@ void AstCoverpoint::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem
void AstCoverBin::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_type.ascii();
str << " " << m_binsType.ascii();
if (m_isArray) str << "[]";
if (m_isWildcard) str << "[*]";
}
void AstCoverBin::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"binsType\": \"" << m_type.ascii() << "\"";
if (m_isArray) str << ", \"isArray\": true";
dumpJsonBoolIf(str, "isArray", isArray());
dumpJsonBoolIf(str, "isWildcard", isWildcard());
str << ", \"binsType\": \"" << binsType().ascii() << "\"";
}
void AstCoverTransItem::dump(std::ostream& str) const {
@@ -3891,12 +4252,12 @@ void AstCoverCross::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem
void AstCoverOption::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_type.ascii();
str << " " << m_optType.ascii();
}
void AstCoverOption::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"optionType\": \"" << m_type.ascii() << "\"";
str << ", \"optType\": \"" << m_optType.ascii() << "\"";
}
void AstCoverpointRef::dump(std::ostream& str) const { this->AstNode::dump(str); }

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