Compare commits

..
Author SHA1 Message Date
Wilson Snyder 5ed3b46dba No need to revert verilatedos.h 2026-09-15 13:40:10 -04:00
Wilson Snyder 6da2657b56 Revert "Fix wide enum names (#8344)"
This reverts commit c65f312d6e.
2026-09-15 12:22:58 -04:00
Geza Lore 0f3a84643b CI: Fix pr-notification deletion 2026-09-15 17:01:45 +01:00
Marco Bartoli 253f51f1dc Fix %p format on class members (#8360) 2026-09-15 09:43:12 -04:00
Mike Murunov 270c528afd Fix missing implicit net for bufif/notif/nmos/pmos data input (#8353) (#8354) 2026-09-14 17:31:51 -04:00
Marco Bartoli c65f312d6e Fix wide enum names (#8344) 2026-09-14 16:29:42 -04:00
Dragon-Git 377865e2a0 Fix class-qualified self reference to a typedef in a parameterized class (#8348) (#8349) 2026-09-14 10:54:15 -04:00
Artur Bieniek 6a57cf10f4 Obtimize NBA assertions by keeping all writers before readers (#8326)
Signed-off-by: Artur Bieniek <[email protected]>
2026-09-14 10:29:27 -04:00
CatalpaEel 96e6bf02c4 Optimize wide four-state constant operations in V3Number (#8333) 2026-09-14 10:12:30 -04:00
Marco Bartoli 0bad0faca6 Fix ignoring cross_auto_bin_max instead of hard error (#8340) 2026-09-13 14:59:42 -04:00
Wilson Snyder 547eb6a836 Remove deprecated --structs-packed. 2026-09-13 14:54:34 -04:00
Wilson Snyder e172e94b30 Fix pattern type matching against nested aggregates (#7304).
Fixes #7304.
2026-09-13 14:48:07 -04:00
Wilson Snyder e871c3657e Commentary: Changes update 2026-09-13 14:00:43 -04:00
Wilson Snyder 3200946bb9 Tests: Improve t_string_to_bit 2026-09-13 13:41:53 -04:00
Marco Bartoli fb8bcdc0df Fix format of signed enums (#8338) 2026-09-13 13:34:10 -04:00
Marco Bartoli 47ead4e89d Support logic and set operations on binsof (#8306) 2026-09-13 12:59:10 -04:00
Kristof Marien 298922d887 Fix parameterized class nested in a parameterized interface (#8328) (#8329) 2026-09-12 15:31:10 -04:00
Wilson Snyder 4fedb791ed Add -fno-dead-members and optimize dead class member functions. (#8330) 2026-09-12 15:22:42 -04:00
Wilson Snyder 3bf0018354 Optimize away __Vconfigure if empty function 2026-09-12 14:36:45 -04:00
Wilson Snyder d523500b9b Optimize UVM reporting functions as branch-unlikely 2026-09-12 13:54:15 -04:00
Wilson Snyder a8a50bd8a0 Commentary 2026-09-12 12:22:14 -04:00
Wilson Snyder c7b1151f13 Fix unlinked VlProcess error 2026-09-12 11:00:29 -04:00
Wilson Snyder 73957647cc Add IEEE reference to some warnings 2026-09-12 09:06:09 -04:00
Wilson Snyder d048b62c13 Fix marking inherited virtual functions (part 2) 2026-09-12 08:55:45 -04:00
Wilson Snyder c8a75e9b4d Tests: Reformat some tests to mostly verilog-format standard. No test functional change. 2026-09-12 08:38:51 -04:00
Wilson Snyder 012b647833 Fix marking inherited virtual functions 2026-09-12 08:32:53 -04:00
Wilson Snyder 005b33faa1 Commentary: Changes update 2026-09-12 08:21:01 -04:00
Todd Strader 0c574618d0 Support VPI interface references (#8081) 2026-09-12 05:38:20 -04:00
Marco Bartoli 4aeb3dbc5a Fix wide typed pattern formatting primitives (#8325) 2026-09-11 18:11:14 -04:00
Wilson Snyder bd32c0e4c1 Internals: Refactor V3Dead ftask removal to use graph (#8322) 2026-09-11 06:52:25 -04:00
Wilson Snyder 72523c3be1 Tests: Fix coverage.dat in wrong directory 2026-09-10 21:21:04 -04:00
Andrii 2421c7b73b Fix property and sequence line coverage (#8314)
Signed-off-by: Andrii Andrieiev <[email protected]>
2026-09-10 18:00:24 -04:00
Nick Brereton dada720bca Internals: Inline VerilatedContext::assertCtlGet (#8320) 2026-09-10 15:48:58 -04:00
Artur Bieniek b06bd46dc9 Avoid duplicate lookup in NFA property recursion guard (#8321)
Signed-off-by: Artur Bieniek <[email protected]>
2026-09-10 20:46:54 +02:00
Artur Bieniek 5b24226b3b Avoid unnecessary per-vertex allocations in NFA lowering (#8319)
Signed-off-by: Artur Bieniek <[email protected]>
2026-09-10 20:21:37 +02:00
Artur Bieniek 09fd15af7b Narrow NBA shadow copies for partial writes (#8266)
Signed-off-by: Artur Bieniek <[email protected]>
2026-09-10 11:38:46 -04:00
Kamil Danecki ee792e235a Support rand dynamic arrays used inside with clause of randomize (#8246)
Signed-off-by: Kamil Danecki <[email protected]>
Signed-off-by: Igor Zaworski <[email protected]>
2026-09-10 10:40:30 -04:00
Artur Bieniek 557c21a337 Support multiplicity in SVA shapes that use NFA rings (#8267)
Signed-off-by: Artur Bieniek <[email protected]>
2026-09-10 10:39:35 -04:00
Wilson Snyder 24692ad3ef Internals: Change user1-4 storage to uint64 (#8317) 2026-09-10 06:45:54 -04:00
Wilson Snyder 3c8630744d Internals: V3Dead refactor and test update. No functional change. 2026-09-10 06:43:47 -04:00
Wilson Snyder 0dd45238a7 Internals: Update verilog_format flags for recent Verible 2026-09-10 06:39:43 -04:00
Geza Lore 1d2781091c Internals: Color densely in V3Graph::weaklyConnected, and return the count
No algorithm depends on this, so can be changed, later improvement will.
No functional change.
2026-09-09 21:11:23 +01:00
Geza Lore 5bf74e34b1 Internals: Move SplitReorderBaseVisitor next to SplitVisitor
Pure code movement to minimize change in subsequent patch.
2026-09-09 13:02:42 +01:00
Wilson Snyder 599f8b8e35 Tests: Reformat some tests to mostly verilog-format standard. No test functional change. 2026-09-08 21:26:11 -04:00
Geza Lore da39cea1bd Tests: Add -fno-exceptions to t_verilated_all (#8312)
Fixes #6771
2026-09-09 00:33:36 +01:00
Drew Risinger 3e874fb4d1 Fix CMake to call Verilator perl wrapper (#8269) (#8270) (#8311)
This reimplements commit c1c19494c4.
2026-09-08 18:29:09 -04:00
Geza Lore e4f210eb38 Optimize Dfg algorithms with an open addressing hash table (#8307)
This patch introduces V3HashTable.h, which defines an open addressing,
linear probing hash table. The table implement the public V3HashSet and
V3HashMap templates, which are generic containers. The benefit of this
over std::unordered_map and std::unordered_set is far better memory
locality during lookup. (The STL containers use chaining and require a
new heap allocation for every insertion, similarly probing involves
pointer chasing on collisions).

The new data structure is use in V3DfgCache, and V3DfgCse and yields a
significant speed improvement of those passes on large designs.
2026-09-08 23:21:13 +01:00
Wilson Snyder 5bb0e9b216 Commentary: Changes update 2026-09-08 18:09:39 -04:00
Wilson Snyder 5ce1abc2b4 Tests: Fix cleaning t_a6_examples (#8270 comment) 2026-09-08 18:07:12 -04:00
Geza Lore b835308a31 Optimize AstNodeDType::skipRefp to not track the type chain (#8310) 2026-09-08 18:00:08 -04:00
Geza Lore fa30985cda Internals: Exclude UDEBUGONLY from branch coverage 2026-09-08 21:49:10 +01:00
Kornel Uriasz f8c296ce2e Support unique constraint inside std::randomize (#8241)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-09-08 11:08:34 -04:00
Kornel Uriasz 47b9fba5e5 Fix array-size constraint on arrays inside base cass (#8219)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-09-08 09:11:02 -04:00
Martijn Wobbes d70d1e5cd0 Fix DECLFILENAME warning on nested declarations (#8302) 2026-09-08 07:15:37 -04:00
Artur Bieniek 76df8b4a7e Fix multidimensional packed array type handling (#8235)
Signed-off-by: Artur Bieniek <[email protected]>
2026-09-07 14:04:17 -04:00
Matthew Ballance 048d18a84f Add covergroup runtime registry (#8134) 2026-09-07 14:02:03 -04:00
Artur Bieniek cd2475fbd4 Support non-overlapped property implications correctly (#8268)
Signed-off-by: Artur Bieniek <[email protected]>
2026-09-07 13:50:13 -04:00
Igor Zaworski 9c6126b834 [#99994] Fix of detecting non-inlined awaits in V3Delayed.cpp (#7982)
Signed-off-by: Igor Zaworski <[email protected]>
2026-09-07 13:46:30 -04:00
dependabot[bot] 2bffeec8b9 CI: Bump the everything group with 2 updates (#8304) 2026-09-07 10:58:37 -04:00
Marco Frank 63bcfff80a Fix array slice crash when used as a bare value (#5132) (#8262) 2026-09-06 20:57:44 -04:00
Marco Bartoli 9da900a5e2 Add initial support for solo binsof (#8298) 2026-09-06 19:51:31 -04:00
Geza Lore bbbb9c3e7c Optimize away netlist teardown when about to exit (#8294) 2026-09-06 18:08:34 -04:00
Geza Lore 849cc5aeca Internals: Move wrapTop before V3Coverage (#8291) 2026-09-06 17:34:40 -04:00
github action 8213c0ea98 Apply 'make format' [ci skip] 2026-09-06 21:19:40 +00:00
Geza Lore 62bec1bffc Internals: Use auto in UASSERT_SELFTEST (#8299) 2026-09-06 17:16:08 -04:00
Marco Bartoli 0b824f160f Fix escape strings in pattern formatting (#8297) 2026-09-06 13:22:03 -04:00
Nick Brereton 4d1f208cf0 Fix assignment pattern default fill of an array of unpacked structs (#8289) 2026-09-06 11:44:44 -04:00
Geza Lore 7cf8c5cca6 Optimize more selects in DFG (#8292) 2026-09-05 19:57:26 -04:00
Kamil Danecki 73708c55c3 Support nested array and associative array element member access in constraint 1/4 - pre-cleanup (#8237)
Signed-off-by: Kamil Danecki <[email protected]>
2026-09-05 15:33:24 -04:00
Sumanth KadiyalaandSumanth Kadiyala 3990376c57 Internals: resolved incorrect array bounds guarding (#8224)
Co-authored-by: Sumanth Kadiyala <[email protected]>
2026-09-05 15:33:06 -04:00
Geza Lore d04647f459 Fix library method argument references (#8252)
Each VCMethod now carries a signature describing the access required of
the references passed as arguments to the call. 'r' if the argument is
read, 'w' if it is fully assigned so the old value does not matter, 'm'
if it is modified (or only conditionally assigned), with a trailing '+'
repeating the preceding entry for all remaining arguments.  Signatures
are validated at compile time, and V3Broken checks the arguments of
every AstCMethodHard against them.

The incorrect references this found and that are easy to fix are
repaired in this patch. "TODO" marks method that are currently broken or
not yet fit the scheme, these will be fixed in follow up patches.

Also renames SCHED_COMMIT and SCHED_ENQUEUE to NBA_COMMIT and
NBA_ENQUEUE, and remove unused methods.
2026-09-05 19:48:07 +01:00
Matthew Ballance 338c6c2885 Fix covergroup-internal references multi-threaded ordering (#7779) (#8255) 2026-09-05 14:08:03 -04:00
Aditya Shevade a6e21e0eb7 Fix phased solve...before diversity (#8189) (#8194) 2026-09-05 14:06:57 -04:00
Geza Lore f2fe2b6f2e Optimize Dfg synthesis of simple continuous assignments (#8286) 2026-09-05 13:07:53 -04:00
Geza Lore bcf613c4fe Optimize redundant memoization in DFG CSE (#8285) 2026-09-05 12:13:11 -04:00
Geza Lore b907a1a75d Optimize DFG vertex iteration callbacks (#8284) 2026-09-05 12:12:46 -04:00
Geza Lore 87108ff943 Optimize redundant vertex cache lookups in DFG peephole (#8283) 2026-09-05 11:13:30 -04:00
Geza Lore fa59f8f302 Optimize DFG synthesis symbol table ordering (#8282) 2026-09-05 11:13:09 -04:00
Wilson Snyder 35c1a244a8 Internals: Introduce Ast{class}::Super and cleanup. No functional change intended (but possible if was wrong base used earlier) 2026-09-05 10:45:39 -04:00
Wilson Snyder 3493e409b8 Internals: Sort Ast{class} functions in src/V3AstNodes.cpp, and enforce. No functional change. 2026-09-05 10:38:18 -04:00
Wilson Snyder 4f557a4b0c Internals: Reorganize non-Ast{class} functions in src/V3AstNodes.cpp. No functional change. 2026-09-05 10:32:27 -04:00
Wilson Snyder 13c60bfede Commentary: Changes update 2026-09-05 10:10:10 -04:00
Wilson Snyder d6441c9cd3 Change JSON dumps to suppress empty strings, etc 2026-09-05 10:09:25 -04:00
Wilson Snyder 32695cf8be Tests: Enforce Ast*::dump/dumpJson member dumps 2026-09-05 10:06:14 -04:00
Wilson Snyder 5ab54f3c01 Commentary. (Restore CONTRIBUTORS from earlier rev) 2026-09-05 10:05:47 -04:00
Wilson Snyder c1c19494c4 Revert 81fafe237e: Fix CMake to call Verilator perl wrapper (#8269 revert) (#8270 revert) 2026-09-05 09:59:16 -04:00
Wilson Snyder e7b30e214e Internals: Add missing classes to dumps(), enforce with new test. No simulation change. (#8293) 2026-09-05 09:50:13 -04:00
Drew Risinger 81fafe237e Fix CMake to call Verilator perl wrapper (#8269) (#8270) 2026-09-05 09:47:15 -04:00
Wilson Snyder 80de081af0 devel release 2026-09-05 08:45:33 -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
1362 changed files with 164968 additions and 20971 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@1f40c72289eff860ee54a304f1438e3cff362e0a # 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
+22 -8
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,10 +63,17 @@ 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
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
id: deploy-1
continue-on-error: true
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5
- name: Deploy to GitHub Pages (retry)
id: deploy-2
if: steps.deploy-1.outcome == 'failure'
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5
notify:
name: 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.053
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()
+227 -4
View File
@@ -10,6 +10,229 @@ The changes in each Verilator version are described below. The
contributors that suggested or implemented a given issue are shown in []. Thanks!
Verilator 5.053 devel
==========================
**Other:**
* Add covergroup runtime registry (#8134). [Matthew Ballance]
* Add initial support for solo `binsof` (#8298). [Marco Bartoli]
* Add `-fno-dead-members` and optimize dead class member functions (#8830).
* Remove deprecated `--structs-packed`.
* Change JSON dumps to suppress empty strings, etc.
* Support VPI interface references (#8081). [Todd Strader]
* Support nested array and associative array element member access in constraint 1/4 - pre-cleanup (#8237). [Kamil Danecki, Antmicro Ltd.]
* Support unique constraint inside std::randomize (#8241). [Kornel Uriasz, Antmicro Ltd.]
* Support rand dynamic arrays used inside `with` clause of randomize (#8246). [Kamil Danecki, Antmicro Ltd.]
* Support multiplicity in SVA shapes that use NFA rings (#8267). [Artur Bieniek, Antmicro Ltd.]
* Support non-overlapped property implications correctly (#8268). [Artur Bieniek, Antmicro Ltd.]
* Support logic and set operations on binsof (#8306). [Marco Bartoli]
* Optimize NBA shadow copies for partial writes (#8266). [Artur Bieniek, Antmicro Ltd.]
* Optimize DFG synthesis symbol table ordering (#8282). [Geza Lore, Testorrent USA, Inc.]
* Optimize redundant vertex cache lookups in DFG peephole (#8283). [Geza Lore, Testorrent USA, Inc.]
* Optimize DFG vertex iteration callbacks (#8284). [Geza Lore, Testorrent USA, Inc.]
* Optimize redundant memoization in DFG CSE (#8285). [Geza Lore, Testorrent USA, Inc.]
* Optimize DFG synthesis of simple continuous assignments (#8286). [Geza Lore, Testorrent USA, Inc.]
* Optimize more selects in DFG (#8292). [Geza Lore, Testorrent USA, Inc.]
* Optimize netlist teardown when about to exit (#8294). [Geza Lore, Testorrent USA, Inc.]
* Optimize DFG algorithms with an open addressing hash table (#8307). [Geza Lore, Testorrent USA, Inc.]
* Optimize data-type type chain recursion (#8310). [Geza Lore, Testorrent USA, Inc.]
* Optimize unnecessary per-vertex allocations in NFA lowering (#8319). [Artur Bieniek, Antmicro Ltd.]
* Optimize duplicate lookup in NFA property recursion guard (#8321). [Artur Bieniek, Antmicro Ltd.]
* Fix array slice crash when used as a bare value (#5132) (#8262). [Marco Frank]
* Fix pattern type matching against nested aggregates (#7304).
* Fix covergroup-internal references multi-threaded ordering (#7779) (#8255). [Matthew Ballance]
* Fix detecting non-inlined awaits in V3Delayed.cpp (#7982). [Igor Zaworski, Antmicro Ltd.]
* Fix phased `solve...before` diversity (#8189) (#8194). [Aditya Shevade]
* Fix array-size constraint on arrays inside base class (#8219). [Kornel Uriasz, Antmicro Ltd.]
* Fix multidimensional packed array type handling (#8235). [Artur Bieniek, Antmicro Ltd.]
* Fix library method argument references (#8252). [Geza Lore, Testorrent USA, Inc.]
* Fix CMake to call Verilator Perl wrapper (#8269) (#8270) (#8311). [Drew Risinger]
* Fix assignment pattern default fill of an array of unpacked structs (#8289). [Nick Brereton]
* Fix escape strings in pattern formatting (#8297). [Marco Bartoli]
* Fix DECLFILENAME warning on nested declarations (#8302). [Martijn Wobbes]
* Fix property and sequence line coverage (#8314). [Andrii, Antmicro Ltd.]
* Fix wide typed pattern formatting primitives (#8325). [Marco Bartoli]
* Fix parameterized class nested in a parameterized interface (#8328) (#8329). [Kristof Marien]
* Fix format of signed enums (#8338). [Marco Bartoli]
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
==========================
@@ -1679,7 +1902,7 @@ Verilator 5.016 2023-09-16
* Optimize Verilator executable size by refactoring error reporting routines (#4446). [Anthony Donlon]
* Optimize Verilation runtime pointers and graphs (#4396) (#4397) (#4398). [Krzysztof Bieganski, Antmicro Ltd]
* Optimize preparations towards multithreaded Verilation (#4291) (#4463) (#4476) (#4477) (#4479). [Kamil Rakoczy, Antmicro Ltd]
* Fix Windows filename format, etc (#3873) (#4421). [Anthony Donlon].
* Fix Windows filename format, etc (#3873) (#4421). [Anthony Donlon]
* Fix t_dist_cppstyle Perl performance issue (#4085). [Srinivasan Venkataramanan]
* Fix using type in parameterized classes without #() (#4281) (#4440). [Anthony Donlon]
* Fix false INFINITELOOP on forever..mailbox.get() (#4323). [Srinivasan Venkataramanan]
@@ -3464,7 +3687,7 @@ Verilator 3.880 2015-12-19
* Fix model restore crash. (#1013) [Jason McMullan]
* Fix arrayed instances to unpacked of same size. (#1015) [Varun Koyyalagunta]
* Fix slices of unpacked arrays with non-zero LSBs.
* Fix ternary operation with unpacked array. (#1017) [Varun Koyyalagunta].
* Fix ternary operation with unpacked array. (#1017) [Varun Koyyalagunta]
Verilator 3.878 2015-11-01
@@ -3554,7 +3777,7 @@ Verilator 3.870 2015-02-12
* Suppress COMBDLY when inside always_latch. (#864) [Iztok Jeras]
* Support cast operator with expression size. (#865) [Iztok Jeras]
* Add warning on slice selection out of bounds. (#875) [Cong Van Nguyen].
* Add warning on slice selection out of bounds. (#875) [Cong Van Nguyen]
* Fix member select error broke in 3.868. (#867) [Iztok Jeras]
* Fix $sccanf from string. (#866) [David Pierce]
* Fix VM_PARALLEL_BUILDS broke in 3.868. (#870) [Hiroki Honda]
@@ -5738,7 +5961,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:**
+18 -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/*'
@@ -735,6 +747,7 @@ FASTCOV_OPT += BROKEN_RTN
FASTCOV_OPT += NUM_ASSERT
FASTCOV_OPT += SELF_CHECK
FASTCOV_OPT += UASSERT
FASTCOV_OPT += UDEBUGONLY
FASTCOV_OPT += UINFO
FASTCOV_OPT += assert
FASTCOV_OPT += 'if (VL_UNCOVERABLE'
@@ -749,6 +762,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 +794,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 +803,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?name=pr-notification" --jq '.artifacts[].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.053 devel],
[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)
+42 -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,19 +29,24 @@ 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
CatalpaEel
Chih-Mao Chen
Chris Bachhuber
Chris Randall
@@ -53,27 +61,37 @@ 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 Risinger
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 +126,8 @@ Ilya Barkov
Iru Cai
Ivan Vnučec
Iztok Jeras
JAEUK LEE
Jaeuk Lee (이재욱)
Jake Merdich
Jakub Michalski
Jakub Wasilewski
@@ -122,6 +142,8 @@ Jamie Iles
Jan Van Winkel
Jean Berniolles
Jean-Nicolas Strauss
Jeffrey Song
jeffrey.song
Jens Yuechao Liu
Jeremy Bennett
Jesse Taube
@@ -147,6 +169,7 @@ Josep Sans
Joseph Nwabueze
Josh Redford
Joshua Leahy
JOTEGO (Jose Tejada)
Julian Carrier
Julian Daube
Julie Schwartz
@@ -161,6 +184,7 @@ Keith Colbert
Kevin Kiningham
Kevin Nygaard
Kornel Uriasz
Kristof Marien
Kritik Bhimani
Krzysztof Bieganski
Krzysztof Boronski
@@ -183,11 +207,14 @@ Maarten De Braekeleer
Maciej Sobkowski
Marcel Chang
Marco Bartoli
Marco Brambilla
Marco Frank
Marco Widmer
Mariusz Glebocki
Markus Krause
Marlon James
Marshal Qiao
Martijn Wobbes
Martin Schmidt
Martin Stadler
Mateusz Gancarz
@@ -202,12 +229,14 @@ Michael Rogenmoser
Michal Czyz
Michaël Lefebvre
Miguel Perez Andrade
Mike Murunov
Mike Popoloski
Miodrag Milanović
Mladen Slijepcevic
Morten Borup Petersen
Mostafa Gamal
Moubarak Jeje
Muzaffer Kal
Nandu Raj
Natan Kreimer
Nathan Graybeal
@@ -218,8 +247,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 +294,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 +327,7 @@ Tracy Narine
Trung Nguyen
Tudor Timi
Tymoteusz Blazejczyk
Tyrone Marhguy
Udaya Raj Subedi
Udi Finkelstein
Unai Martinez-Corral
@@ -308,9 +345,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 +360,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
View File
@@ -18,14 +18,6 @@ C++14 compiler support
the Ubuntu LTS versions of GCC and clang use C++20 by default, estimated
May 2028.)
`--structs-packed` option
The :vlopt:`--structs-packed` option was introduced when Verilator was
first implementing unpacked structs. That feature has been stable now
for multiple years, so :vlopt:`--structs-packed` should no longer be
used. Thus :vlopt:`--structs-packed` will change to a no-operation flag
and the related :option:`UNPACKED` warning will never be issued no
sooner than September 2026.
tcmalloc support
Verilator currently supports the default malloc, tcmalloc, or jemalloc.
As jemalloc has better performance, support for tcmalloc may be removed
+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>
+17 -6
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
@@ -702,6 +706,8 @@ Summary:
.. option:: -fno-dead-cells
.. option:: -fno-dead-methods
.. option:: -fno-dedup
.. option:: -fno-dfg
@@ -715,6 +721,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 +946,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 +1229,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>
@@ -1780,12 +1792,11 @@ Summary:
.. option:: --structs-packed
Deprecated; discontinue use of this option.
Removed in 5.054.
Converts all unpacked structures to packed structures, and issues an
:option:`UNPACKED` warning. Specifying this option allows for backward
compatibility with versions before Verilator 5.006, when Verilator would
always pack unpacked structures.
Converted all unpacked structures to packed structures, Specifying this
option allowed for backward compatibility with versions before Verilator
5.006, when Verilator would always pack unpacked structures.
.. option:: -sv
+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
@@ -174,7 +174,7 @@ once, after ``configure``:
# Install dependencies
sudo apt install python3-pip
# Create Python virutal environment in .venv:
# Create Python virtual environment in .venv:
make venv
# Or alternatively, to put it somewhere else:
+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.
+89 -7
View File
@@ -592,6 +592,10 @@ List Of Warnings
``covergroup``, ``coverpoint``, and coverage options, and the
construct was ignored.
This includes crosses whose normal-bin Cartesian product exceeds
``2**32 - 1`` tuples. The limit is checked during Verilation for both
automatic and explicit cross bins.
Disabling the :option:`UNSUPPORTED` error also disables this warning.
Ignoring this warning may make Verilator ignore lint checking on the
@@ -1469,11 +1473,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 +1564,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 +2149,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
@@ -2417,14 +2503,10 @@ List Of Warnings
.. option:: UNPACKED
Warns that unpacked structs and unions are not supported because
:vlopt:`--structs-packed` was used, or by up through version 5.004.
Historical, never issued since version 5.004.
Ignoring this warning will make Verilator treat the structure as packed,
which may make Verilator simulations differ from other simulators. This
downgrading may also result in what would typically be a legal unpacked
struct/array inside an unpacked struct/array becoming an illegal
unpacked struct/array inside a packed struct/array.
Warned that unpacked structs and unions were not supported, or disabled
by the since-removed `--structs-packed` option.
.. option:: UNSATCONSTR
+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``.
+14
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
@@ -621,6 +627,7 @@ benchmarking
biguint
binToOneHot
bindir
binsof
biops
bisonpre
bitOpTree
@@ -644,6 +651,7 @@ casez
casted
castro
cb
cbValueChange
ccache
ccall
cdc
@@ -707,6 +715,7 @@ de
dearray
deassign
debugi
deduplication
defenv
defname
defparam
@@ -939,6 +948,7 @@ makefile
makefiles
malloc
manpages
memoization
metacomment
metacomments
miree
@@ -947,6 +957,7 @@ misconnected
misconversion
misdetecting
misoptimized
misoptimizing
missized
mk
mno
@@ -995,6 +1006,7 @@ noprivate
noreturn
notif
nullptr
num
numactl
onehot
ooo
@@ -1172,8 +1184,10 @@ systemc
taskify
tcmalloc
tcmalloc
teardown
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
+311 -69
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);
@@ -1173,8 +1178,12 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
} else if (formatAttr == VL_VFORMATATTR_STRING) {
thingp = va_arg(ap, std::string*);
if (fmt != 'p' && fmt != 'x') fmt = 's'; // Override
} else if (formatAttr == VL_VFORMATATTR_ENUM) {
} else if (formatAttr == VL_VFORMATATTR_ENUM
|| formatAttr == VL_VFORMATATTR_ENUM_SIGNED) {
// Always <= VL_QUADSIZE; emit uses non-ENUM format for wider enums
const int numericAttr = formatAttr == VL_VFORMATATTR_ENUM_SIGNED
? VL_VFORMATATTR_SIGNED
: VL_VFORMATATTR_UNSIGNED;
lbits = va_arg(ap, int);
ld = VL_VA_ARG_Q_(ap, lbits);
strwide.resize(2);
@@ -1187,6 +1196,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
enump = va_arg(ap, std::string*);
if (enump && !enump->empty()) {
formatAttr = (fmt == 'p') ? VL_VFORMATATTR_COMPLEX : VL_VFORMATATTR_STRING;
if (fmt == 'd') formatAttr = numericAttr;
thingp = const_cast<std::string*>(enump);
} else if (fmt == 'p' && widthSet && width == 0) {
output += "'h";
@@ -1196,7 +1206,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
if (fmt == 'p') width = 0;
widthSet = true;
fmt = 'd';
formatAttr = VL_VFORMATATTR_UNSIGNED;
formatAttr = numericAttr;
}
if (widthSet && width == 0) {
while (lsb && !VL_BITISSET_W(lwp, lsb)) --lsb;
@@ -1277,7 +1287,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
output += t_tmp;
} else if (formatAttr == VL_VFORMATATTR_STRING) {
const std::string* const strp = static_cast<const std::string*>(thingp);
output += '"' + *strp + '"';
output += VL_TO_STRING(*strp);
} else if (formatAttr == VL_VFORMATATTR_COMPLEX) {
const std::string* const strp = static_cast<const std::string*>(thingp);
output += *strp;
@@ -2483,6 +2493,30 @@ std::string VL_TO_STRING(QData lhs) {
std::string VL_TO_STRING(double lhs) {
return VL_SFORMATF_N_NX("%g", 1, VL_VFORMATATTR_DOUBLE, lhs);
}
std::string VL_TO_STRING(const std::string& obj) VL_PURE {
std::string out{"\""};
out.reserve(obj.size() + 2);
for (const unsigned char ch : obj) {
switch (ch) {
case '\n': out += "\\n"; break;
case '\r': out += "\\r"; break;
case '\t': out += "\\t"; break;
case '"': out += "\\\""; break;
case '\\': out += "\\\\"; break;
default:
if (std::isprint(ch)) {
out += static_cast<char>(ch);
} else {
out += '\\';
out += static_cast<char>('0' + ((ch >> 6) & 3));
out += static_cast<char>('0' + ((ch >> 3) & 7));
out += static_cast<char>('0' + (ch & 7));
}
break;
}
}
return out + '"';
}
std::string VL_TO_STRING_W(int words, const WDataInP obj) {
return VL_SFORMATF_N_NX("'h%0x", 1, VL_VFORMATATTR_UNSIGNED, words * VL_EDATASIZE, obj);
}
@@ -3067,9 +3101,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 +3123,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;
}
@@ -3094,27 +3131,24 @@ bool VerilatedContext::assertOnGet(VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
return assertCtlGet(VerilatedAssertCtlQuery::ASSERT_CTL_ON, type, directive);
}
uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_PURE {
// Place the directive bits at each selected assertion type's 3-bit group.
uint32_t mask = 0;
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
if (VL_BITISSET_I(types, i)) mask |= directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH);
}
return mask;
}
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) {
@@ -3164,26 +3198,6 @@ void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t typ
.c_str());
}
}
uint32_t
VerilatedContext::assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
const uint32_t mask = assertOnMask(type, directive);
if (!mask) return 0;
switch (query) { // LCOV_EXCL_BR_LINE
case VerilatedAssertCtlQuery::ASSERT_CTL_ON: return (m_s.m_assertOn & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_KILL:
assert(mask && (mask & (mask - 1)) == 0);
return m_s.m_assertKill[VL_CLOG2_I(mask)];
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_VACUOUS:
return (m_s.m_assertPassOnVacuous & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_NONVACUOUS:
return (m_s.m_assertPassOnNonvacuous & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_FAIL_ON: return (m_s.m_assertFailOn & mask) != 0;
default: // LCOV_EXCL_START
VL_FATAL_MT("", 0, "", "Internal: Bad assertCtlGet query");
VL_UNREACHABLE;
} // LCOV_EXCL_STOP
}
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_s.m_calcUnusedSigs = flag;
@@ -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)) {
@@ -3719,11 +3745,17 @@ void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
// VerilatedContext:: Methods - scopes
void VerilatedContext::scopesDump() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
VL_PRINTF_MT(" scopesDump:\n");
for (const auto& i : m_impdatap->m_nameMap) {
const VerilatedScope* const scopep = i.second;
scopep->scopeDump();
{
const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
VL_PRINTF_MT(" scopesDump:\n");
for (const auto& i : m_impdatap->m_nameMap) {
const VerilatedScope* const scopep = i.second;
scopep->scopeDump();
}
}
{
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
for (const auto& i : m_impdatap->m_ifaceRefMap) i.second.ifaceRefDump();
}
VL_PRINTF_MT("\n");
}
@@ -3753,6 +3785,31 @@ const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
return &(impp()->m_impdatap->m_nameMap);
}
void VerilatedContextImp::ifaceRefInsert(const VerilatedIfaceRef& ifaceRef) VL_MT_SAFE {
// Slow ok - called once/interface-reference at construction
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
m_impdatap->m_ifaceRefMap.emplace(ifaceRef.fullname(), ifaceRef);
}
void VerilatedContextImp::ifaceRefErase(const std::string& fullname,
const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/interface-reference at destruction
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
const auto it = m_impdatap->m_ifaceRefMap.find(fullname);
// Models sharing an instance name collide on the key; only erase our own,
// so tearing one down leaves another's live reference registered
if (it != m_impdatap->m_ifaceRefMap.end() && it->second.scopep() == scopep) {
m_impdatap->m_ifaceRefMap.erase(it);
}
}
const VerilatedIfaceRef*
VerilatedContext::ifaceRefFind(const char* namep) const VL_MT_SAFE_POSTINIT {
// Thread safe only assuming this is called only after model construction completed
const VerilatedLockGuard lock{m_impdatap->m_ifaceRefMutex};
const auto& it = m_impdatap->m_ifaceRefMap.find(namep);
if (VL_UNLIKELY(it == m_impdatap->m_ifaceRefMap.end())) return nullptr;
return &it->second;
}
//======================================================================
// VerilatedContext:: Methods - trace
@@ -3891,7 +3948,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 +4069,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 +4235,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 +4299,85 @@ 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};
}
}
// Prefix with the model instance name, as VerilatedScope's constructor does
static std::string vl_ifaceRefFullname(const VerilatedSyms* symsp, const char* suffixp) {
const char* const prefixp = symsp->name();
std::string out{prefixp};
if (*prefixp && *suffixp) out += '.';
out += suffixp;
return out;
}
void VerilatedScope::ifaceRefsInsertFromTable(const VlIfaceRefTableEntry* entp, size_t n,
VerilatedSyms* symsp) VL_MT_UNSAFE {
// Use the model's own context; at destruction threadContextp() may be another's
VerilatedContextImp* const impp = symsp->_vm_contextp__->impp();
uint8_t* const base = reinterpret_cast<uint8_t*>(symsp);
for (size_t i = 0; i < n; ++i) {
const VlIfaceRefTableEntry& e = entp[i];
const VerilatedScope* const scopep
= *reinterpret_cast<VerilatedScope**>(base + e.ptrOffset);
impp->ifaceRefInsert(
VerilatedIfaceRef{scopep, e.namep, vl_ifaceRefFullname(symsp, e.suffixp), e.modportp});
}
}
void VerilatedScope::ifaceRefsEraseFromTable(const VlIfaceRefTableEntry* entp, size_t n,
const VerilatedSyms* symsp) VL_MT_UNSAFE {
VerilatedContextImp* const impp = symsp->_vm_contextp__->impp();
uint8_t* const base = reinterpret_cast<uint8_t*>(const_cast<VerilatedSyms*>(symsp));
for (size_t i = 0; i < n; ++i) {
const VlIfaceRefTableEntry& e = entp[i];
const VerilatedScope* const scopep
= *reinterpret_cast<VerilatedScope**>(base + e.ptrOffset);
impp->ifaceRefErase(vl_ifaceRefFullname(symsp, e.suffixp), scopep);
}
}
VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, int udims,
uint32_t entSize...) VL_MT_UNSAFE {
@@ -4287,6 +4523,12 @@ void VerilatedScope::scopeDump() const {
}
}
void VerilatedIfaceRef::ifaceRefDump() const VL_MT_SAFE_POSTINIT {
VL_PRINTF_MT(" IFACEREF %p: %s -> %s", this, fullname(), scopep()->name());
if (hasModport()) VL_PRINTF_MT(".%s", modport());
VL_PRINTF_MT("\n");
}
void VerilatedHierarchy::add(const VerilatedScope* fromp, const VerilatedScope* top) {
VerilatedImp::hierarchyAdd(fromp, top);
}
+251 -6
View File
@@ -100,6 +100,9 @@ class VerilatedFstC;
class VerilatedFstSc;
class VerilatedScope;
class VerilatedScopeNameMap;
class VerilatedIfaceRef;
class VerilatedIfaceRefMap;
struct VlIfaceRefTableEntry;
template <typename, typename>
class VerilatedTrace;
class VerilatedTraceBaseC;
@@ -109,6 +112,7 @@ class VerilatedVarNameMap;
class VerilatedVcd;
class VerilatedVcdC;
class VerilatedVcdSc;
class VlCovRegistry;
//=========================================================================
// Basic types
@@ -160,6 +164,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 +305,8 @@ public:
#endif
};
class VlExecutionProfilerBase;
//=========================================================================
/// Base class of a Verilator generated (Verilated) model.
///
@@ -298,6 +319,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 +339,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 +439,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
///
@@ -369,9 +483,11 @@ private:
= ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits<VerilatedAssertType_t>::digits
+ 1;
// Build the assertion-control bit mask for the given assertion x directive types.
static uint32_t assertOnMask(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_PURE;
static inline 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 +546,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
@@ -472,6 +594,9 @@ protected:
std::unique_ptr<VerilatedVirtualBase> m_executionProfiler;
// Coverage access
std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep()
// Covergroup type/instance nodes. Covergroup data is always collected,
// independent of whether coverage data is recorded (--coverage).
std::unique_ptr<VerilatedVirtualBase> m_covergroupsp; // Pointer for covergroupRegistryp()
// File I/O
// Not serialized
@@ -510,13 +635,19 @@ 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;
/// Get assertion-control runtime state. Boolean queries return 0/1, Kill returns
/// the generation count.
uint32_t assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
inline uint32_t assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
/// Return if calculating of unused signals (for traces)
bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
/// Enable calculation of unused signals (for traces)
@@ -535,6 +666,8 @@ public:
/// Return VerilatedCovContext, allocate if needed
/// Note if get unresolved reference then likely forgot to link verilated_cov.cpp
VerilatedCovContext* coveragep() VL_MT_SAFE;
/// Returns VlCovRegistry. Allocated on-demand
VlCovRegistry* covergroupRegistryp() VL_MT_SAFE;
/// Return debug level
static inline int debug() VL_MT_SAFE; /// Set debug level
/// Debug is currently global, but for forward compatibility have a per-context method
@@ -669,6 +802,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 {
@@ -723,6 +877,9 @@ public:
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE;
// Internal: Find interface reference by fully qualified path
const VerilatedIfaceRef* ifaceRefFind(const char* namep) const VL_MT_SAFE_POSTINIT;
// Internal: Serialization setup
static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); }
void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; }
@@ -752,17 +909,46 @@ public: // But for internal use only
virtual const char* name() const = 0;
};
// An interface reference port, and the concrete interface it is connected to.
// Used for VPI; references are not scopes, so are not in VerilatedScopeNameMap.
class VerilatedIfaceRef final {
const VerilatedScope* m_scopep = nullptr; // Concrete interface referred to
const char* m_namep = ""; // Name of the reference port
// Fully qualified path; owned, as the instance name prefix is set at construction
std::string m_fullname;
const char* m_modportp = ""; // Modport name, or "" if none
public:
VerilatedIfaceRef() = default;
VerilatedIfaceRef(const VerilatedScope* scopep, const char* namep, const std::string& fullname,
const char* modportp)
: m_scopep{scopep}
, m_namep{namep}
, m_fullname{fullname}
, m_modportp{modportp} {}
~VerilatedIfaceRef() = default;
// ACCESSORS
const VerilatedScope* scopep() const VL_MT_SAFE_POSTINIT { return m_scopep; }
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
const char* fullname() const VL_MT_SAFE_POSTINIT { return m_fullname.c_str(); }
const char* modport() const VL_MT_SAFE_POSTINIT { return m_modportp; }
bool hasModport() const VL_MT_SAFE_POSTINIT { return m_modportp[0] != '\0'; }
void ifaceRefDump() const VL_MT_SAFE_POSTINIT;
};
//===========================================================================
// 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 {
SCOPE_MODULE,
SCOPE_OTHER,
SCOPE_PACKAGE
}; // Type of a scope, currently only module and package are interesting
SCOPE_PACKAGE,
SCOPE_INTERFACE
}; // Type of a scope, currently only module, package and interface are interesting
private:
// Fastpath:
VerilatedSyms* const m_symsp; // Symbol table
@@ -792,6 +978,13 @@ 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;
static void ifaceRefsInsertFromTable(const VlIfaceRefTableEntry* entp, size_t n,
VerilatedSyms* symsp) VL_MT_UNSAFE;
static void ifaceRefsEraseFromTable(const VlIfaceRefTableEntry* entp, size_t n,
const 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 +998,27 @@ 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 interface reference, consumed by VerilatedScope::ifaceRefsInsertFromTable()
struct VlIfaceRefTableEntry final {
uint32_t ptrOffset; // offsetof of the referred-to __Vscopep_* member within the Syms object
const char* namep; // Name of the reference port
// Path within the model; as VlScopeTableEntry::namep, instance name prepended at construction
const char* suffixp;
const char* modportp; // Modport name, or "" if none
};
// 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 {
@@ -1114,5 +1328,36 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
#endif
}
// Defined here, not in-class: VL_CLOG2_I / VL_FATAL_MT (verilated_funcs.h) are not yet in scope
uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_PURE {
// Place the directive bits at each selected assertion type's 3-bit group.
uint32_t mask = 0;
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
if (VL_BITISSET_I(types, i)) mask |= directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH);
}
return mask;
}
uint32_t
VerilatedContext::assertCtlGet(VerilatedAssertCtlQuery query, VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
const uint32_t mask = assertOnMask(type, directive);
if (!mask) return 0;
switch (query) { // LCOV_EXCL_BR_LINE
case VerilatedAssertCtlQuery::ASSERT_CTL_ON: return (m_s.m_assertOn & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_KILL:
assert(mask && (mask & (mask - 1)) == 0);
return m_s.m_assertKill[VL_CLOG2_I(mask)];
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_VACUOUS:
return (m_s.m_assertPassOnVacuous & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_NONVACUOUS:
return (m_s.m_assertPassOnNonvacuous & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_FAIL_ON: return (m_s.m_assertFailOn & mask) != 0;
default: // LCOV_EXCL_START
VL_FATAL_MT("", 0, "", "Internal: Bad assertCtlGet query");
VL_UNREACHABLE;
} // LCOV_EXCL_STOP
}
#undef VERILATOR_VERILATED_H_INTERNAL_
#endif // Guard
+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;
};
+477 -10
View File
@@ -23,34 +23,58 @@
#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
// owned by the covergroup instance nodes in the VerilatedContext's registry, 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 the registry owns and
// reads it at write time. Only that publication step needs the database, so
// only the registerBins() bodies -- and this include -- 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 +83,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 +105,446 @@ 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;
assert(dims == m_dims);
// Accumulate in 64 bits so the overflow check itself cannot overflow.
uint64_t product = m_numAutoBins ? 1 : 0;
for (uint32_t d = 0; d < dims; ++d) {
m_dimensionsp[d] = {cps[d], nullptr, cps[d]->normalBinCount(), 1};
product *= m_dimensionsp[d].bins;
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
}
assert(product == m_numAutoBins);
// 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.
for (uint32_t d = dims; d > 1; --d) {
m_dimensionsp[d - 2].stride = m_dimensionsp[d - 1].stride * m_dimensionsp[d - 1].bins;
}
}
void VlCoverCross::addBin(std::initializer_list<uint64_t> selection, const char* namep,
const char* filep, int line, int col) {
if (!m_numAutoBins) return; // An empty product creates no cross bin.
Explicit& data = *m_explicitp;
const uint32_t words = m_numAutoBins / 64 + (m_numAutoBins % 64 != 0);
assert(selection.size() == words);
assert(data.numBins < data.bins.size());
uint64_t* const selectionp = data.selectionp + static_cast<uint64_t>(data.numBins) * words;
std::copy(selection.begin(), selection.end(), selectionp);
Bin& bin = data.bins[data.numBins++];
bin.selectionp = selectionp;
bin.namep = namep;
bin.filep = filep;
bin.line = line;
bin.col = col;
uint32_t word = 0;
for (const uint64_t bits : selection) { data.wordsp[word++].autoExcluded |= bits; }
}
void VlCoverCross::finalizeBins() {
if (!hasExplicitBins()) return;
Explicit& data = *m_explicitp;
assert(data.numBins == data.bins.size());
uint32_t autoIdx = 0;
for (uint32_t flat = 0; flat < m_numAutoBins; ++flat) {
if (!(data.wordsp[flat / 64].autoExcluded & (uint64_t{1} << (flat % 64)))) {
assert(autoIdx < data.autoBins.size());
data.autoBins[autoIdx++] = flat;
}
}
const uint32_t words = m_numAutoBins / 64 + (m_numAutoBins % 64 != 0);
assert(autoIdx == data.autoBins.size());
data.minBinWords = words;
uint64_t pos = 0;
const uint32_t* const indicesp = data.binWords.begin();
for (Bin& bin : data.bins) {
const uint64_t begin = pos;
for (uint32_t word = 0; word < words; ++word) {
if (bin.selectionp[word]) {
assert(pos < data.binWords.size());
data.binWords[pos++] = word;
}
}
bin.wordIndicesp = indicesp ? indicesp + begin : nullptr;
bin.numWords = static_cast<uint32_t>(pos - begin);
data.minBinWords = std::min(data.minBinWords, bin.numWords);
}
assert(pos == data.binWords.size());
}
template <bool T_Explicit, bool T_RecordHits>
void VlCoverCross::iterateProduct(uint32_t dim, uint32_t baseIdx) {
const VlCoverpoint* const cpp = m_dimensionsp[dim].cpp;
const uint32_t hits = cpp->hitCount();
const uint32_t* const list = m_dimensionsp[dim].hitsp;
const bool last = (dim == m_dims - 1);
const uint32_t stride = m_dimensionsp[dim].stride;
for (uint32_t hit = 0; hit < hits; ++hit) {
const uint32_t idx = baseIdx + list[hit] * stride;
if (last) {
if (T_Explicit) {
incrementTuple<T_RecordHits>(idx);
} else {
incrementAuto(idx);
}
} else {
iterateProduct<T_Explicit, T_RecordHits>(dim + 1, idx);
}
}
}
template <bool T_ApplyIffs>
void VlCoverCross::sampleSingleTuple(uint32_t idx, const bool* binIffs) {
Explicit& data = *m_explicitp;
const uint32_t word = idx / 64;
const uint64_t bit = uint64_t{1} << (idx % 64);
if (!(data.wordsp[word].autoExcluded & bit)) {
incrementAuto(idx);
return;
}
for (Bin& bin : data.bins) {
if (T_ApplyIffs && !*binIffs++) continue;
if (bin.selectionp[word] & bit) {
if (bin.count++ == 0) ++m_numCovered;
}
}
}
template <bool T_ApplyIffs, uint32_t T_Touched, bool T_Dense>
void VlCoverCross::sampleBins(const bool* binIffs) {
struct HitWord final {
uint32_t index;
uint64_t bits;
};
Explicit& data = *m_explicitp;
const uint64_t bins = data.numBins;
const uint64_t touched = T_Touched ? T_Touched : data.numTouchedWords;
const Word* const wordsp = data.wordsp;
std::array<HitWord, T_Touched> cached{};
for (uint32_t i = 0; i < T_Touched; ++i) {
const uint32_t word = wordsp[i].touchedWord;
cached[i] = {word, wordsp[word].hitBits};
}
for (uint64_t binIdx = 0; binIdx < bins; ++binIdx) {
if (T_ApplyIffs && !*binIffs++) continue;
Bin& bin = data.bins[binIdx];
bool matched = false;
if (T_Touched == 1) {
matched = (bin.selectionp[cached[0].index] & cached[0].bits) != 0;
} else if (T_Dense || bin.numWords >= touched) {
for (uint64_t i = 0; i < touched; ++i) {
const uint32_t word = T_Touched ? cached[i].index : wordsp[i].touchedWord;
const uint64_t hits = T_Touched ? cached[i].bits : wordsp[word].hitBits;
if (bin.selectionp[word] & hits) {
matched = true;
break;
}
}
} else {
for (uint32_t pos = 0; pos < bin.numWords; ++pos) {
const uint32_t word = bin.wordIndicesp[pos];
if (bin.selectionp[word] & wordsp[word].hitBits) {
matched = true;
break;
}
}
}
if (matched && bin.count++ == 0) ++m_numCovered;
}
for (uint32_t i = 0; i < data.numTouchedWords; ++i) {
data.wordsp[wordsp[i].touchedWord].hitBits = 0;
}
data.numTouchedWords = 0;
}
template <bool T_ApplyIffs, bool T_Dense>
void VlCoverCross::sampleHitWords(const bool* binIffs) {
switch (m_explicitp->numTouchedWords) {
case 1: sampleBins<T_ApplyIffs, 1, T_Dense>(binIffs); break;
case 2: sampleBins<T_ApplyIffs, 2, T_Dense>(binIffs); break;
case 3: sampleBins<T_ApplyIffs, 3, T_Dense>(binIffs); break;
default: sampleBins<T_ApplyIffs, 0, T_Dense>(binIffs); break;
}
}
void VlCoverCross::sample(const bool* binIffs) {
// Fast path: if any dimension had no Normal-bin hit, the cross cannot hit.
bool single = true;
for (uint32_t d = 0; d < m_dims; ++d) {
const uint32_t hits = m_dimensionsp[d].cpp->hitCount();
if (hits == 0) return;
single &= hits == 1;
}
if (single) {
uint32_t idx = 0;
for (uint32_t d = 0; d < m_dims; ++d) {
idx += m_dimensionsp[d].cpp->hitList()[0] * m_dimensionsp[d].stride;
}
if (hasExplicitBins()) {
if (binIffs) {
sampleSingleTuple<true>(idx, binIffs);
} else {
sampleSingleTuple<false>(idx, nullptr);
}
} else {
incrementAuto(idx);
}
return;
}
bool enabled = true;
if (hasExplicitBins() && binIffs && !binIffs[0]) {
const bool* const endp = binIffs + m_explicitp->bins.size();
enabled = std::find(binIffs + 1, endp, true) != endp;
if (!enabled && m_explicitp->autoBins.empty()) return;
}
for (uint32_t d = 0; d < m_dims; ++d) {
m_dimensionsp[d].hitsp = m_dimensionsp[d].cpp->hitList();
}
if (!hasExplicitBins()) {
iterateProduct<false>(0, 0);
return;
}
if (!enabled) {
iterateProduct<true, false>(0, 0);
return;
}
iterateProduct<true>(0, 0);
if (m_explicitp->numTouchedWords) {
const bool dense = m_explicitp->minBinWords >= m_explicitp->numTouchedWords;
if (binIffs) {
if (dense) {
sampleHitWords<true, true>(binIffs);
} else {
sampleHitWords<true, false>(binIffs);
}
} else {
if (dense) {
sampleHitWords<false, true>(nullptr);
} else {
sampleHitWords<false, false>(nullptr);
}
}
}
}
std::string VlCoverCross::binName(uint32_t i) const {
if (hasExplicitBins()) {
if (i < m_explicitp->bins.size()) return m_explicitp->bins[i].namep;
i -= static_cast<uint32_t>(m_explicitp->bins.size());
}
return autoBinName(autoIndex(i));
}
std::string VlCoverCross::autoBinName(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 Dimension& dimension = m_dimensionsp[d];
const uint32_t crossIdx = (flat / dimension.stride) % dimension.bins;
if (d > 0) name += "_x_";
name += dimension.cpp->normalBinName(crossIdx);
}
return name;
}
#if VM_COVERAGE
void VlCoverCross::registerBins(VerilatedCovContext* covcontextp, const char* page) {
const std::string lineStr = std::to_string(m_line);
const std::string colStr = std::to_string(m_col);
const uint32_t explicitCount
= hasExplicitBins() ? static_cast<uint32_t>(m_explicitp->bins.size()) : 0;
// Use the same indexed names for registration and the runtime read interface.
for (uint32_t i = 0; i < binCount(); ++i) {
const std::string bin = binName(i);
const std::string full = m_hier + "." + bin;
if (i < explicitCount) {
Bin& userBin = m_explicitp->bins[i];
const std::string binLineStr = std::to_string(userBin.line);
const std::string binColStr = std::to_string(userBin.col);
VL_COVER_INSERT(covcontextp, full.c_str(), &userBin.count, "page", page, "filename",
userBin.filep, "lineno", binLineStr.c_str(), "column",
binColStr.c_str(), "bin", bin.c_str(), "cross", "1");
continue;
}
const uint32_t flat = autoIndex(i - explicitCount);
// 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 Dimension& dimension = m_dimensionsp[d];
const uint32_t crossIdx = (flat / dimension.stride) % dimension.bins;
if (d > 0) crossBins += ",";
crossBins += dimension.cpp->normalBinName(crossIdx);
}
VL_COVER_INSERT(covcontextp, full.c_str(), &m_flatCountsp[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
//=============================================================================
// VlCovergroupType / VlCovRegistry
VlCovergroupInst* VlCovergroupType::newInstance() {
VlCovergroupInst* const instp = new VlCovergroupInst{this, m_nextInstId++};
m_insts.emplace_back(instp);
#if !VM_COVERAGE
instp->m_slot = static_cast<uint32_t>(m_insts.size() - 1);
#endif
++m_createdInsts;
return instp;
}
void VlCovergroupType::foldResidue(const VlCovergroupInst* instp) {
double covered = 0.0;
double total = 0.0;
instp->coverageParts(covered, total);
// Nothing coverable: excluded from both sums, so it moves neither the mean
// nor the denominator. Never-sampled is different: it has bins, none hit,
// and folds as 0%.
if (total == 0.0) return;
// TODO(P5): IEEE 1800-2023 19.5 defines covergroup coverage as the weighted
// mean of the per-item ratios, not the ratio of the summed parts. This
// matches what the generated get_inst_coverage() computes today, so that a
// live instance and the same instance one delta after death never disagree.
m_retired.sumCoverage += 100.0 * covered / total;
++m_retired.count;
}
// Runs when the last handle to instp drops, possibly after ~VlCovRegistry, on a
// type teardown leaked to keep this valid (see ~VlCovRegistry). That late case
// needs no special handling: the leaked type is self-consistent.
void VlCovergroupType::retire(VlCovergroupInst* instp) {
foldResidue(instp); // Before unlink: reads instp's items, freed below
#if VM_COVERAGE
// registerBins() gave the coverage database raw &m_counts[i], read at
// write() time. Keep the node alive, marked dead so it counts as neither
// live nor residue. Freeing here needs the coverage-writer rework.
instp->m_retained = true;
#else
// Move out first, so the node destructs at end of scope with m_insts
// already consistent rather than mid-swap.
const uint32_t slot = instp->m_slot;
const std::unique_ptr<VlCovergroupInst> dying = std::move(m_insts[slot]);
if (slot != m_insts.size() - 1) {
m_insts[slot] = std::move(m_insts.back());
m_insts[slot]->m_slot = slot; // Moved node's slot is now stale
}
m_insts.pop_back();
#endif
}
uint32_t VlCovergroupType::liveInstanceCount() const {
uint32_t live = 0;
// Under VM_COVERAGE m_insts also holds retained (dead) nodes; otherwise
// retained() is never set and this equals m_insts.size().
for (const auto& instp : m_insts) {
if (!instp->retained()) ++live;
}
return live;
}
bool VlCovergroupType::anyAttached() const {
for (const auto& instp : m_insts) {
if (instp->m_attachCount > 0) return true;
}
return false;
}
double VlCovergroupType::retiredCoverage() const {
if (m_retired.count == 0) return -1.0;
return m_retired.sumCoverage / static_cast<double>(m_retired.count);
}
// Defined here, not in verilated.cpp, so that the registry costs nothing in a model with no
// covergroups: this file is linked only when covergroups are used (or --coverage is on).
// Mirrors VerilatedContext::coveragep(), which lives in verilated_cov.cpp for the same reason.
VlCovRegistry* VerilatedContext::covergroupRegistryp() VL_MT_SAFE {
static VerilatedMutex s_mutex;
// cppcheck-suppress identicalInnerCondition
if (VL_UNLIKELY(!m_covergroupsp)) {
const VerilatedLockGuard lock{s_mutex};
// cppcheck-suppress identicalInnerCondition
if (VL_LIKELY(!m_covergroupsp)) { // LCOV_EXCL_LINE // Not redundant, prevents race
m_covergroupsp.reset(new VlCovRegistry{});
}
}
return static_cast<VlCovRegistry*>(m_covergroupsp.get());
}
VlCovergroupInst* VlCovRegistry::newCovergroupInst(const char* typeName) {
VlCovergroupType*& typep = m_byName[typeName];
if (!typep) { // First instance of this type
m_types.emplace_back(new VlCovergroupType{});
typep = m_types.back().get();
}
return typep->newInstance();
}
// A covergroup object can outlive the registry: models must be destroyed before
// their context, and a user who gets that backwards drops covergroup handles
// after ~VerilatedContext. Those handle destructors call attachDec(), which
// reads the instance node and its type -- so freeing the nodes here is itself
// what would make the wrong ordering a use-after-free, and a "retirement
// disarmed" flag could not help. Instead, leak any type that still has an
// attached node, keeping the type, its nodes and their items valid; the late
// retire() then frees the nodes itself, so only the type object leaks.
VlCovRegistry::~VlCovRegistry() {
for (auto& typep : m_types) {
// Normally nothing is still attached; if something is, the model
// outlived its context and those handles still reach this type.
if (VL_UNLIKELY(typep->anyAttached())) {
VlCovergroupType* const leakedp = typep.release();
static_cast<void>(leakedp); // Deliberate leak
}
}
}
VlCovergroupType* VlCovRegistry::findType(const char* typeName) const {
const auto it = m_byName.find(typeName);
return it == m_byName.end() ? nullptr : it->second;
}
uint32_t VlCovRegistry::liveInstanceCount() const {
uint32_t total = 0;
for (const auto& typep : m_types) total += typep->liveInstanceCount();
return total;
}
uint32_t VlCovRegistry::createdInstanceCount() const {
uint32_t total = 0;
for (const auto& typep : m_types) total += typep->createdInstanceCount();
return total;
}
uint32_t VlCovRegistry::liveInstanceCount(const char* typeName) const {
const VlCovergroupType* const typep = findType(typeName);
return typep ? typep->liveInstanceCount() : 0;
}
uint32_t VlCovRegistry::createdInstanceCount(const char* typeName) const {
const VlCovergroupType* const typep = findType(typeName);
return typep ? typep->createdInstanceCount() : 0;
}
uint32_t VlCovRegistry::retiredInstanceCount(const char* typeName) const {
const VlCovergroupType* const typep = findType(typeName);
return typep ? typep->retiredInstanceCount() : 0;
}
double VlCovRegistry::retiredCoverage(const char* typeName) const {
const VlCovergroupType* const typep = findType(typeName);
return typep ? typep->retiredCoverage() : -1.0;
}
+506 -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_
@@ -29,10 +32,15 @@
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_cov_model.h"
#include <array>
#include <cstdint>
#include <initializer_list>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
class VerilatedCovContext;
@@ -49,8 +57,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 +67,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 +80,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 +95,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 +127,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 +173,455 @@ 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 cross runtime. Holds flat uint32_t[] storage over the
/// Cartesian product of the feeding coverpoints' Normal bins. Each sample()
/// walks only hit tuples, not the entire product. Bin names are
/// built on demand for automatic bins; explicit bins select sets of tuples
/// and replace the corresponding automatic cross bins. Explicit selections
/// are intersected with hit-tuple words once per sample.
/// VlCoverCrossT owns the fixed arrays. This shared core does not allocate bin
/// storage, and its borrowed storage pointers remain valid for the instance.
class VlCoverCross VL_NOT_FINAL : public VlCoverpointIf {
protected:
struct Dimension final {
VlCoverpoint* cpp; // Feeding coverpoint
const uint32_t* hitsp; // Hit list cached for Cartesian traversal
uint32_t bins; // Normal bin count
uint32_t stride; // Flat-index stride
};
struct Bin final {
const uint64_t* selectionp; // Slice of the fixed selection storage
const char* namep; // Explicit bin name
const char* filep; // Bin declaration file
int line; // Bin declaration line
int col; // Bin declaration column
uint32_t count = 0; // Samples matching the selection and guard
uint32_t numWords = 0; // Number of nonzero selection-word indices
const uint32_t* wordIndicesp = nullptr; // Slice of the packed selection-word indices
};
struct Word final {
uint64_t autoExcluded = 0; // Tuples replaced by explicit bins
uint64_t hitBits = 0; // Selected hit tuples, cleared after each sample
uint32_t touchedWord = 0; // Flat word ID, stored by touched-list position
};
template <typename T>
class View final {
T* m_beginp;
T* m_endp;
public:
View(T* datap, uint64_t size)
: m_beginp{datap}
, m_endp{datap ? datap + size : nullptr} {}
T& operator[](uint64_t i) const { return m_beginp[i]; }
uint64_t size() const { return m_beginp == m_endp ? 0 : m_endp - m_beginp; }
bool empty() const { return m_beginp == m_endp; }
T* begin() const { return m_beginp; }
T* end() const { return m_endp; }
};
struct Explicit final {
View<Bin> bins; // Explicit bins in declaration order
Word* wordsp; // Masks use flat word indices; touchedWord uses a dense prefix
View<uint32_t> autoBins; // Retained flat indices
View<uint32_t> binWords; // Nonzero selection words, grouped by bin
uint64_t* selectionp; // [bins.size() * ceil(m_numAutoBins / 64)]
uint32_t numBins = 0; // Bins configured by addBin()
uint32_t minBinWords = 0; // Minimum nonzero-word count across explicit bins
uint32_t numTouchedWords = 0; // Active prefix of wordsp[].touchedWord
};
private:
// 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_flatCountsp 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)
Dimension* m_dimensionsp = nullptr; // [m_dims], owned by VlCoverCrossT
uint32_t* m_flatCountsp = nullptr; // [m_numAutoBins] Per-bin hit counts
Explicit* m_explicitp = nullptr; // Absent for automatic-only crosses
// PRIVATE METHODS
bool hasExplicitBins() const { return m_explicitp != nullptr; }
template <bool T_Explicit, bool T_RecordHits = true>
void iterateProduct(uint32_t dim, uint32_t baseIdx);
void incrementAuto(uint32_t idx) {
if (m_flatCountsp[idx]++ == 0) ++m_numCovered;
}
template <bool T_RecordHits>
void incrementTuple(uint32_t idx) {
Explicit& data = *m_explicitp;
const uint32_t wordIdx = idx / 64;
Word& word = data.wordsp[wordIdx];
if ((word.autoExcluded >> (idx % 64)) & 1U) {
if (T_RecordHits) {
if (!word.hitBits) { data.wordsp[data.numTouchedWords++].touchedWord = wordIdx; }
word.hitBits |= uint64_t{1} << (idx % 64);
}
// Explicit selections consume automatic tuples independently of iff.
return;
}
incrementAuto(idx);
}
template <bool T_ApplyIffs>
void sampleSingleTuple(uint32_t idx, const bool* binIffs);
template <bool T_ApplyIffs, uint32_t T_Touched, bool T_Dense>
void sampleBins(const bool* binIffs);
template <bool T_ApplyIffs, bool T_Dense>
void sampleHitWords(const bool* binIffs);
uint32_t autoIndex(uint32_t i) const {
return hasExplicitBins() ? m_explicitp->autoBins[i] : i;
}
std::string autoBinName(uint32_t flat) const;
protected:
// CONSTRUCTORS
VlCoverCross(uint32_t dims, uint32_t tuples)
: m_dims{dims}
, m_numAutoBins{tuples} {}
void bindStorage(Dimension* dimensionsp, uint32_t* countsp, Explicit* explicitp = nullptr) {
m_dimensionsp = dimensionsp;
m_flatCountsp = countsp;
m_explicitp = explicitp;
}
public:
VL_UNCOPYABLE(VlCoverCross);
// 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);
/// Add a cross bin using a verilation-time bitmap of selected Normal-bin tuples.
void addBin(std::initializer_list<uint64_t> selection, const char* namep, const char* filep,
int line, int col);
/// Retain only automatic cross bins not selected by any explicit bin.
void finalizeBins();
void registerBins(VerilatedCovContext* covcontextp, const char* page);
// ---- hot path (from generated sample(), after all coverpoints sampled) ----
/// Sample automatic and explicit bins, optionally applying per-bin iff guards.
/// Reads the feeding coverpoints saved by init(), so the caller passes no coverpoints.
void sample(const bool* binIffs = nullptr);
// ---- VlCoverpointIf ----
// Explicit bins precede retained automatic bins; all are Normal bins.
uint32_t binCount() const override {
return hasExplicitBins()
? static_cast<uint32_t>(m_explicitp->bins.size() + m_explicitp->autoBins.size())
: m_numAutoBins;
}
std::string binName(uint32_t i) const override;
void coverageParts(double& covered, double& total) const override {
covered = m_numCovered;
total = binCount();
}
};
//=============================================================================
// VlCoverCrossT
/// Cross storage with verilation-time dimensions and bin capacities. All bin
/// data stays at the registry-owned object's address; no per-buffer allocations
/// or per-shape copies of the sampling algorithm are needed.
template <uint32_t Dims, uint32_t Tuples, uint32_t Bins, uint32_t AutoBins, uint64_t BinWords>
class VlCoverCrossT final : public VlCoverCross {
static constexpr uint32_t WORDS = Tuples / 64 + (Tuples % 64 != 0);
static_assert(Bins > 0, "Explicit cross storage requires bins");
std::array<Dimension, Dims> m_dimensions;
std::array<uint32_t, Tuples> m_counts{};
std::array<Bin, Bins> m_bins;
std::array<Word, WORDS> m_words{};
std::array<uint32_t, AutoBins> m_autoBins;
std::array<uint32_t, BinWords> m_binWords;
std::array<uint64_t, static_cast<uint64_t>(Bins) * WORDS> m_selections;
Explicit m_explicit;
public:
VlCoverCrossT()
: VlCoverCross{Dims, Tuples}
, m_explicit{{m_bins.data(), Bins},
m_words.data(),
{m_autoBins.data(), AutoBins},
{m_binWords.data(), BinWords},
m_selections.data()} {
bindStorage(m_dimensions.data(), m_counts.data(), &m_explicit);
}
};
/// Automatic-only crosses omit every explicit-bin array and its bookkeeping.
template <uint32_t Dims, uint32_t Tuples>
class VlCoverCrossT<Dims, Tuples, 0, 0, 0> final : public VlCoverCross {
std::array<Dimension, Dims> m_dimensions;
std::array<uint32_t, Tuples> m_counts{};
public:
VlCoverCrossT()
: VlCoverCross{Dims, Tuples} {
bindStorage(m_dimensions.data(), m_counts.data());
}
};
class VlCovergroupType;
//=============================================================================
// VlCovergroupInst
/// One covergroup instance: owns the coverpoint/cross runtimes created by one
/// SV 'new'. The generated class holds borrowed pointers to them, so the bins
/// outlive the SV object -- the coverage database registers raw count pointers
/// and reads them at write() time, long after the object may have been freed.
///
/// Attach-counted: every VlCovInstHandle bound here holds one count, and the
/// node is retired (see VlCovergroupType::retire) when the last one drops.
class VlCovergroupInst final {
// MEMBERS
// Coverpoint and cross runtimes of this instance; creation == declaration order
std::vector<std::unique_ptr<VlCoverpointIf>> m_items;
VlCovergroupType* const m_typep; // Owning type; outlives this node
const uint32_t m_instId; // Stable identity across churn; NOT the slot
#if !VM_COVERAGE
// Only retire()'s free path uses this; under VM_COVERAGE the node is never
// unlinked, so the slot would be dead. VlCovergroupType sets it.
uint32_t m_slot = 0; // Index into m_typep->m_insts; unlink-by-swap rewrites
#endif
uint32_t m_attachCount = 1; // SV handles bound here; 1 from construction
bool m_retained = false; // VM_COVERAGE: dead, but kept for registered count pointers
// Reads m_items to fold the residue; owns m_slot and m_retained.
friend class VlCovergroupType;
public:
// CONSTRUCTORS
VlCovergroupInst(VlCovergroupType* typep, uint32_t instId)
: m_typep{typep}
, m_instId{instId} {}
VL_UNCOPYABLE(VlCovergroupInst);
// METHODS
// ---- construction (from the generated covergroup constructor) ----
template <uint32_t MaxHits>
VlCoverpointT<MaxHits>* addCoverpoint() {
VlCoverpointT<MaxHits>* const cpp = new VlCoverpointT<MaxHits>{};
m_items.emplace_back(cpp);
return cpp; // borrowed by the generated class
}
template <uint32_t Dims, uint32_t Tuples, uint32_t Bins, uint32_t AutoBins, uint64_t BinWords>
VlCoverCrossT<Dims, Tuples, Bins, AutoBins, BinWords>* addCross() {
auto* const cxp = new VlCoverCrossT<Dims, Tuples, Bins, AutoBins, BinWords>{};
m_items.emplace_back(cxp);
return cxp; // borrowed by the generated class
}
// ---- attach counting (from VlCovInstHandle) ----
void attachInc() { ++m_attachCount; }
// Drops one handle; true if it was the last and the caller must retire the
// node. Retiring is the caller's job because VlCovergroupType is incomplete
// here, and because it frees 'this'.
bool attachDec() { return --m_attachCount == 0; }
// ---- introspection ----
VlCovergroupType* typep() const { return m_typep; }
uint32_t instId() const { return m_instId; }
// True once retired but kept alive because the coverage database holds raw
// pointers into this node's bin counts (VM_COVERAGE); see retire().
bool retained() const { return m_retained; }
// Sum of the instance's items' covered/total bin counts. Matches what the
// generated get_inst_coverage() computes; see foldResidue().
void coverageParts(double& covered, double& total) const {
covered = 0.0;
total = 0.0;
for (const auto& itemp : m_items) {
double c = 0.0;
double t = 0.0;
itemp->coverageParts(c, t);
covered += c;
total += t;
}
}
};
//=============================================================================
// VlCovRetiredAvg
/// Per-type residue: what survives an instance's death. Fixed size, so it does
/// not grow with churn. Weight is 1 everywhere until option.weight is plumbed.
struct VlCovRetiredAvg final {
uint64_t count = 0; // Retired instances that contributed (nonzero denominator)
double sumCoverage = 0.0; // Sigma of per-instance coverage, each in 0..100
};
//=============================================================================
// VlCovergroupType
/// One covergroup type: owns its live instances, in creation order, plus the
/// residue of the ones that have died.
class VlCovergroupType final {
// MEMBERS
// Live nodes, and -- under VM_COVERAGE -- retired-but-retained ones. Slot
// order is creation order only until the first unlink-by-swap.
std::vector<std::unique_ptr<VlCovergroupInst>> m_insts;
uint32_t m_createdInsts = 0; // Instances ever created; never decremented
uint32_t m_nextInstId = 0; // Monotonic; slots are reused, ids never are
VlCovRetiredAvg m_retired; // Contribution of every instance that has died
// PRIVATE METHODS
// Harvest instp's contribution into m_retired. Must run before instp is
// unlinked: it reads the instance's items.
void foldResidue(const VlCovergroupInst* instp);
public:
// CONSTRUCTORS
VlCovergroupType() = default;
VL_UNCOPYABLE(VlCovergroupType);
// METHODS
VlCovergroupInst* newInstance();
// Called when the last handle to instp drops. Folds the residue, then
// unlinks and frees the node -- except under VM_COVERAGE, where the coverage
// database still holds raw pointers into it and it is only marked retained.
void retire(VlCovergroupInst* instp);
// True if any node here still has an SV handle bound to it, and so can be
// retired again after the registry is destroyed. See ~VlCovRegistry.
bool anyAttached() const;
// ---- introspection ----
// Test and debug only; generated code never calls these, and SV reaches them
// only via explicit $c. They let a regression test pin node accumulation
// (otherwise visible only as memory growth) and the residue fold.
//
// Instance nodes still reachable from SV. Under VM_COVERAGE this is smaller
// than m_insts.size(), which also holds retained (dead) nodes.
uint32_t liveInstanceCount() const;
// Instances ever created, live or not. Wraps after 4G instances, which no
// introspection use cares about.
uint32_t createdInstanceCount() const { return m_createdInsts; }
// Instances that have died and contributed to the residue.
uint32_t retiredInstanceCount() const { return static_cast<uint32_t>(m_retired.count); }
// Mean coverage over the retired instances only, in 0..100; -1.0 if none.
double retiredCoverage() const;
};
//=============================================================================
// VlCovRegistry
/// Every covergroup type and instance in one VerilatedContext. Owned by the
/// VerilatedContext (not by the coverage database, which is only linked under
/// --coverage and is a *consumer* of this data), reached through
/// VerilatedContext::covergroupRegistryp().
class VlCovRegistry final : public VerilatedVirtualBase {
// MEMBERS
std::vector<std::unique_ptr<VlCovergroupType>> m_types; // Creation order
std::unordered_map<std::string, VlCovergroupType*> m_byName; // Lookup, borrowed
// PRIVATE METHODS
VlCovergroupType* findType(const char* typeName) const; // nullptr if unknown
public:
// CONSTRUCTORS
VlCovRegistry() = default;
~VlCovRegistry() override;
VL_UNCOPYABLE(VlCovRegistry);
// METHODS
// Find-or-create the type node, then add an instance to it. typeName is the
// generated covergroup class name, already --protect-ids obfuscated, and is
// the same string that keys the coverage database's hier/page.
VlCovergroupInst* newCovergroupInst(const char* typeName);
// ---- introspection (see VlCovergroupType) ----
// typeName is the obfuscated generated name, so a test using these under
// --protect-ids must pass the obfuscated string; the no-argument form does not.
uint32_t liveInstanceCount() const; // Summed over every type
uint32_t createdInstanceCount() const; // Summed over every type
uint32_t liveInstanceCount(const char* typeName) const; // 0 if type unknown
uint32_t createdInstanceCount(const char* typeName) const; // 0 if type unknown
uint32_t retiredInstanceCount(const char* typeName) const; // 0 if type unknown
double retiredCoverage(const char* typeName) const; // -1.0 if type unknown or none
};
//=============================================================================
// VlCovInstHandle
/// The generated covergroup class's link to its instance node. Attach-counting:
/// the registry owns the node, but the handles are what keep it reachable, and
/// the last one to go retires it.
///
/// Must stay copyable: every generated clone() copy-constructs. A copy shares
/// the node, and so the bin counts -- pre-existing covergroup-copy aliasing.
/// Attach counting makes that lifetime-safe, not correct.
class VlCovInstHandle final {
// MEMBERS
VlCovergroupInst* m_p = nullptr; // Attach-counted; the registry owns the node
// PRIVATE METHODS
// Drop one attach count, retiring the node if that was the last handle.
// Nothing may touch instp afterwards: retire() may have freed it.
static void release(VlCovergroupInst* instp) {
if (VL_UNCOVERABLE(!instp)) return; // Never attach()ed; codegen always does
if (instp->attachDec()) instp->typep()->retire(instp);
}
public:
// CONSTRUCTORS
VlCovInstHandle() = default;
VlCovInstHandle(const VlCovInstHandle& o)
: m_p{o.m_p} {
if (VL_UNCOVERABLE(!m_p)) return; // Unbound source; see release above
m_p->attachInc();
}
// Deleted, not implemented: nothing generates an assignment, and the
// implicit one would copy m_p raw -- no attachInc, no release.
VlCovInstHandle& operator=(const VlCovInstHandle&) = delete;
~VlCovInstHandle() { release(m_p); }
// METHODS
// Bind to a freshly created node, taking over the attach count of 1 it was
// created with. Called once, from the generated covergroup constructor.
void attach(VlCovergroupInst* p) { m_p = p; }
VlCovergroupInst* p() const { return m_p; }
};
#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];
+1 -1
View File
@@ -303,7 +303,7 @@ public:
}
template <typename T>
T readIndex(T origVal, int index) const {
T readIndex(const T origVal, int index) const {
if (m_entries.empty()) return origVal;
T result = origVal;
+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) {
+408 -467
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -207,6 +207,11 @@ protected:
// Used by scopeInsert, scopeFind, scopeErase, scopeNameMap
mutable VerilatedMutex m_nameMutex; // Protect m_nameMap
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex);
// Map of <interface_reference_name, interface reference>
// Used by ifaceRefInsert, ifaceRefFind, ifaceRefErase, ifaceRefMap
mutable VerilatedMutex m_ifaceRefMutex; // Protect m_ifaceRefMap
VerilatedIfaceRefMap m_ifaceRefMap VL_GUARDED_BY(m_ifaceRefMutex);
};
//======================================================================
@@ -270,6 +275,10 @@ public: // But only for verilated*.cpp
void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE;
void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE;
// METHODS - interface references - INTERNAL only for verilated*.cpp
void ifaceRefInsert(const VerilatedIfaceRef& ifaceRef) VL_MT_SAFE;
void ifaceRefErase(const std::string& fullname, const VerilatedScope* scopep) VL_MT_SAFE;
// METHODS - file IO - INTERNAL only for verilated*.cpp
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
+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
+1169 -473
View File
File diff suppressed because it is too large Load Diff
+166 -37
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 {
@@ -214,11 +259,12 @@ class VlRandomizer VL_NOT_FINAL {
m_constraints_line; // fileline content of the constraint for unsat constraints
std::vector<std::string> m_softConstraints; // Soft constraints
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent
// Scratch buffer for randomConstraint(), reused across calls
std::vector<const VlRandomVar*> m_randomConstraintVars;
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 +276,89 @@ 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);
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits,
const std::vector<std::string>* layerVarsp = nullptr);
// 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 +367,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 ---
@@ -351,16 +469,16 @@ public:
// Mark a variable as rand_mode-disabled: solver keeps it in m_vars
// (so constraints still reference it) but skips write-back after solving.
void set_var_disabled(const char* name) { m_disabledVars.insert(name); }
void set_var_disabled(const std::string& name) { m_disabledVars.insert(name); }
// Clear disabled state for a variable
void clear_var_disabled(const char* name) { m_disabledVars.erase(name); }
void clear_var_disabled(const std::string& name) { m_disabledVars.erase(name); }
// --- write_var to register variables ---
// Register scalar variable (non-struct, basic type)
template <typename T>
typename std::enable_if<!VlContainsCustomStruct<T>::value && !IsVlUnpacked<T>::value,
void>::type
write_var(T& var, int width, const char* name, int dimension,
write_var(T& var, int width, const std::string& name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (m_vars.find(name) != m_vars.end()) return;
// TODO: make_unique once VlRandomizer is per-instance not per-ref
@@ -377,12 +495,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 +512,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 +551,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 +585,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 +607,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 +641,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 +682,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 +716,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);
}
}
@@ -648,7 +768,7 @@ public:
void disable_soft(const std::string& varName);
void clearConstraints();
void clearAll(); // Clear both constraints and variables
void markRandc(const char* name); // Mark variable as randc for cyclic tracking
void markRandc(const std::string& name); // Mark variable as randc for cyclic tracking
void solveBefore(const std::string& beforeName,
const std::string& afterName); // Register solve-before ordering
void set_randmode(const VlQueue<CData>& randmode) { m_randmodep = &randmode; }
@@ -666,7 +786,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 +844,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
View File
@@ -72,4 +72,13 @@ public:
~VerilatedHierarchyMap() = default;
};
// Map of sorted interface reference names to the concrete interface they refer to
// Keyed by value, as the full name is built at construction
// This is a class instead of typedef/using to allow forward declaration in verilated.h
class VerilatedIfaceRefMap final : public std::map<std::string, VerilatedIfaceRef, std::less<>> {
public:
VerilatedIfaceRefMap() = default;
~VerilatedIfaceRefMap() = default;
};
#endif // Guard
+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);
}
+55 -15
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 {
@@ -231,7 +231,7 @@ extern std::string VL_TO_STRING(SData lhs);
extern std::string VL_TO_STRING(IData lhs);
extern std::string VL_TO_STRING(QData lhs);
extern std::string VL_TO_STRING(double lhs);
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
extern std::string VL_TO_STRING(const std::string& obj) VL_PURE;
template <std::size_t N_Words>
inline std::string VL_TO_STRING(const VlWide<N_Words>& obj) {
return VL_TO_STRING_W(N_Words, obj);
@@ -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;
}
@@ -1452,6 +1468,14 @@ public:
constexpr std::size_t size() const { return N_Depth; }
// Runtime slice v[loIdx +: N_Out], loIdx being an index into m_storage
template <std::size_t N_Out>
VlUnpacked<T_Value, N_Out> slice(int32_t loIdx) const {
VlUnpacked<T_Value, N_Out> out;
for (std::size_t i = 0; i < N_Out; ++i) out.m_storage[i] = m_storage[loIdx + i];
return out;
}
void fill(const T_Value& value) {
std::fill(std::begin(m_storage), std::end(m_storage), value);
}
@@ -2069,8 +2093,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 +2263,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 +2277,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;
}
+263 -43
View File
@@ -363,11 +363,13 @@ protected:
public:
explicit VerilatedVpioScope(const VerilatedScope* scopep)
: m_scopep{scopep} {
m_fullname = m_scopep->name();
if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
m_fullname = vpiFullnamep(m_scopep->name());
m_name = m_scopep->identifier();
m_defname = m_scopep->defname();
}
static const char* vpiFullnamep(const char* namep) VL_PURE {
return (std::strncmp(namep, "TOP.", 4) == 0) ? namep + 4 : namep;
}
~VerilatedVpioScope() override = default;
// cppcheck-suppress duplInheritedMember
static VerilatedVpioScope* castp(vpiHandle h) {
@@ -539,7 +541,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)) {
@@ -721,6 +723,17 @@ public:
uint32_t type() const override { return vpiModule; }
};
class VerilatedVpioInterface final : public VerilatedVpioScope {
public:
explicit VerilatedVpioInterface(const VerilatedScope* scopep)
: VerilatedVpioScope{scopep} {}
// cppcheck-suppress duplInheritedMember
static VerilatedVpioInterface* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioInterface*>(reinterpret_cast<VerilatedVpio*>(h));
}
uint32_t type() const override { return vpiInterface; }
};
class VerilatedVpioModuleIter final : public VerilatedVpio {
const std::vector<const VerilatedScope*>* m_vec;
std::vector<const VerilatedScope*>::const_iterator m_it;
@@ -778,6 +791,8 @@ public:
return (new VerilatedVpioScope{modp})->castVpiHandle();
} else if (itype == VerilatedScope::SCOPE_MODULE) {
return (new VerilatedVpioModule{modp})->castVpiHandle();
} else if (itype == VerilatedScope::SCOPE_INTERFACE) {
return (new VerilatedVpioInterface{modp})->castVpiHandle();
}
}
}
@@ -803,6 +818,90 @@ public:
uint32_t type() const override { return vpiPackage; }
};
class VerilatedVpioModport final : public VerilatedVpio {
const VerilatedScope* const m_scopep; // Interface the modport is within
const char* const m_name; // Modport name
const std::string m_fullname; // Interface full name + "." + modport name
public:
VerilatedVpioModport(const VerilatedScope* scopep, const char* namep)
: m_scopep{scopep}
, m_name{namep}
, m_fullname{std::string{VerilatedVpioScope::vpiFullnamep(scopep->name())} + "." + namep} {
}
~VerilatedVpioModport() override = default;
// cppcheck-suppress duplInheritedMember
static VerilatedVpioModport* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModport*>(reinterpret_cast<VerilatedVpio*>(h));
}
uint32_t type() const override { return vpiModport; }
const VerilatedScope* scopep() const { return m_scopep; }
const char* name() const override { return m_name; }
const char* fullname() const override { return m_fullname.c_str(); }
// IEEE 1800-2023 37.15
const char* defname() const override { return m_name; }
};
class VerilatedVpioIfaceRef final : public VerilatedVpio {
// Held by value, as a handle may outlive the model that registered it
const VerilatedIfaceRef m_ifaceRef;
public:
explicit VerilatedVpioIfaceRef(const VerilatedIfaceRef& ifaceRef)
: m_ifaceRef{ifaceRef} {}
~VerilatedVpioIfaceRef() override = default;
// cppcheck-suppress duplInheritedMember
static VerilatedVpioIfaceRef* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioIfaceRef*>(reinterpret_cast<VerilatedVpio*>(h));
}
uint32_t type() const override { return vpiRefObj; }
const VerilatedIfaceRef* ifaceRefp() const { return &m_ifaceRef; }
const char* name() const override { return m_ifaceRef.name(); }
const char* fullname() const override {
return VerilatedVpioScope::vpiFullnamep(m_ifaceRef.fullname());
}
// IEEE 1800-2023 37.15: modport name, else the interface definition name
const char* defname() const override {
return m_ifaceRef.hasModport() ? m_ifaceRef.modport() : m_ifaceRef.scopep()->defname();
}
vpiHandle actual() const {
if (m_ifaceRef.hasModport()) {
return (new VerilatedVpioModport{m_ifaceRef.scopep(), m_ifaceRef.modport()})
->castVpiHandle();
}
return (new VerilatedVpioInterface{m_ifaceRef.scopep()})->castVpiHandle();
}
};
class VerilatedVpioInterfaceIter final : public VerilatedVpio {
const std::vector<const VerilatedScope*>* m_vec;
std::vector<const VerilatedScope*>::const_iterator m_it;
public:
explicit VerilatedVpioInterfaceIter(const std::vector<const VerilatedScope*>& vec)
: m_vec{&vec} {
m_it = m_vec->begin();
}
~VerilatedVpioInterfaceIter() override = default;
// cppcheck-suppress duplInheritedMember
static VerilatedVpioInterfaceIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioInterfaceIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
while (true) {
if (m_it == m_vec->end()) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
const VerilatedScope* const scopep = *m_it++;
if (scopep->type() == VerilatedScope::SCOPE_INTERFACE) {
return (new VerilatedVpioInterface{scopep})->castVpiHandle();
}
}
}
};
class VerilatedVpioInstanceIter final : public VerilatedVpio {
const std::vector<const VerilatedScope*>* m_vec;
std::vector<const VerilatedScope*>::const_iterator m_it;
@@ -1027,6 +1126,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 +1288,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 +1372,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 +1383,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;
@@ -1920,6 +2098,9 @@ const char* VerilatedVpiError::strFromVpiMethod(PLI_INT32 vpiVal) VL_PURE {
"vpiStmt"
};
// clang-format on
// SystemVerilog relations are numbered far above the Verilog ones
if (vpiVal == vpiActual) return "vpiActual";
if (vpiVal >= vpiPackage && vpiVal <= vpiPropFormalDecl) return strFromVpiObjType(vpiVal);
if (vpiVal > vpiStmt || vpiVal < vpiCondition) return "*undefined*";
return names[vpiVal - vpiCondition];
}
@@ -2323,6 +2504,8 @@ void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiCondition);
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiStmt);
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiActual);
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiInterface);
SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbValueChange);
SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbAtEndOfSimTime);
@@ -2410,7 +2593,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;
}
@@ -2692,6 +2875,10 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
const VerilatedVpioVar* const voVarp = VerilatedVpioVar::castp(scope);
// Not scopes, so no name resolves relative to them; must not fall through to
// the unprefixed lookup below, which would resolve from the top level
if (VerilatedVpioIfaceRef::castp(scope) || VerilatedVpioModport::castp(scope)) return nullptr;
if (0 == std::strncmp(scopeAndName.c_str(), "$root.", std::strlen("$root."))) {
scopeAndName.erase(0, std::strlen("$root."));
} else if (voScopep) {
@@ -2719,8 +2906,15 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
if (scopep->type() == VerilatedScope::SCOPE_PACKAGE) {
return (new VerilatedVpioPackage{scopep})->castVpiHandle();
}
if (scopep->type() == VerilatedScope::SCOPE_INTERFACE) {
return (new VerilatedVpioInterface{scopep})->castVpiHandle();
}
return (new VerilatedVpioScope{scopep})->castVpiHandle();
}
if (const VerilatedIfaceRef* const ifaceRefp
= Verilated::threadContextp()->ifaceRefFind(scopeAndName.c_str())) {
return (new VerilatedVpioIfaceRef{*ifaceRefp})->castVpiHandle();
}
std::string basename = scopeAndName;
std::string scopename;
std::string::size_type prevpos = std::string::npos;
@@ -2856,7 +3050,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 +3064,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;
}
@@ -2880,6 +3074,24 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
const int32_t val = vop->index().back();
return (new VerilatedVpioConst{val})->castVpiHandle();
}
case vpiActual: {
if (const VerilatedVpioIfaceRef* const vop = VerilatedVpioIfaceRef::castp(object)) {
return vop->actual();
}
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Unsupported vpiHandle '%p' for type '%s', nothing will be returned",
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
return nullptr;
}
case vpiInterface: {
if (const VerilatedVpioModport* const vop = VerilatedVpioModport::castp(object)) {
return (new VerilatedVpioInterface{vop->scopep()})->castVpiHandle();
}
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Unsupported vpiHandle '%p' for type '%s', nothing will be returned",
__func__, object, VerilatedVpiError::strFromVpiMethod(type));
return nullptr;
}
case vpiScope: {
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
@@ -2946,6 +3158,15 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
if (it == map->end()) return nullptr;
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
}
case vpiInterface: {
// IEEE 1800-2023 37.5: interfaces are a one-to-many of a module
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
if (it == map->end()) return nullptr;
return ((new VerilatedVpioInterfaceIter{it->second})->castVpiHandle());
}
case vpiInternalScope: {
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
@@ -3031,8 +3252,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 +3417,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 +3472,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 +3501,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 +3720,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 +4061,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 +4456,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 +4638,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;
}
+28 -5
View File
@@ -318,6 +318,15 @@
# define VL_CONSTEXPR_CXX17
#endif
//=========================================================================
// C++-2020
#if __cplusplus >= 202002L
# define VL_NO_UNIQUE_ADDRESS_CXX20 [[no_unique_address]]
#else
# define VL_NO_UNIQUE_ADDRESS_CXX20
#endif
//=========================================================================
// Optimization
@@ -332,10 +341,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 +386,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>
@@ -442,7 +461,8 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#define VL_VFORMATATTR_SIGNED '~' // (int widthMin, IData/VlWide/etc) Signed number; for %d showing sign
#define VL_VFORMATATTR_COMPLEX '!' // (std::string*); for non-POD; e.g. struct, requires %p typically
#define VL_VFORMATATTR_DOUBLE 'D' // (double); promote %p to %f
#define VL_VFORMATATTR_ENUM 'E' // (width, IData/QData, std::string* name); <= 64 bit enum with runtime %p/%s
#define VL_VFORMATATTR_ENUM 'E' // (width, IData/QData/const EData*, STRING, std::string* name)
#define VL_VFORMATATTR_ENUM_SIGNED 'F' // Same arguments as ENUM, with a signed numeric value
#define VL_VFORMATATTR_SCOPE 'M' // (char* name, char* scope); for scopes
#define VL_VFORMATATTR_STRING 'S' // (char* name, char* scope); for scopes // (std::string*); for %p/%s
#define VL_VFORMATATTR_TIMEUNIT 'T' // (int timeunit); timeunits passed from V3Emit to runtime
@@ -460,6 +480,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 +493,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 +557,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 +629,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
}
+3 -1
View File
@@ -25,8 +25,10 @@ def message_section(msg: str) -> int:
return 50
if re.match(r'^Support', msg, flags=re.IGNORECASE):
return 60
if re.match(r'^Fix', msg, flags=re.IGNORECASE):
if re.match(r'^Optimize', msg, flags=re.IGNORECASE):
return 70
if re.match(r'^Fix', msg, flags=re.IGNORECASE):
return 80
if re.match(r'^(Internals|CI|Tests)', msg, flags=re.IGNORECASE):
return -1
if re.match(r'^Bump.* from .* to .*', msg, flags=re.IGNORECASE): # dependabot
+6 -1
View File
@@ -12,18 +12,23 @@
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: CC0-1.0
# These flags tested against verible-v0.0-4192-g682a1d50
verible-verilog-format \
--inplace \
--wrap_end_else_clauses \
\
--assignment_statement_alignment=flush-left \
--case_items_alignment=flush-left \
--class_member_variable_alignment=flush-left \
--distribution_items_alignment=flush-left \
--enum_assignment_statement_alignment=flush-left \
--expand_coverpoints \
--formal_parameters_alignment=flush-left \
--module_net_variable_alignment=flush-left \
--named_parameter_alignment=flush-left \
--named_port_alignment=flush-left \
--parameter_declaration_alignment=flush-left \
--port_declarations_alignment=flush-left \
--struct_union_members_alignment=flush-left \
--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
+10 -2
View File
@@ -62,6 +62,7 @@ set(HEADERS
V3Cast.h
V3Cfg.h
V3Class.h
V3ClassGraph.h
V3Clean.h
V3Clock.h
V3Combine.h
@@ -113,6 +114,7 @@ set(HEADERS
V3GraphPathChecker.h
V3GraphStream.h
V3Hash.h
V3HashTable.h
V3Hasher.h
V3HierBlock.h
V3Inline.h
@@ -149,6 +151,7 @@ set(HEADERS
V3OrderGraph.h
V3OrderInternal.h
V3OrderMoveGraph.h
V3OrderMTaskGraph.h
V3Os.h
V3PairingHeap.h
V3Param.h
@@ -156,6 +159,7 @@ set(HEADERS
V3ParseImp.h
V3PchAstMT.h
V3PchAstNoMT.h
V3PoolAllocator.h
V3PreExpr.h
V3PreLex.h
V3PreProc.h
@@ -170,7 +174,6 @@ set(HEADERS
V3Sampled.h
V3Sched.h
V3Scope.h
V3Scoreboard.h
V3SenExprBuilder.h
V3SenTree.h
V3Simulate.h
@@ -229,6 +232,7 @@ set(COMMON_SOURCES
V3CfgBuilder.cpp
V3CfgLiveVariables.cpp
V3Class.cpp
V3ClassGraph.cpp
V3Clean.cpp
V3Clock.cpp
V3Combine.cpp
@@ -289,6 +293,7 @@ set(COMMON_SOURCES
V3GraphPathChecker.cpp
V3GraphTest.cpp
V3Hash.cpp
V3HashTable.cpp
V3Hasher.cpp
V3HierBlock.cpp
V3Inline.cpp
@@ -319,6 +324,9 @@ set(COMMON_SOURCES
V3Order.cpp
V3OrderGraphBuilder.cpp
V3OrderMoveGraph.cpp
V3OrderMTaskContraction.cpp
V3OrderMTaskFixHazards.cpp
V3OrderMTaskGraph.cpp
V3OrderParallel.cpp
V3OrderProcessDomains.cpp
V3OrderSerial.cpp
@@ -339,6 +347,7 @@ set(COMMON_SOURCES
V3Sampled.cpp
V3Sched.cpp
V3SchedAcyclic.cpp
V3SchedCovergroup.cpp
V3SchedPartition.cpp
V3SchedReplicate.cpp
V3SchedTiming.cpp
@@ -346,7 +355,6 @@ set(COMMON_SOURCES
V3SchedUtil.cpp
V3SchedVirtIface.cpp
V3Scope.cpp
V3Scoreboard.cpp
V3Slice.cpp
V3Split.cpp
V3SplitVar.cpp
+9 -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
@@ -193,6 +196,7 @@ RAW_OBJS = \
V3GraphPathChecker.o \
V3GraphTest.o \
V3Hash.o \
V3HashTable.o \
V3OptionParser.o \
V3Os.o \
V3ParseGrammar.o \
@@ -244,6 +248,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3CfgBuilder.o \
V3CfgLiveVariables.o \
V3Class.o \
V3ClassGraph.o \
V3Clean.o \
V3Clock.o \
V3Combine.o \
@@ -309,6 +314,9 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Order.o \
V3OrderGraphBuilder.o \
V3OrderMoveGraph.o \
V3OrderMTaskContraction.o \
V3OrderMTaskFixHazards.o \
V3OrderMTaskGraph.o \
V3OrderParallel.o \
V3OrderProcessDomains.o \
V3OrderSerial.o \
@@ -323,6 +331,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Sampled.o \
V3Sched.o \
V3SchedAcyclic.o \
V3SchedCovergroup.o \
V3SchedPartition.o \
V3SchedReplicate.o \
V3SchedTiming.o \
@@ -330,7 +339,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.
+258 -40
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,27 +427,26 @@ 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) {
m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum",
nodep->findUInt64DType()};
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorNumVarp);
v3Global.rootp()->dollarUnitPkgp()->addStmtsp(m_monitorNumVarp);
}
AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorNumVarp, access};
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgp());
return varrefp;
}
AstVarRef* newMonitorOffVarRefp(const AstNode* nodep, VAccess access) {
if (!m_monitorOffVarp) {
m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff",
nodep->findBitDType()};
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorOffVarp);
v3Global.rootp()->dollarUnitPkgp()->addStmtsp(m_monitorOffVarp);
}
AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorOffVarp, access};
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgp());
return varrefp;
}
static AstIf* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
@@ -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 {
+1220 -601
View File
File diff suppressed because it is too large Load Diff
+45 -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,
@@ -406,6 +414,7 @@ private:
flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, VCMethod::DYN_POP,
new AstTime{nodep->fileline(), m_modp->timeunit()}};
popp->addPinsp(skewp->unlinkFrBack());
refp->access(VAccess::READWRITE); // Only conditionally assigned
popp->addPinsp(refp);
popp->dtypeSetVoid();
m_clockingp->addNextHere(
@@ -683,6 +692,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 +706,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 +723,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 +787,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 +801,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 +864,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 +932,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 +948,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 +959,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 +974,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 +982,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 +1225,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 +1251,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 +1285,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 +1310,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 +1328,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 +1489,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 +1501,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 uint64_t* const uip = std::get_if<uint64_t>(&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;
}
+93 -34
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,18 +146,54 @@ 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, uint64_t, void*> m_u;
#else
union {
void* up;
int ui;
uint64_t uq;
} m_u;
#endif
public:
#ifdef VL_USER_TYPE_CHECKS
VNUser() = default;
// non-explicit:
// cppcheck-suppress noExplicitConstructor
VNUser(int i) {
VNUser(uint64_t 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 uint64_t, read as pointer");
return reinterpret_cast<T>(*upp);
}
uint64_t toUQuad() const {
if (std::holds_alternative<std::monostate>(m_u)) return 0;
const uint64_t* const uip = std::get_if<uint64_t>(&m_u);
UASSERT_STATIC(uip, "AstNode user() slot written as pointer, read as uint64_t");
return *uip;
}
#else
VNUser() = default;
// non-explicit:
// cppcheck-suppress noExplicitConstructor
VNUser(uint64_t i) {
m_u.up = nullptr;
m_u.ui = i;
m_u.uq = i;
}
explicit VNUser(void* p) { m_u.up = p; }
~VNUser() = default;
@@ -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);
}
uint64_t toUQuad() const { return m_u.uq; }
#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,14 +490,16 @@ 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
VNUser m_user1u{0}; // Contains any information the user iteration routine wants
// (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{nullptr}; // 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
VNUser m_user2u{0}; // Contains any information the user iteration routine wants
VNUser m_user3u{0}; // Contains any information the user iteration routine wants
VNUser m_user2u{nullptr}; // Contains any information the user iteration routine wants
VNUser m_user3u{nullptr}; // Contains any information the user iteration routine wants
uint32_t m_user3Cnt = 0; // Mark of when userp was set
uint32_t m_user4Cnt = 0; // Mark of when userp was set
VNUser m_user4u{0}; // Contains any information the user iteration routine wants
VNUser m_user4u{nullptr}; // Contains any information the user iteration routine wants
// METHODS
void op1p(AstNode* nodep) {
@@ -657,61 +708,61 @@ public:
VNUser user1u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "user1p used without AstUserInUse");
return ((m_user1Cnt == VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{0});
return ((m_user1Cnt == VNUser1InUse::s_userCntGbl) ? m_user1u : VNUser{nullptr});
}
AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
void user1u(const VNUser& user) { m_user1u = user; m_user1Cnt = VNUser1InUse::s_userCntGbl; }
void user1p(void* userp) { user1u(VNUser{userp}); }
void user1(int val) { user1u(VNUser{val}); }
int user1() const { return user1u().toInt(); }
int user1Inc(int val = 1) { const int v = user1(); user1(v + val); return v; }
int user1Or(int val) { const int v = user1(); user1(v | val); return v; }
int user1SetOnce() { const int v = user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
void user1(uint64_t val) { user1u(VNUser{val}); }
uint64_t user1() const { return user1u().toUQuad(); }
uint64_t user1Inc(uint64_t val = 1) { const uint64_t v = user1(); user1(v + val); return v; }
uint64_t user1Or(uint64_t val) { const uint64_t v = user1(); user1(v | val); return v; }
uint64_t user1SetOnce() { const uint64_t v = user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc()
static void user1ClearTree() { VNUser1InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user2u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "user2p used without AstUserInUse");
return ((m_user2Cnt == VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{0});
return ((m_user2Cnt == VNUser2InUse::s_userCntGbl) ? m_user2u : VNUser{nullptr});
}
AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
void user2u(const VNUser& user) { m_user2u = user; m_user2Cnt = VNUser2InUse::s_userCntGbl; }
void user2p(void* userp) { user2u(VNUser{userp}); }
void user2(int val) { user2u(VNUser{val}); }
int user2() const { return user2u().toInt(); }
int user2Inc(int val = 1) { const int v = user2(); user2(v + val); return v; }
int user2Or(int val) { const int v = user2(); user2(v | val); return v; }
int user2SetOnce() { const int v = user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
void user2(uint64_t val) { user2u(VNUser{val}); }
uint64_t user2() const { return user2u().toUQuad(); }
uint64_t user2Inc(uint64_t val = 1) { const uint64_t v = user2(); user2(v + val); return v; }
uint64_t user2Or(uint64_t val) { const uint64_t v = user2(); user2(v | val); return v; }
uint64_t user2SetOnce() { const uint64_t v = user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc()
static void user2ClearTree() { VNUser2InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user3u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "user3p used without AstUserInUse");
return ((m_user3Cnt == VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{0});
return ((m_user3Cnt == VNUser3InUse::s_userCntGbl) ? m_user3u : VNUser{nullptr});
}
AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
void user3u(const VNUser& user) { m_user3u = user; m_user3Cnt = VNUser3InUse::s_userCntGbl; }
void user3p(void* userp) { user3u(VNUser{userp}); }
void user3(int val) { user3u(VNUser{val}); }
int user3() const { return user3u().toInt(); }
int user3Inc(int val = 1) { const int v = user3(); user3(v + val); return v; }
int user3Or(int val) { const int v = user3(); user3(v | val); return v; }
int user3SetOnce() { const int v = user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
void user3(uint64_t val) { user3u(VNUser{val}); }
uint64_t user3() const { return user3u().toUQuad(); }
uint64_t user3Inc(uint64_t val = 1) { const uint64_t v = user3(); user3(v + val); return v; }
uint64_t user3Or(uint64_t val) { const uint64_t v = user3(); user3(v | val); return v; }
uint64_t user3SetOnce() { const uint64_t v = user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc()
static void user3ClearTree() { VNUser3InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user4u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "user4p used without AstUserInUse");
return ((m_user4Cnt == VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{0});
return ((m_user4Cnt == VNUser4InUse::s_userCntGbl) ? m_user4u : VNUser{nullptr});
}
AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
void user4u(const VNUser& user) { m_user4u = user; m_user4Cnt = VNUser4InUse::s_userCntGbl; }
void user4p(void* userp) { user4u(VNUser{userp}); }
void user4(int val) { user4u(VNUser{val}); }
int user4() const { return user4u().toInt(); }
int user4Or(int val) { const int v = user4(); user4(v | val); return v; }
int user4Inc(int val = 1) { const int v = user4(); user4(v + val); return v; }
int user4SetOnce() { const int v = user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
void user4(uint64_t val) { user4u(VNUser{val}); }
uint64_t user4() const { return user4u().toUQuad(); }
uint64_t user4Or(uint64_t val) { const uint64_t v = user4(); user4(v | val); return v; }
uint64_t user4Inc(uint64_t val = 1) { const uint64_t v = user4(); user4(v + val); return v; }
uint64_t user4SetOnce() { const uint64_t v = user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc()
static void user4ClearTree() { VNUser4InUse::clear(); } // Clear userp()'s across the entire tree
// clang-format on
@@ -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
@@ -983,7 +1035,6 @@ protected:
AstNode* iterateSubtreeReturnEdits(VNVisitor& v);
static void dumpJsonNum(std::ostream& os, const std::string& name, int64_t val);
static void dumpJsonBool(std::ostream& os, const std::string& name, bool val);
static void dumpJsonBoolIf(std::ostream& os, const std::string& name, bool val);
static void dumpJsonStr(std::ostream& os, const std::string& name, const std::string& val);
static void dumpJsonPtr(std::ostream& os, const std::string& name, const AstNode* const valp);
@@ -1260,6 +1311,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
+354 -256
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
@@ -481,6 +509,7 @@ public:
PROCESS_REFERENCE,
RANDOM_GENERATOR,
RANDOM_STDGENERATOR,
COVERGROUP_INSTHANDLE,
// Unsigned and two state; fundamental types
UINT32,
UINT64,
@@ -516,6 +545,7 @@ public:
"VlProcessRef",
"VlRandomizer",
"VlStdRandomizer",
"VlCovInstHandle",
"IData",
"QData",
"LOGIC_IMPLICIT",
@@ -548,6 +578,7 @@ public:
"%E-proc-ref",
"%E-rand-gen",
"%E-stdrand-gen",
"%E-cg-insthandle",
"IData",
"QData",
"%E-logic-implct",
@@ -592,6 +623,7 @@ public:
case PROCESS_REFERENCE: return 0; // opaque
case RANDOM_GENERATOR: return 0; // opaque
case RANDOM_STDGENERATOR: return 0; // opaque
case COVERGROUP_INSTHANDLE: return 0; // opaque
case UINT32: return 32;
case UINT64: return 64;
default: return 0;
@@ -632,8 +664,8 @@ public:
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|| m_e == MTASKSTATE || m_e == DELAY_SCHEDULER || m_e == TRIGGER_SCHEDULER
|| m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC || m_e == PROCESS_REFERENCE
|| m_e == RANDOM_GENERATOR || m_e == RANDOM_STDGENERATOR || m_e == DOUBLE
|| m_e == UNTYPED);
|| m_e == RANDOM_GENERATOR || m_e == RANDOM_STDGENERATOR
|| m_e == COVERGROUP_INSTHANDLE || m_e == DOUBLE || m_e == UNTYPED);
}
bool isCHandle() const VL_MT_SAFE { return m_e == CHANDLE; }
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
@@ -688,6 +720,7 @@ public:
/* PROCESS_REFERENCE: */ "", // Should not be traced
/* RANDOM_GENERATOR: */ "", // Should not be traced
/* RANDOM_STD_GENERATOR: */ "", // Should not be traced
/* COVERGROUP_INSTHANDLE: */ "", // Should not be traced
/* UINT32: */ "BIT",
/* UINT64: */ "BIT",
/* LOGIC_IMPLICIT: */ "", // Should not be traced
@@ -724,6 +757,7 @@ public:
static const char* const names[] = {"FALSE", "TRUE", "UNK"};
return names[m_e];
}
bool isKnown() const { return m_e != BU_UNKNOWN; }
bool trueKnown() const { return m_e == BU_TRUE; }
void setTrueOrFalse(bool flag) { m_e = flag ? BU_TRUE : BU_FALSE; }
};
@@ -767,6 +801,10 @@ public:
static const char* const names[] = {"", "VL_LIKELY", "VL_UNLIKELY"};
return names[m_e];
}
const char* asciiShort() const {
static const char* const names[] = {"", "L", "!L"};
return names[m_e];
}
};
constexpr bool operator==(const VBranchPred& lhs, const VBranchPred& rhs) {
return lhs.m_e == rhs.m_e;
@@ -779,137 +817,158 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) {
// ######################################################################
// C++ methods invoked on runtime library data types via AstCMethodHard.
// The argument descriptor gives the access direction of each argument:
// - 'r' read by the call
// - 'w' written by the call (Use only if unconditionally and wholly written,
// that is: a preceding write can be removed. Otherwise use 'm'.)
// - 'm' read and written (modified) by the call
// - '+' repeats the entry before it for all remaining arguments, must be last
// - "" if the method takes no arguments
// - "TODO" if not yet checked due to existing issues
// clang-format off
#define FOR_EACH_CMETHOD(macro) \
/* id, method, pure, args */ \
macro(_NONE, "_none", false, "") \
macro(ARRAY_AT, "at", PURE, "r") \
macro(ARRAY_AT_BACK, "atBack", PURE, "r") \
macro(ARRAY_AT_WRITE, "atWrite", PURE, "r") \
macro(ARRAY_FIND, "find", PURE, "") \
macro(ARRAY_FIND_FIRST, "find_first", PURE, "") \
macro(ARRAY_FIND_FIRST_INDEX, "find_first_index", PURE, "") \
macro(ARRAY_FIND_INDEX, "find_index", PURE, "") \
macro(ARRAY_FIND_LAST, "find_last", PURE, "") \
macro(ARRAY_FIND_LAST_INDEX, "find_last_index", PURE, "") \
macro(ARRAY_FIRST, "first", false, "m") \
macro(ARRAY_INSIDE, "inside", PURE, "r") \
macro(ARRAY_LAST, "last", false, "m") \
macro(ARRAY_MAP, "map", PURE, "") \
macro(ARRAY_MAX, "max", PURE, "") \
macro(ARRAY_MIN, "min", PURE, "") \
macro(ARRAY_NEXT, "next", false, "m") \
macro(ARRAY_POP_BACK, "pop_back", false, "") \
macro(ARRAY_POP_FRONT, "pop_front", false, "") \
macro(ARRAY_PREV, "prev", false, "m") \
macro(ARRAY_PUSH_BACK, "push_back", false, "r") \
macro(ARRAY_PUSH_FRONT, "push_front", false, "r") \
macro(ARRAY_REVERSE, "reverse", false, "") \
macro(ARRAY_RSORT, "rsort", false, "") \
macro(ARRAY_R_AND, "r_and", PURE, "") \
macro(ARRAY_R_OR, "r_or", PURE, "") \
macro(ARRAY_R_PRODUCT, "r_product", PURE, "") \
macro(ARRAY_R_SUM, "r_sum", PURE, "") \
macro(ARRAY_R_XOR, "r_xor", PURE, "") \
macro(ARRAY_SHUFFLE, "shuffle", false, "") \
macro(ARRAY_SLICE, "slice", PURE, "r") \
macro(ARRAY_SORT, "sort", false, "") \
macro(ARRAY_UNIQUE, "unique", PURE, "") \
macro(ARRAY_UNIQUE_INDEX, "unique_index", PURE, "") \
macro(ASSOC_CLEAR, "clear", false, "") \
macro(ASSOC_ERASE, "erase", false, "r") \
macro(ASSOC_EXISTS, "exists", PURE, "r") \
macro(ASSOC_FIRST, "first", false, "m") \
macro(ASSOC_NEXT, "next", false, "m") \
macro(ASSOC_SIZE, "size", PURE, "") \
macro(CLASS_SET_RANDMODE, "set_randmode", false, "r") \
macro(COVERGROUP_ADD_ARRAY_NAMER, "addArrayNamer", false, "r+") \
macro(COVERGROUP_ADD_BIN, "addBin", false, "r+") \
macro(COVERGROUP_ADD_COVERPOINT, "addCoverpoint", false, "") \
macro(COVERGROUP_ADD_CROSS, "addCross", false, "") \
macro(COVERGROUP_ADD_SINGLE_NAMER, "addSingleNamer", false, "r+") \
macro(COVERGROUP_ATTACH, "attach", false, "r") \
macro(COVERGROUP_CLEAR_HIT_LIST, "clearHitList", false, "") \
macro(COVERGROUP_COVERAGE_PARTS, "coverageParts", false, "TODO") \
macro(COVERGROUP_FINALIZE_BINS, "finalizeBins", false, "") \
macro(COVERGROUP_INCREMENT_BIN, "incrementBin", false, "r") \
macro(COVERGROUP_INIT, "init", false, "r+") \
macro(COVERGROUP_INST_P, "p", PURE, "") \
macro(COVERGROUP_RECORD_HIT, "recordHit", false, "r") \
macro(COVERGROUP_REGISTER_BINS, "registerBins", false, "rr") \
macro(COVERGROUP_SAMPLE, "sample", false, "") \
macro(COVERGROUP_SAMPLE_IFFS, "sample", false, "r") \
macro(DYN_AT_WRITE_APPEND, "atWriteAppend", false, "r") \
macro(DYN_AT_WRITE_APPEND_BACK, "atWriteAppendBack", false, "r") \
macro(DYN_CLEAR, "clear", false, "") \
macro(DYN_ERASE, "erase", false, "r") \
macro(DYN_INSERT, "insert", false, "rr") \
macro(DYN_POP, "pop", false, "rrm") \
macro(DYN_POP_FRONT, "pop_front", false, "") \
macro(DYN_PUSH, "push", false, "rr") \
macro(DYN_PUSH_FRONT, "push_front", false, "r") \
macro(DYN_RENEW, "renew", false, "r") \
macro(DYN_RENEW_COPY, "renew_copy", false, "rr") \
macro(DYN_RESIZE, "resize", false, "TODO") \
macro(DYN_SIZE, "size", PURE, "") \
macro(DYN_SLICE, "slice", PURE, "rr") \
macro(DYN_SLICE_ASSIGN, "sliceAssign", false, "rrr") \
macro(DYN_SLICE_ASSIGN_BACK_BACK, "sliceAssignBackBack", false, "rrr") \
macro(DYN_SLICE_ASSIGN_FRONT_BACK, "sliceAssignFrontBack", false, "rrr") \
macro(DYN_SLICE_BACK_BACK, "sliceBackBack", PURE, "rr") \
macro(DYN_SLICE_FRONT_BACK, "sliceFrontBack", PURE, "rr") \
macro(EVENT_CLEAR_FIRED, "clearFired", false, "") \
macro(EVENT_CLEAR_TRIGGERED, "clearTriggered", false, "") \
macro(EVENT_FIRE, "fire", false, "") \
macro(EVENT_IS_FIRED, "isFired", PURE, "") \
macro(EVENT_IS_TRIGGERED, "isTriggered", PURE, "") \
macro(FORCE_ADD, "addForce", false, "rrrr+") \
macro(FORCE_READ, "read", PURE, "r") \
macro(FORCE_READ_INDEX, "readIndex", PURE, "rr") \
macro(FORCE_READ_SEL, "readSel", PURE, "TODO") \
macro(FORCE_RELEASE, "release", false, "rr+") \
macro(FORCE_TOUCH, "touch", false, "") \
macro(FORK_DONE, "done", false, "rr") \
macro(FORK_INIT, "init", false, "rr") \
macro(FORK_JOIN, "join", false, "rrr") \
macro(FORK_ON_KILL, "onKill", false, "r") \
macro(NBA_COMMIT, "commit", false, "w") \
macro(NBA_ENQUEUE, "enqueue", false, "r+") \
macro(RANDOMIZER_BASIC_STD_RANDOMIZATION, "basicStdRandomization", false, "mr") \
macro(RANDOMIZER_CLEARCONSTRAINTS, "clearConstraints", false, "") \
macro(RANDOMIZER_CLEARALL, "clearAll", false, "") \
macro(RANDOMIZER_DISABLE_SOFT, "disable_soft", false, "r") \
macro(RANDOMIZER_HARD, "hard", false, "r+") \
macro(RANDOMIZER_SOFT, "soft", false, "rrrr") \
macro(RANDOMIZER_UNIQUE, "rand_unique", false, "r") \
macro(RANDOMIZER_MARK_RANDC, "markRandc", false, "r") \
macro(RANDOMIZER_SOLVE_BEFORE, "solveBefore", false, "rr") \
macro(RANDOMIZER_PIN_VAR, "pin_var", false, "rrr") \
macro(RANDOMIZER_WRITE_VAR, "write_var", false, "TODO") \
macro(RANDOMIZER_SET_VAR_DISABLED, "set_var_disabled", false, "r") \
macro(RANDOMIZER_CLEAR_VAR_DISABLED, "clear_var_disabled", false, "r") \
macro(RANDOMIZER_MARK_VAR_STATIC, "mark_var_static", false, "r") \
macro(RANDOMIZER_SET_STATIC_RANDMODE, "set_static_randmode", false, "r") \
macro(RNG_GET_RANDSTATE, "__Vm_rng.get_randstate", PURE, "") \
macro(RNG_SET_RANDSTATE, "__Vm_rng.set_randstate", false, "r") \
macro(SCHED_ANY_TRIGGERED, "anyTriggered", false, "r") \
macro(SCHED_AWAITING_CURRENT_TIME, "awaitingCurrentTime", PURE, "") \
macro(SCHED_AWAITING_ZERO_DELAY, "awaitingZeroDelay", PURE, "") \
macro(SCHED_READY, "ready", false, "r") \
macro(SCHED_MOVE_TO_RESUME_QUEUE, "moveToResumeQueue", false, "r") \
macro(SCHED_DELAY, "delay", false, "rrrr") \
macro(SCHED_DO_POST_UPDATES, "doPostUpdates", false, "") \
macro(SCHED_EVALUATE, "evaluate", false, "") \
macro(SCHED_EVALUATION, "evaluation", false, "rrrr") \
macro(SCHED_POST_UPDATE, "postUpdate", false, "rrrr") \
macro(SCHED_RESUME, "resume", false, "TODO") \
macro(SCHED_RESUME_ZERO_DELAY, "resumeZeroDelay", false, "") \
macro(SCHED_RESUMPTION, "resumption", false, "rrrr") \
macro(SCHED_TRIGGER, "trigger", false, "rrrrr") \
macro(SCHED_WAIT_FOREVER, "waitForever", false, "rrr") \
macro(UNPACKED_ASSIGN, "assign", false, "r") \
macro(UNPACKED_FILL, "fill", false, "r") \
macro(UNPACKED_NEQ, "neq", PURE, "r")
// clang-format on
class VCMethod final {
static constexpr bool PURE = true; // For macro expansion of 'pure' field only
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, args) 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 +978,50 @@ 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, args) 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, args) pure,
FOR_EACH_CMETHOD(VL_CMETHOD_PURE)
#undef VL_CMETHOD_PURE
false //
};
return values[m_e];
}
const char* args() const VL_PURE {
static const char* const values[] = {
#define VL_CMETHOD_ARGS(id, method, pure, args) args,
FOR_EACH_CMETHOD(VL_CMETHOD_ARGS)
#undef VL_CMETHOD_ARGS
"" //
};
return values[m_e];
}
// Return array method for given name
static VCMethod arrayMethod(const string& name);
static void selfTest();
// Validate the arguments descriptor
static constexpr bool validateArgsDescriptor(const char* descrp) {
// Is "TODO"
if (descrp[0] == 'T' && descrp[1] == 'O' && descrp[2] == 'D' && descrp[3] == 'O'
&& !descrp[4]) {
return true;
}
// Is a sequence of 'r'/'w'/'m' with an optional trailing '+'
for (const char* cp = descrp; *cp; ++cp) {
if (*cp == '+') return cp != descrp && !cp[1];
if (*cp != 'r' && *cp != 'w' && *cp != 'm') return false;
}
return true;
}
};
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 +1030,14 @@ 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}};
// Static assert all argument descriptors are well formed
#define VL_CMETHOD_ARGS_CHECK(id, method, pure, args) \
static_assert(VCMethod::validateArgsDescriptor(args), \
"Malformed argument descriptor for " #id);
FOR_EACH_CMETHOD(VL_CMETHOD_ARGS_CHECK)
#undef VL_CMETHOD_ARGS_CHECK
#undef FOR_EACH_CMETHOD
// ######################################################################
@@ -1117,6 +1102,11 @@ public:
explicit VCaseType(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 {
static const char* const names[]
= {"CASE", "CASEX", "CASEZ", "CASEINSIDE", "CASEMATCHES", "RANDSEQUENCE"};
return names[m_e];
}
};
constexpr bool operator==(const VCaseType& lhs, const VCaseType& rhs) {
return lhs.m_e == rhs.m_e;
@@ -1231,6 +1221,8 @@ public:
MERGE_INSTANCES,
DISTRIBUTE_FIRST,
REAL_INTERVAL,
// Legacy option.* accepted for compatibility
CROSS_AUTO_BIN_MAX,
// sentinel - should never appear after parse-time validation
UNKNOWN
};
@@ -1254,6 +1246,7 @@ public:
"merge_instances",
"distribute_first",
"real_interval",
"cross_auto_bin_max",
"unknown"};
return names[m_e];
}
@@ -1509,6 +1502,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 };
@@ -1700,13 +1797,14 @@ public:
int hiMaxSelect() const {
return (lo() < 0 ? hi() - lo() : hi());
} // Maximum value a [] select may index
void dump(std::ostream& str) const {
string ascii() const {
if (ranged()) {
str << "[" << left() << ":" << right() << "]";
return "["s + std::to_string(left()) + ":" + std::to_string(right()) + "]";
} else {
str << "[norg]";
return "[norg]";
}
}
void dump(std::ostream& str) const { str << ascii(); }
};
inline std::ostream& operator<<(std::ostream& os, const VNumRange& rhs) {
rhs.dump(os);

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