Compare commits

...
301 Commits
Author SHA1 Message Date
Geza Lore cb3a916d0c Fix missing dtype on sliced $sampled of an unpacked array 2026-10-05 15:43:07 +01:00
Geza Lore 23012752c3 Internals: Remove AstVar::isStatic
Was only ever set for ConstPool variables, which have special handling
anyway so this was redundant.
2026-10-05 11:16:31 +01:00
Marco Bartoli 19254541ee Fix cycle delay in synchronous drive not counting its clocking block (#8487) 2026-10-05 05:49:37 -04:00
Geza Lore 8cc1ad620e CI: Another attempt at fixing gcov OOM in CI 2026-10-04 18:00:11 +01:00
Geza Lore 37c3475759 CI: Fix Coverage job failing on empty patch
Check if merged coverage contains any DA (line coverage) records,
instead of checking the diff to decide if empty.

Fixes #8552
2026-10-04 16:58:29 +01:00
Geza Lore b77b711377 CI: Add HammerBlade to --hierarchical RTLMeter runs 2026-10-04 15:56:39 +01:00
Michael Bedford Taylor 4e194cc3ec Improve parameterized hierarchical block substitution (#7009) (#8605) 2026-10-04 10:15:58 -04:00
Saqib Khan 19a3d2d6b3 Fix enum item reference through parameterized instance (#8347) (#8567) 2026-10-04 06:57:31 -04:00
Geza Lore 3e96aba1d6 Fix package removed while a struct type still refers to it (#8603)
V3Dead keeps the package of a data type located in the package alive while
the data type is used, but struct types moved to the type table refer to their
package only via classOrPackagep, so the package could be removed while a
nested struct type still pointed to it.
2026-10-04 11:49:55 +01:00
Geza Lore ec2f3cb2d4 CI: Avoid OOM on coverage merge in GitHub actions 2026-10-04 11:43:44 +01:00
Michael Bedford Taylor c8171ae37b Fix class specializations named before all their pins are folded (#8604) 2026-10-03 12:37:32 -04:00
Marco Bartoli 256468e8ae Fix synchronous drives of an unchanged value not assigning the signal (#8486) 2026-10-03 09:59:24 -04:00
Martin Velay 9f72509635 Fix preserving macro argument depth during stringification (#8592) (#8596) 2026-10-03 09:54:36 -04:00
Geza Lore 5c9a804e8b Internals: Add range Left/Right/Lo/Hi iterable sequences (#8585) 2026-10-02 20:19:10 +01:00
yash e7abfa391b Fix Windows cmake flex error (#8571) (#8572) 2026-10-02 14:05:21 -04:00
yash 9b9d690024 Tests: Fix Windows import of POSIX-only pty and resource in driver.py (#8573) 2026-10-02 14:03:27 -04:00
Michael Bedford Taylor b080950f76 Fix hierarchical library block source ordering (#8578) (#8579) 2026-10-02 13:23:56 -04:00
Marco Bartoli a278652439 Fix typenames to be more readable (#8568) (#8570) 2026-10-02 10:54:43 -04:00
Martin Velay 9dac976008 Fix underscore handling in decimal scans (#8576) (#8577) 2026-10-02 10:44:17 -04:00
Geza Lore e4cc6cadca Fix %m in hierarchical models (#8575)
The model of a hierarchical block, or of a --lib-create library, is
named after the %m of its instance in the parent model, but %m within it
also used to include the name of its top module (l2-name within the
partition), adding an extra level per block. %m now matches across
non-hierarchical, --hierarchical and manual --lib-create builds.

before: top.t.u_sub.sub.u_leaf.leaf
after:  top.t.u_sub.u_leaf
2026-10-02 14:20:41 +01:00
Kornel Uriasz 93e9660ea5 Internals: V3Randomize cleanup. No functional change intended (#8574)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-10-02 07:54:51 -04:00
Geza Lore 5ba2a4836d Tests: Fix DFG tests using same name for models 2026-10-02 09:32:27 +01:00
Wilson Snyder bdfb2e8db1 Fix --hierarchical parameter specialization name collisions (#8555) (#8565)
Fixes #8565.
2026-10-01 08:04:56 -04:00
Geza Lore c5b6f4c80f CI: Add HammerBlade to RTLMeter runs 2026-10-01 12:49:21 +01:00
Nick Brereton 0fc4310048 Fix VPI rows for dynamic and class type variables (#8564) 2026-10-01 07:48:56 -04:00
Wilson Snyder e04f350109 Revert: Fix --hierarchical parameter specialization name collisions (#8555 revert) (#8565). 2026-10-01 07:48:36 -04:00
verilator-bot[bot] 33a6d05382 Apply 'make format' 2026-10-01 11:03:14 +00:00
Nazar Zibilyuk ea536a54b8 Fix waking --sc wrapper when DPI/delay makes work runnable (#8394) (#8534)
Fixes #8394
2026-10-01 12:01:17 +01:00
Wilson Snyder 71c6638ccf Commentary: Changes update 2026-09-30 20:33:45 -04:00
Wilson Snyder 5ac4a7eb22 Commentary 2026-09-30 20:33:39 -04:00
Wilson Snyder f8bf67111e Tests: Fix some scenarios labels. No test coverage change. 2026-09-30 20:25:54 -04:00
Nick Brereton 368f59d5f2 Optimize exhaustive case if-chains with an unconditional else (#8554) 2026-09-30 20:17:00 -04:00
Marco Bartoli 2a82047e4e Fix --hierarchical parameter specialization name collisions (#8555) 2026-09-30 20:15:47 -04:00
Ethan Sifferman 6438d977fa Fix dehash of a suffix after a hashed name (#8556) 2026-09-30 19:51:19 -04:00
Nick Brereton af1c668b99 Fix vpi_put_value crash on NULL value pointer (#8553) 2026-09-30 18:44:09 -04:00
Saqib Khan 5458bd2e77 Fix circular class inheritance hang (#8324) (#8545) 2026-09-30 18:42:58 -04:00
Martijn Wobbes 61289bc80b Fix cascading errors after unsupported class dot reference in parameter value (#8551)
Signed-off-by: Martijn Wobbes <[email protected]>
2026-09-30 17:53:42 -04:00
Aditya Shevade 1edf8f986a Fix real values and unenforced rand/randc type-eligibility rules in constraints (#8273) (#8275) (#8276) 2026-09-30 17:52:43 -04:00
Nick Brereton 226cf2f850 Fix stack depth calculations over calls (#8544) 2026-09-30 13:28:55 -04:00
Artur Bieniek d4e4b1d795 Optimize assertion control queries (#8547) 2026-09-30 11:32:56 -04:00
Kristof Marien 055b870e11 Fix derived class construction resets inherited per-instance rand_mode state (#8527) (#8528) 2026-09-30 11:07:49 -04:00
Marco Bartoli c686a25f54 Fix V3Const__gen.o not rebuilt when headers change (#8550) 2026-09-30 11:07:22 -04:00
Krzysztof Bieganski 1f0e80dbd6 Optimize sched-initial/static/final: call splitCheck once per sub-function (#8549)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2026-09-30 10:26:54 -04:00
Wilson Snyder a419e91574 Fix $c() inside a DPI export (#8402). [Nazar Zibilyuk]
Fixes #8402.
2026-09-29 19:17:34 -04:00
Nick Brereton 0993179e7e Tests: Add VPI tests for combinational, cross-scope and savable access (#8533) 2026-09-29 19:16:22 -04:00
Wilson Snyder 9637c62dfe Tests: Repair new test (#8538 repair) 2026-09-29 17:45:58 -04:00
Wilson Snyder 39796ca954 Commentary: Changes update 2026-09-29 17:40:07 -04:00
Noam Gallmann 7f9a8695c1 Fix crash on class handle passed to string port (#8536) (#8538) 2026-09-29 17:25:52 -04:00
Kristof Marien feb1f64be8 Fix extern class method rejects matching virtual interface argument type #8525 (#8526) 2026-09-29 16:53:54 -04:00
Marco Bartoli 68439fbb72 Fix nonblocking event trigger never firing in suspendable processes (#8485) 2026-09-29 16:48:09 -04:00
Marco Bartoli 21bae44b03 Fix verilator_coverage report to align with get_coverage() (#8522) (#8524) (#8531) 2026-09-29 16:36:33 -04:00
em2machine bbdc2cce08 Internals: fixDeadRefs, findLiveCloneOf, TypeTableDeadRefVisitor removal (#8492 partial) (#8537) 2026-09-29 16:33:17 -04:00
Hongseok Choi c2479ce807 Fix function reference into generate case block (#5312) (#8532) 2026-09-29 12:59:23 -04:00
Igor Zaworski 3fd0948cf2 Fix of queue pop return value access (#8529)
Fixes #8511,  #8380 repair

Signed-off-by: Igor Zaworski <[email protected]>
2026-09-29 16:45:28 +01:00
Kornel Uriasz 38f011ed2c Fix enums inside randomize() with (#8517)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-09-29 10:01:43 -04:00
em2machine 58fc34e6df Internals: Remove AstRefDType ledger parallel scaffolding in favor of AstRefDType tags (#8492 partial) (#8513) 2026-09-29 09:03:16 -04:00
Saqib Khan 9d9d367302 Support waiting on clocking block event through modport (#8403) (#8530) 2026-09-29 08:50:57 -04:00
Wilson Snyder 647b2056ed Fix verilator_coverage reports rounding incomplete coverage to 100.0% (#8523).
Fixes #8523.
2026-09-29 08:50:20 -04:00
Wilson Snyder 2c32da73c3 Commentary: Changes update 2026-09-29 08:37:08 -04:00
Geza Lore b3139df707 Fix emitting public parameters (#8520)
Fixes #8520, #8512 repair.
2026-09-29 12:23:40 +01:00
Geza Lore da4903f721 Fix bear version detection if uninstalled
Fallout from b8068fad46 The CI 'test' jobs
has no 'bear' installed but attempted to run due to caching the result
of the version check on disk. Check every time instead of caching.
2026-09-29 11:20:45 +01:00
Kristof Marien 640607a0d0 Fix rand_mode(0) not preserving dynamic array of struct values used in a dist constraint (#8508) (#8509) 2026-09-28 22:23:01 -04:00
Wilson Snyder f83ed971e2 Use reversable base64 for -generate-key 2026-09-28 20:11:31 -04:00
Wilson Snyder b8068fad46 CI: Make twice to see errors easier 2026-09-28 19:59:13 -04:00
Wilson Snyder bafdae1847 Internals: Favor constexpr for constants. No functional change intended. (#8521) 2026-09-28 19:58:56 -04:00
Marco Bartoli bde1b98ee0 Support coverpoint bin 'with' filters (#8518) 2026-09-28 18:29:54 -04:00
Kamil Danecki a949674a6b Support nested array and associative array element member access in constraint 3/4 - associative arrays (#8244)
Signed-off-by: Kamil Danecki <[email protected]>
2026-09-28 08:08:57 -04:00
Ethan Sifferman 510ae7b38b Fix duplicate struct declaration when inlining (#8515) 2026-09-28 09:41:56 +01:00
verilator-bot[bot] f5f9ddef9c Apply 'make format' 2026-09-28 01:06:50 +00:00
Marco Bartoli 4ab229e659 Support wildcard bins (#8516) 2026-09-27 21:06:09 -04:00
Michael Bedford Taylor 2c441d853c Optimize DFG temporary sharing across module instances (#8456) 2026-09-28 00:46:27 +01:00
Wilson Snyder c30604222f Tests: Fix stale file reference 2026-09-27 14:31:42 -04:00
Marco Bartoli a933e723c2 Support covergroup fixed-size bin arrays (#8514) 2026-09-27 13:57:38 -04:00
Wilson Snyder 6956befd29 Tests: Reformat some tests to mostly verilog-format standard. No test functional change. 2026-09-27 13:41:11 -04:00
Wilson Snyder f691cd1936 Change --protect to use SHA512 per IEEE 1735. 2026-09-27 13:34:11 -04:00
Wilson Snyder 626333df02 Commentary: Changes update 2026-09-27 13:28:00 -04:00
Geza Lore 43c3c88c38 Internals: AstCoverToggle is not an AstNodeStmt (#6280)
It is more akin to an AstAlways, being a module item.
2026-09-27 13:46:58 +01:00
Geza Lore 9e2c30faab CI: Move Code coverage to 26.04 2026-09-27 13:35:03 +01:00
Geza Lore 348b9b6209 Internals: Make the constant pool a regular package (#8512)
The constant pool is now an ordinary package, created with the netlist
and instantiated under $root like any other package, and most special
handling has been removed, including AstConstPool. Lookups go through
V3ConstPool, which has a singleton instance owned by V3Global. Static
methods on V3Common form the public interfce to add/find constant pool
entries.

With that, the constant pool is usable at any stage during compilation,
so enum and dimension tables created by V3Width, and enum value tables
created by V3Randomize now also live in the constant pool instead of
$unit, so identical tables are shared.

Associative array constants are handled separately from unpacked
tables, which used to be broken but unused.

Emitted constant pool variables use direct initialization, and
`constinit` with C++20 where the type allows it. This ensures we don't
change a run-time in a way that would result in unintended code size
increase.

-fno-merge-const-pool, which was introduced years ago but never prompted
a bug report is deprecated and has no effect.

This is also prep for future work.
2026-09-27 13:25:41 +01:00
Geza Lore d234caafc1 Optimize through package variables in Dfg 2026-09-26 15:01:32 +01:00
Francesco Urbani eec929e40e Support empty queue literals as function and task arguments (#8501) (#8502) 2026-09-26 08:11:49 -04:00
verilator-bot[bot] d1eb78f4e8 Apply 'make format' 2026-09-26 11:03:09 +00:00
Geza Lore 4c4d9cc7d2 CI: Fix previous commit 2026-09-26 12:01:57 +01:00
Geza Lore 98250f6cd2 CI: Format as the Verilator bot when running on main repo 2026-09-26 11:56:58 +01:00
Geza Lore f2114d3dc3 Internals: Make node type hashes stable with Ast changes
Instead of hashing node types by type id (which can change when
adding/removing Ast node types), astgen a stable hash based on node
name. This makes output more stable through Ast node
additions/deletions. Largely to help with 'make test-diff', should have
no user noticeable impact.
2026-09-26 10:37:56 +01:00
Geza Lore 33d63ac0b0 Internals: Identify $unit package by netlist pointer 2026-09-26 07:48:56 +01:00
em2machine 435af6afe6 Internals: Coverage cleanups before UAF fix (#8494) 2026-09-25 18:54:53 -04:00
Ethan Sifferman f6ff5b4cfb Fix elaboration of classes reached through deferred references (#8440) (#8510) 2026-09-25 17:34:47 -04:00
Marco Bartoli 4e29ecbf44 Fix disable fork and wait fork affecting pending NBA updates (#8483) 2026-09-25 16:35:51 -04:00
Deanyone Su 8b663f9c84 Optimize modport resolution with many interface instances (#8496) (#8497) 2026-09-25 14:38:18 -04:00
Marco Bartoli 3a76b29cb3 Add --coverage-max-bins and --coverage-max-real-bins and optimize covergroup bin generation (#8499) 2026-09-25 13:23:46 -04:00
Geza Lore 2e5e34d426 Internals: Set fabricated variable lifetimes
Set implicit net and traced coverage var lifetimes. No functional change.
2026-09-25 16:33:53 +01:00
Ryszard Rozak 0e0ce2be52 Fix mis-setting of process state from FINISHED to KILLED (#8506)
Signed-off-by: Ryszard Rozak <[email protected]>
2026-09-25 09:43:55 -04:00
Ryszard Rozak 765d05acf8 Fix semaphore waiting on killed process (#8505)
Signed-off-by: Ryszard Rozak <[email protected]>
2026-09-25 09:27:36 -04:00
Ryszard Rozak ba8e0a1437 Set status of finished process immediately (#8503)
Signed-off-by: Ryszard Rozak <[email protected]>
2026-09-25 09:26:22 -04:00
Geza Lore 84dec259c8 Fix constant oversized shifts in V3Premit
- Arithmetic right shift used the MSB of the C++ type as the sign bit,
  instead of the MSB at the Verilog width.
- Constant shift amounts with their top bit set were treated as
  negative, so the oversized shift was emitted as undefined behavior.
- Shifts of an impure operand were replaced by zero, discarding the
  side effects of the operand.
2026-09-25 14:12:00 +01:00
Geza Lore f2dbf210a8 Fix masking of right shifted values with dirty upper bits (#8500)
Fixes #8500
2026-09-25 11:12:30 +01:00
Geza Lore 1c94fbb5d6 Internals: Remove code dead after recent refactoring (#8498) 2026-09-25 08:30:34 +02:00
Kristof Marien 4202862472 Fix disabled random dynamic array of structs referenced in a constraint (#8454) (#8455) 2026-09-24 20:44:09 -04:00
Geza Lore 02546791da Internals: Remove V3LinkDot::linkDotArrayed (#8495)
Follow up to #8476 and #8493, now that elaboration fully expands
instance arrays, linkDotArrayed's only remaining purpose was to name
AstBegin blocks created after V3Width.

Fix up earlier passes to name all Begin blocks they create, or avoid
creating AstBegin in the first place if possible.

One warning about assigning to a modport input of a generic interface
port had to move into LinkLValue (previously this only used to be
reported by linkDotArrayed, as the type/modportness only resolved in
V3Param, and the lvalueness is subsequently only known in LinkLValue).

Then remove linkDotArrayed and simplify V3LinkDot.cpp
2026-09-24 22:41:24 +01:00
Geza Lore 9e03fed56b Support multi-dim interface instance arrays and interface array ports (#8493)
Companion to #8476, and this now moves all instance array and interface
array/modport array port expansion into elaboration. Fixes #2675.

Arrays of interface instances and interface array ports are now split
into their elements during elaboration, instead of after V3Width in
V3Inst::dearrayAll, which is removed.

Background: next to every interface instance 'intf ifs()', V3LinkCells
creates a companion variable 'ifs__Viftop' of type IfaceRefDType
referring to the instance, so the instantiating module can use the
interface as a value (in pin connections, virtual interface assignments,
arguments, ...) just like a module referring to it through an interface
port. For an array of instances 'intf ifs[N]()', this variable is an
unpacked array of IfaceRefDType, but an IfaceRefDType can only refer to
a single instance, so it must be split into a variable for each element,
and every use of the whole array replaced.

V3Param expands interface instance arrays like module instance arrays,
also splitting the __Viftop variable referencing the whole array into a
variable for each element cell ('ifs__BRA__i__KET____Viftop', the
companion of element cell 'ifs__BRA__i__KET__'), and splits each
interface array port into a variable for each element. The whole array
variables remain until all references to them are replaced, so V3LinkDot
can link hierarchical references to the correct specialization.

In V3Width, every reference to a whole interface array, including
hierarchical ones, is replaced by an AstInitArray of references to its
element variables, typed as the whole array, e.g. 'ifs' becomes
'{ifs[0], ifs[1], ...}. The AstInitArray keeps its own type, so its uses
are checked as those of the whole array. The whole array variables are
removed at the end of V3Width.

V3Width then treats a whole interface array replaced by an AstInitArray
as an ordinary unpacked array value:
- A constant select yields the element, a slice the selected elements.
- A pin of an element of a module instance array selects its part of an
  interface array connection like any other connection.
- A pin of an interface array port is split into a pin for each element
  port, connected left index to left index.
- A member of an element is referenced through the interface, as for
  any other interface.

This supports slices of interface arrays, and interface ports of
multi-dimensional module instance arrays connected to interface arrays.
2026-09-24 19:10:33 +01:00
Marco Bartoli 1aa5ab2e3a Support weight in coverage (#8472) 2026-09-24 14:01:45 -04:00
David Harris 88082a4e96 Support automatic bins for enum coverpoints (#8462) (#8464) 2026-09-24 09:58:22 -04:00
Geza Lore 976ec63531 Support multi dimensional module instance arrays (#2675) (#8476)
Arrays of module instances are now expanded into their element cells
during elaboration in V3Param, instead of after V3Width in
V3Inst::dearrayAll. Element cells are named as before
(name__BRA__i__KET__...), are ordered from the left index to the right
index, and hierarchical references to them resolve in linkDotParamed.

Arrays of interface instances are still expanded by V3Inst. They will
move in a follow up patch.

Algorithmically:
- V3Praram replaces an arrayed AstCell, with a scalar Cell for each
  element. linkDotParamed can resolve against these as normal.
- V3Width then fixes up the AstPins of each expanded Cell according to
  the LRM rules. Either leaving the alone to connect to all instances,
  or inserting ArraySel/Sel to connect the relevant part to the relevant
  instance.

Partial #2675
2026-09-24 10:59:41 +01:00
Marco Bartoli 26dd04c020 Fix ##0 synchronous drive overriding later drives (#8482) 2026-09-23 21:32:14 -04:00
Wilson Snyder 3d386f8fa5 Internals: Add VL_DO_DANGLING in some missing places. No functional change intended. 2026-09-23 18:27:40 -04:00
Bartłomiej Chmiel 7fd68b58ca Fix VPI discoverability of forceable unpacked struct members (#8445) 2026-09-23 14:54:55 -04:00
David Harris 729a51e26e Fix unsized condition not reduced to one bit (#8465) (#8466) 2026-09-23 14:50:16 -04:00
em2machine f82cdbeaa7 Fix typedef through a parameterized sub-interface (#7943 repair) (#8124) (#8128) 2026-09-23 14:32:23 -04:00
David Harris e6ecf3a014 Fix embedded covergroup calling an enclosing class method (#8461) (#8463) 2026-09-23 14:26:03 -04:00
Artur Bieniek e9f60f3282 Fix model failing when host has fewer cores than --threads (#8475)
Signed-off-by: Artur Bieniek <[email protected]>
2026-09-23 13:52:47 -04:00
Geza Lore f458e40060 Move module inlining after V3Scope (#8453)
This moves V3Inline after V3Scope. Not doing #2226 yet as it has some
fallout. The inlining heuristic is identical to before, but the node
counts might have shifted, so output can be perturbed.

There is a wart needing to be fixed up with how Fsm coverage is
collected, which inserts declarations into the MODULE for each instance
of that module, but we can fix it up.

Otherwise it's fairly straight forward translation of the old algorithm,
there are some quirks due to V3Scope, but nothing drastic. Moving it far
later might be hard though, because of the Fsm-like rewrites, but that's
not something we want to do anyway.

Fixes #1539
2026-09-23 18:15:46 +01:00
Noam Gallmann ceb7dde938 Fix V3OutJsonFile comma emission for nested JSON scopes (#8469) (#8474) 2026-09-23 13:03:45 -04:00
David Harris a99ced0ed4 Fix covergroup '0/'1 bin values not filling to coverpoint width (#8458) (#8460) 2026-09-23 12:50:39 -04:00
Todd Strader e9a24647d8 CI: Fix regress wording (#8471) 2026-09-23 08:29:06 -04:00
Wilson Snyder 9401876de5 Commentary: Changes update 2026-09-23 08:16:58 -04:00
Wilson Snyder 492a8f7b67 Commentary 2026-09-23 08:16:51 -04:00
David Harris fe4b3bdec4 Fix $sscanf/$fscanf returning -1 after a partial match (#8457) (#8459) 2026-09-23 05:35:17 -04:00
Marco Bartoli 39e2d8e853 Support dynamic cross layouts and fix covergroup bin exclusions priority (#8424) 2026-09-22 20:44:25 -04:00
David Harris 525d1fade3 Fix internal error on covergroup in a specialized parameterized class (#8450) (#8452) 2026-09-22 19:00:20 -04:00
Geza Lore 6eced23e84 Optimize instance port connections by aliasing rather than by assignment (#8427)
Use AstAlias instead of AstAssignW in V3Inst port connection lowering,
the same way as V3Inline would do if the instance is inlined. This
closes some of the gap between -finline and -fno-inline behaviour, and
is also required for moving V3Inline post scope, where it could not
create aliases anymore (hence required for future patch to maintain
current behaviour).

This is also a partial fix to #4698 (only when the connected expression
is a plain VarRef)
2026-09-22 20:27:21 +01:00
Tynan McAuley 0ab3915de6 Fix data race in AstTypeTable by serializing V3VariableOrder (#8451) 2026-09-22 12:49:23 -04:00
Patrick Creighton d637b665ce Synthesize successive whole-element array assignments (#8316)
Preserve previous whole-element drivers when synthesizing successive
constant-index assignments to unpacked arrays. Retain earlier values
through their assignment temporaries and keep the default while some
elements remain uncovered.

Complete the array-default tracing path exposed by this representation.
Keep public-write checks, control-flow restrictions, and scheduling
unchanged, and cover propagation and conservative fallbacks in the
existing DFG regressions.

Part of #7964; array-only follow-up to #7966.
2026-09-22 17:46:38 +01:00
Artur Bieniek f04eb8de81 Fix compound types in anonymous structs (#8449)
Signed-off-by: Artur Bieniek <[email protected]>
2026-09-22 12:41:11 -04:00
Artur Bieniek 08c9032927 Fix internal error when task without CFunc is left in the tree (#8437)
Signed-off-by: Artur Bieniek <[email protected]>
2026-09-22 11:36:20 -04:00
Wilson Snyder 4110d13131 Tests: Also check for brace-init comments 2026-09-22 09:13:42 -04:00
Wilson Snyder 5dab8ab576 Commentary 2026-09-22 09:13:31 -04:00
Wilson Snyder e63ca70d92 Tests: Add memers commented test 2026-09-22 07:21:32 -04:00
Wilson Snyder f883b1c858 Commentary. No functional change 2026-09-22 07:20:46 -04:00
Ryszard Rozak dc179243eb Tests: Fix t_constraint_global_cls_arr (#8446)
Signed-off-by: Ryszard Rozak <[email protected]>
2026-09-22 06:35:43 -04:00
Kornel Uriasz a1e6252e77 Internals: V3Randomize cleanup. No functional change intended (#8443)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-09-22 06:34:32 -04:00
Jonathan Drolet e71d4e7fe5 Fix force read update sensitivity quadratic (#8431) (#8433) 2026-09-21 23:17:39 -04:00
Artur Bieniek 9e6af08c84 Fix false MULTIDRIVEN for writes in tasks never executed (#8401) 2026-09-21 20:57:40 -04:00
Kristof Marien a50aea7709 Fix streaming dynamic arrays with different element types failing to compile (#8434) (#8435) 2026-09-21 20:57:03 -04:00
Kristof Marien 9ca90f414c Fix nested random dynamic arrays internal error (#8438) (#8439) 2026-09-21 20:50:37 -04:00
Kornel Uriasz ba1961c70b Fix unique constraint with multiple arrays (#8378)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-09-21 17:47:18 -04:00
dependabot[bot] 5883a5a40d CI: Bump the everything group with 4 updates (#8432) 2026-09-21 17:45:59 -04:00
Wilson Snyder d321a5fa5d Update IEEE references 2026-09-21 08:46:58 -04:00
Wilson Snyder 2180cfd635 Tests: Less aggressive waiting message 2026-09-21 08:11:21 -04:00
Wilson Snyder 232d3ecc0c Commentary: Changes update 2026-09-21 08:11:21 -04:00
Wilson Snyder 3884b3ddf3 Tests: skip t_forceable_unpacked_large (#8431 disable) 2026-09-21 08:11:21 -04:00
Kamil Danecki f954f4fd1d Support nested array element member access in constraint (#8243) 2026-09-21 08:10:53 -04:00
Geza Lore 46774fded9 Optimize away dead ports (#8426) 2026-09-21 02:02:59 +01:00
Geza Lore 3c05eef216 Fix losing coverage declarations if first in a module body (#8425) 2026-09-21 00:47:04 +01:00
Geza Lore e5384c7b68 CI: Fix yaml format 2026-09-20 23:56:59 +01:00
Geza Lore 33d1e4e685 CI: Fix more unintended cancellations 2026-09-20 23:38:41 +01:00
Geza Lore 652495a932 Fix interface modport resolution against non-scope symbol tables (#8420)
LinkDotFindVisitor queued an AstIface into m_ifaceModSyms once for every
symbol table entry it was reached through. When creating scopes,
LinkDotIfaceVisitor resolves the modport items against the AstVarScopes,
which LinkDotScopeVisitor only inserts under the entry of an AstScope. A
hierarchical block wrapper builds a second copy of the instance tree in
the symbol table, and an interface reached through that copy has no
AstVarScopes to resolve against.

Resolving is destructive (the AstModportVarRef is deleted once resolved),
so this only worked when a resolvable entry happened to be processed
first, and failed with "Modport item not found" otherwise. When creating
scopes, register from the AstScopes instead, as those are exactly the
entries that hold the AstVarScopes.

Reproduces on t_hier_block with -fno-inline, added as a test.
2026-09-20 20:52:17 +01:00
Geza Lore bef6c8afa0 CI: Fix imprecise 'onMaster' checks 2026-09-20 20:40:22 +01:00
Geza Lore 77e9938188 CI: Prevent concurrent format jobs on same ref 2026-09-20 20:40:22 +01:00
Geza Lore 8f0bd99449 CI: Fold pr-conflicting into pr-automation 2026-09-20 20:40:22 +01:00
Kaleb Barrett c4957ef206 Fix MSYS header include search to be after toolchain headers (#8422) 2026-09-20 15:14:11 -04:00
Geza Lore 2ee6f7435d CI: Fix unlabeling closed PRs canceling unrelated jobs 2026-09-20 20:08:41 +01:00
Shriyans Sahoo e71f6f3b7a Fix cache of boundary vertices in Dfg cycle breaking (#8308) 2026-09-20 17:05:56 +01:00
Geza Lore c545552cc1 Fix FSM coverage hierarchy being emitted as an absolute path (#8421)
AstNodeCoverDecl::hier() is relative to the scope the declaration is
emitted from, as V3EmitCImp builds the reported path as 'vlNamep + hierp',
and that is how V3Coverage uses it. V3FsmDetect instead set it to the
absolute scope name, so the instance path was counted twice.

This was masked whenever the owning module was inlined, as the declaration
then ended up in the top scope. With inlining disabled, an FSM in an
instance reported 'top.t.forced_wide_u.t.forced_wide_u'. In the inlined
case it reported 'top.TOP', leaking the internal top wrapper name into
user visible coverage output, rather than plain 'top'.
2026-09-20 16:21:40 +01:00
Geza Lore fcb2a22d8e Tests: Skip t_forceable_unpacked_large with dev-coverage
This test takes over 1 hour to run with code coverage collection.
2026-09-20 16:12:29 +01:00
Geza Lore 8f6d6cd13b Fix placing Always into List[VarScope] in LinkDot (#8419)
Updated order dependent tests.
2026-09-20 14:15:41 +01:00
Geza Lore dfa4ace3dd Optimize V3Scope to move nodes on the last instance of a module (#8417)
Nodes were always cloned under the new scope, with the originals deleted
at the end of the pass. Count total module instantiations up front, then
move the nodes instead of cloning them when scoping the last instance.

Required to avoid a memory regression in a follow up, but also faster
and less peak memory overall.

Output is identical.
2026-09-20 14:15:29 +01:00
Geza Lore d30487e0f7 Optimize V3Scope to gather variable reference fixups in a vector (#8418)
Each AstVarRef is visited exactly once (blocks are iterated under
their per-scope clone), and the fixups are independent, so no need
for the ordered set.
2026-09-20 14:15:07 +01:00
Marco Bartoli dc946aee2b Support enclosing cross identifiers in bin selections (#8416) 2026-09-20 08:57:14 -04:00
Geza Lore 9e3ec98461 CI: Don't skip CI on format (#8408) 2026-09-20 07:14:47 -04:00
Wilson Snyder c8b8c79ee9 Commentary: Changes update 2026-09-19 21:33:08 -04:00
Wilson Snyder 67e6d09768 Commentary 2026-09-19 21:31:14 -04:00
Wilson Snyder 3444d57929 Tests: Add VtOs.system 2026-09-19 21:31:14 -04:00
Geza Lore 8c6dd7a164 CI: Auto label on RTLMeter and Code coverage failure 2026-09-19 21:45:20 +01:00
Marco Bartoli 18ad801887 Fix covergroup constructor initialization (#8410) 2026-09-19 16:44:04 -04:00
Geza Lore 87983308c7 CI: Add job to block PR merges with pr-blocked: labels (#8415) 2026-09-19 21:18:29 +01:00
Geza Lore 23a4c8ef46 CI: Label as the CI app to trigger workflows 2026-09-19 20:50:50 +01:00
Geza Lore acb1333a85 CI: Auto label PRs with pr-blocked: improve-coverage 2026-09-19 19:59:34 +01:00
Kristof Marien c266da882d Fix crash when widthing a covergroup option through a forward class (#8351) (#8352) 2026-09-19 14:47:07 -04:00
Geza Lore ba68360819 CI: Auto label PRs with pr-blocked: fix-regression 2026-09-19 19:33:25 +01:00
Jonathan Drolet 6999fe0f29 Fix O(N^2) compile-time blowup forcing large unpacked arrays (#8395) 2026-09-19 14:22:38 -04:00
Kaleb Barrett 8fbfc82cc2 Fix issues with configuring flex during build on MacOS (#8412) 2026-09-19 13:34:30 -04:00
Geza Lore b0b4714a9d CI: Make regressions label driven (#8413) 2026-09-19 18:31:58 +01:00
Geza Lore ee51f322c7 CI: Fix previous commit 2026-09-19 18:00:57 +01:00
Geza Lore f6801633f8 CI: Add Regression passed job for status checks 2026-09-19 17:57:43 +01:00
Geza Lore f2fcacfe3d CI: Rename build-test.yml to regression.yml 2026-09-19 17:04:09 +01:00
Geza Lore 0b661a00c1 CI: Also mop up after the Regression job 2026-09-19 16:59:19 +01:00
Geza Lore 7400d28e6d CI: Don't restart RTLMeter/Coverage on unrelated label changes (#8411) 2026-09-19 16:52:27 +01:00
Geza Lore 36aa3e88c7 CI: Add PR automation job (#8203) 2026-09-19 16:30:50 +01:00
Geza Lore a9f959ff28 CI: Fix yaml format 2026-09-19 15:51:59 +01:00
Geza Lore b75ceeb121 CI: Delete skipped PR workflows 2026-09-19 15:48:24 +01:00
Kaleb Barrett d797c2009b CI: Move macOS build from 15 to 26 #8409 (#8409) 2026-09-19 13:02:48 +01:00
Marco Bartoli 014c9820de Warn on not yet implemented and ignored iff in default or transition bins (#8406) (#8407) 2026-09-18 21:34:54 -04:00
Marco Bartoli 1cefe95c85 Support iff in covergroup bins (#8406) 2026-09-18 20:29:22 -04:00
Nick Brereton a93317b6cc tests: Add VM_PREFIX_INCLUDE_DPI (#8386) 2026-09-18 17:29:53 -04:00
Jesús Alberto López Soto 724a42e31d Fix reevaluation of method calls in wait conditions (#8391) 2026-09-18 15:41:01 -04:00
Marco Bartoli 830cb633e5 Optimize covergroup bin metadata lookup (#8404) 2026-09-18 14:25:37 -04:00
Geza Lore ccb4ab9fe3 Optimize Dfg vertex input edge storage (#8313)
Store input edges of fixed arity vertices inline in the vertex class.
This reduces heap allocations, memory fragmentation, and pointer chasing
and speeds up Dfg passes. The extra branch introduced in inputEdgep() is
well predictable and profiling shows branchless alternatives are a loss.
2026-09-18 18:19:53 +01:00
Geza Lore 079422d6ae Fix Dfg driver tracing of partial assignments (#8377) (#8397)
Fixes #8377
2026-09-18 17:51:54 +01:00
Kornel Uriasz ce482bb087 Fix weighted dist constraints in presence of other constraints (#8392)
Signed-off-by: Kornel Uriasz <[email protected]>
2026-09-18 10:30:39 -04:00
Andrii Andrieiev 064ecbcb82 Fix error on non-existent interface ports (#8393)
Signed-off-by: Andrii Andrieiev <[email protected]>
2026-09-18 08:26:56 -04:00
Ryszard Rozak c25f287779 Fix copy constructor of classes with constraints (#8381) 2026-09-18 07:47:25 -04:00
Igor Zaworski 5f38b797f3 Fix access types of refs and inouts (#8380) 2026-09-18 07:46:56 -04:00
Nick Brereton 3fd16ed498 Internals: Hoist vpi_put_value_array argument validation to entry point. (#8384) 2026-09-17 18:08:16 -04:00
Geza Lore 1f9c9681a7 CI: Move RTLMeter jobs to 26.04 (#8387)
Fixes #8379
2026-09-17 22:47:06 +01:00
Geza Lore 1c4c6c9e9d Improve V3Split (#8385)
Various cleanups, simplifications and improvements:
- Build the output bottom up, eliminating AstSplitPlaceholder
- Replace the 'ignore step' pruning with removing the vertices and edges
- Treat an NBA written variable as a block input, enabling more splits
- Move instead of clone leaf statements

Overall V3Split is faster, uses less memory, can do more splits, is
simpler algorithmically, and has half the lines of code in V3Split

Most notably this can now be split, which could not be before:

```systemverilog
always @(posedge clk) begin
    a <= !a;
    if (a) b <= c;
end
```

t_x_rand_mt_stability* pin $random values, but there is one random seed
per C thread, so they depend on which thread runs the block. More
splitting moved that block onto a different thread, so force a single
MTask in those tests to keep them stable.

Some property/assert test changed hit counts due to races, but are now
closer to what might be expected.
2026-09-17 23:11:26 +02:00
Geza Lore 2d0eaab5df Add -fno-slp-vectorize if C++ compiler is clang 18 (#8383)
Partial #8379
2026-09-17 18:38:39 +01:00
Nick Brereton bc7fd47908 Internals: Prepare VPI runtime for lazy public signals. No functional change. (#8382) 2026-09-17 13:00:19 -04:00
Marco Bartoli 1aa87913c8 Support illegal_bins and ignore_bins for cross bins (#8372) 2026-09-16 17:53:15 -04:00
Artur Bieniek e2caf971de Fix NFA rings for bounded SVA delays (#8371)
Signed-off-by: Artur Bieniek <[email protected]>
2026-09-16 17:52:28 -04:00
Marco Bartoli 08cadc034d Reland and fix "Fix wide enum names" (#8344) (#8362) (#8365) (#8366) 2026-09-16 17:47:51 -04:00
Geza Lore 9f66980355 Revert "Internals: Improve V3Split (#8373)" (#8374)
This reverts commit d8a3208bea.
2026-09-16 21:44:19 +01:00
Geza Lore d8a3208bea Internals: Improve V3Split (#8373) 2026-09-16 16:25:36 -04:00
Wilson Snyder 40c102b4f8 Fix docker compilation with --trace-fst (#8370).
Fixes #8370.
2026-09-16 12:36:14 -04:00
Geza Lore 573c2e61a6 Internals: Make V3LiftExpr non optional (#8369) 2026-09-16 12:29:56 -04:00
Pawel Kojma 282b898b21 Use VIsCached for checking purity of arguments in AstCMethodHard (#8350) 2026-09-16 11:23:34 -04:00
Wilson Snyder 195d0db009 Commentary: Changes update 2026-09-15 21:36:01 -04:00
Wilson Snyder 45dbc90343 Tests: Reformat some tests to mostly verilog-format standard. No test functional change. 2026-09-15 21:35:50 -04:00
Edmund Lam 529d98918a Fix deferring parameters for class::localparam access (#7746) (#7748) 2026-09-15 21:17:26 -04:00
Artur Bieniek c9e3f59f26 Fix stale reads of continuously assigned forceable signals (#8223)
Signed-off-by: Artur Bieniek <[email protected]>
2026-09-15 21:15:37 -04:00
Bartosz Skorowski 3e30f4476a Support constant expressions in bins (#8309)
Signed-off-by: Bartosz Skorowski <[email protected]>
2026-09-15 21:15:25 -04:00
Drew Risinger e0e0011b36 Optimize recursion depth in Dfg circular driver tracing (#8272) 2026-09-15 23:23:54 +01:00
Wilson Snyder a9ddb4fab3 Revert "Fix wide enum names" (#8344) (#8362) 2026-09-15 13:42:35 -04:00
Geza Lore 3343f4d6c8 Fix V3InstrCount with recursive functions (#8361)
Fixes #8327
2026-09-15 18:30:16 +01: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
1300 changed files with 68927 additions and 22589 deletions
@@ -0,0 +1,62 @@
---
# DESCRIPTION: Github actions composite action
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: Update PR label
description: >-
Add or remove a label on a pull request. Does nothing unless the pull
request is still open and still at the commit the result is for, so that a
result superseded by a push does not label/unlabel the work that replaced it.
inputs:
token:
description: "Token to label with"
required: true
pr-number:
description: "Number of the pull request"
required: true
head-sha:
description: "Commit the result is for"
required: true
label:
description: "Label to set"
required: true
set:
description: "'true' to add the label, 'false' to remove it"
required: true
runs:
using: composite
steps:
- name: Set the label
shell: bash
env:
GH_TOKEN: ${{ inputs.token }}
REPO: ${{ github.repository }}
PR: ${{ inputs.pr-number }}
SHA: ${{ inputs.head-sha }}
LABEL: ${{ inputs.label }}
SET: ${{ inputs.set }}
run: |-
# The state and head of the pull request as they are now, and whether
# it carries the label already
read -r STATE HEAD LABELLED < <(gh pr view "${PR}" \
--repo "${REPO}" \
--json state,headRefOid,labels \
--jq '"\(.state) \(.headRefOid) \(any(.labels[]; .name == env.LABEL))"')
# Only act on a change, so a run does not reapply what is already set
if [ "${STATE}" != OPEN ]; then
# Only label open PRs
echo "PR #${PR} is ${STATE}"
elif [ "${HEAD}" != "${SHA}" ]; then
# Pushed to since, so this result is stale and a new run will decide
echo "PR #${PR} has moved on from ${SHA}"
elif [ "${SET}" = true ] && [ "${LABELLED}" = false ]; then
echo "Adding '${LABEL}' to PR #${PR}"
gh pr edit "${PR}" --repo "${REPO}" --add-label "${LABEL}"
elif [ "${SET}" = false ] && [ "${LABELLED}" = true ]; then
echo "Removing '${LABEL}' from PR #${PR}"
gh pr edit "${PR}" --repo "${REPO}" --remove-label "${LABEL}"
else
echo "No change to PR #${PR}"
fi
+71 -12
View File
@@ -20,28 +20,77 @@ defaults:
shell: bash shell: bash
concurrency: concurrency:
# At most 1 job per branch. Auto cancel all but scheduled jobs # At most 1 job per branch. Auto cancel all but scheduled jobs.
group: ${{ github.workflow }}-${{ github.ref }} # Of the pull request events, only one that starts a run, or that removes the
# 'pr: dev-coverage' label to cancel one, shares the group. The rest are
# skipped (a change of another label, or a payload that does not carry the
# label, like an 'opened' delivered after it was added), and a skipped run
# must not cancel the one in progress, so they are given a unique group.
# This way adding the label starts a run, removing it cancels the in-progress
# run, and anything else has no effect. Note such an event still creates a
# run of this workflow, which is skipped, and would then hide the one in
# progress on the pull request. 'pr-run-cleanup.yml' deletes those.
# Key pull request runs on the number: 'github.ref' is the base branch once
# the pull request is closed, so unlabel events would cancel base runs.
# NOTE: The last part of the group expression must be kept in sync with the
# 'start' job condition below: it maps to 'shared' exactly those events that
# satisfy that condition, plus the 'unlabeled' event of the gating label.
group: |-
${{
github.workflow
}}-${{
(github.event_name == 'pull_request')
&& format('pr-{0}', github.event.number) || github.ref
}}-${{
(github.event_name == 'pull_request'
&& (((github.event.action == 'labeled' || github.event.action == 'unlabeled')
&& github.event.label.name != 'pr: dev-coverage')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& !contains(github.event.pull_request.labels.*.name, 'pr: dev-coverage'))))
&& github.run_id || 'shared'
}}
cancel-in-progress: ${{ github.event_name != 'schedule' }} cancel-in-progress: ${{ github.event_name != 'schedule' }}
jobs: jobs:
build: start:
name: Build name: Start
# Only run scheduled jobs if explicitly enabled for that repo (e.g.: not on forks) # Only run scheduled jobs if explicitly enabled for that repo (e.g.: not on forks)
# Only run pull request jobs if labelled as needing an coverage run # Only run pull request jobs if labelled as needing a coverage run
# Always run workflow dispatch jobs # Always run workflow dispatch jobs
if: | if: |
(github.event_name == 'schedule' (github.event_name == 'schedule'
&& vars.ENABLE_SCHEDULED_JOBS == 'true') || && vars.ENABLE_SCHEDULED_JOBS == 'true') ||
(github.event_name == 'pull_request' (github.event_name == 'pull_request'
&& contains(github.event.pull_request.labels.*.name, 'pr: dev-coverage')) || && ((github.event.action == 'labeled'
&& github.event.label.name == 'pr: dev-coverage')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& contains(github.event.pull_request.labels.*.name, 'pr: dev-coverage')))) ||
(github.event_name == 'workflow_dispatch') (github.event_name == 'workflow_dispatch')
runs-on: ubuntu-slim
steps:
- name: Save the pull request number
if: ${{ github.event_name == 'pull_request' }}
run: echo "${{ github.event.number }}" > pr-number.txt
- name: Upload the pull request number
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: pr-number
path: pr-number.txt
overwrite: true
build:
name: Build
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: gcc cc: gcc
dev-gcov: true dev-gcov: true
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
# For pull requests, build the head of the pull request branch, not the # For pull requests, build the head of the pull request branch, not the
# merge commit, otherwise patch coverage would include the changes # merge commit, otherwise patch coverage would include the changes
# between the root of the pull request and the target branch # between the root of the pull request and the target branch
@@ -55,7 +104,7 @@ jobs:
archive: ${{ needs.build.outputs.archive }} archive: ${{ needs.build.outputs.archive }}
cc: gcc cc: gcc
dev-gcov: true dev-gcov: true
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
suite: ${{ matrix.test }}${{ matrix.num }} suite: ${{ matrix.test }}${{ matrix.num }}
strategy: strategy:
fail-fast: false fail-fast: false
@@ -69,7 +118,7 @@ jobs:
name: Publish results to codecov.io name: Publish results to codecov.io
needs: test needs: test
if: ${{ contains(needs.*.result, 'success') && !cancelled() }} if: ${{ contains(needs.*.result, 'success') && !cancelled() }}
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
@@ -88,7 +137,7 @@ jobs:
find obj_coverage -type f | paste -sd, | sed "s/^/files=/" >> "$GITHUB_OUTPUT" find obj_coverage -type f | paste -sd, | sed "s/^/files=/" >> "$GITHUB_OUTPUT"
- name: Upload to codecov.io - name: Upload to codecov.io
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7 uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7
with: with:
disable_file_fixes: true disable_file_fixes: true
disable_search: true disable_search: true
@@ -102,7 +151,7 @@ jobs:
name: Prepare HTML report name: Prepare HTML report
needs: [build, test] needs: [build, test]
if: ${{ contains(needs.*.result, 'success') && !cancelled() }} if: ${{ contains(needs.*.result, 'success') && !cancelled() }}
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
steps: steps:
- name: Download repository archive - name: Download repository archive
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
@@ -150,6 +199,16 @@ jobs:
path: report-artifact path: report-artifact
name: coverage-report name: coverage-report
- name: Upload coverage status
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
path: repo/coverage-status.txt
name: coverage-status
overwrite: true
# Not written if the coverage could not be read, see the script above
if-no-files-found: ignore
- name: Upload PR notification - name: Upload PR notification
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
uses: ./repo/.github/actions/upload-pr-notification uses: ./repo/.github/actions/upload-pr-notification
@@ -163,7 +222,7 @@ jobs:
name: Create issue on failure name: Create issue on failure
needs: [publish-codecov, prepare-report] needs: [publish-codecov, prepare-report]
if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && failure() && !cancelled() }} if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && failure() && !cancelled() }}
runs-on: ubuntu-24.04 runs-on: ubuntu-slim
steps: steps:
# Creating issues requires elevated privilege # Creating issues requires elevated privilege
- name: Generate access token - name: Generate access token
+3 -3
View File
@@ -64,10 +64,10 @@ jobs:
type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }} type=raw,value=latest,enable=${{ inputs.add_latest_tag == true }}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4 uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4 uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4
with: with:
buildkitd-flags: --debug buildkitd-flags: --debug
@@ -78,7 +78,7 @@ jobs:
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Build and Push to Docker - name: Build and Push to Docker
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7 uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
with: with:
context: ${{ env.build_context }} context: ${{ env.build_context }}
+33 -4
View File
@@ -15,16 +15,36 @@ defaults:
run: run:
working-directory: repo working-directory: repo
concurrency:
# At most 1 run per branch: the job pushes what 'make format' changed, so
# concurrent runs on the same branch race on that push. Cancel the run in
# progress, as the newer one formats a tree that already holds its commits.
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
format: format:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
name: Ubuntu 24.04 | format name: Ubuntu 24.04 | format
steps: steps:
# On the main repository, push as the Verilator CI app.
# Forks do not have the app, so fall back to GITHUB_TOKEN there.
- name: Generate access token
id: generate-token
if: ${{ github.repository == 'verilator/verilator' }}
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
owner: verilator
repositories: verilator
permission-contents: write
- name: Checkout - name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with: with:
path: repo path: repo
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ steps.generate-token.outputs.token || secrets.GITHUB_TOKEN }}
- name: Install packages for build - name: Install packages for build
uses: ./repo/.github/actions/install-deps uses: ./repo/.github/actions/install-deps
@@ -32,9 +52,18 @@ jobs:
stage: format stage: format
- name: Configure git identity - name: Configure git identity
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APP_SLUG: ${{ steps.generate-token.outputs.app-slug }}
run: | run: |
git config --global user.email "[email protected]" if [ -n "$APP_SLUG" ]; then
git config --global user.name "github action" user_id=$(gh api "/users/${APP_SLUG}[bot]" --jq .id)
git config --global user.email "${user_id}+${APP_SLUG}[bot]@users.noreply.github.com"
git config --global user.name "${APP_SLUG}[bot]"
else
git config --global user.email "[email protected]"
git config --global user.name "github action"
fi
- name: Configure - name: Configure
run: | run: |
@@ -55,6 +84,6 @@ jobs:
- name: Push - name: Push
run: |- run: |-
if [ -n "$(git status --porcelain)" ]; then if [ -n "$(git status --porcelain)" ]; then
git commit . -m "Apply 'make format' [ci skip]" && git commit . -m "Apply 'make format'" &&
git push origin git push origin
fi fi
+2 -2
View File
@@ -69,11 +69,11 @@ jobs:
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deploy-1 id: deploy-1
continue-on-error: true continue-on-error: true
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5 uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5
- name: Deploy to GitHub Pages (retry) - name: Deploy to GitHub Pages (retry)
id: deploy-2 id: deploy-2
if: steps.deploy-1.outcome == 'failure' if: steps.deploy-1.outcome == 'failure'
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5 uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5
notify: notify:
name: Notify name: Notify
+281
View File
@@ -0,0 +1,281 @@
---
# DESCRIPTION: Github actions config
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# Drives the automation of a pull request.
name: Maintenance - PR automation
on:
pull_request_target: # 'pull_request' can have no write permissions
types: [opened, synchronize, reopened, closed]
workflow_run: # To react to the result of a workflow on a pull request
workflows: ["Code coverage", "RTLMeter", "Regression"]
types: [completed]
push: # A pull request can start conflicting when its base branch moves
branches: [master]
permissions:
contents: read # To check out the local action, everything else uses the CI app token
defaults:
run:
shell: bash
# One job per event source, to avoid startup churn. Use separate gated steps for actions
jobs:
pr-event:
name: PR event
if: |
github.repository == 'verilator/verilator'
&& github.event_name == 'pull_request_target'
runs-on: ubuntu-slim
steps:
# Label as the CI app, and not with 'github.token', as events from the
# latter do not create workflow runs, so the labels would drive nothing
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
permission-pull-requests: write
- name: "Add 'pr: regression' label on open"
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
if: ${{ github.event.action == 'opened' || github.event.action == 'reopened' }}
run: |-
gh pr edit "${{ github.event.number }}" \
--repo "${{ github.repository }}" \
--add-label 'pr: regression'
- name: "Optionally add 'pr: dev-coverage' label on open"
if: ${{ github.event.action == 'opened' || github.event.action == 'reopened' }}
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# Grab changed files
FILES=$(gh api --paginate \
"repos/${{ github.repository }}/pulls/${{ github.event.number }}/files" \
--jq '.[].filename')
echo "Files changed:"
echo "${FILES}"
# Add label if src or include chagned
if grep -q -E '^(src|include)/' <<< "${FILES}"; then
gh pr edit "${{ github.event.number }}" \
--repo "${{ github.repository }}" \
--add-label 'pr: dev-coverage'
fi
- name: "Remove all 'pr*' labels on close"
if: ${{ github.event.action == 'closed' }}
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# Filter in 'jq', so an empty result is not an error, as it is with 'grep'
LABELS=$(gh api \
"repos/${{ github.repository }}/issues/${{ github.event.number }}/labels?per_page=100" \
--jq '[.[].name | select(startswith("pr"))] | join(",")')
if [ -n "${LABELS}" ]; then
echo "Removing labels: ${LABELS}"
gh pr edit "${{ github.event.number }}" \
--repo "${{ github.repository }}" \
--remove-label "${LABELS}"
fi
conflicting:
name: Conflict check
# A pull request can start conflicting when its own head moves, or when the
# base branch moves under it, so sweep the open pull requests on both.
if: |
github.repository == 'verilator/verilator'
&& (github.event_name == 'push'
|| (github.event_name == 'pull_request_target'
&& github.event.action == 'synchronize'))
runs-on: ubuntu-slim
steps:
# Label as the CI app, and not with 'github.token', as events from the
# latter do not create workflow runs, so the labels would drive nothing
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
permission-pull-requests: write
- name: "Update the 'pr-blocked: needs-rebase' label"
uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # 3.1.0
with:
dirtyLabel: "pr-blocked: needs-rebase"
repoToken: ${{ steps.generate-token.outputs.token }}
regression-complete:
name: Regression complete
if: |
github.repository == 'verilator/verilator'
&& github.event_name == 'workflow_run'
&& github.event.workflow_run.name == 'Regression'
&& github.event.workflow_run.event == 'pull_request'
&& (github.event.workflow_run.conclusion == 'success'
|| github.event.workflow_run.conclusion == 'failure')
runs-on: ubuntu-slim
steps:
# Label as the CI app, and not with 'github.token', as events from the
# latter do not create workflow runs, so the labels would drive nothing
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
permission-actions: read
permission-pull-requests: write
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
sparse-checkout: .github/actions/update-pr-label
- name: Get the pull request number
id: pr
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# The run tells us which pull request it was for
if ! gh run download "${{ github.event.workflow_run.id }}" --name pr-number; then
echo "Run has no 'pr-number' artifact"
exit 0
fi
echo "number=$(cat pr-number.txt)" >> "$GITHUB_OUTPUT"
- name: "Update the 'pr-blocked: fix-regression' label"
if: ${{ steps.pr.outputs.number }}
uses: ./.github/actions/update-pr-label
with:
token: ${{ steps.generate-token.outputs.token }}
pr-number: ${{ steps.pr.outputs.number }}
head-sha: ${{ github.event.workflow_run.head_sha }}
label: "pr-blocked: fix-regression"
set: ${{ github.event.workflow_run.conclusion == 'failure' }}
rtlmeter-complete:
name: RTLMeter complete
if: |
github.repository == 'verilator/verilator'
&& github.event_name == 'workflow_run'
&& github.event.workflow_run.name == 'RTLMeter'
&& github.event.workflow_run.event == 'pull_request'
&& (github.event.workflow_run.conclusion == 'success'
|| github.event.workflow_run.conclusion == 'failure')
runs-on: ubuntu-slim
steps:
# Label as the CI app, and not with 'github.token', as events from the
# latter do not create workflow runs, so the labels would drive nothing
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
permission-actions: read
permission-pull-requests: write
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
sparse-checkout: .github/actions/update-pr-label
- name: Get the pull request number
id: pr
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# The run tells us which pull request it was for
if ! gh run download "${{ github.event.workflow_run.id }}" --name pr-number; then
echo "Run has no 'pr-number' artifact"
exit 0
fi
echo "number=$(cat pr-number.txt)" >> "$GITHUB_OUTPUT"
- name: "Update the 'pr-blocked: fix-rtlmeter' label"
if: ${{ steps.pr.outputs.number }}
uses: ./.github/actions/update-pr-label
with:
token: ${{ steps.generate-token.outputs.token }}
pr-number: ${{ steps.pr.outputs.number }}
head-sha: ${{ github.event.workflow_run.head_sha }}
label: "pr-blocked: fix-rtlmeter"
set: ${{ github.event.workflow_run.conclusion == 'failure' }}
coverage-complete:
name: Coverage complete
if: |
github.repository == 'verilator/verilator'
&& github.event_name == 'workflow_run'
&& github.event.workflow_run.name == 'Code coverage'
&& github.event.workflow_run.event == 'pull_request'
&& (github.event.workflow_run.conclusion == 'success'
|| github.event.workflow_run.conclusion == 'failure')
runs-on: ubuntu-slim
steps:
# Label as the CI app, and not with 'github.token', as events from the
# latter do not create workflow runs, so the labels would drive nothing
- name: Generate access token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.VERILATOR_CI_ID }}
private-key: ${{ secrets.VERILATOR_CI_KEY }}
permission-actions: read
permission-pull-requests: write
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
sparse-checkout: .github/actions/update-pr-label
- name: Get the pull request number
id: pr
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# The run tells us which pull request it was for
if ! gh run download "${{ github.event.workflow_run.id }}" --name pr-number; then
echo "Run has no 'pr-number' artifact"
exit 0
fi
echo "number=$(cat pr-number.txt)" >> "$GITHUB_OUTPUT"
- name: "Update the 'pr-blocked: fix-dev-coverage' label"
if: ${{ steps.pr.outputs.number }}
uses: ./.github/actions/update-pr-label
with:
token: ${{ steps.generate-token.outputs.token }}
pr-number: ${{ steps.pr.outputs.number }}
head-sha: ${{ github.event.workflow_run.head_sha }}
label: "pr-blocked: fix-dev-coverage"
set: ${{ github.event.workflow_run.conclusion == 'failure' }}
- name: Get the line coverage
id: coverage
if: ${{ github.event.workflow_run.conclusion == 'success' }}
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |-
# Whether every line the patch touches is covered
if ! gh run download "${{ github.event.workflow_run.id }}" --name coverage-status; then
echo "Run has no 'coverage-status' artifact"
exit 0
fi
echo "covered=$(cat coverage-status.txt)" >> "$GITHUB_OUTPUT"
- name: "Update the 'pr-blocked: improve-coverage' label"
if: ${{ steps.pr.outputs.number && steps.coverage.outputs.covered }}
uses: ./.github/actions/update-pr-label
with:
token: ${{ steps.generate-token.outputs.token }}
pr-number: ${{ steps.pr.outputs.number }}
head-sha: ${{ github.event.workflow_run.head_sha }}
label: "pr-blocked: improve-coverage"
set: ${{ steps.coverage.outputs.covered != 'true' }}
+52
View File
@@ -0,0 +1,52 @@
---
# DESCRIPTION: Github actions config
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# Fails while a pull request carries a 'pr-blocked:*' label, so that it can be
# used as a required status check to hold up the merge. The labels themselves
# are set by hand, or by 'pr-automation.yml'.
#
# Note this runs from the pull request's own merge commit, so a pull request
# can change the check itself, but any such change shows up in its review.
#
# It also runs on a push, and not only on a label change, as a check run is
# reported against the head commit, and so every head needs one of its own.
name: Maintenance - PR blocked
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
permissions:
pull-requests: read
defaults:
run:
shell: bash
concurrency:
group: ${{ github.workflow }}-${{ github.event.number }}
cancel-in-progress: true
jobs:
not-blocked:
name: Not blocked
runs-on: ubuntu-slim
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: "Check for 'pr-blocked:*' labels"
run: |-
# Note these are the labels the pull request has now, and not the ones
# in 'github.event', which is a snapshot that can be stale when
# several are changed in one go
BLOCKED=$(gh pr view "${{ github.event.number }}" \
--repo "${{ github.repository }}" \
--json labels \
--jq '[.labels[].name | select(startswith("pr-blocked:"))] | join(", ")')
if [ -n "${BLOCKED}" ]; then
echo "::error::${BLOCKED}"
exit 1
fi
echo "Not blocked"
-28
View File
@@ -1,28 +0,0 @@
---
# 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 }}"
+42
View File
@@ -0,0 +1,42 @@
---
# DESCRIPTION: Github actions config
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# Deletes the skipped runs of the label gated workflows on pull requests.
#
# GitHub shows only the latest run of a workflow on a pull request, so a run
# that was skipped hides an earlier run that is actually still in progress, and
# the pull request then reads as though that workflow had finished and
# everything is green. Every label change creates a run of every workflow that
# triggers on label events, and label events cannot be filtered by label name
# under 'on', so these runs cannot be prevented. Deleting them restores the
# in-progress run in the UI.
name: Maintenance - PR run cleanup
on:
workflow_run:
workflows: ["Code coverage", "Regression", "RTLMeter"]
types: [completed]
permissions:
actions: write
defaults:
run:
shell: bash
jobs:
delete-skipped-run:
name: Delete skipped run
if: |
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'skipped'
runs-on: ubuntu-slim
steps:
- name: Delete run
env:
GH_TOKEN: ${{ github.token }}
run: |-
echo "Deleting skipped '${{ github.event.workflow_run.name }}' run #${{ github.event.workflow_run.run_number }}"
gh api --method DELETE "repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}"
@@ -2,13 +2,14 @@
# DESCRIPTION: Github actions config # DESCRIPTION: Github actions config
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
name: Regressions name: Regression
on: on:
push: push:
branches-ignore: branches-ignore:
- 'dependabot/**' # Avoid duplicates: only run the PR, not the push - 'dependabot/**' # Avoid duplicates: only run the PR, not the push
pull_request: pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
workflow_dispatch: workflow_dispatch:
schedule: schedule:
- cron: '0 0 * * 0' # weekly - cron: '0 0 * * 0' # weekly
@@ -22,14 +23,77 @@ defaults:
working-directory: repo working-directory: repo
concurrency: concurrency:
# At most 1 job per branch. Auto cancel on pull requests and on all forks # At most 1 job per branch. Auto cancel on pull requests and on all forks.
group: ${{ github.workflow }}-${{ github.ref }} # Of the pull request events, only one that starts a run, or that removes the
# 'pr: regression' label to cancel one, shares the group. The rest are
# skipped (a change of another label, or a payload that does not carry the
# label, like an 'opened' delivered after it was added), and a skipped run
# must not cancel the one in progress, so they are given a unique group.
# This way adding the label starts a run, removing it cancels the in-progress
# run, and anything else has no effect. Note such an event still creates a
# run of this workflow, which is skipped, and would then hide the one in
# progress on the pull request. 'pr-run-cleanup.yml' deletes those.
# Key pull request runs on the number: 'github.ref' is the base branch once
# the pull request is closed, so unlabel events would cancel base runs.
# NOTE: The last part of the group expression must be kept in sync with the
# 'start' job condition below: it maps to 'shared' exactly those events that
# satisfy that condition, plus the 'unlabeled' event of the gating label.
group: |-
${{
github.workflow
}}-${{
(github.event_name == 'pull_request')
&& format('pr-{0}', github.event.number) || github.ref
}}-${{
(github.event_name == 'pull_request'
&& (((github.event.action == 'labeled' || github.event.action == 'unlabeled')
&& github.event.label.name != 'pr: regression')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& !contains(github.event.pull_request.labels.*.name, 'pr: regression'))))
&& github.run_id || 'shared'
}}
cancel-in-progress: ${{ github.event_name == 'pull_request' || github.repository != 'verilator/verilator' }} cancel-in-progress: ${{ github.event_name == 'pull_request' || github.repository != 'verilator/verilator' }}
jobs: jobs:
start:
name: Start
# Only run pull request jobs if labelled as needing a regression run.
# Always run push, workflow dispatch and scheduled jobs
if: |
(github.event_name == 'pull_request'
&& ((github.event.action == 'labeled'
&& github.event.label.name == 'pr: regression')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& contains(github.event.pull_request.labels.*.name, 'pr: regression')))) ||
(github.event_name == 'push') ||
(github.event_name == 'workflow_dispatch') ||
(github.event_name == 'schedule')
runs-on: ubuntu-slim
defaults:
run:
working-directory: . # The workflow default is not checked out here
steps:
- name: Enabled
run: echo "Regression is enabled for this run"
- name: Save the pull request number
if: ${{ github.event_name == 'pull_request' }}
run: echo "${{ github.event.number }}" > pr-number.txt
- name: Upload the pull request number
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: pr-number
path: pr-number.txt
overwrite: true
build-2604-gcc: build-2604-gcc:
name: Build | 26.04 | gcc name: Build | 26.04 | gcc
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: gcc cc: gcc
@@ -38,6 +102,7 @@ jobs:
build-2604-clang: build-2604-clang:
name: Build | 26.04 | clang name: Build | 26.04 | clang
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: clang cc: clang
@@ -47,6 +112,7 @@ jobs:
build-2404-gcc: build-2404-gcc:
name: Build | 24.04 | gcc name: Build | 24.04 | gcc
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: gcc cc: gcc
@@ -55,6 +121,7 @@ jobs:
build-2404-clang: build-2404-clang:
name: Build | 24.04 | clang name: Build | 24.04 | clang
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: clang cc: clang
@@ -63,22 +130,25 @@ jobs:
build-2204-gcc: build-2204-gcc:
name: Build | 22.04 | gcc name: Build | 22.04 | gcc
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: gcc cc: gcc
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
sha: ${{ github.sha }} sha: ${{ github.sha }}
build-macos-15-clang: build-macos-26-clang:
name: Build | macos-15 | clang name: Build | macos-26 | clang
needs: start
uses: ./.github/workflows/reusable-build.yml uses: ./.github/workflows/reusable-build.yml
with: with:
cc: clang cc: clang
runs-on: macos-15 runs-on: macos-26
sha: ${{ github.sha }} sha: ${{ github.sha }}
build-windows: build-windows:
name: Build | windows-2025-vs2026 | msvc name: Build | windows-2025-vs2026 | msvc
needs: start
runs-on: windows-2025-vs2026 runs-on: windows-2025-vs2026
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
@@ -167,4 +237,29 @@ jobs:
lint-py: lint-py:
name: Lint Python name: Lint Python
needs: start
uses: ./.github/workflows/reusable-lint-py.yml uses: ./.github/workflows/reusable-lint-py.yml
passed:
name: Regression tests passed # This job is used as a required status check on PRs
if: ${{ !cancelled() && needs.start.result != 'skipped' }}
needs:
- start
- build-2604-gcc
- build-2604-clang
- build-2404-gcc
- build-2404-clang
- build-2204-gcc
- build-macos-26-clang
- build-windows
- test-2604-gcc
- test-2604-clang
- test-2404-gcc
- test-2404-clang
- test-2204-gcc
- lint-py
runs-on: ubuntu-slim
steps:
- name: Check all needed jobs succeeded
working-directory: .
run: jq -e 'all(.result == "success")' <<< '${{ toJSON(needs) }}'
+2 -1
View File
@@ -36,7 +36,8 @@ jobs:
- name: Set up Python venv - name: Set up Python venv
uses: ./repo/.github/actions/setup-venv uses: ./repo/.github/actions/setup-venv
with: with:
save: ${{ github.ref == 'refs/heads/master' }} # Check the event too: 'github.ref' is the base branch on a closed pull request
save: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/master' }}
- name: Lint - name: Lint
run: |- run: |-
+3 -2
View File
@@ -92,7 +92,8 @@ jobs:
- name: Set up Python venv - name: Set up Python venv
uses: ./repo/.github/actions/setup-venv uses: ./repo/.github/actions/setup-venv
with: with:
save: ${{ github.ref == 'refs/heads/master' }} # Check the event too: 'github.ref' is the base branch on a closed pull request
save: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/master' }}
- name: Test - name: Test
id: run-test id: run-test
@@ -125,7 +126,7 @@ jobs:
path: ${{ env.CCACHE_DIR }} path: ${{ env.CCACHE_DIR }}
key: ${{ steps.ccache.outputs.key }} key: ${{ steps.ccache.outputs.key }}
# Keep master's cache around longer; PR/branch caches churn faster # Keep master's cache around longer; PR/branch caches churn faster
retention-days: ${{ github.ref == 'refs/heads/master' && 7 || 3 }} retention-days: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/master' && 7 || 3 }}
- name: Fail job if a test failed - name: Fail job if a test failed
if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }} if: ${{ steps.run-test.outcome == 'failure' && !cancelled() }}
+59 -15
View File
@@ -22,8 +22,36 @@ defaults:
shell: bash shell: bash
concurrency: concurrency:
# At most 1 job per branch. Auto cancel all but scheduled jobs # At most 1 job per branch. Auto cancel all but scheduled jobs.
group: ${{ github.workflow }}-${{ github.ref }} # Of the pull request events, only one that starts a run, or that removes the
# 'pr: rtlmeter' label to cancel one, shares the group. The rest are
# skipped (a change of another label, or a payload that does not carry the
# label, like an 'opened' delivered after it was added), and a skipped run
# must not cancel the one in progress, so they are given a unique group.
# This way adding the label starts a run, removing it cancels the in-progress
# run, and anything else has no effect. Note such an event still creates a
# run of this workflow, which is skipped, and would then hide the one in
# progress on the pull request. 'pr-run-cleanup.yml' deletes those.
# Key pull request runs on the number: 'github.ref' is the base branch once
# the pull request is closed, so unlabel events would cancel base runs.
# NOTE: The last part of the group expression must be kept in sync with the
# 'start' job condition below: it maps to 'shared' exactly those events that
# satisfy that condition, plus the 'unlabeled' event of the gating label.
group: |-
${{
github.workflow
}}-${{
(github.event_name == 'pull_request')
&& format('pr-{0}', github.event.number) || github.ref
}}-${{
(github.event_name == 'pull_request'
&& (((github.event.action == 'labeled' || github.event.action == 'unlabeled')
&& github.event.label.name != 'pr: rtlmeter')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& !contains(github.event.pull_request.labels.*.name, 'pr: rtlmeter'))))
&& github.run_id || 'shared'
}}
cancel-in-progress: ${{ github.event_name != 'schedule' }} cancel-in-progress: ${{ github.event_name != 'schedule' }}
jobs: jobs:
@@ -36,14 +64,30 @@ jobs:
(github.event_name == 'schedule' (github.event_name == 'schedule'
&& vars.ENABLE_SCHEDULED_JOBS == 'true') || && vars.ENABLE_SCHEDULED_JOBS == 'true') ||
(github.event_name == 'pull_request' (github.event_name == 'pull_request'
&& contains(github.event.pull_request.labels.*.name, 'pr: rtlmeter')) || && ((github.event.action == 'labeled'
&& github.event.label.name == 'pr: rtlmeter')
|| (github.event.action != 'labeled'
&& github.event.action != 'unlabeled'
&& contains(github.event.pull_request.labels.*.name, 'pr: rtlmeter')))) ||
(github.event_name == 'workflow_dispatch') || (github.event_name == 'workflow_dispatch') ||
(github.event_name == 'push') (github.event_name == 'push')
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
outputs: outputs:
old-sha: ${{ steps.start.outputs.old-sha }} old-sha: ${{ steps.start.outputs.old-sha }}
cases: ${{ steps.cases.outputs.cases }} cases: ${{ steps.cases.outputs.cases }}
steps: steps:
- name: Save the pull request number
if: ${{ github.event_name == 'pull_request' }}
run: echo "${{ github.event.number }}" > pr-number.txt
- name: Upload the pull request number
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: pr-number
path: pr-number.txt
overwrite: true
- name: Checkout - name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
@@ -70,7 +114,7 @@ jobs:
cc: gcc cc: gcc
ccwarn: false ccwarn: false
install: true install: true
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
sha: ${{ github.sha }} sha: ${{ github.sha }}
build-clang-new: build-clang-new:
@@ -81,7 +125,7 @@ jobs:
cc: clang cc: clang
ccwarn: false ccwarn: false
install: true install: true
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
sha: ${{ github.sha }} sha: ${{ github.sha }}
build-gcc-old: build-gcc-old:
@@ -93,7 +137,7 @@ jobs:
cc: gcc cc: gcc
ccwarn: false ccwarn: false
install: true install: true
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
sha: ${{ needs.start.outputs.old-sha }} sha: ${{ needs.start.outputs.old-sha }}
build-clang-old: build-clang-old:
@@ -105,7 +149,7 @@ jobs:
cc: clang cc: clang
ccwarn: false ccwarn: false
install: true install: true
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
sha: ${{ needs.start.outputs.old-sha }} sha: ${{ needs.start.outputs.old-sha }}
run-gcc: run-gcc:
@@ -121,7 +165,7 @@ jobs:
uses: ./.github/workflows/reusable-rtlmeter-run.yml uses: ./.github/workflows/reusable-rtlmeter-run.yml
with: with:
tag: gcc tag: gcc
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
cc: gcc cc: gcc
verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }} verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }}
verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }} verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }}
@@ -148,7 +192,7 @@ jobs:
uses: ./.github/workflows/reusable-rtlmeter-run.yml uses: ./.github/workflows/reusable-rtlmeter-run.yml
with: with:
tag: clang tag: clang
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
cc: clang cc: clang
verilator-archive-new: ${{ needs.build-clang-new.outputs.archive }} verilator-archive-new: ${{ needs.build-clang-new.outputs.archive }}
verilator-archive-old: ${{ needs.build-clang-old.outputs.archive }} verilator-archive-old: ${{ needs.build-clang-old.outputs.archive }}
@@ -175,7 +219,7 @@ jobs:
uses: ./.github/workflows/reusable-rtlmeter-run.yml uses: ./.github/workflows/reusable-rtlmeter-run.yml
with: with:
tag: gcc-hier tag: gcc-hier
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
cc: gcc cc: gcc
verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }} verilator-archive-new: ${{ needs.build-gcc-new.outputs.archive }}
verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }} verilator-archive-old: ${{ needs.build-gcc-old.outputs.archive }}
@@ -200,7 +244,7 @@ jobs:
(github.event_name == 'pull_request' && (contains(needs.*.result, 'success') && !contains(needs.*.result, 'failure'))) (github.event_name == 'pull_request' && (contains(needs.*.result, 'success') && !contains(needs.*.result, 'failure')))
|| (github.event_name != 'pull_request' && (contains(needs.*.result, 'success') || contains(needs.*.result, 'failure'))) || (github.event_name != 'pull_request' && (contains(needs.*.result, 'success') || contains(needs.*.result, 'failure')))
)}} )}}
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
outputs: outputs:
# The run tags, derived from the 'run-*' dependency job names # The run tags, derived from the 'run-*' dependency job names
run-tags: ${{ steps.tags.outputs.tags }} run-tags: ${{ steps.tags.outputs.tags }}
@@ -283,7 +327,7 @@ jobs:
# dashboard UI currently assumes there is only one data point per # dashboard UI currently assumes there is only one data point per
# calendar day. Results from reruns can be imported manually if needed. # calendar day. Results from reruns can be imported manually if needed.
if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && contains(needs.*.result, 'success') && !cancelled() }} if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && contains(needs.*.result, 'success') && !cancelled() }}
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
steps: steps:
- name: Download combined results - name: Download combined results
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
@@ -334,7 +378,7 @@ jobs:
name: Prepare Pull Request results name: Prepare Pull Request results
needs: combine-results needs: combine-results
if: ${{ github.event_name == 'pull_request' && github.repository == 'verilator/verilator' && needs.combine-results.result == 'success' }} if: ${{ github.event_name == 'pull_request' && github.repository == 'verilator/verilator' && needs.combine-results.result == 'success' }}
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
permissions: permissions:
actions: read actions: read
steps: steps:
@@ -391,7 +435,7 @@ jobs:
name: Create issue on failure name: Create issue on failure
needs: publish-scheduled-results needs: publish-scheduled-results
if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && failure() && !cancelled() }} if: ${{ github.event_name == 'schedule' && github.repository == 'verilator/verilator' && github.run_attempt == 1 && failure() && !cancelled() }}
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
steps: steps:
# Creating issues requires elevated privilege # Creating issues requires elevated privilege
- name: Generate access token - name: Generate access token
+7 -6
View File
@@ -72,7 +72,7 @@ top-of-file comment.
- Build in the source tree: `autoconf && ./configure && make -j8`. Configure with - Build in the source tree: `autoconf && ./configure && make -j8`. Configure with
`--enable-ccwarn` so a new compiler warning stops the build. `--enable-ccwarn` so a new compiler warning stops the build.
- Run one test from the repository root: `test_regress/t/t_<name>.py`. - Run one test from the repository root: `test_regress/t/t_<name>.py`.
- Run the full regression with `make test`. The complete suite requires - Run the full regression tests with `make test`. The complete suite requires
configuring with `--enable-longtests` (works on every OS, including macOS). configuring with `--enable-longtests` (works on every OS, including macOS).
______________________________________________________________________ ______________________________________________________________________
@@ -93,8 +93,8 @@ ______________________________________________________________________
- [ ] Ran `make format` (clang-format), `make cppcheck`, and `make lint-py`; - [ ] Ran `make format` (clang-format), `make cppcheck`, and `make lint-py`;
self-reviewed the diff for leftover debug code, stale comments, and self-reviewed the diff for leftover debug code, stale comments, and
copy-paste errors. copy-paste errors.
- [ ] Ran the full regression on at least one OS before submitting. Partial runs - [ ] Ran the full regression tests on at least one OS before submitting. Partial
are fine during development, but the submitted PR is expected to pass every runs are fine during development, but the submitted PR is expected to pass every
test. test.
- [ ] Validated the code changes against recommendations in the AGENTS.md files. - [ ] Validated the code changes against recommendations in the AGENTS.md files.
- [ ] Did not edit `docs/CONTRIBUTORS` (humans only) or `Changes` (maintainer - [ ] Did not edit `docs/CONTRIBUTORS` (humans only) or `Changes` (maintainer
@@ -143,6 +143,7 @@ The API you choose determines which test must accompany the change.
## Commits ## Commits
- Subject line is short and imperative and conventionally ends with the PR number: - Subject line is short and imperative and conventionally ends with the
`Support property case (#7721)`. A body is optional and common for non-trivial issue number being fixed. No need for the PR number this will be added
changes. during git squash. e.g `Support property case (#7721)`. The commit body message is
optional and common for non-trivial changes.
+38 -10
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 cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project( project(
Verilator Verilator
VERSION 5.052 VERSION 5.053
HOMEPAGE_URL https://verilator.org HOMEPAGE_URL https://verilator.org
LANGUAGES CXX LANGUAGES CXX
) )
@@ -49,19 +49,18 @@ if(WIN32)
if(DEFINED ENV{WIN_FLEX_BISON}) if(DEFINED ENV{WIN_FLEX_BISON})
set(WIN_FLEX_BISON "$ENV{WIN_FLEX_BISON}") set(WIN_FLEX_BISON "$ENV{WIN_FLEX_BISON}")
endif() endif()
if(IS_DIRECTORY ${WIN_FLEX_BISON})
list(APPEND CMAKE_PREFIX_PATH ${WIN_FLEX_BISON})
else()
message(
FATAL_ERROR
"The path specified by WIN_FLEX_BISON environment variable is not an existing folder: ${WIN_FLEX_BISON}"
)
endif()
if(NOT WIN_FLEX_BISON) if(NOT WIN_FLEX_BISON)
message( message(
FATAL_ERROR FATAL_ERROR
"Please install https://github.com/lexxmark/winflexbison and set WIN_FLEX_BISON environment variable. Please use install cmake target after a successful build." "Please install https://github.com/lexxmark/winflexbison and set WIN_FLEX_BISON environment variable. Please use install cmake target after a successful build."
) )
elseif(IS_DIRECTORY "${WIN_FLEX_BISON}")
list(APPEND CMAKE_PREFIX_PATH "${WIN_FLEX_BISON}")
else()
message(
FATAL_ERROR
"The path specified by WIN_FLEX_BISON environment variable is not an existing folder: ${WIN_FLEX_BISON}"
)
endif() endif()
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
endif() endif()
@@ -93,7 +92,36 @@ if(OBJCACHE_ENABLED)
endif() endif()
find_package(BISON) find_package(BISON)
find_package(FLEX) # Prefer the header installed with the selected generator, including through
# symlinks such as Homebrew's bin/flex. Explicit cache entries take precedence.
find_program(
FLEX_EXECUTABLE
NAMES flex win-flex win_flex
DOC "Path to the flex executable"
)
if(NOT FLEX_EXECUTABLE)
message(
FATAL_ERROR
"Cannot find Flex; set FLEX_EXECUTABLE to its executable path"
)
endif()
get_filename_component(flex_real_executable "${FLEX_EXECUTABLE}" REALPATH)
get_filename_component(flex_bin_dir "${flex_real_executable}" DIRECTORY)
find_path(
FLEX_INCLUDE_DIR
FlexLexer.h
PATHS "${flex_bin_dir}/../include" "${flex_bin_dir}"
NO_DEFAULT_PATH
DOC "Path to the flex headers"
)
find_path(FLEX_INCLUDE_DIR FlexLexer.h)
if(NOT FLEX_INCLUDE_DIR)
message(
FATAL_ERROR
"Cannot find FlexLexer.h; set FLEX_INCLUDE_DIR to its directory"
)
endif()
mark_as_advanced(FLEX_EXECUTABLE FLEX_INCLUDE_DIR)
# Build # Build
#set_property(GLOBAL PROPERTY JOB_POOLS one_job=1) #set_property(GLOBAL PROPERTY JOB_POOLS one_job=1)
+151 -3
View File
@@ -10,6 +10,154 @@ The changes in each Verilator version are described below. The
contributors that suggested or implemented a given issue are shown in []. Thanks! 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).
* Add `--coverage-max-bins` and `--coverage-max-real-bins` (#8499). [Marco Bartoli]
* Remove deprecated `--structs-packed`.
* Change internal module inlining to after V3Scope (#1539) (#8453). [Geza Lore, Testorrent USA, Inc.]
* Change JSON dumps to suppress empty strings, etc.
* Change `--protect` to use SHA512 per IEEE 1735.
* Support multi-dimensional module instance arrays (#2675) (#8476). [Geza Lore, Testorrent USA, Inc.]
* 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 nested array and associative array element member access in constraint (#8244). [Kamil Danecki, 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]
* Support `illegal_bins` and `ignore_bins` for cross bins (#8372). [Marco Bartoli]
* Support `iff` in covergroup bins (#8406) (#8407). [Marco Bartoli]
* Support enclosing cross identifiers in bin selections (#8416). [Marco Bartoli]
* Support dynamic cross layouts and fix covergroup bin exclusions priority (#8424). [Marco Bartoli]
* Support automatic bins for enum coverpoints (#8462) (#8464). [David Harris]
* Support empty queue literals as function and task arguments (#8501) (#8502). [Francesco Urbani]
* Support covergroup fixed-size bin arrays (#8514). [Marco Bartoli]
* Support wildcard bins (#8516). [Marco Bartoli]
* Support coverpoint bin `with` filters (#8518). [Marco Bartoli]
* Support waiting on clocking block event through modport (#8403) (#8530). [Saqib Khan]
* Optimize NBA shadow copies for partial writes (#8266). [Artur Bieniek, Antmicro Ltd.]
* Optimize recursion depth in DFG circular driver tracing (#8272). [Drew Risinger]
* 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 DFG vertex input edge storage (#8313). [Geza Lore, Testorrent USA, Inc.]
* Optimize successive whole-element array assignments (#8316). [Patrick Creighton]
* 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.]
* Optimize NBA assertions by keeping all writers before readers (#8326). [Artur Bieniek, Antmicro Ltd.]
* Optimize wide four-state constant operations in V3Number (#8333). [CatalpaEel]
* Optimize split pass (#8385). [Geza Lore, Testorrent USA, Inc.]
* Optimize covergroup bin metadata lookup (#8404). [Marco Bartoli]
* Optimize dead ports (#8426). [Geza Lore, Testorrent USA, Inc.]
* Optimize instance port connections by aliasing rather than by assignment (#8427). [Geza Lore, Testorrent USA, Inc.]
* Optimize DFG temporary sharing across module instances (#8456). [Michael Bedford Taylor]
* Optimize modport resolution with many interface instances (#8496) (#8497). [Deanyone Su]
* Optimize covergroup bin generation (#8499). [Marco Bartoli]
* Optimize through-package variables in DFG. [Geza Lore, Testorrent USA, Inc.]
* Optimize assertion control queries (#8547). [Artur Bieniek, Antmicro Ltd.]
* Optimize sched-initial/static/final split to once per sub-function (#8549). [Krzysztof Bieganski, Antmicro Ltd.]
* Optimize exhaustive case if-chains with an unconditional else (#8554). [Nick Brereton]
* Move module inlining after V3Scope (#8453) (#1539). [Geza Lore, Testorrent USA, Inc.]
* Fix array slice crash when used as a bare value (#5132) (#8262). [Marco Frank]
* Fix function reference into generate case block (#5312) (#8532). [Hongseok Choi]
* Fix pattern type matching against nested aggregates (#7304).
* Fix covergroup-internal references multi-threaded ordering (#7779) (#8255). [Matthew Ballance]
* Fix typedef through a parameterized sub-interface (#7943 repair) (#8124) (#8128). [em2machine]
* 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 real values and unenforced rand/randc type-eligibility rules in constraints (#8273) (#8275) (#8276). [Aditya Shevade]
* 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 cache of boundary vertices in DFG cycle-breaking (#8308). [Shriyans Sahoo]
* Fix property and sequence line coverage (#8314). [Andrii, Antmicro Ltd.]
* Fix wide typed pattern formatting primitives (#8325). [Marco Bartoli]
* Fix circular class inheritance hang (#8324) (#8545). [Saqib Khan]
* Fix V3InstrCount with recursive functions (#8327) (#8361). [Geza Lore, Testorrent USA, Inc.]
* Fix parameterized class nested in a parameterized interface (#8328) (#8329). [Kristof Marien]
* Fix printing signed enums and wide enum names (#8338). [Marco Bartoli]
* Fix ignoring cross_auto_bin_max instead of hard error (#8340). [Marco Bartoli]
* Fix wide enum names (#8344) (#8362) (#8365) (#8366). [Marco Bartoli]
* Fix class-qualified self reference to a typedef in a parameterized class (#8348) (#8349). [Dragon-Git]
* Fix crash when elaborating a covergroup option through a forward class (#8351) (#8352). [Kristof Marien]
* Fix missing implicit net for bufif/notif/nmos/pmos data input (#8353) (#8354). [Mike Murunov]
* Fix %p format on class members (#8360). [Marco Bartoli]
* Fix Docker compilation with --trace-fst (#8370). [Giuseppe Manzoni]
* Fix NFA rings for bounded SVA delays (#8371). [Artur Bieniek, Antmicro Ltd.]
* Fix DFG driver tracing of partial assignments (#8377) (#8397). [Geza Lore, Testorrent USA, Inc.]
* Fix unique constraint with multiple arrays (#8378). [Kornel Uriasz, Antmicro Ltd.]
* Fix access types of refs and inouts (#8380). [Igor Zaworski, Antmicro Ltd.]
* Fix copy constructor of classes with constraints (#8381). [Ryszard Rozak, Antmicro Ltd.]
* Fix C++ clang 18 compiler with -fno-slp-vectorize (#8383). [Geza Lore, Testorrent USA, Inc.]
* Fix reevaluation of method calls in wait conditions (#8391). [Jesús Alberto López Soto]
* Fix weighted dist constraints in presence of other constraints (#8392). [Kornel Uriasz, Antmicro Ltd.]
* Fix error on non-existent interface ports (#8393). [Andrii Andrieiev, Antmicro Ltd.]
* Fix compile-time blowup forcing large unpacked arrays (#8395). [Jonathan Drolet]
* Fix false MULTIDRIVEN for writes in tasks never executed (#8401). [Artur Bieniek, Antmicro Ltd.]
* Fix $c() inside a DPI export (#8402). [Nazar Zibilyuk]
* Fix covergroup constructor initialization (#8410). [Marco Bartoli]
* Fix issues with configuring flex during build on MacOS (#8412). [Kaleb Barrett]
* Fix interface modport resolution against non-scope symbol tables (#8420). [Geza Lore, Testorrent USA, Inc.]
* Fix FSM coverage hierarchy being emitted as an absolute path (#8421). [Geza Lore, Testorrent USA, Inc.]
* Fix MSYS header include search to be after toolchain headers (#8422). [Kaleb Barrett]
* Fix losing coverage declarations if first in a module body (#8425). [Geza Lore, Testorrent USA, Inc.]
* Fix force read update sensitivity performance (#8431) (#8433). [Jonathan Drolet]
* Fix streaming dynamic arrays with different element types failing to compile (#8434) (#8435). [Kristof Marien]
* Fix internal error when task without C function is left in the tree (#8437). [Artur Bieniek, Antmicro Ltd.]
* Fix nested random dynamic arrays internal error (#8438) (#8439). [Kristof Marien]
* Fix elaboration of classes reached through deferred references (#8440) (#8510). [Ethan Sifferman]
* Fix VPI discoverability of forceable unpacked struct members (#8445). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix compound types in anonymous structs (#8449). [Artur Bieniek, Antmicro Ltd.]
* Fix internal error on covergroup in a specialized parameterized class (#8450) (#8452). [David Harris]
* Fix data race in type handling (#8451). [Tynan McAuley]
* Fix $sscanf/$fscanf returning -1 after a partial match (#8457) (#8459). [David Harris]
* Fix covergroup '0/'1 bin values not filling to coverpoint width (#8458) (#8460). [David Harris]
* Fix embedded covergroup calling an enclosing class method (#8461) (#8463). [David Harris]
* Fix unsized condition not reduced to one bit (#8465) (#8466). [David Harris]
* Fix comma emission for nested JSON scopes (#8469) (#8474). [Noam Gallmann]
* Fix model failing when host has fewer cores than --threads (#8475). [Artur Bieniek, Antmicro Ltd.]
* Fix `##0` synchronous drive overriding later drives (#8482). [Marco Bartoli]
* Fix disable fork and wait fork affecting pending NBA updates (#8483). [Marco Bartoli]
* Fix nonblocking event trigger never firing in suspendable processes (#8485). [Marco Bartoli]
* Fix setting status of finished process immediately (#8503). [Ryszard Rozak, Antmicro Ltd.]
* Fix constant oversized shifts in V3Premit. [Geza Lore, Testorrent USA, Inc.]
* Fix semaphore waiting on killed process (#8505). [Ryszard Rozak, Antmicro Ltd.]
* Fix mis-setting of process state from FINISHED to KILLED (#8506). [Ryszard Rozak, Antmicro Ltd.]
* Fix rand_mode(0) not preserving dynamic array of struct values used in a dist constraint (#8508) (#8509). [Kristof Marien]
* Fix duplicate struct declaration when inlining (#8515). [Ethan Sifferman]
* Fix bear version detection if uninstalled. [Geza Lore, Testorrent USA, Inc.]
* Fix enums inside randomize() with (#8517). [Kornel Uriasz, Antmicro Ltd.]
* Fix emitting public parameters (#8520). [Geza Lore, Testorrent USA, Inc.]
* Fix verilator_coverage report to align with get_coverage() (#8522) (#8524) (#8531). [Marco Bartoli]
* Fix verilator_coverage reports rounding incomplete coverage to 100.0% (#8523). [Jordan Carlin]
* Fix extern class method rejects matching virtual interface argument type (#8525) (#8526). [Kristof Marien]
* Fix of queue pop return value access (#8529) (#8511). [Igor Zaworski, Antmicro Ltd.]
* Fix derived class construction resets inherited per-instance rand_mode state (#8527) (#8528). [Kristof Marien]
* Fix crash on class handle passed to string port (#8536) (#8538). [Noam Gallmann]
* Fix stack depth calculations over calls (#8544). [Nick Brereton]
* Fix cascading errors after unsupported class dot reference in parameter value (#8551). [Martijn Wobbes, Antmicro Ltd.]
* Fix vpi_put_value crash on NULL value pointer (#8553). [Nick Brereton]
* Fix --hierarchical parameter specialization name collisions (#8555) (#8565). [Marco Bartoli]
* Fix dehash of a suffix after a hashed name (#8556). [Ethan Sifferman]
Verilator 5.052 2026-09-05 Verilator 5.052 2026-09-05
========================== ==========================
@@ -1855,7 +2003,7 @@ Verilator 5.016 2023-09-16
* Optimize Verilator executable size by refactoring error reporting routines (#4446). [Anthony Donlon] * 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 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] * 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 t_dist_cppstyle Perl performance issue (#4085). [Srinivasan Venkataramanan]
* Fix using type in parameterized classes without #() (#4281) (#4440). [Anthony Donlon] * Fix using type in parameterized classes without #() (#4281) (#4440). [Anthony Donlon]
* Fix false INFINITELOOP on forever..mailbox.get() (#4323). [Srinivasan Venkataramanan] * Fix false INFINITELOOP on forever..mailbox.get() (#4323). [Srinivasan Venkataramanan]
@@ -3640,7 +3788,7 @@ Verilator 3.880 2015-12-19
* Fix model restore crash. (#1013) [Jason McMullan] * Fix model restore crash. (#1013) [Jason McMullan]
* Fix arrayed instances to unpacked of same size. (#1015) [Varun Koyyalagunta] * Fix arrayed instances to unpacked of same size. (#1015) [Varun Koyyalagunta]
* Fix slices of unpacked arrays with non-zero LSBs. * 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 Verilator 3.878 2015-11-01
@@ -3730,7 +3878,7 @@ Verilator 3.870 2015-02-12
* Suppress COMBDLY when inside always_latch. (#864) [Iztok Jeras] * Suppress COMBDLY when inside always_latch. (#864) [Iztok Jeras]
* Support cast operator with expression size. (#865) [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 member select error broke in 3.868. (#867) [Iztok Jeras]
* Fix $sccanf from string. (#866) [David Pierce] * Fix $sccanf from string. (#866) [David Pierce]
* Fix VM_PARALLEL_BUILDS broke in 3.868. (#870) [Hiroki Honda] * Fix VM_PARALLEL_BUILDS broke in 3.868. (#870) [Hiroki Honda]
+8 -4
View File
@@ -710,8 +710,10 @@ override undefine COVERAGE_BASE # Use the above variabels instead
# 'fastcov' setup # 'fastcov' setup
FASTCOV := nodist/fastcov.py FASTCOV := nodist/fastcov.py
FASTCOV_OPT := -j $(shell nproc) FASTCOV_JOBS ?= $(shell nproc)
FASTCOV_OPT += --gcov $(GCOV) FASTCOV_OPT := -j $(FASTCOV_JOBS)
# gcov does not free memory between files, so run it in batches via a wrapper
FASTCOV_OPT += --gcov nodist/gcov_batch
FASTCOV_OPT += --lcov FASTCOV_OPT += --lcov
FASTCOV_OPT += --process-gcno FASTCOV_OPT += --process-gcno
FASTCOV_OPT += --branch-coverage FASTCOV_OPT += --branch-coverage
@@ -747,6 +749,7 @@ FASTCOV_OPT += BROKEN_RTN
FASTCOV_OPT += NUM_ASSERT FASTCOV_OPT += NUM_ASSERT
FASTCOV_OPT += SELF_CHECK FASTCOV_OPT += SELF_CHECK
FASTCOV_OPT += UASSERT FASTCOV_OPT += UASSERT
FASTCOV_OPT += UDEBUGONLY
FASTCOV_OPT += UINFO FASTCOV_OPT += UINFO
FASTCOV_OPT += assert FASTCOV_OPT += assert
FASTCOV_OPT += 'if (VL_UNCOVERABLE' FASTCOV_OPT += 'if (VL_UNCOVERABLE'
@@ -762,6 +765,7 @@ GENHTML_OPT += --rc branch_coverage=1
GENHTML_OPT += --rc genhtml_hi_limit=100 GENHTML_OPT += --rc genhtml_hi_limit=100
GENHTML_OPT += --ignore-errors negative GENHTML_OPT += --ignore-errors negative
GENHTML_OPT += --ignore-errors inconsistent GENHTML_OPT += --ignore-errors inconsistent
GENHTML_OPT += --ignore-errors empty
ifeq ($(COVERAGE_REF_BASE),) ifeq ($(COVERAGE_REF_BASE),)
GENHTML_OPT += --header-title "Code coverage for Verilator $(shell git describe --dirty)" GENHTML_OPT += --header-title "Code coverage for Verilator $(shell git describe --dirty)"
else else
@@ -783,7 +787,7 @@ $(COVERAGE_DIR)/verilator.info: $(GCNO_FILES) $(GCDA_FILES)
@echo "# fastcov: combining all .gcda files into lcov .info" @echo "# fastcov: combining all .gcda files into lcov .info"
@echo "####################################################################" @echo "####################################################################"
@mkdir -p $(COVERAGE_DIR) @mkdir -p $(COVERAGE_DIR)
/usr/bin/time -f "That took %E" \ GCOV=$(GCOV) /usr/bin/time -f "That took %E" \
$(FASTCOV) $(FASTCOV_OPT) --output $@ $(FASTCOV) $(FASTCOV_OPT) --output $@
# Filter combined .info file for patch coverage # Filter combined .info file for patch coverage
@@ -793,8 +797,8 @@ $(COVERAGE_DIR)/verilator-patch.info: $(COVERAGE_DIR)/verilator.info
@echo "####################################################################" @echo "####################################################################"
rm -f $(COVERAGE_DIR)/empty-patch rm -f $(COVERAGE_DIR)/empty-patch
git diff $(COVERAGE_REF_BASE) -- include src > $(COVERAGE_DIR)/filter.patch git diff $(COVERAGE_REF_BASE) -- include src > $(COVERAGE_DIR)/filter.patch
[ -s $(COVERAGE_DIR)/filter.patch ] || touch $(COVERAGE_DIR)/empty-patch
$(FASTCOV) -C $^ --lcov -o $@ --diff-filter $(COVERAGE_DIR)/filter.patch $(FASTCOV) -C $^ --lcov -o $@ --diff-filter $(COVERAGE_DIR)/filter.patch
grep -q '^DA:' $@ || touch $(COVERAGE_DIR)/empty-patch
# Build coverage report # Build coverage report
$(COVERAGE_DIR)/report/index.html: $(COVERAGE_DIR)/verilator$(if $(COVERAGE_REF_BASE),-patch).info $(COVERAGE_DIR)/report/index.html: $(COVERAGE_DIR)/verilator$(if $(COVERAGE_REF_BASE),-patch).info
+11 -11
View File
@@ -23,8 +23,8 @@
.. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator .. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator
:target: https://hub.docker.com/r/verilator/verilator :target: https://hub.docker.com/r/verilator/verilator
.. |badge7| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/build-test.yml?branch=master&label=regressions .. |badge7| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/regression.yml?branch=master&label=regression
:target: https://github.com/verilator/verilator/actions/workflows/build-test.yml :target: https://github.com/verilator/verilator/actions/workflows/regression.yml
.. |badge8| image:: https://img.shields.io/github/actions/workflow/status/verilator/verilator/rtlmeter.yml?branch=master&event=schedule&label=benchmarks .. |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 :target: https://verilator.github.io/verilator-rtlmeter-results
@@ -35,7 +35,7 @@ Welcome to Verilator
.. list-table:: .. list-table::
- - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.** - - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.**
- Accepts Verilog or SystemVerilog - Accepts Verilog or SystemVerilog, including UVM
- Performs lint code-quality checks - Performs lint code-quality checks
- Compiles into multithreaded C++, or SystemC - Compiles into multithreaded C++, or SystemC
- Creates JSON to front-end your own tools - Creates JSON to front-end your own tools
@@ -75,14 +75,14 @@ instantiate the model. The resulting Verilated executable performs the
design simulation. Verilator also supports linking Verilator-generated design simulation. Verilator also supports linking Verilator-generated
libraries, optionally encrypted, into other simulators. libraries, optionally encrypted, into other simulators.
Verilator supports all design constructs, most verification constructs, Verilator supports all design constructs, most verification constructs
intra-assignment delays (e.g, `#10`), and events. Tristate-bus (`z`) and including UVM, intra-assignment delays (e.g, `#10`), and events.
unknowns (`x`) are handled in limited contexts, in a special manor for Tristate-bus (`z`) and unknowns (`x`) are handled in limited contexts, in a
performance. It currently may not be the best choice if you are expecting a special manor for performance. It currently may not be the best choice if
full-featured replacement for a closed-source Verilog simulator, performing you are expecting a full-featured replacement for a closed-source Verilog
SDF annotation, or mixed-signal simulation. However, if you are looking for simulator, performing SDF annotation, or mixed-signal simulation. However,
a path to migrate SystemVerilog to C++/SystemC, or want high-speed if you are looking for a path to migrate SystemVerilog to C++/SystemC, or
simulation, Verilator is the tool for you. want high-speed simulation, Verilator is the tool for you.
Performance Performance
=========== ===========
+2
View File
@@ -419,6 +419,8 @@ detailed descriptions of these arguments.
--coverage-expr-max <value> Maximum permutations allowed for an expression --coverage-expr-max <value> Maximum permutations allowed for an expression
--coverage-fsm Enable FSM state/arc coverage --coverage-fsm Enable FSM state/arc coverage
--coverage-line Enable line coverage --coverage-line Enable line coverage
--coverage-max-bins <value> Maximum bins of an integral coverpoint bins declaration
--coverage-max-real-bins <value> Maximum bins of a real coverpoint bins declaration
--coverage-max-width <width> Maximum array depth for coverage --coverage-max-width <width> Maximum array depth for coverage
--coverage-per-instance Enable per-instance coverage counters --coverage-per-instance Enable per-instance coverage counters
--coverage-toggle Enable toggle coverage --coverage-toggle Enable toggle coverage
+10
View File
@@ -79,6 +79,13 @@ fi
if [ "$OPT_LIGHT_DEBUG" = 1 ]; then if [ "$OPT_LIGHT_DEBUG" = 1 ]; then
CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-light-debug" CONFIGURE_ARGS="$CONFIGURE_ARGS --enable-light-debug"
fi fi
if [ "$(uname -s)" = Darwin ]; then
# Force the use of the Homebrew flex over the one included as part of the
# Apple SDK that's preinstalled on the MacOS Github Action runners.
FLEX_PREFIX=$(brew --prefix flex)
export LEX="$FLEX_PREFIX/bin/flex"
export CPPFLAGS="${CPPFLAGS:+$CPPFLAGS }-I$FLEX_PREFIX/include"
fi
autoconf autoconf
./configure $CONFIGURE_ARGS CXX="$CXX" ./configure $CONFIGURE_ARGS CXX="$CXX"
@@ -88,6 +95,9 @@ autoconf
ccache -z ccache -z
BUILD_START=$SECONDS BUILD_START=$SECONDS
"$MAKE" -j "$NPROC" -k "$MAKE" -j "$NPROC" -k
# Second make to get failures at bottom, to avoid needing to
# scroll up GitHub action logs (to see first errors half-way up)
"$MAKE" -j "$NPROC" -k
ccache -svv ccache -svv
ccache --evict-older-than "$((SECONDS - BUILD_START + 60))s" ccache --evict-older-than "$((SECONDS - BUILD_START + 60))s"
ccache -svv ccache -svv
+33
View File
@@ -98,3 +98,36 @@ SUMMARY_TEMPLATE
echo "Workflow [#${RUN_NUM}](${RUN_URL}) report: [${RUN_ID}](${REPORT_URL})" > ${NOTIFICATION_DIR}/hist.txt echo "Workflow [#${RUN_NUM}](${RUN_URL}) report: [${RUN_ID}](${REPORT_URL})" > ${NOTIFICATION_DIR}/hist.txt
fi fi
###############################################################################
# Create the coverage status
###############################################################################
# 'pr-automation.yml' labels the pull request from this. Note it is only
# written when the coverage could actually be determined, so that a report we
# could not read leaves the labels of the pull request alone.
if [ -f ${COVERAGE_DIR}/empty-patch ]; then
# Nothing to cover, so count it as covered
COVERED=true
else
# Take the counts, and not the percentage, which is rounded. The line reads
# for example " lines......: 100.00% (60 of 60 lines)"
COUNTS=$(sed -nE 's/^ *lines\.*: *[0-9.]+% \(([0-9]+) of ([0-9]+) lines\).*/\1 \2/p' \
${MAKE_LOG} | tail -n 1)
HIT=${COUNTS%% *}
TOTAL=${COUNTS##* }
if [ -z "${TOTAL}" ]; then
echo "Could not read the line coverage from ${MAKE_LOG}" >&2
COVERED=
elif [ "${HIT}" = "${TOTAL}" ]; then
COVERED=true
else
COVERED=false
fi
fi
if [ -n "${COVERED}" ]; then
echo "Line coverage complete: ${COVERED}"
echo "${COVERED}" > coverage-status.txt
fi
+1 -1
View File
@@ -158,7 +158,7 @@ NEXTHIST_TEMPLATE
# 'pr-notification' for a single run, as the artifacts endpoint lists # '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 # artifacts across all run attempts, and a re-run uploads a new one while
# keeping the previous attempt's artifact. # keeping the previous attempt's artifact.
ARTIFACT_IDS=$(gh api "repos/{owner}/{repo}/actions/runs/${RUN_ID}/artifacts" --jq '.artifacts[] | select(.name == "pr-notification") | .id') 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 # Delete them all, so we only notify once
for ARTIFACT_ID in ${ARTIFACT_IDS}; do for ARTIFACT_ID in ${ARTIFACT_IDS}; do
+12
View File
@@ -25,6 +25,8 @@ CASES = {
("BlackParrot:2x2:*", 14), ("BlackParrot:2x2:*", 14),
("BlackParrot:4x4:*", 25), ("BlackParrot:4x4:*", 25),
("Caliptra:default:*", 19), ("Caliptra:default:*", 19),
("HammerBlade:16x8:*", 5),
("HammerBlade:2x1:*", 3),
("HummingbirdV2-E203:default:*", 3), ("HummingbirdV2-E203:default:*", 3),
("HummingbirdV2-E203:nodelay:*", 3), ("HummingbirdV2-E203:nodelay:*", 3),
("NVDLA:*", 29), ("NVDLA:*", 29),
@@ -59,6 +61,8 @@ CASES = {
("BlackParrot:2x2:*", 9), ("BlackParrot:2x2:*", 9),
("BlackParrot:4x4:*", 12), ("BlackParrot:4x4:*", 12),
("Caliptra:default:*", 16), ("Caliptra:default:*", 16),
("HammerBlade:16x8:*", 4),
("HammerBlade:2x1:*", 2),
("HummingbirdV2-E203:default:*", 10), ("HummingbirdV2-E203:default:*", 10),
("HummingbirdV2-E203:nodelay:*", 10), ("HummingbirdV2-E203:nodelay:*", 10),
("NVDLA:*", 28), ("NVDLA:*", 28),
@@ -92,6 +96,8 @@ CASES = {
("BlackParrot:1x1:*", 4), ("BlackParrot:1x1:*", 4),
("BlackParrot:2x2:*", 16), ("BlackParrot:2x2:*", 16),
("BlackParrot:4x4:*", 25), ("BlackParrot:4x4:*", 25),
("HammerBlade:16x8:*", 5),
("HammerBlade:2x1:*", 3),
("NVDLA:*", 25), ("NVDLA:*", 25),
("OpenPiton:16x16:dhry", 16), ("OpenPiton:16x16:dhry", 16),
("OpenPiton:1x1:*", 4), ("OpenPiton:1x1:*", 4),
@@ -110,6 +116,8 @@ CASES_PR = {
("BlackParrot:1x1:*", 5), ("BlackParrot:1x1:*", 5),
("BlackParrot:4x4:*", 25), ("BlackParrot:4x4:*", 25),
("Caliptra:default:*", 19), ("Caliptra:default:*", 19),
("HammerBlade:16x8:*", 5),
("HammerBlade:2x1:*", 3),
("NVDLA:*", 29), ("NVDLA:*", 29),
("HummingbirdV2-E203:default:*", 3), ("HummingbirdV2-E203:default:*", 3),
("HummingbirdV2-E203:nodelay:*", 3), ("HummingbirdV2-E203:nodelay:*", 3),
@@ -142,6 +150,8 @@ CASES_PR = {
("BlackParrot:1x1:*", 5), ("BlackParrot:1x1:*", 5),
("BlackParrot:4x4:*", 12), ("BlackParrot:4x4:*", 12),
("Caliptra:default:*", 16), ("Caliptra:default:*", 16),
("HammerBlade:16x8:*", 4),
("HammerBlade:2x1:*", 2),
("NVDLA:*", 28), ("NVDLA:*", 28),
("HummingbirdV2-E203:default:*", 10), ("HummingbirdV2-E203:default:*", 10),
("HummingbirdV2-E203:nodelay:*", 10), ("HummingbirdV2-E203:nodelay:*", 10),
@@ -173,6 +183,8 @@ CASES_PR = {
"gcc-hier": [ "gcc-hier": [
("BlackParrot:1x1:*", 4), ("BlackParrot:1x1:*", 4),
("BlackParrot:4x4:*", 25), ("BlackParrot:4x4:*", 25),
("HammerBlade:16x8:*", 5),
("HammerBlade:2x1:*", 3),
("NVDLA:*", 25), ("NVDLA:*", 25),
("OpenPiton:16x16:dhry", 16), ("OpenPiton:16x16:dhry", 16),
("OpenPiton:1x1:*", 4), ("OpenPiton:1x1:*", 4),
+1
View File
@@ -29,6 +29,7 @@ RUN apt-get update \
python3 \ python3 \
zlib1g \ zlib1g \
zlib1g-dev \ zlib1g-dev \
liblz4-dev \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
+18 -1
View File
@@ -12,7 +12,7 @@
# Then 'make maintainer-dist' # Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel]) #AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.052 2026-09-05], AC_INIT([Verilator],[5.053 devel],
[https://verilator.org], [https://verilator.org],
[verilator],[https://verilator.org]) [verilator],[https://verilator.org])
@@ -264,6 +264,7 @@ fi
python3_version=$($PYTHON3 --version | head -1) python3_version=$($PYTHON3 --version | head -1)
AC_MSG_RESULT([$PYTHON3 --version = $python3_version]) AC_MSG_RESULT([$PYTHON3 --version = $python3_version])
AC_ARG_VAR([LEX], [Flex executable])
AC_CHECK_PROG(LEX,flex,flex) AC_CHECK_PROG(LEX,flex,flex)
if test "x$LEX" = "x" ; then if test "x$LEX" = "x" ; then
AC_MSG_ERROR([Cannot find "flex" in your PATH, please install it]) AC_MSG_ERROR([Cannot find "flex" in your PATH, please install it])
@@ -271,6 +272,22 @@ fi
flex_version=$($LEX --version | head -1) flex_version=$($LEX --version | head -1)
AC_MSG_RESULT([$LEX --version = $flex_version]) AC_MSG_RESULT([$LEX --version = $flex_version])
# Use the header installed with the selected generator.
flex_include=$($PYTHON3 -c '
import pathlib
import shutil
import sys
print(pathlib.Path(shutil.which(sys.argv@<:@1@:>@) or sys.argv@<:@1@:>@).resolve().parent.parent / "include")
' "$LEX")
if test -f "$flex_include/FlexLexer.h"; then
if test -n "$MSYSTEM"; then
# Search MSYS Flex headers after the active toolchain's system headers.
CPPFLAGS="$CPPFLAGS -idirafter $flex_include"
else
CPPFLAGS="$CPPFLAGS -I$flex_include"
fi
fi
AC_CHECK_PROG(YACC,bison,bison) AC_CHECK_PROG(YACC,bison,bison)
if test "x$YACC" = "x" ; then if test "x$YACC" = "x" ; then
AC_MSG_ERROR([Cannot find "bison" in your PATH, please install it]) AC_MSG_ERROR([Cannot find "bison" in your PATH, please install it])
+25 -1
View File
@@ -7,6 +7,7 @@ contribution terms including the AI policy in ``docs/CONTRIBUTING.rst``.
Please see the Verilator manual for 200+ additional contributors. Thanks to all. Please see the Verilator manual for 200+ additional contributors. Thanks to all.
00>> Please insert into sorted position, and see header as to agreement
24bit-xjkp 24bit-xjkp
404allen404 404allen404
Adam Bagley Adam Bagley
@@ -46,6 +47,7 @@ BRDR LIFE
Brian Li Brian Li
Cameron Kirk Cameron Kirk
Cameron Waite Cameron Waite
CatalpaEel
Chih-Mao Chen Chih-Mao Chen
Chris Bachhuber Chris Bachhuber
Chris Randall Chris Randall
@@ -56,16 +58,19 @@ Chykon
Congcong Cai Congcong Cai
Conor McCullough Conor McCullough
Cookie Cookie
cynicalmindset
Dan Ruelas-Petrisko Dan Ruelas-Petrisko
Daniel Bates Daniel Bates
Danny Oler Danny Oler
Dave Sargeant Dave Sargeant
David Garau David Garau
David Harris
David Horton David Horton
David Ledger David Ledger
David Metz David Metz
David Stanford David Stanford
David Turner David Turner
Deanyone Su
dependabot[bot] dependabot[bot]
Dercury Dercury
Demin Han Demin Han
@@ -74,9 +79,11 @@ Dominick Grochowina
Don Williamson Don Williamson
Dragon-Git Dragon-Git
Drew Ranck Drew Ranck
Drew Risinger
Drew Taussig Drew Taussig
Driss Hafdi Driss Hafdi
Edgar E. Iglesias Edgar E. Iglesias
Edmund Lam
Eddie Rydell Eddie Rydell
em2machine em2machine
emmettifelts emmettifelts
@@ -92,6 +99,7 @@ Fan Shupei
february cozzocrea february cozzocrea
Felix Neumärker Felix Neumärker
Felix Yan Felix Yan
Francesco Urbani
Frans Skarman Frans Skarman
Fuad Ismail Fuad Ismail
Furqan Nadir Furqan Nadir
@@ -113,6 +121,7 @@ Han Qi
Harald Heckmann Harald Heckmann
Hennadii Chernyshchyk Hennadii Chernyshchyk
hjsanjana hjsanjana
Hongseok Choi
Howard Su Howard Su
Huang Rui Huang Rui
Huanghuang Zhou Huanghuang Zhou
@@ -145,6 +154,7 @@ jeffrey.song
Jens Yuechao Liu Jens Yuechao Liu
Jeremy Bennett Jeremy Bennett
Jesse Taube Jesse Taube
Jesús Alberto López Soto
Jevin Sweval Jevin Sweval
Jiacheng Qian Jiacheng Qian
Jiamin Zhu Jiamin Zhu
@@ -182,6 +192,7 @@ Keith Colbert
Kevin Kiningham Kevin Kiningham
Kevin Nygaard Kevin Nygaard
Kornel Uriasz Kornel Uriasz
Kristof Marien
Kritik Bhimani Kritik Bhimani
Krzysztof Bieganski Krzysztof Bieganski
Krzysztof Boronski Krzysztof Boronski
@@ -205,13 +216,16 @@ Maciej Sobkowski
Marcel Chang Marcel Chang
Marco Bartoli Marco Bartoli
Marco Brambilla Marco Brambilla
Marco Frank
Marco Widmer Marco Widmer
Mariusz Glebocki Mariusz Glebocki
Markus Krause Markus Krause
Marlon James Marlon James
Marshal Qiao Marshal Qiao
Martijn Wobbes
Martin Schmidt Martin Schmidt
Martin Stadler Martin Stadler
Martin Velay
Mateusz Gancarz Mateusz Gancarz
Matt Stroud Matt Stroud
Matthew Ballance Matthew Ballance
@@ -224,6 +238,7 @@ Michael Rogenmoser
Michal Czyz Michal Czyz
Michaël Lefebvre Michaël Lefebvre
Miguel Perez Andrade Miguel Perez Andrade
Mike Murunov
Mike Popoloski Mike Popoloski
Miodrag Milanović Miodrag Milanović
Mladen Slijepcevic Mladen Slijepcevic
@@ -236,9 +251,11 @@ Natan Kreimer
Nathan Graybeal Nathan Graybeal
Nathan Kohagen Nathan Kohagen
Nathan Myers Nathan Myers
Nazar Zibilyuk
Nick Brereton Nick Brereton
Nikolai Kumar Nikolai Kumar
Nikolay Puzanov Nikolay Puzanov
Noam Gallmann
Nolan Poe Nolan Poe
Oleh Maksymenko Oleh Maksymenko
Patrick Creighton Patrick Creighton
@@ -281,6 +298,7 @@ Ryan Ziegler
Ryszard Rozak Ryszard Rozak
Saksham Gupta Saksham Gupta
Samuel Riedel Samuel Riedel
Saqib Khan
Sean Cross Sean Cross
Sebastien Van Cauwenberghe Sebastien Van Cauwenberghe
Secturion Secturion
@@ -291,6 +309,7 @@ Seth Pellegrino
Shashvat Prabhu Shashvat Prabhu
Shogo Yamazaki Shogo Yamazaki
Shou-Li Hsu Shou-Li Hsu
Shriyans Sahoo
spomatasmd spomatasmd
Srinivasan Venkataramanan Srinivasan Venkataramanan
Stefan Wallentowitz Stefan Wallentowitz
@@ -299,8 +318,10 @@ Steven Hugg
Stuart Morris Stuart Morris
sumpster sumpster
Szymon Gizler Szymon Gizler
Szymon Jędras
Sören Tempel Sören Tempel
Sumanth Kadiyala Sumanth Kadiyala
Sunimali Rathnayake
Teng Huang Teng Huang
Thomas Aldrian Thomas Aldrian
Thomas Brown Thomas Brown
@@ -321,9 +342,11 @@ Tracy Narine
Trung Nguyen Trung Nguyen
Tudor Timi Tudor Timi
Tymoteusz Blazejczyk Tymoteusz Blazejczyk
Tynan McAuley
Tyrone Marhguy Tyrone Marhguy
Udaya Raj Subedi Udaya Raj Subedi
Udi Finkelstein Udi Finkelstein
UnsignedByte
Unai Martinez-Corral Unai Martinez-Corral
Valentin Atepalikhin Valentin Atepalikhin
Varun Koyyalagunta Varun Koyyalagunta
@@ -339,6 +362,7 @@ Wolfgang Mayerwieser
Xi Zhang Xi Zhang
Yan Xu Yan Xu
Yangyu Chen Yangyu Chen
yash
Yilin Li Yilin Li
Yilou Wang Yilou Wang
Yinan Xu Yinan Xu
@@ -357,4 +381,4 @@ Zixi Li
Zubin Jain Zubin Jain
Àlex Torregrosa Àlex Torregrosa
Ícaro Lima Ícaro Lima
Sunimali Rathnayake ÍÍ>> Please insert into sorted position, and see header as to agreement
+1 -1
View File
@@ -1,5 +1,5 @@
.. comment: generated by t_lint_didnotconverge_bad .. comment: generated by t_lint_didnotconverge_bad
.. code-block:: .. code-block::
-V{t#,#} 'stl' region trigger index 0 is active: @([hybrid] a) -V{t#,#} 'stl' region trigger index 0 is active: @([hybrid] b)
%Error-DIDNOTCONVERGE: Settle region did not converge after '--converge-limit' of 10000 tries %Error-DIDNOTCONVERGE: Settle region did not converge after '--converge-limit' of 10000 tries
+27
View File
@@ -40,6 +40,30 @@ model:
internals, including ``/* verilator public_flat */`` items. internals, including ``/* verilator public_flat */`` items.
.. _model header include macros:
Model Header Include Macros
---------------------------
Code compiled with the generated :file:`{prefix}.mk`, or with the CMake
``verilate()`` command, gets preprocessor macros that expand to the model
header includes, so user C++ need not hard-code the :vlopt:`--prefix`:
.. code-block:: C++
#include VM_PREFIX_INCLUDE // e.g. <Vtop.h>
#include VM_PREFIX_INCLUDE_DPI // e.g. <Vtop__Dpi.h>
``VM_PREFIX_INCLUDE_DPI`` is defined only when Verilator generates the
:file:`{prefix}__Dpi.h` header, which is the case for designs with DPI and
also for public-signal access such as :vlopt:`--public-flat-rw`. Using it
otherwise is a compile-time error.
A target built from several models defines each macro once, naming the
first model, as one target has one set of preprocessor definitions. Code
that must reach a second model's header names it directly.
.. _porting from pre 4.210: .. _porting from pre 4.210:
Model interface changes in version 4.210 Model interface changes in version 4.210
@@ -207,6 +231,9 @@ command line, or the link), you'd then:
#include "Vour__Dpi.h" #include "Vour__Dpi.h"
int add(int a, int b) { return a+b; } int add(int a, int b) { return a+b; }
Rather than hard-coding the prefix, ``#include VM_PREFIX_INCLUDE_DPI`` may
be used instead; see :ref:`Model Header Include Macros`.
DPI System Task/Functions DPI System Task/Functions
------------------------- -------------------------
-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 the Ubuntu LTS versions of GCC and clang use C++20 by default, estimated
May 2028.) 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 tcmalloc support
Verilator currently supports the default malloc, tcmalloc, or jemalloc. Verilator currently supports the default malloc, tcmalloc, or jemalloc.
As jemalloc has better performance, support for tcmalloc may be removed As jemalloc has better performance, support for tcmalloc may be removed
+44 -7
View File
@@ -316,6 +316,33 @@ Summary:
Enables basic block line coverage analysis. See :ref:`Line Coverage`. Enables basic block line coverage analysis. See :ref:`Line Coverage`.
.. option:: --coverage-max-bins <value>
Rarely needed. Specifies the maximum number of bins one covergroup bins
declaration of an integral coverpoint may create: an array of bins such
as ``bins b[] = {[0:$]}``, automatic bins ``bins auto[N]``, or the
automatic bins of a coverpoint from ``option.auto_bin_max``. Defaults to
1024, and may be at most 4294967295 (``2**32 - 1``).
A larger array of bins is ignored with a :option:`COVERIGN` warning, a
larger ``bins auto[N]`` is an error, and a larger
``option.auto_bin_max`` is reduced to this limit with a
:option:`COVERIGN` warning. Increasing the limit increases the memory
the model uses for the bins. See also
:vlopt:`--coverage-max-real-bins`.
.. option:: --coverage-max-real-bins <value>
Rarely needed. Specifies the maximum number of values of one array of
bins of a coverpoint of a ``real`` expression, such as
``bins b[] = {[1:10]}``, each of which is a bin compared separately.
Defaults to 1024, and may be at most 4294967295 (``2**32 - 1``).
A larger array of bins is ignored with a :option:`COVERIGN` warning.
Increasing the limit increases the size of the generated code, which
grows with the number of these bins. See also
:vlopt:`--coverage-max-bins`.
.. option:: --coverage-max-width <width> .. option:: --coverage-max-width <width>
Rarely needed. Specify the maximum bit width of a signal subject to Rarely needed. Specify the maximum bit width of a signal subject to
@@ -706,6 +733,8 @@ Summary:
.. option:: -fno-dead-cells .. option:: -fno-dead-cells
.. option:: -fno-dead-methods
.. option:: -fno-dedup .. option:: -fno-dedup
.. option:: -fno-dfg .. option:: -fno-dfg
@@ -811,6 +840,10 @@ Summary:
.. option:: -fno-lift-expr .. option:: -fno-lift-expr
Deprecated and has no effect (ignored).
In versions before 5.054: Disable lifting of expressions out of statements.
.. option:: -fno-localize .. option:: -fno-localize
.. option:: -fno-merge-cond .. option:: -fno-merge-cond
@@ -819,6 +852,11 @@ Summary:
.. option:: -fno-merge-const-pool .. option:: -fno-merge-const-pool
Deprecated and has no effect (ignored).
In versions before 5.054: Disable merging constant pool entries that have
the same value but different data types.
.. option:: -fno-reloop .. option:: -fno-reloop
.. option:: -fno-reorder .. option:: -fno-reorder
@@ -1790,12 +1828,11 @@ Summary:
.. option:: --structs-packed .. option:: --structs-packed
Deprecated; discontinue use of this option. Removed in 5.054.
Converts all unpacked structures to packed structures, and issues an Converted all unpacked structures to packed structures, Specifying this
:option:`UNPACKED` warning. Specifying this option allows for backward option allowed for backward compatibility with versions before Verilator
compatibility with versions before Verilator 5.006, when Verilator would 5.006, when Verilator would always pack unpacked structures.
always pack unpacked structures.
.. option:: -sv .. option:: -sv
@@ -2267,7 +2304,7 @@ Summary:
If using `--x-assign unique`, use the If using `--x-assign unique`, use the
:vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\<value\>>` :vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\<value\>>`
runtime option, and seed the runtime random number generator such that runtime option, and seed the runtime random number generator such that
each regression run gets a different randomization sequence with each regression test run gets a different randomization sequence with
:vlopt:`+verilator+seed+\<value\>`. You'll probably also want to print :vlopt:`+verilator+seed+\<value\>`. You'll probably also want to print
any seeds selected, and code to enable rerunning with that same seed, any seeds selected, and code to enable rerunning with that same seed,
so you can reproduce bugs. so you can reproduce bugs.
@@ -2296,7 +2333,7 @@ Summary:
If using `--x-initial unique`, use the If using `--x-initial unique`, use the
:vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\<value\>>` :vlopt:`+verilator+rand+reset+2 <+verilator+rand+reset+\<value\>>`
runtime option, and seed the runtime random number generator such that runtime option, and seed the runtime random number generator such that
each regression run gets a different randomization sequence with each regression test run gets a different randomization sequence with
:vlopt:`+verilator+seed+\<value\>`. You'll probably also want to print :vlopt:`+verilator+seed+\<value\>`. You'll probably also want to print
any seeds selected, and code to enable rerunning with that same seed, any seeds selected, and code to enable rerunning with that same seed,
so you can reproduce bugs. so you can reproduce bugs.
+1 -1
View File
@@ -28,7 +28,7 @@ Quick Install
and install. and install.
#. For build on Windows using MSVC set environment variable WIN_FLEX_BISON #. For build on Windows using MSVC set environment variable WIN_FLEX_BISON
to install directory. For build on Windows/Linux/OS-X using ninja set to install directory. For build on Windows/Linux/OS-X using ninja set
the environment variable FLEX_INCLUDE to the directory containing the CMake variable FLEX_INCLUDE_DIR to the directory containing
FlexLexer.h and ensure that flex/bison is available within the PATH. FlexLexer.h and ensure that flex/bison is available within the PATH.
To obtain Verilator sources download To obtain Verilator sources download
+1 -1
View File
@@ -174,7 +174,7 @@ once, after ``configure``:
# Install dependencies # Install dependencies
sudo apt install python3-pip sudo apt install python3-pip
# Create Python virutal environment in .venv: # Create Python virtual environment in .venv:
make venv make venv
# Or alternatively, to put it somewhere else: # Or alternatively, to put it somewhere else:
+1 -1
View File
@@ -153,7 +153,7 @@ experience depends on the use case, and some experimentation can pay
dividends. For a speedy debug cycle during development, especially on large dividends. For a speedy debug cycle during development, especially on large
designs where C++ compilation speed can dominate, consider using lower designs where C++ compilation speed can dominate, consider using lower
optimization to get to an executable faster. For throughput-oriented use optimization to get to an executable faster. For throughput-oriented use
cases, for example, regressions, it is usually worth spending extra cases, for example, regression testing, it is usually worth spending extra
compilation time to reduce total CPU time. compilation time to reduce total CPU time.
If you will be running many simulations on a single model, you can If you will be running many simulations on a single model, you can
+12
View File
@@ -162,6 +162,18 @@ Hierarchy blocks have some limitations, including:
- Delays are not allowed in hierarchy blocks. - Delays are not allowed in hierarchy blocks.
- Setting type parameters of a hierarchy block is unsupported.
- A ``defparam`` setting a parameter inside a hierarchy block is
unsupported.
- String parameter values of a hierarchy block containing a newline, a
double quote, ``/*``, or whitespace followed by ``//`` are unsupported.
- Values that are equal but differ in width or signedness (``5`` and
``8'd5``), including relative to the default value, are unsupported for a
hierarchy block parameter declared without a data type.
But, the following usage is supported: But, the following usage is supported:
- Nested hierarchy blocks. A hierarchy block may instantiate other - Nested hierarchy blocks. A hierarchy block may instantiate other
+7 -7
View File
@@ -592,6 +592,10 @@ List Of Warnings
``covergroup``, ``coverpoint``, and coverage options, and the ``covergroup``, ``coverpoint``, and coverage options, and the
construct was ignored. 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. Disabling the :option:`UNSUPPORTED` error also disables this warning.
Ignoring this warning may make Verilator ignore lint checking on the Ignoring this warning may make Verilator ignore lint checking on the
@@ -2499,14 +2503,10 @@ List Of Warnings
.. option:: UNPACKED .. option:: UNPACKED
Warns that unpacked structs and unions are not supported because Historical, never issued since version 5.004.
:vlopt:`--structs-packed` was used, or by up through version 5.004.
Ignoring this warning will make Verilator treat the structure as packed, Warned that unpacked structs and unions were not supported, or disabled
which may make Verilator simulations differ from other simulators. This by the since-removed `--structs-packed` option.
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.
.. option:: UNSATCONSTR .. option:: UNSATCONSTR
+2 -11
View File
@@ -1031,15 +1031,6 @@ Profile-guided optimization make this a bit better, by adjusting mtask
scheduling, but this does not yet guide the packing into mtasks. scheduling, but this does not yet guide the packing into mtasks.
Performance Regression
++++++++++++++++++++++
It would be nice if we had a regression of large designs, with some
diversity of design styles, to test on both single- and multithreaded
modes. This would help to avoid performance regressions, and also to
evaluate the optimizations while minimizing the impact of parasitic noise.
Per-Instance Classes Per-Instance Classes
++++++++++++++++++++ ++++++++++++++++++++
@@ -1645,7 +1636,7 @@ There are some traps to avoid when running regression tests
Hat/Fedora/Centos offer *perl-Pod-Perldoc*', while Debian/Ubuntu/Linux Hat/Fedora/Centos offer *perl-Pod-Perldoc*', while Debian/Ubuntu/Linux
Mint offer \`perl-doc'. Mint offer \`perl-doc'.
- Running regression may exhaust resources on some Linux systems, - Running regression tests may exhaust resources on some Linux systems,
particularly file handles and user processes. Increase these to particularly file handles and user processes. Increase these to
respectively 16,384 and 4,096. The method of doing this is respectively 16,384 and 4,096. The method of doing this is
system-dependent, but on Fedora Linux it would require editing the system-dependent, but on Fedora Linux it would require editing the
@@ -2422,7 +2413,7 @@ driver.py Non-Scenario Arguments
.. option:: --quiet .. option:: --quiet
Suppress all output except for failures and progress messages every 15 Suppress all output except for failures and progress messages every 15
seconds. Intended for use only in automated regressions. See also seconds. Intended for use only in automated regression testing. See also
``--rerun``, and ``--verbose`` which is not the opposite of ``--quiet``. ``--rerun``, and ``--verbose`` which is not the opposite of ``--quiet``.
.. option:: --rerun .. option:: --rerun
+10
View File
@@ -305,6 +305,7 @@ Mahmoudy
Makefile Makefile
Makefiles Makefiles
Maksymenko Maksymenko
Manzoni
Marche Marche
Markley Markley
Marquet Marquet
@@ -346,6 +347,7 @@ Narcis
Nassim Nassim
Nauticus Nauticus
Nayak Nayak
Nazar
Nenakhov Nenakhov
Newgard Newgard
Nigam Nigam
@@ -588,6 +590,7 @@ Yurii
Zaruba Zaruba
Zhang Zhang
Zhi Zhi
Zibilyuk
abirkmanis abirkmanis
accessor accessor
accessors accessors
@@ -627,6 +630,7 @@ benchmarking
biguint biguint
binToOneHot binToOneHot
bindir bindir
binsof
biops biops
bisonpre bisonpre
bitOpTree bitOpTree
@@ -718,6 +722,7 @@ deduplication
defenv defenv
defname defname
defparam defparam
dehash
demangling demangling
dep dep
deparametrized deparametrized
@@ -737,6 +742,7 @@ devel
difftree difftree
dir dir
disambiguates disambiguates
discoverability
displayb displayb
distcc distcc
doxygen doxygen
@@ -947,6 +953,7 @@ makefile
makefiles makefiles
malloc malloc
manpages manpages
memoization
metacomment metacomment
metacomments metacomments
miree miree
@@ -1132,6 +1139,7 @@ shortint
shortreal shortreal
signame signame
signedness signedness
slp
sp sp
specparam specparam
splitme splitme
@@ -1182,6 +1190,7 @@ systemc
taskify taskify
tcmalloc tcmalloc
tcmalloc tcmalloc
teardown
tenghtt tenghtt
testbench testbench
testbenches testbenches
@@ -1255,6 +1264,7 @@ vcddiff
vcoverage vcoverage
vdhotre vdhotre
vec vec
vectorize
ventana ventana
venv venv
ver ver
+152 -97
View File
@@ -1178,19 +1178,29 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
} else if (formatAttr == VL_VFORMATATTR_STRING) { } else if (formatAttr == VL_VFORMATATTR_STRING) {
thingp = va_arg(ap, std::string*); thingp = va_arg(ap, std::string*);
if (fmt != 'p' && fmt != 'x') fmt = 's'; // Override if (fmt != 'p' && fmt != 'x') fmt = 's'; // Override
} else if (formatAttr == VL_VFORMATATTR_ENUM) { } else if (formatAttr == VL_VFORMATATTR_ENUM
// Always <= VL_QUADSIZE; emit uses non-ENUM format for wider enums || formatAttr == VL_VFORMATATTR_ENUM_SIGNED) {
const int numericAttr = formatAttr == VL_VFORMATATTR_ENUM_SIGNED
? VL_VFORMATATTR_SIGNED
: VL_VFORMATATTR_UNSIGNED;
lbits = va_arg(ap, int); lbits = va_arg(ap, int);
ld = VL_VA_ARG_Q_(ap, lbits); if (lbits <= VL_QUADSIZE) {
strwide.resize(2); ld = VL_VA_ARG_Q_(ap, lbits);
WDataOutP strwidep = WDataOutP::external(strwide.data()); strwide.resize(2);
VL_SET_WQ(strwidep, ld); WDataOutP strwidep = WDataOutP::external(strwide.data());
lwp = strwidep; VL_SET_WQ(strwidep, ld);
lwp = strwidep;
} else {
lwp = WDataInP::external(va_arg(ap, const EData*));
ld = VL_SET_QW(lwp);
}
lsb = lbits - 1; lsb = lbits - 1;
++argn; // Enum value is followed by the generated name string argument ++argn; // Enum value is followed by the generated name string argument
static_cast<void>(va_arg(ap, int)); // VL_VFORMATATTR_STRING static_cast<void>(va_arg(ap, int)); // VL_VFORMATATTR_STRING
enump = va_arg(ap, std::string*); enump = va_arg(ap, std::string*);
if (enump && !enump->empty()) { if (fmt != 'p' && fmt != 's') {
formatAttr = numericAttr;
} else if (enump && !enump->empty()) {
formatAttr = (fmt == 'p') ? VL_VFORMATATTR_COMPLEX : VL_VFORMATATTR_STRING; formatAttr = (fmt == 'p') ? VL_VFORMATATTR_COMPLEX : VL_VFORMATATTR_STRING;
thingp = const_cast<std::string*>(enump); thingp = const_cast<std::string*>(enump);
} else if (fmt == 'p' && widthSet && width == 0) { } else if (fmt == 'p' && widthSet && width == 0) {
@@ -1201,7 +1211,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
if (fmt == 'p') width = 0; if (fmt == 'p') width = 0;
widthSet = true; widthSet = true;
fmt = 'd'; fmt = 'd';
formatAttr = VL_VFORMATATTR_UNSIGNED; formatAttr = numericAttr;
} }
if (widthSet && width == 0) { if (widthSet && width == 0) {
while (lsb && !VL_BITISSET_W(lwp, lsb)) --lsb; while (lsb && !VL_BITISSET_W(lwp, lsb)) --lsb;
@@ -1282,7 +1292,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc,
output += t_tmp; output += t_tmp;
} else if (formatAttr == VL_VFORMATATTR_STRING) { } else if (formatAttr == VL_VFORMATATTR_STRING) {
const std::string* const strp = static_cast<const std::string*>(thingp); const std::string* const strp = static_cast<const std::string*>(thingp);
output += '"' + *strp + '"'; output += VL_TO_STRING(*strp);
} else if (formatAttr == VL_VFORMATATTR_COMPLEX) { } else if (formatAttr == VL_VFORMATATTR_COMPLEX) {
const std::string* const strp = static_cast<const std::string*>(thingp); const std::string* const strp = static_cast<const std::string*>(thingp);
output += *strp; output += *strp;
@@ -1685,7 +1695,8 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
_vl_vsss_read_str(fp, floc, fromp, fstr, _vl_vsss_read_str(fp, floc, fromp, fstr,
std::back_insert_iterator<std::string>{t_tmp}, std::back_insert_iterator<std::string>{t_tmp},
"0123456789+-xXzZ?_"); "0123456789+-xXzZ?_");
if (!t_tmp[0]) goto done; t_tmp.erase(std::remove(t_tmp.begin(), t_tmp.end(), '_'), t_tmp.end());
if (t_tmp.empty()) goto done;
if (formatAttr == VL_VFORMATATTR_SIGNED) { if (formatAttr == VL_VFORMATATTR_SIGNED) {
QData ld = 0; QData ld = 0;
std::sscanf(t_tmp.c_str(), "%30" PRIu64, &ld); std::sscanf(t_tmp.c_str(), "%30" PRIu64, &ld);
@@ -1822,8 +1833,8 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
return got; return got;
done: done:
// Scan stopped early, return parsed or EOF // Scan stopped early; EOF only if input ended before the first conversion
if (_vl_vsss_eof(fp, floc)) return -1; if (got == 0 && _vl_vsss_eof(fp, floc)) return -1;
return got; return got;
} }
@@ -2391,8 +2402,10 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
VL_ZERO_W(rbits, rwp); VL_ZERO_W(rbits, rwp);
switch (std::tolower(fmt)) { switch (std::tolower(fmt)) {
case 'd': { case 'd': {
std::string digits{dp};
digits.erase(std::remove(digits.begin(), digits.end(), '_'), digits.end());
int64_t lld = 0; int64_t lld = 0;
std::sscanf(dp, "%30" PRId64, &lld); std::sscanf(digits.c_str(), "%30" PRId64, &lld);
VL_SET_WQ(rwp, lld); VL_SET_WQ(rwp, lld);
break; break;
} }
@@ -2488,6 +2501,30 @@ std::string VL_TO_STRING(QData lhs) {
std::string VL_TO_STRING(double lhs) { std::string VL_TO_STRING(double lhs) {
return VL_SFORMATF_N_NX("%g", 1, VL_VFORMATATTR_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) { 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); return VL_SFORMATF_N_NX("'h%0x", 1, VL_VFORMATATTR_UNSIGNED, words * VL_EDATASIZE, obj);
} }
@@ -3094,41 +3131,16 @@ VerilatedContext::Serialized::Serialized() {
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; } bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE { void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
if (assertCtlsLocked()) return; if (m_ns.m_assertCtlsLocked) return;
// Set all assert and directive types when true, clear otherwise. // Set all assert and directive types when true, clear otherwise.
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag; m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
} }
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, void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE { VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
// IEEE 1800-2023 Table 20-5 control_type. Lock freezes the On/Off state of the // 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. // selected bits until Unlock; On/Off/Kill leave locked bits unchanged.
// +verilator+assert+lock freezes everything, including Lock/Unlock itself. // +verilator+assert+lock freezes everything, including Lock/Unlock itself.
if (assertCtlsLocked()) return; if (m_ns.m_assertCtlsLocked) return;
const uint32_t mask = assertOnMask(types, directives); const uint32_t mask = assertOnMask(types, directives);
const uint32_t lockedMask = mask & ~m_s.m_assertLock; const uint32_t lockedMask = mask & ~m_s.m_assertLock;
switch (controlType) { switch (controlType) {
@@ -3178,26 +3190,6 @@ void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t typ
.c_str()); .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 { void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex}; const VerilatedLockGuard lock{m_mutex};
m_s.m_calcUnusedSigs = flag; m_s.m_calcUnusedSigs = flag;
@@ -3381,6 +3373,15 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
return vl_time_str(timeprecision()); return vl_time_str(timeprecision());
} }
static void warnThreadsOversubscribed(unsigned threads) VL_MT_SAFE {
const unsigned threadsAvailableToProcess = VlOs::getProcessDefaultParallelism();
if (threads > threadsAvailableToProcess) {
VL_PRINTF_MT("%%Warning: Process has %u hardware threads available, but simulation thread "
"count set to %u. This will likely cause significant slowdown.\n",
threadsAvailableToProcess, threads);
}
}
void VerilatedContext::threads(unsigned n) { void VerilatedContext::threads(unsigned n) {
if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "Simulation threads must be >= 1"); if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "Simulation threads must be >= 1");
@@ -3391,14 +3392,10 @@ void VerilatedContext::threads(unsigned n) {
} }
m_useNumaAssign = true; m_useNumaAssign = true;
m_threadsSet = true;
if (m_threads == n) return; // To avoid unnecessary warnings if (m_threads == n) return; // To avoid unnecessary warnings
m_threads = n; m_threads = n;
const unsigned threadsAvailableToProcess = VlOs::getProcessDefaultParallelism(); warnThreadsOversubscribed(m_threads);
if (m_threads > threadsAvailableToProcess) {
VL_PRINTF_MT("%%Warning: Process has %u hardware threads available, but simulation thread "
"count set to %u. This will likely cause significant slowdown.\n",
threadsAvailableToProcess, m_threads);
}
} }
void VerilatedContext::useNumaAssign(bool flag) { m_useNumaAssign = flag; } void VerilatedContext::useNumaAssign(bool flag) { m_useNumaAssign = flag; }
@@ -3464,7 +3461,15 @@ void VerilatedContext::addModel(const VerilatedModel* modelp) {
} }
} }
VerilatedVirtualBase* VerilatedContext::threadPoolp() { VerilatedVirtualBase* VerilatedContext::threadPoolp(unsigned modelThreads) {
// The thread count defaults to the number of threads available to the process, which may be
// fewer than a model uses, e.g. a model Verilated with --threads 4 on a single core machine.
// Oversubscribing is slow but works, so grow to fit the model, unless the user picked the
// thread count themselves, in which case addModel() reports the mismatch instead.
if (VL_UNLIKELY(modelThreads > m_threads) && !m_threadsSet && !m_threadPool) {
m_threads = modelThreads;
warnThreadsOversubscribed(m_threads);
}
if (m_threads == 1) return nullptr; if (m_threads == 1) return nullptr;
if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1}); if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
return m_threadPool.get(); return m_threadPool.get();
@@ -3580,7 +3585,7 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
std::string str; std::string str;
uint64_t u64; uint64_t u64;
if (arg == "+verilator+assert+lock") { if (arg == "+verilator+assert+lock") {
assertCtlsLocked(true); m_ns.m_assertCtlsLocked = true;
} else if (commandArgVlString(arg, "+verilator+coverage+file+", str)) { } else if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
coverageFilename(str); coverageFilename(str);
} else if (arg == "+verilator+debug") { } else if (arg == "+verilator+debug") {
@@ -3745,11 +3750,17 @@ void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
// VerilatedContext:: Methods - scopes // VerilatedContext:: Methods - scopes
void VerilatedContext::scopesDump() const VL_MT_SAFE { void VerilatedContext::scopesDump() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_impdatap->m_nameMutex}; {
VL_PRINTF_MT(" scopesDump:\n"); const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
for (const auto& i : m_impdatap->m_nameMap) { VL_PRINTF_MT(" scopesDump:\n");
const VerilatedScope* const scopep = i.second; for (const auto& i : m_impdatap->m_nameMap) {
scopep->scopeDump(); 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"); VL_PRINTF_MT("\n");
} }
@@ -3779,6 +3790,31 @@ const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
return &(impp()->m_impdatap->m_nameMap); 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 // VerilatedContext:: Methods - trace
@@ -4149,28 +4185,6 @@ std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { retu
//====================================================================== //======================================================================
// VerilatedVar:: Methods // VerilatedVar:: Methods
// cppcheck-suppress unusedFunction // Used by applications
uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
if (m_entSize) return m_entSize;
uint32_t size = 1;
switch (vltype()) {
case VLVT_PTR: size = sizeof(void*); break;
case VLVT_UINT8: size = sizeof(CData); break;
case VLVT_UINT16: size = sizeof(SData); break;
case VLVT_UINT32: size = sizeof(IData); break;
case VLVT_UINT64: size = sizeof(QData); break;
case VLVT_WDATA: size = VL_WORDS_I(entBits()) * sizeof(IData); break;
default: size = 0; break; // LCOV_EXCL_LINE
}
return size;
}
size_t VerilatedVarProps::totalSize() const {
size_t size = entSize();
for (int udim = 0; udim < udims(); ++udim) size *= m_unpacked[udim].elements();
return size;
}
void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE { void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE {
if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr; if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr;
if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr; if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr;
@@ -4312,6 +4326,41 @@ void VerilatedScope::scopesConstructFromTable(const VlScopeTableEntry* entp, siz
} }
} }
// 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, VerilatedVar* VerilatedScope::varInsertSized(const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, int udims, VerilatedVarType vltype, int vlflags, int udims,
uint32_t entSize...) VL_MT_UNSAFE { uint32_t entSize...) VL_MT_UNSAFE {
@@ -4429,7 +4478,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
// Slowpath - Called only when find has failed // Slowpath - Called only when find has failed
const std::string msg = ("Testbench C called '"s + VerilatedImp::exportName(funcnum) const std::string msg = ("Testbench C called '"s + VerilatedImp::exportName(funcnum)
+ "' but scope wasn't set, perhaps due to dpi import call without " + "' but scope wasn't set, perhaps due to dpi import call without "
+ "'context', or missing svSetScope. See IEEE 1800-2023 35.5.3."); + "'context', or missing svSetScope (IEEE 1800-2023 35.5.3)");
VL_FATAL_MT("unknown", 0, "", msg.c_str()); VL_FATAL_MT("unknown", 0, "", msg.c_str());
return nullptr; return nullptr;
} }
@@ -4457,6 +4506,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) { void VerilatedHierarchy::add(const VerilatedScope* fromp, const VerilatedScope* top) {
VerilatedImp::hierarchyAdd(fromp, top); VerilatedImp::hierarchyAdd(fromp, top);
} }
+141 -42
View File
@@ -100,6 +100,9 @@ class VerilatedFstC;
class VerilatedFstSc; class VerilatedFstSc;
class VerilatedScope; class VerilatedScope;
class VerilatedScopeNameMap; class VerilatedScopeNameMap;
class VerilatedIfaceRef;
class VerilatedIfaceRefMap;
struct VlIfaceRefTableEntry;
template <typename, typename> template <typename, typename>
class VerilatedTrace; class VerilatedTrace;
class VerilatedTraceBaseC; class VerilatedTraceBaseC;
@@ -109,6 +112,7 @@ class VerilatedVarNameMap;
class VerilatedVcd; class VerilatedVcd;
class VerilatedVcdC; class VerilatedVcdC;
class VerilatedVcdSc; class VerilatedVcdSc;
class VlCovRegistry;
//========================================================================= //=========================================================================
// Basic types // Basic types
@@ -257,7 +261,7 @@ class VL_SCOPED_CAPABILITY VerilatedLockGuard final {
VL_UNCOPYABLE(VerilatedLockGuard); VL_UNCOPYABLE(VerilatedLockGuard);
private: private:
VerilatedMutex& m_mutexr; VerilatedMutex& m_mutexr; // Mutex protecting the guard
public: public:
/// Construct and hold given mutex lock until destruction or unlock() /// Construct and hold given mutex lock until destruction or unlock()
@@ -301,6 +305,34 @@ public:
#endif #endif
}; };
// Internals: VlFileLineDebug stores a SystemVerilog source code location. Used in
// VlCoroutineHandle for debugging purposes, and to locate covergroup weight errors.
class VlFileLineDebug final {
// MEMBERS
#ifdef VL_DEBUG
const char* m_filename = nullptr; // Filename from sources, nullptr for unlnown
int m_lineno = 0; // Line number from sources
#endif
public:
// CONSTRUCTORS
VlFileLineDebug() = default;
VlFileLineDebug(const char* filename, int lineno)
#ifdef VL_DEBUG
: m_filename{filename}
, m_lineno{lineno}
#endif
{
}
// METHODS
#ifdef VL_DEBUG
const char* filename() const { return m_filename; }
int lineno() const { return m_lineno; }
#endif
};
class VlExecutionProfilerBase; class VlExecutionProfilerBase;
//========================================================================= //=========================================================================
@@ -376,8 +408,7 @@ class VerilatedEvalLoop final {
const uint32_t m_convergeLimit; // --converge-limit from compiler command line const uint32_t m_convergeLimit; // --converge-limit from compiler command line
// Where to record --prof-exec sections, or null if not profiling // Where to record --prof-exec sections, or null if not profiling
VlExecutionProfilerBase* m_profilerp = nullptr; VlExecutionProfilerBase* m_profilerp = nullptr;
// Whether this is the top level model during profiling bool m_profTopLevel = false; // Top level model during profiling
bool m_profTopLevel = false;
public: public:
// CONSTRUCTORS // CONSTRUCTORS
@@ -479,8 +510,8 @@ private:
= ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits<VerilatedAssertType_t>::digits = ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits<VerilatedAssertType_t>::digits
+ 1; + 1;
// Build the assertion-control bit mask for the given assertion x directive types. // Build the assertion-control bit mask for the given assertion x directive types.
static uint32_t assertOnMask(VerilatedAssertType_t types, static constexpr uint32_t assertOnMask(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_PURE; VerilatedAssertDirectiveType_t directives) VL_PURE;
static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1; static constexpr size_t ASSERT_CONTROL_SLOT_COUNT = ASSERT_ON_WIDTH - 1;
// No termination request has stamped m_finishPendingTime yet // No termination request has stamped m_finishPendingTime yet
static constexpr uint64_t TIME_UNSET = ~0ULL; static constexpr uint64_t TIME_UNSET = ~0ULL;
@@ -498,20 +529,18 @@ protected:
// No std::strings or pointers or will serialize badly! // No std::strings or pointers or will serialize badly!
// Fast path // Fast path
uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
std::atomic<uint32_t> m_assertOn{ // Enabled assertions, for each VerilatedAssertType we store 3-bits, one for each directive
std::numeric_limits<uint32_t>::max()}; // Enabled assertions, // type. Last bit guards internal directive types.
// for each VerilatedAssertType we store std::atomic<uint32_t> m_assertOn{std::numeric_limits<uint32_t>::max()};
// 3-bits, one for each directive type. Last
// bit guards internal directive types.
std::atomic<uint32_t> m_assertLock{0}; // Locked assertion bits (IEEE 1800-2023 20.11 std::atomic<uint32_t> m_assertLock{0}; // Locked assertion bits (IEEE 1800-2023 20.11
// Lock/Unlock); same layout as m_assertOn. While // Lock/Unlock); same layout as m_assertOn. While
// a bit is locked, On/Off/Kill leave it unchanged. // a bit is locked, On/Off/Kill leave it unchanged.
std::atomic<uint32_t> m_assertPassOnVacuous{ // Enabled vacuous pass actions
std::numeric_limits<uint32_t>::max()}; // Enabled vacuous pass actions std::atomic<uint32_t> m_assertPassOnVacuous{std::numeric_limits<uint32_t>::max()};
std::atomic<uint32_t> m_assertPassOnNonvacuous{ // Enabled nonvacuous pass actions
std::numeric_limits<uint32_t>::max()}; // Enabled nonvacuous pass actions std::atomic<uint32_t> m_assertPassOnNonvacuous{std::numeric_limits<uint32_t>::max()};
std::atomic<uint32_t> m_assertFailOn{ // Enabled fail actions
std::numeric_limits<uint32_t>::max()}; // Enabled fail actions std::atomic<uint32_t> m_assertFailOn{std::numeric_limits<uint32_t>::max()};
std::array<std::atomic<uint32_t>, ASSERT_CONTROL_SLOT_COUNT> m_assertKill{}; std::array<std::atomic<uint32_t>, ASSERT_CONTROL_SLOT_COUNT> m_assertKill{};
bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated
bool m_fatalOnError = true; // Fatal on $stop/non-fatal error bool m_fatalOnError = true; // Fatal on $stop/non-fatal error
@@ -545,8 +574,7 @@ protected:
// A worker queues $finish before the main thread callback can set m_gotFinish. // 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<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<uint64_t> m_finishPendingTime{TIME_UNSET}; // Time of the first callback
std::atomic<bool> m_assertCtlsLocked{ std::atomic<bool> m_assertCtlsLocked{false}; // All assertion-control updates are ignored
false}; // When true, all assertion-control updates are ignored
int m_stopReserved = 0; // Posted $stop requests not yet executed int m_stopReserved = 0; // Posted $stop requests not yet executed
bool m_executingFinal = false; // Running generated final() code bool m_executingFinal = false; // Running generated final() code
uint64_t m_profExecStart = 1; // +prof+exec+start time uint64_t m_profExecStart = 1; // +prof+exec+start time
@@ -580,6 +608,8 @@ protected:
const std::unique_ptr<VerilatedContextImpData> m_impdatap; const std::unique_ptr<VerilatedContextImpData> m_impdatap;
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread) // Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
unsigned m_threads = VlOs::getProcessDefaultParallelism(); unsigned m_threads = VlOs::getProcessDefaultParallelism();
// True if m_threads was set by the user, rather than being the default
bool m_threadsSet = false;
// Use numa automatic CPU-to-thread assignment // Use numa automatic CPU-to-thread assignment
bool m_useNumaAssign = false; bool m_useNumaAssign = false;
// Number of threads in added models // Number of threads in added models
@@ -590,6 +620,9 @@ protected:
std::unique_ptr<VerilatedVirtualBase> m_executionProfiler; std::unique_ptr<VerilatedVirtualBase> m_executionProfiler;
// Coverage access // Coverage access
std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep() 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 // File I/O
// Not serialized // Not serialized
@@ -600,9 +633,8 @@ protected:
// List of free descriptors in the MCT region [4, 32) // List of free descriptors in the MCT region [4, 32)
std::vector<IData> m_fdFreeMct VL_GUARDED_BY(m_fdMutex); std::vector<IData> m_fdFreeMct VL_GUARDED_BY(m_fdMutex);
// Magic to check for bad construction
static constexpr uint64_t MAGIC = 0xC35F9A6E5298EE6EULL; // SHA256 "VerilatedContext" static constexpr uint64_t MAGIC = 0xC35F9A6E5298EE6EULL; // SHA256 "VerilatedContext"
uint64_t m_magic = MAGIC; uint64_t m_magic = MAGIC; // Magic to check for bad construction
private: private:
// CONSTRUCTORS // CONSTRUCTORS
@@ -619,28 +651,9 @@ public:
bool assertOn() const VL_MT_SAFE; bool assertOn() const VL_MT_SAFE;
/// Enable all assertion types /// Enable all assertion types
void assertOn(bool flag) VL_MT_SAFE; void assertOn(bool flag) VL_MT_SAFE;
/// Get enabled status for given assertion types
bool assertOnGet(VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
/// Set enabled status for given assertion types
void assertOnSet(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
/// 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 /// Apply assertion control for given control, assertion, and directive types
void assertCtl(uint32_t controlType, VerilatedAssertType_t types, void assertCtl(uint32_t controlType, VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE; 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;
/// Return if calculating of unused signals (for traces) /// Return if calculating of unused signals (for traces)
bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; } bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
/// Enable calculation of unused signals (for traces) /// Enable calculation of unused signals (for traces)
@@ -659,6 +672,8 @@ public:
/// Return VerilatedCovContext, allocate if needed /// Return VerilatedCovContext, allocate if needed
/// Note if get unresolved reference then likely forgot to link verilated_cov.cpp /// Note if get unresolved reference then likely forgot to link verilated_cov.cpp
VerilatedCovContext* coveragep() VL_MT_SAFE; VerilatedCovContext* coveragep() VL_MT_SAFE;
/// Returns VlCovRegistry. Allocated on-demand
VlCovRegistry* covergroupRegistryp() VL_MT_SAFE;
/// Return debug level /// Return debug level
static inline int debug() VL_MT_SAFE; /// Set 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 /// Debug is currently global, but for forward compatibility have a per-context method
@@ -814,6 +829,10 @@ public:
bool stopRequestReserve(bool maybe) VL_MT_SAFE; bool stopRequestReserve(bool maybe) VL_MT_SAFE;
void stopRequestRelease() VL_MT_SAFE; void stopRequestRelease() VL_MT_SAFE;
// Internal: assertCtlGet() for generated code, with constant type and directive
template <VerilatedAssertType_t T_Type, VerilatedAssertDirectiveType_t T_Directive>
VL_ATTR_ALWINLINE uint32_t assertCtlGet(VerilatedAssertCtlQuery query) const VL_MT_SAFE;
// Internal: access to implementation class // Internal: access to implementation class
VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast<VerilatedContextImp*>(this); } VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast<VerilatedContextImp*>(this); }
const VerilatedContextImp* impp() const VL_MT_SAFE { const VerilatedContextImp* impp() const VL_MT_SAFE {
@@ -822,7 +841,9 @@ public:
// Internal: Model and thread setup // Internal: Model and thread setup
void addModel(const VerilatedModel* modelp); void addModel(const VerilatedModel* modelp);
VerilatedVirtualBase* threadPoolp(); // Get the thread pool, creating it if needed. 'modelThreads' is the parallelism of the model
// being constructed, so that the context can grow to the number of threads it requires.
VerilatedVirtualBase* threadPoolp(unsigned modelThreads = 1);
void prepareClone(); void prepareClone();
VerilatedVirtualBase* threadPoolpOnClone(); VerilatedVirtualBase* threadPoolpOnClone();
VerilatedVirtualBase* VerilatedVirtualBase*
@@ -868,6 +889,9 @@ public:
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE; const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
const VerilatedScopeNameMap* scopeNameMap() 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 // Internal: Serialization setup
static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); } static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); }
void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; } void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; }
@@ -897,6 +921,32 @@ public: // But for internal use only
virtual const char* name() const = 0; 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 // Verilator scope information class
// Used for internal VPI implementation, and introspection into scopes // Used for internal VPI implementation, and introspection into scopes
@@ -908,8 +958,9 @@ public:
enum Type : uint8_t { enum Type : uint8_t {
SCOPE_MODULE, SCOPE_MODULE,
SCOPE_OTHER, SCOPE_OTHER,
SCOPE_PACKAGE SCOPE_PACKAGE,
}; // Type of a scope, currently only module and package are interesting SCOPE_INTERFACE
}; // Type of a scope, currently only module, package and interface are interesting
private: private:
// Fastpath: // Fastpath:
VerilatedSyms* const m_symsp; // Symbol table VerilatedSyms* const m_symsp; // Symbol table
@@ -942,6 +993,10 @@ public: // But internals only - called from verilated modules, VerilatedSyms
void varsInsertFromTable(const VlVarTableEntry* entp, size_t n, void* basep) 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, static void scopesConstructFromTable(const VlScopeTableEntry* entp, size_t n,
VerilatedSyms* symsp) VL_MT_UNSAFE; 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 // ACCESSORS
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; } const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; } const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; }
@@ -958,6 +1013,15 @@ public: // But internals only - called from verilated modules, VerilatedSyms
VerilatedContext* contextp() const { return m_symsp->_vm_contextp__; } 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 // One scope, consumed by VerilatedScope::scopesConstructFromTable(); replaces
// per-scope 'new VerilatedScope{...}' statements, which compiles faster at scale. // per-scope 'new VerilatedScope{...}' statements, which compiles faster at scale.
struct VlScopeTableEntry final { struct VlScopeTableEntry final {
@@ -1276,5 +1340,40 @@ void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
#endif #endif
} }
// Defined here, not in-class: VL_CLOG2_I / VL_FATAL_MT (verilated_funcs.h) are not yet in scope
constexpr 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;
}
template <VerilatedAssertType_t T_Type, VerilatedAssertDirectiveType_t T_Directive>
uint32_t VerilatedContext::assertCtlGet(VerilatedAssertCtlQuery query) const VL_MT_SAFE {
// A constexpr local forces compile-time evaluation of the mask, which a plain
// assertOnMask() call does not get from GCC at -Os
constexpr uint32_t mask = assertOnMask(T_Type, T_Directive);
if (!mask) return 0;
// Explicit load(): G++ -Os inlines it but not the implicit conversion.
switch (query) { // LCOV_EXCL_BR_LINE
case VerilatedAssertCtlQuery::ASSERT_CTL_ON: return (m_s.m_assertOn.load() & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_KILL:
assert(mask && (mask & (mask - 1)) == 0);
return m_s.m_assertKill[VL_CLOG2_I(mask)].load();
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_VACUOUS:
return (m_s.m_assertPassOnVacuous.load() & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_PASS_ON_NONVACUOUS:
return (m_s.m_assertPassOnNonvacuous.load() & mask) != 0;
case VerilatedAssertCtlQuery::ASSERT_CTL_FAIL_ON:
return (m_s.m_assertFailOn.load() & mask) != 0;
default: // LCOV_EXCL_START
VL_FATAL_MT("", 0, "", "Internal: Bad assertCtlGet query");
VL_UNREACHABLE;
} // LCOV_EXCL_STOP
}
#undef VERILATOR_VERILATED_H_INTERNAL_ #undef VERILATOR_VERILATED_H_INTERNAL_
#endif // Guard #endif // Guard
+10
View File
@@ -99,6 +99,16 @@ VK_CPPFLAGS_ALWAYS += \
-DVM_VPI=$(VM_VPI) \ -DVM_VPI=$(VM_VPI) \
$(CFG_CXXFLAGS_NO_UNUSED) \ $(CFG_CXXFLAGS_NO_UNUSED) \
# Left undefined when there is no generated model, so misuse fails at compile time
ifneq ($(VM_PREFIX_INCLUDE),)
VK_CPPFLAGS_ALWAYS += -DVM_PREFIX_INCLUDE="$(VM_PREFIX_INCLUDE)"
endif
# Left undefined when the design has no DPI, so misuse fails at compile time
ifneq ($(VM_PREFIX_INCLUDE_DPI),)
VK_CPPFLAGS_ALWAYS += -DVM_PREFIX_INCLUDE_DPI="$(VM_PREFIX_INCLUDE_DPI)"
endif
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
VK_CPPFLAGS_WALL += -Wall $(CFG_CXXFLAGS_WEXTRA) -Werror VK_CPPFLAGS_WALL += -Wall $(CFG_CXXFLAGS_WEXTRA) -Werror
endif endif
+13 -1
View File
@@ -39,26 +39,34 @@ VLCOVGEN_ITEM("'name':'per_instance','short':'P', 'group':1, 'default':0, 'd
VLCOVGEN_ITEM("'name':'thresh', 'short':'s', 'group':1, 'default':None, 'descr':'Number of hits to consider covered (aka at_least)'") VLCOVGEN_ITEM("'name':'thresh', 'short':'s', 'group':1, 'default':None, 'descr':'Number of hits to consider covered (aka at_least)'")
VLCOVGEN_ITEM("'name':'type', 'short':'t', 'group':1, 'default':'', 'descr':'Type of coverage (block, line, fsm, etc)'") VLCOVGEN_ITEM("'name':'type', 'short':'t', 'group':1, 'default':'', 'descr':'Type of coverage (block, line, fsm, etc)'")
// Bin attributes // Bin attributes
VLCOVGEN_ITEM("'name':'cross_bins', 'short':'Cb', 'group':0, 'default':'', 'descr':'Comma-separated per-dimension bin names for cross coverage points'") VLCOVGEN_ITEM("'name':'bin', 'short':'B', 'group':0, 'default':'', 'descr':'Bin name for covergroup coverage points'")
VLCOVGEN_ITEM("'name':'bin_type', 'short':'Bt', 'group':0, 'default':'', 'descr':'Kind of a covergroup bin that is not coverable: ignore, illegal, or default'")
VLCOVGEN_ITEM("'name':'comment', 'short':'o', 'group':0, 'default':'', 'descr':'Textual description for the item'") VLCOVGEN_ITEM("'name':'comment', 'short':'o', 'group':0, 'default':'', 'descr':'Textual description for the item'")
VLCOVGEN_ITEM("'name':'cross', 'short':'C', 'group':0, 'default':0, 'descr':'True for cross coverage points'")
VLCOVGEN_ITEM("'name':'cross_bins', 'short':'Cb', 'group':0, 'default':'', 'descr':'Comma-separated per-dimension bin names for cross coverage points'")
VLCOVGEN_ITEM("'name':'fsm_from', 'short':'Ff', 'group':0, 'default':'', 'descr':'FSM source state name for structured FSM coverage points'") VLCOVGEN_ITEM("'name':'fsm_from', 'short':'Ff', 'group':0, 'default':'', 'descr':'FSM source state name for structured FSM coverage points'")
VLCOVGEN_ITEM("'name':'fsm_tag', 'short':'Fg', 'group':0, 'default':'', 'descr':'FSM point tag such as reset, reset_include, or default'") VLCOVGEN_ITEM("'name':'fsm_tag', 'short':'Fg', 'group':0, 'default':'', 'descr':'FSM point tag such as reset, reset_include, or default'")
VLCOVGEN_ITEM("'name':'fsm_to', 'short':'Ft', 'group':0, 'default':'', 'descr':'FSM destination state name for structured FSM coverage points'") VLCOVGEN_ITEM("'name':'fsm_to', 'short':'Ft', 'group':0, 'default':'', 'descr':'FSM destination state name for structured FSM coverage points'")
VLCOVGEN_ITEM("'name':'fsm_var', 'short':'Fv', 'group':0, 'default':'', 'descr':'FSM state variable name for structured FSM coverage points'") VLCOVGEN_ITEM("'name':'fsm_var', 'short':'Fv', 'group':0, 'default':'', 'descr':'FSM state variable name for structured FSM coverage points'")
VLCOVGEN_ITEM("'name':'group_weight','short':'Gw', 'group':0, 'default':None, 'descr':'For totaling covergroups, type_option.weight of the covergroup of this item'")
VLCOVGEN_ITEM("'name':'hier', 'short':'h', 'group':0, 'default':'', 'descr':'Hierarchy path name for the item'") VLCOVGEN_ITEM("'name':'hier', 'short':'h', 'group':0, 'default':'', 'descr':'Hierarchy path name for the item'")
VLCOVGEN_ITEM("'name':'lineno', 'short':'l', 'group':0, 'default':0, 'descr':'Line number for the item'") VLCOVGEN_ITEM("'name':'lineno', 'short':'l', 'group':0, 'default':0, 'descr':'Line number for the item'")
VLCOVGEN_ITEM("'name':'weight', 'short':'w', 'group':0, 'default':None, 'descr':'For totaling items, weight of this item'") VLCOVGEN_ITEM("'name':'weight', 'short':'w', 'group':0, 'default':None, 'descr':'For totaling items, weight of this item'")
// clang-format on // clang-format on
// VLCOVGEN_CIK_AUTO_EDIT_BEGIN // VLCOVGEN_CIK_AUTO_EDIT_BEGIN
#define VL_CIK_BIN "B"
#define VL_CIK_BIN_TYPE "Bt"
#define VL_CIK_COLUMN "n" #define VL_CIK_COLUMN "n"
#define VL_CIK_COMMENT "o" #define VL_CIK_COMMENT "o"
#define VL_CIK_CROSS "C"
#define VL_CIK_CROSS_BINS "Cb" #define VL_CIK_CROSS_BINS "Cb"
#define VL_CIK_FILENAME "f" #define VL_CIK_FILENAME "f"
#define VL_CIK_FSM_FROM "Ff" #define VL_CIK_FSM_FROM "Ff"
#define VL_CIK_FSM_TAG "Fg" #define VL_CIK_FSM_TAG "Fg"
#define VL_CIK_FSM_TO "Ft" #define VL_CIK_FSM_TO "Ft"
#define VL_CIK_FSM_VAR "Fv" #define VL_CIK_FSM_VAR "Fv"
#define VL_CIK_GROUP_WEIGHT "Gw"
#define VL_CIK_HIER "h" #define VL_CIK_HIER "h"
#define VL_CIK_LINENO "l" #define VL_CIK_LINENO "l"
#define VL_CIK_LINESCOV "S" #define VL_CIK_LINESCOV "S"
@@ -77,14 +85,18 @@ public:
// Return the short key code for a given a long coverage key // Return the short key code for a given a long coverage key
static std::string shortKey(const std::string& key) VL_PURE { static std::string shortKey(const std::string& key) VL_PURE {
// VLCOVGEN_SHORT_AUTO_EDIT_BEGIN // VLCOVGEN_SHORT_AUTO_EDIT_BEGIN
if (key == "bin") return VL_CIK_BIN;
if (key == "bin_type") return VL_CIK_BIN_TYPE;
if (key == "column") return VL_CIK_COLUMN; if (key == "column") return VL_CIK_COLUMN;
if (key == "comment") return VL_CIK_COMMENT; if (key == "comment") return VL_CIK_COMMENT;
if (key == "cross") return VL_CIK_CROSS;
if (key == "cross_bins") return VL_CIK_CROSS_BINS; if (key == "cross_bins") return VL_CIK_CROSS_BINS;
if (key == "filename") return VL_CIK_FILENAME; if (key == "filename") return VL_CIK_FILENAME;
if (key == "fsm_from") return VL_CIK_FSM_FROM; if (key == "fsm_from") return VL_CIK_FSM_FROM;
if (key == "fsm_tag") return VL_CIK_FSM_TAG; if (key == "fsm_tag") return VL_CIK_FSM_TAG;
if (key == "fsm_to") return VL_CIK_FSM_TO; if (key == "fsm_to") return VL_CIK_FSM_TO;
if (key == "fsm_var") return VL_CIK_FSM_VAR; if (key == "fsm_var") return VL_CIK_FSM_VAR;
if (key == "group_weight") return VL_CIK_GROUP_WEIGHT;
if (key == "hier") return VL_CIK_HIER; if (key == "hier") return VL_CIK_HIER;
if (key == "lineno") return VL_CIK_LINENO; if (key == "lineno") return VL_CIK_LINENO;
if (key == "linescov") return VL_CIK_LINESCOV; if (key == "linescov") return VL_CIK_LINESCOV;
+11
View File
@@ -28,6 +28,8 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
class VlFileLineDebug;
// Per-bin classification. A bin's kind is which set it lives in (structural), // Per-bin classification. A bin's kind is which set it lives in (structural),
// not a per-bin field. Only Normal feeds coverage(); the rest are recorded. // not a per-bin field. Only Normal feeds coverage(); the rest are recorded.
// Enumerators are 'KIND_'-prefixed because the bare LRM terms collide with // Enumerators are 'KIND_'-prefixed because the bare LRM terms collide with
@@ -50,6 +52,9 @@ enum class VlCovBinKind : uint8_t {
/// Bounded bin count, so random access by index is the primary usage. /// Bounded bin count, so random access by index is the primary usage.
class VlCoverpointIf VL_NOT_FINAL { class VlCoverpointIf VL_NOT_FINAL {
// MEMBERS
int32_t m_weight = 1; // option.weight; never negative
public: public:
// CONSTRUCTORS // CONSTRUCTORS
virtual ~VlCoverpointIf() = default; virtual ~VlCoverpointIf() = default;
@@ -62,6 +67,12 @@ public:
virtual std::string binName(uint32_t i) const = 0; virtual std::string binName(uint32_t i) const = 0;
// Bins covered / effective total (Normal set only) for the coverage calc // Bins covered / effective total (Normal set only) for the coverage calc
virtual void coverageParts(double& covered, double& total) const = 0; virtual void coverageParts(double& covered, double& total) const = 0;
/// Weight of this item in its covergroup instance's coverage (option.weight,
/// IEEE 1800-2023 19.11)
int32_t weight() const { return m_weight; }
/// Load option.weight, as evaluated by the covergroup constructor. A negative
/// weight is reported as an error, and counts as zero.
void weight(uint32_t value, VlFileLineDebug fileline);
}; };
#endif // Guard #endif // Guard
File diff suppressed because it is too large Load Diff
+585 -39
View File
@@ -32,10 +32,16 @@
#include "verilatedos.h" #include "verilatedos.h"
#include "verilated.h"
#include "verilated_cov_model.h" #include "verilated_cov_model.h"
#include <array>
#include <cstdint> #include <cstdint>
#include <initializer_list>
#include <memory>
#include <string> #include <string>
#include <unordered_map>
#include <utility>
#include <vector> #include <vector>
class VerilatedCovContext; class VerilatedCovContext;
@@ -44,6 +50,15 @@ class VerilatedCovContext;
enum class VlCovBinNaming : uint8_t { enum class VlCovBinNaming : uint8_t {
Single, // "<name>" one bin Single, // "<name>" one bin
Array, // "<name>[i]" bins b[N] value array Array, // "<name>[i]" bins b[N] value array
Numbered, // "<name>_<i>" automatic bins of a coverpoint without bins
Values, // "<name>[v]" bins b[] with a 'with' filter, a bin per value v
};
// How the bins of a 'with' filter (IEEE 1800-2023 19.5.1.1) hold the values it keeps
enum class VlCovBinGrouping : uint8_t {
Single, // bins b = ...: one bin
Values, // bins b[] = ...: a bin for each value, in value order
Fixed, // bins b[N] = ...: distributed over N bins, as a sized array's
}; };
// Specifies the naming scheme for a range of bins, allowing the // Specifies the naming scheme for a range of bins, allowing the
@@ -94,6 +109,10 @@ public:
// VlCoverpointT<MaxHits> adds the inline hit-list array and the incrementBin write // VlCoverpointT<MaxHits> adds the inline hit-list array and the incrementBin write
// path; the cross holds VlCoverpoint* and reads via hitCount()/hitList(). // path; the cross holds VlCoverpoint* and reads via hitCount()/hitList().
class VlCoverpoint VL_NOT_FINAL : public VlCoverpointIf { class VlCoverpoint VL_NOT_FINAL : public VlCoverpointIf {
struct ValueData;
std::unique_ptr<ValueData> m_valuesp; // Optional value metadata and exclusion state
friend class VlCoverCrossDyn;
protected: protected:
// MEMBERS (protected so VlCoverpointT::incrementBin can update them) // MEMBERS (protected so VlCoverpointT::incrementBin can update them)
std::string m_hier; // "covergroup.coverpoint" std::string m_hier; // "covergroup.coverpoint"
@@ -110,15 +129,27 @@ protected:
std::vector<uint32_t> m_crossToBin; std::vector<uint32_t> m_crossToBin;
uint32_t m_hitCount = 0; // entries valid in the hit list this sample uint32_t m_hitCount = 0; // entries valid in the hit list this sample
// PROTECTED METHODS
// Normal bin: VlCoverpointT::incrementBin(), for the bins sizedSample() finds
virtual void incrementNormalBin(uint32_t i) = 0;
private: private:
// PRIVATE METHODS // PRIVATE METHODS
const VlCovNamer& namerFor(uint32_t i) const; // obtain the bin-specific name producer 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, void addNamer(VlCovBinKind set, uint32_t count, VlCovBinNaming naming, const char* name,
const char* file, int line, int col); const char* file, int line, int col);
// Declared bin index of the i-th bin reported through VlCoverpointIf
uint32_t reportedBin(uint32_t i) const;
std::string declaredBinName(uint32_t bin) const; // Name of a declared bin index
bool liveBin(uint32_t bin) const; // Normal bin keeps a value outside the exclusions
// Count a sample, if enabled, in a bin of a sized array holding the value, unless it is
// 'last', the bin found before; set 'last'
void sizedHit(VlCovBinKind kind, uint32_t bin, bool enabled, uint32_t& last);
public: public:
// CONSTRUCTORS // CONSTRUCTORS
VlCoverpoint() = default; VlCoverpoint();
~VlCoverpoint() override;
// METHODS // METHODS
// ---- configuration (from generated constructor) ---- // ---- configuration (from generated constructor) ----
@@ -130,13 +161,78 @@ public:
int line, int col) { int line, int col) {
addNamer(set, count, VlCovBinNaming::Array, name, file, line, col); addNamer(set, count, VlCovBinNaming::Array, name, file, line, col);
} }
void registerBins(VerilatedCovContext* covcontextp, const char* page); void addNumberedNamer(VlCovBinKind set, uint32_t count, const char* name, const char* file,
int line, int col) {
addNamer(set, count, VlCovBinNaming::Numbered, name, file, line, col);
}
/// Register the bins in the coverage database, with what verilator_coverage needs to
/// compute coverage (IEEE 1800-2023 19.11): option.at_least, and the weights of the
/// coverpoint, itemWeight, and of its covergroup, groupWeight. The weights are those of
/// every instance, as the database merges the instances.
void registerBins(VerilatedCovContext* covcontextp, const char* page, uint32_t itemWeight,
uint32_t groupWeight);
/// Configure construction-time value metadata for exclusions and cross selections.
void valueType(uint32_t bits, bool isSigned);
/// Describe bin values as {bin, low words, high words} entries, without enumerating them.
void valueRanges(std::initializer_list<EData> entries);
/// Describe runs of bins as {first bin, count, low words, span words, high words} entries:
/// bin k of a run holds [low + k * (span + 1), low + k * (span + 1) + span], and its last
/// bin extends to high.
void valueRuns(std::initializer_list<EData> entries);
/// Describe wildcard patterns as {bin, value words, mask words, low words, high words}.
void valuePatterns(std::initializer_list<EData> entries);
/// State exclusions do not remove values from these transition bins.
void valueTransitions(std::initializer_list<uint32_t> bins);
/// Apply exclusions and freeze the live Normal-bin index space used by crosses.
void valueFinalize();
/// Drop the per-bin values once every cross has been built; sampling needs only exclusions.
void valueRelease();
/// Test state exclusions independently of sampling-time iff guards.
bool valueExcluded(QData value) const;
bool valueExcludedW(WDataInP valuep) const;
/// Add the coverpoint values lo..hi of the next range list element of a sized array of
/// bins, in declaration order.
void sizedRange(QData lo, QData hi);
void sizedRangeW(WDataInP lop, WDataInP hip);
/// Distribute the values sizedRange() added over the bins of the sized array 'name[count]'
/// (IEEE 1800-2023 19.5.1). 'positive' is false for a count below one, which is invalid.
/// At most 'limit' bins may hold values. Needs valueType(); bins append after those of
/// init().
void sizedFinish(VlCovBinKind kind, QData count, bool positive, uint32_t limit,
const char* name, const char* file, int line, int col);
/// Declared bins [sizedFirst(), sizedEnd()) of the sized array 'sized', counted in
/// sizedFinish() order, for cross selections.
uint32_t sizedFirst(uint32_t sized) const;
uint32_t sizedEnd(uint32_t sized) const;
/// Begin the bins of a 'with' filter (IEEE 1800-2023 19.5.1.1), whose candidates are the
/// values sizedRange() added, and whose bins then count as a sized array's. At most
/// 'limit' bins, or runs of values kept.
void withBegin(VlCovBinGrouping grouping, uint32_t limit);
/// Advance to the next run of candidates, withLo() to withHi(); false after the last, or
/// once too many values are kept
bool withNext();
QData withLo() const;
void withLoW(WDataOutP valuep) const;
QData withHi() const;
void withHiW(WDataOutP valuep) const;
/// Keep the values lo..hi, in the order the filter kept them; false once too many are
bool withRun(QData lo, QData hi);
bool withRunW(WDataInP lop, WDataInP hip);
/// Make the bins of the values kept, a sized array 'name[count]' for Fixed grouping (see
/// sizedFinish())
void withFinish(VlCovBinKind kind, QData count, bool positive, const char* name,
const char* file, int line, int col);
// ---- hot path (from generated sample()) ---- // ---- hot path (from generated sample()) ----
// Clear the hit list at the start of each sample() for a cross-fed coverpoint. // Clear the hit list at the start of each sample() for a cross-fed coverpoint.
void clearHitList() { m_hitCount = 0; } void clearHitList() { m_hitCount = 0; }
// Ignore/Illegal/Default: count only; never propagates to cross coverage. // Ignore/Illegal/Default: count only; never propagates to cross coverage.
void recordHit(uint32_t i) { ++m_counts[i]; } void recordHit(uint32_t i) { ++m_counts[i]; }
/// Count a sample in the bins of the sized array 'sized' holding the value, once each,
/// if 'enabled'. True if a bin holds the value, enabled or not.
bool sizedSample(uint32_t sized, QData value, bool enabled);
bool sizedSampleW(uint32_t sized, WDataInP valuep, bool enabled);
// incrementBin (Normal bin: count + hit-list append) lives in VlCoverpointT<MaxHits>, // incrementBin (Normal bin: count + hit-list append) lives in VlCoverpointT<MaxHits>,
// where MaxHits is the gen-time max per-sample bin overlap. // where MaxHits is the gen-time max per-sample bin overlap.
@@ -147,21 +243,17 @@ public:
std::string normalBinName(uint32_t crossIdx) const; // name of the crossIdx-th Normal bin std::string normalBinName(uint32_t crossIdx) const; // name of the crossIdx-th Normal bin
// ---- VlCoverpointIf ---- // ---- VlCoverpointIf ----
uint32_t binCount() const override { return m_total; } /// Bins removed for having no value (IEEE 1800-2023 19.11.1) are not reported.
uint32_t binCount() const override;
std::string binName(uint32_t i) const override; std::string binName(uint32_t i) const override;
// Deliberately not on VlCoverpointIf: only registerBins() needs it, via the // Deliberately not on VlCoverpointIf: only coverage-database registration needs it.
// concrete coverpoint. A cross has all-Normal bins and exposes no kind, so the VlCovBinKind binKind(uint32_t i) const { return namerFor(reportedBin(i)).set(); }
// 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 { void coverageParts(double& covered, double& total) const override {
// Count Normal bins that reached option.at_least on demand, so the hot // Count Normal bins that reached option.at_least on demand, so the hot
// path (incrementBin) stays a plain counter bump. // path (incrementBin) stays a plain counter bump.
uint32_t numCovered = 0; uint32_t numCovered = 0;
for (const VlCovNamer& nm : m_namers) { for (const uint32_t bin : m_crossToBin) {
if (nm.set() != VlCovBinKind::KIND_NORMAL) continue; if (m_counts[bin] >= m_atLeast) ++numCovered;
for (uint32_t i = nm.base(); i < nm.base() + nm.count(); ++i) {
if (m_counts[i] >= m_atLeast) ++numCovered;
}
} }
covered = numCovered; covered = numCovered;
total = m_normal; total = m_normal;
@@ -198,16 +290,75 @@ public:
if (cx >= 0 && m_hitCount < MaxHits) m_hits[m_hitCount++] = static_cast<uint32_t>(cx); if (cx >= 0 && m_hitCount < MaxHits) m_hits[m_hitCount++] = static_cast<uint32_t>(cx);
} }
const uint32_t* hitList() const override { return m_hits; } const uint32_t* hitList() const override { return m_hits; }
protected:
void incrementNormalBin(uint32_t i) override { incrementBin(i); }
}; };
//============================================================================= //=============================================================================
// VlCoverCross // VlCoverCross
/// Per-instance auto cross runtime. Holds flat uint32_t[] storage over the /// Per-instance cross runtime. Holds flat uint32_t[] storage over the
/// Cartesian product of the feeding coverpoints' Normal bins. Each sample() /// Cartesian product of the feeding coverpoints' Normal bins. Each sample()
/// walks the coverpoint hit lists (O(hits), not O(product)). Bin names are /// walks only hit tuples, not the entire product. Bin names are
/// built on demand from the coverpoints, so no per-bin name is stored. /// 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 and VlCoverCrossDyn own their storage. This shared core does not allocate bin
/// storage, and its borrowed storage pointers remain valid for the instance.
class VlCoverCross final : public VlCoverpointIf { 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 cross's selection storage
const char* namep; // Explicit bin name
const char* filep; // Bin declaration file
const uint32_t* wordIndicesp = nullptr; // Slice of the packed selection-word indices
int line; // Bin declaration line
int col; // Bin declaration column
VlCovBinKind kind = VlCovBinKind::KIND_NORMAL; // Normal, ignore, or illegal bin
uint32_t count = 0; // Samples matching the selection and guard
uint32_t numWords = 0; // Number of nonzero selection-word indices
uint32_t iffIndex = 0; // Original guard index, including bins removed during finalization
};
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; // First element of the viewed slice
T* m_endp; // One past the last element of the viewed slice
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 normalBins = 0; // Explicit bins contributing to coverage
uint32_t minBinWords = 0; // Minimum nonzero-word count across explicit bins
uint32_t numTouchedWords = 0; // Active prefix of wordsp[].touchedWord
};
private:
// MEMBERS // MEMBERS
std::string m_hier; // "covergroup.cross" std::string m_hier; // "covergroup.cross"
const char* m_file = nullptr; // Cross declaration file (registration metadata) const char* m_file = nullptr; // Cross declaration file (registration metadata)
@@ -217,41 +368,436 @@ class VlCoverCross final : public VlCoverpointIf {
// Cross bin indexes are unsigned, like the coverpoint bin indexes they are // 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 // built from. init() fatals if the product would exceed UINT32_MAX, so every
// index computed here provably fits. That bound is far beyond anything // index computed here provably fits. That bound is far beyond anything
// storable anyway: m_flatCounts alone would need 16GB. // storable anyway: m_flatCountsp alone would need 16GB.
uint32_t m_numAutoBins = 0; // Product of per-dim Normal bin counts uint32_t m_numAutoBins = 0; // Product of per-dim Normal bin counts
uint32_t m_numCovered = 0; // Distinct bins hit >= 1 (maintained incrementally) uint32_t m_numCovered = 0; // Distinct bins hit >= 1 (maintained incrementally)
std::vector<uint32_t> m_cpBinCounts; // [m_dims] Normal bin count per dimension Dimension* m_dimensionsp = nullptr; // [m_dims], owned by the concrete cross runtime
std::vector<uint32_t> m_stride; // [m_dims] Flat-index stride per dimension uint32_t* m_flatCountsp = nullptr; // [m_numAutoBins] Per-bin hit counts
std::vector<uint32_t> m_flatCounts; // [m_numAutoBins] Per-bin hit counts Explicit* m_explicitp = nullptr; // Absent for automatic-only crosses
std::vector<VlCoverpoint*> m_cps; // Feeding coverpoints (the only name source)
// PRIVATE METHODS // PRIVATE METHODS
void iterateProduct(VlCoverpoint* const* cps, uint32_t dim, uint32_t baseIdx); 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;
}
void incrementBin(Bin& bin);
template <bool T_RecordHits>
void incrementTuple(uint32_t idx) { void incrementTuple(uint32_t idx) {
if (m_flatCounts[idx]++ == 0) ++m_numCovered; 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;
}
void shape(uint32_t dims, uint32_t tuples) {
m_dims = dims;
m_numAutoBins = tuples;
}
void addBinImpl(VlCovBinKind kind, const uint64_t* selectionp, uint32_t words,
const char* namep, const char* filep, int line, int col, uint32_t iffIndex);
public:
VL_UNCOPYABLE(VlCoverCross);
// METHODS
// ---- configuration (from generated constructor, after coverpoints init'd) ----
virtual 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(VlCovBinKind kind, 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.
virtual void finalizeBins();
/// Register the bins in the coverage database; see VlCoverpoint::registerBins().
void registerBins(VerilatedCovContext* covcontextp, const char* page, uint32_t itemWeight,
uint32_t groupWeight);
// ---- 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 (including ignore/illegal) precede retained automatic 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 = hasExplicitBins() ? m_explicitp->normalBins + m_explicitp->autoBins.size()
: m_numAutoBins;
}
};
//=============================================================================
// 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());
}
};
// Construction-time cross layout over finalized coverpoints, sharing the sampling core.
class VlCoverCrossDyn final : public VlCoverCross {
class Layout;
std::unique_ptr<Layout> m_layoutp; // Owned cross storage and construction-time selections
public:
// CONSTRUCTORS
VlCoverCrossDyn();
~VlCoverCrossDyn() override;
// METHODS
/// Initialize after all feeding coverpoints have finalized their live bins.
void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file,
int line, int col) override;
/// Build cross-bin selections in postfix order.
void selectAll();
/// Start a binsof term over the live bins declared in [first, end) of dimension 'dim'.
void selectDim(uint32_t dim, uint32_t first, uint32_t end, bool negated, bool intersect);
void selectRange(QData lo, QData hi);
void selectRangeW(WDataInP lop, WDataInP hip);
void selectDimEnd();
void selectAnd();
void selectOr();
// Save a selection without renumbering guards when empty bins are removed.
void selectBin(VlCovBinKind kind, const char* namep, const char* filep, int line, int col,
uint32_t iffIndex);
/// Apply cross exclusions and bind finalized storage to the sampling core.
void finalizeBins() override;
};
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
// option.weight of the SV object that created this node, while that object
// lives; borrowed through VlCovInstHandle::lendWeight().
const IData* m_weightp = nullptr;
IData m_loadedWeight = 1; // Last option.weight loaded through m_weightp
int32_t m_weight = 1; // Weight in use, never negative; kept once the object is gone
VlFileLineDebug m_fileline; // Covergroup declaration, where a negative weight is reported
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
}
VlCoverCrossDyn* addCrossDyn();
// ---- 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; }
// ---- instance weight (from VlCovInstHandle) ----
void lendWeight(const IData* weightp, VlFileLineDebug fileline) {
m_weightp = weightp;
m_fileline = fileline;
loadWeight();
}
// The lending object is being destroyed. Its members may already be gone, so
// the weight is not read again; the last loaded value stays in effect.
void unlendWeight(const IData* weightp) {
if (m_weightp == weightp) m_weightp = nullptr;
}
/// Load option.weight from the lending object. SV writes the member directly
/// (assignments, ref and output arguments, $value$plusargs, ...), so this is
/// where a new value is seen, and checked once: a negative weight is reported as
/// an error, and counts as zero.
void loadWeight();
/// Weight of this instance in its type's coverage (option.weight, IEEE
/// 1800-2023 19.11.3), as last loaded; never negative.
int32_t weight() const { return m_weight; }
// ---- 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; }
/// IEEE 1800-2023 19.11 sums over the items whose coverage has a nonzero
/// denominator: {the sum of each item's option.weight times its coverage
/// (0..100), the sum of those weights}.
std::pair<double, double> coverageSums() const;
/// Instance coverage, as returned by get_inst_coverage(), in 0..100.
double coverage();
};
//=============================================================================
// VlCovRetiredAvg
/// Per-type residue: what survives an instance's death. Fixed size, so it does
/// not grow with churn. Each instance contributes with its option.weight.
struct VlCovRetiredAvg final {
uint64_t count = 0; // Retired instances that contributed (nonzero denominator)
double sumCoverage = 0.0; // Sigma of per-instance weight * coverage (0..100)
double sumWeight = 0.0; // Sigma of per-instance weight
};
//=============================================================================
// 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
IData m_loadedTypeWeight = 1; // Last type_option.weight loaded by coverage()
int32_t m_typeWeight = 1; // type_option.weight in use, never negative
// PRIVATE METHODS
// Harvest instp's contribution into m_retired. Must run before instp is
// unlinked: it reads the instance's items.
void foldResidue(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;
/// Type coverage, as returned by get_coverage(), in 0..100: the average of
/// every instance's coverage, weighted by its option.weight (IEEE 1800-2023
/// 19.11.3, type_option.merge_instances false). typeWeight is
/// type_option.weight, which decides the result when no instance contributes;
/// like option.weight, it is checked as it is loaded.
double coverage(IData typeWeight, VlFileLineDebug fileline);
// ---- 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); }
// Weighted mean coverage over the retired instances only, in 0..100; -1.0 if
// none contributed or their weights sum to zero.
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
VlCovergroupType* findOrCreateType(const char* typeName);
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);
/// Type coverage of a covergroup type (get_coverage()); see
/// VlCovergroupType::coverage(). typeWeight is its type_option.weight.
double typeCoverage(const char* typeName, IData typeWeight, VlFileLineDebug fileline);
// ---- 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
const IData* m_weightp = nullptr; // Owning object's option.weight, if lent to m_p
// 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, const IData* weightp) {
if (VL_UNCOVERABLE(!instp)) return; // Never attach()ed; codegen always does
instp->unlendWeight(weightp);
if (instp->attachDec()) instp->typep()->retire(instp);
} }
public: public:
// CONSTRUCTORS // CONSTRUCTORS
VlCoverCross() = default; VlCovInstHandle() = default;
// The copy's owning object lends no weight; the node keeps reading the lender's.
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, m_weightp); }
// METHODS // METHODS
// ---- configuration (from generated constructor, after coverpoints init'd) ---- // Bind to a freshly created node, taking over the attach count of 1 it was
void init(const char* hier, uint32_t dims, VlCoverpoint* const* cps, const char* file, // created with. Called once, from the generated covergroup constructor.
int line, int col); void attach(VlCovergroupInst* p) { m_p = p; }
void registerBins(VerilatedCovContext* covcontextp, const char* page); // Let the node read the owning object's option.weight until this handle is
// destroyed. Called once, from the generated constructor, after attach().
// ---- hot path (from generated sample(), after all coverpoints sampled) ---- void lendWeight(const IData* weightp, VlFileLineDebug fileline) {
void sample(VlCoverpoint* const* cps); m_weightp = weightp;
m_p->lendWeight(weightp, fileline);
// ---- VlCoverpointIf ----
// A cross is a coverpoint whose bins are the auto cross bins (all Normal).
uint32_t binCount() const override { return m_numAutoBins; }
std::string binName(uint32_t flat) const override;
void coverageParts(double& covered, double& total) const override {
covered = m_numCovered;
total = m_numAutoBins;
} }
VlCovergroupInst* p() const { return m_p; }
}; };
#endif // Guard #endif // Guard
+4 -4
View File
@@ -105,9 +105,9 @@ private:
int m_msb; // Inclusive upper bit for scalar path or element index for unpacked int m_msb; // Inclusive upper bit for scalar path or element index for unpacked
int m_rhsLsb; // Destination index that maps to RHS index 0 int m_rhsLsb; // Destination index that maps to RHS index 0
const void* m_rhsDatap; // Pointer to RHS storage const void* m_rhsDatap; // Pointer to RHS storage
int m_bitLsb = 0; int m_bitLsb = 0; // Bit's LSB
int m_bitMsb = 0; int m_bitMsb = 0; // Bit's MSB
int m_elemWidth = 0; int m_elemWidth = 0; // Element width
}; };
std::vector<Entry> m_entries; // Sorted by msb, non-overlapping std::vector<Entry> m_entries; // Sorted by msb, non-overlapping
@@ -303,7 +303,7 @@ public:
} }
template <typename T> template <typename T>
T readIndex(T origVal, int index) const { T readIndex(const T origVal, int index) const {
if (m_entries.empty()) return origVal; if (m_entries.empty()) return origVal;
T result = origVal; T result = origVal;
+26 -10
View File
@@ -1438,8 +1438,8 @@ inline void _vl_insert_WI(WDataOutP iowp, IData ld, int hbit, int lbit, int rbit
const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword
iowp[lword] = (iowp[lword] & ~linsmask) | ((lde << loffset) & linsmask); iowp[lword] = (iowp[lword] & ~linsmask) | ((lde << loffset) & linsmask);
// Prevent unsafe write where lword was final writable location and hword is // Prevent unsafe write where lword was final writable location and hword is
// out-of-bounds. // out-of-bounds. rbits==0 means the caller guarantees bounds.
if (VL_LIKELY(!(hword == rword && roffset == 0))) { if (VL_LIKELY(!(rbits && hword >= VL_WORDS_I(rbits)))) {
iowp[hword] iowp[hword]
= (iowp[hword] & ~hinsmask) | ((lde >> nbitsonright) & (hinsmask & cleanmask)); = (iowp[hword] & ~hinsmask) | ((lde >> nbitsonright) & (hinsmask & cleanmask));
} }
@@ -2948,8 +2948,8 @@ inline void VL_ZERO_INIT_QUEUE_ELEM(VlWide<N_Words>& elem) {
for (size_t j = 0; j < N_Words; ++j) { elem.at(j) = 0; } for (size_t j = 0; j < N_Words; ++j) { elem.at(j) = 0; }
} }
// This specialization works for both VlQueue<CData> (and similar) as well // These overloads work for both VlQueue<CData> (and similar) as well
// as VlQueue<VlWide<N>>. // as VlQueue<VlWide<N>>, including queues with different element types.
template <typename T> template <typename T>
inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/, int srcElementBits, inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/, int srcElementBits,
int dstElementBits) { int dstElementBits) {
@@ -2971,16 +2971,32 @@ inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int /*lbits*/, int
} }
} }
// This specialization works for both VlQueue<CData> (and similar) as well // Keep the same-type overload separate for its direct-copy optimization and alias handling;
// as VlQueue<VlWide<N>>. // differently typed queues cannot alias.
template <typename T> template <typename T_Dst, typename T_Src>
inline void VL_REVCOPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int lbits, int srcElementBits, inline typename std::enable_if<!std::is_same<T_Dst, T_Src>::value>::type
int dstElementBits) { VL_COPY_Q(VlQueue<T_Dst>& q, const VlQueue<T_Src>& from, int /*lbits*/, int srcElementBits,
int dstElementBits) {
const size_t srcTotalBits = from.size() * srcElementBits;
const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits;
q.renew(dstSize);
for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); }
for (size_t bitIndex = 0; bitIndex < srcTotalBits; ++bitIndex) {
VL_SET_QUEUE_BIT(q, dstElementBits, bitIndex,
VL_GET_QUEUE_BIT(from, srcElementBits, bitIndex));
}
}
// This works for both VlQueue<CData> (and similar) as well as VlQueue<VlWide<N>>,
// including queues with different element types.
template <typename T_Dst, typename T_Src>
inline void VL_REVCOPY_Q(VlQueue<T_Dst>& q, const VlQueue<T_Src>& from, int lbits,
int srcElementBits, int dstElementBits) {
const size_t srcTotalBits = from.size() * srcElementBits; const size_t srcTotalBits = from.size() * srcElementBits;
const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits; const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits;
// Always make a copy to handle the case where q and from are the same queue // Always make a copy to handle the case where q and from are the same queue
VlQueue<T> srcCopy = from; VlQueue<T_Src> srcCopy = from;
// Initialize all elements to zero using appropriate method // Initialize all elements to zero using appropriate method
q.renew(dstSize); q.renew(dstSize);
+9
View File
@@ -207,6 +207,11 @@ protected:
// Used by scopeInsert, scopeFind, scopeErase, scopeNameMap // Used by scopeInsert, scopeFind, scopeErase, scopeNameMap
mutable VerilatedMutex m_nameMutex; // Protect m_nameMap mutable VerilatedMutex m_nameMutex; // Protect m_nameMap
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex); 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 scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE;
void scopeErase(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 // METHODS - file IO - INTERNAL only for verilated*.cpp
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) { IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
+1 -1
View File
@@ -169,7 +169,7 @@ class VlExecutionProfiler final : public VlExecutionProfilerBase {
// STATE // STATE
VerilatedContext& m_context; // The context this profiler is under VerilatedContext& m_context; // The context this profiler is under
static thread_local ExecutionTrace t_trace; // thread-local trace buffers static thread_local ExecutionTrace t_trace; // thread-local trace buffers
mutable VerilatedMutex m_mutex; mutable VerilatedMutex m_mutex; // Mutex protecting traces
// Map from thread id to &t_trace of given thread // Map from thread id to &t_trace of given thread
std::map<uint32_t, ExecutionTrace*> m_traceps VL_GUARDED_BY(m_mutex); std::map<uint32_t, ExecutionTrace*> m_traceps VL_GUARDED_BY(m_mutex);
+59 -23
View File
@@ -674,10 +674,38 @@ void VlRandomVar::set(const std::string& idx, const std::string& val) const {
} }
} }
void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits) { void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits,
const IData hash = VL_RANDOM_RNG_I(rngr) & ((1 << bits) - 1); const std::vector<std::string>* layerVarsp) {
// layerVarsp scopes sampling to the current phase's own layer, so a
// phased solve's diversity constraint can't be built entirely out of a
// later phase's (still-unsolved) variable instead of this one's.
std::vector<const VlRandomVar*>& vars = m_randomConstraintVars;
vars.clear();
int varBits = 0; int varBits = 0;
for (const auto& var : m_vars) varBits += var.second->totalWidth(); if (layerVarsp) {
for (const auto& name : *layerVarsp) {
const auto it = m_vars.find(name);
// buildSolveLayers() only ever adds a name after confirming
// it's already in m_vars.
assert(it != m_vars.end());
vars.push_back(it->second.get());
varBits += it->second->totalWidth();
}
} else {
for (const auto& var : m_vars) {
vars.push_back(var.second.get());
varBits += var.second->totalWidth();
}
}
if (varBits == 0) {
// Nothing to sample (e.g. a still-unsized queue/dynamic array, in a
// layer or the whole class) -- tautology instead of the degenerate
// empty-operand expression the loop below would otherwise build.
os << "(= #b1 #b1)";
return;
}
const IData hash = VL_RANDOM_RNG_I(rngr) & ((1 << bits) - 1);
os << "(= #b"; os << "(= #b";
for (int i = bits - 1; i >= 0; i--) os << (VL_BITISSET_I(hash, i) ? '1' : '0'); for (int i = bits - 1; i >= 0; i--) os << (VL_BITISSET_I(hash, i) ? '1' : '0');
if (bits > 1) os << " (concat"; if (bits > 1) os << " (concat";
@@ -685,11 +713,12 @@ void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits) {
IData varBitsLeft = varBits; IData varBitsLeft = varBits;
IData varBitsWant = (varBits + 1) / 2; IData varBitsWant = (varBits + 1) / 2;
if (varBits > 2) os << " (bvxor"; if (varBits > 2) os << " (bvxor";
for (const auto& var : m_vars) { for (const auto& varp : vars) { // LCOV_EXCL_BR_LINE - reservoir-sampling below
for (int j = 0; j < var.second->totalWidth(); j++, varBitsLeft--) { // always forces its last pick before exhausting vars
for (int j = 0; j < varp->totalWidth(); j++, varBitsLeft--) {
const bool doEmit = (VL_RANDOM_RNG_I(rngr) % varBitsLeft) < varBitsWant; const bool doEmit = (VL_RANDOM_RNG_I(rngr) % varBitsLeft) < varBitsWant;
if (doEmit) { if (doEmit) {
var.second->emitExtract(os, j); varp->emitExtract(os, j);
if (--varBitsWant == 0) break; if (--varBitsWant == 0) break;
} }
} }
@@ -779,7 +808,7 @@ bool VlRandomizer::next_check_only(VlRNGReseeds& rngr) { return nextRandomize(rn
bool VlRandomizer::next(VlRNGReseeds& rngr) { return nextRandomize(rngr, false); } bool VlRandomizer::next(VlRNGReseeds& rngr) { return nextRandomize(rngr, false); }
bool VlRandomizer::nextRandomize(VlRNGReseeds& rngr, bool checkOnly) { bool VlRandomizer::nextRandomize(VlRNGReseeds& rngr, bool checkOnly) {
if (!checkOnly && m_vars.empty() && m_unique_arrays.empty()) return true; if (!checkOnly && m_vars.empty() && m_uniqueArrays.empty()) return true;
if (checkOnly && m_vars.empty()) return true; // No rand members: trivially SAT if (checkOnly && m_vars.empty()) return true; // No rand members: trivially SAT
VlSolverSession& sess = s_solverSession; VlSolverSession& sess = s_solverSession;
const VerilatedLockGuard lock{sess.m_mutex}; const VerilatedLockGuard lock{sess.m_mutex};
@@ -814,21 +843,27 @@ bool VlRandomizer::nextRandomize(VlRNGReseeds& rngr, bool checkOnly) {
std::vector<std::string> VlRandomizer::buildUniqueExprs() const { std::vector<std::string> VlRandomizer::buildUniqueExprs() const {
std::vector<std::string> exprs; std::vector<std::string> exprs;
if (m_unique_arrays.empty()) return exprs; if (m_uniqueArrays.empty()) return exprs;
const auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars); const auto arrVarsp = std::make_shared<const ArrayInfoMap>(m_arr_vars);
for (const std::string& baseName : m_unique_arrays) { for (const auto& [key, arrNameVec] : m_uniqueArrays) {
const auto it = m_vars.find(baseName); uint32_t matchingElementsCount = 0;
if (it == m_vars.end()) continue; std::ostringstream varsString;
const VlRandomVar& var = *it->second;
// Select the elements the array actually holds now, by their own index for (const auto& baseName : arrNameVec) {
// or key, rather than by ordinal position const auto it = m_vars.find(baseName);
var.setArrayInfo(arrVarsp); if (it == m_vars.end()) continue;
// 'distinct' needs at least two operands; fewer elements are trivially unique const VlRandomVar& var = *it->second;
if (var.countMatchingElements(*arrVarsp, baseName) < 2) continue; // Select the elements the array actually holds now, by their own index
// or key, rather than by ordinal position
var.setArrayInfo(arrVarsp);
// 'distinct' needs at least two operands; fewer elements are trivially unique
matchingElementsCount += var.countMatchingElements(*arrVarsp, baseName);
var.emitGetValue(varsString);
}
if (matchingElementsCount < 2) continue;
std::ostringstream os; std::ostringstream os;
os << "(__Vbv (distinct "; os << "(__Vbv (distinct " << varsString.str() << "))";
var.emitGetValue(os);
os << "))";
exprs.push_back(os.str()); exprs.push_back(os.str());
} }
return exprs; return exprs;
@@ -1577,7 +1612,7 @@ void VlRandomizer::clearConstraints() {
m_constraints_line.clear(); m_constraints_line.clear();
m_solveBefore.clear(); m_solveBefore.clear();
m_softConstraints.clear(); m_softConstraints.clear();
m_unique_arrays.clear(); // Re-registered by constraint setup m_uniqueArrays.clear(); // Re-registered by constraint setup
// Keep m_vars for class member randomization // Keep m_vars for class member randomization
} }
@@ -1590,7 +1625,7 @@ void VlRandomizer::clearAll() {
m_randcConstraintHash = 0; m_randcConstraintHash = 0;
} }
void VlRandomizer::markRandc(const char* name) { m_randcVarNames.insert(name); } void VlRandomizer::markRandc(const std::string& name) { m_randcVarNames.insert(name); }
void VlRandomizer::solveBefore(const std::string& beforeName, const std::string& afterName) { void VlRandomizer::solveBefore(const std::string& beforeName, const std::string& afterName) {
m_solveBefore.emplace_back(beforeName, afterName); m_solveBefore.emplace_back(beforeName, afterName);
@@ -1748,8 +1783,9 @@ bool VlRandomizer::solvePhaseValues(VlSolverSession& sess, VlRNG& rngr,
// Try diversity: add random constraint, re-check. If sat, get // Try diversity: add random constraint, re-check. If sat, get
// updated (more diverse) values. If unsat, keep baseline values. // updated (more diverse) values. If unsat, keep baseline values.
// Scoped to this layer's own vars -- see randomConstraint's comment.
os << "(assert "; os << "(assert ";
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN); randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN, &layerVars);
os << ")\n"; os << ")\n";
os << "(check-sat)\n"; os << "(check-sat)\n";
if (sess.readStatus() == VlSolverStatus::SAT) { if (sess.readStatus() == VlSolverStatus::SAT) {
+208 -21
View File
@@ -259,10 +259,13 @@ class VlRandomizer VL_NOT_FINAL {
m_constraints_line; // fileline content of the constraint for unsat constraints m_constraints_line; // fileline content of the constraint for unsat constraints
std::vector<std::string> m_softConstraints; // Soft constraints std::vector<std::string> m_softConstraints; // Soft constraints
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent 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) std::set<std::string> m_disabledVars; // Variables with rand_mode off (skip write-back)
// variables // variables
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
std::vector<std::string> m_unique_arrays; // Arrays whose elements must be distinct // Arrays whose elements must be distinct grouped by unique constraint
std::map<uint32_t, std::unordered_set<std::string>> m_uniqueArrays;
const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state; const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state;
const VlQueue<CData>* m_static_randmodep = nullptr; // Static rand_mode state (shared) const VlQueue<CData>* m_static_randmodep = nullptr; // Static rand_mode state (shared)
std::unordered_set<std::string> m_staticVars; // Names of static rand vars std::unordered_set<std::string> m_staticVars; // Names of static rand vars
@@ -279,7 +282,8 @@ class VlRandomizer VL_NOT_FINAL {
bool hasFrozenVar() const; // true if any var is currently rand_mode(0)-frozen bool hasFrozenVar() const; // true if any var is currently rand_mode(0)-frozen
// PRIVATE METHODS // PRIVATE METHODS
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits); 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. // Fetch the model and write it into the registered variables.
bool applyModel(VlSolverSession& sess); bool applyModel(VlSolverSession& sess);
bool parseModel(std::istream& is, size_t requested); bool parseModel(std::istream& is, size_t requested);
@@ -466,16 +470,16 @@ public:
// Mark a variable as rand_mode-disabled: solver keeps it in m_vars // Mark a variable as rand_mode-disabled: solver keeps it in m_vars
// (so constraints still reference it) but skips write-back after solving. // (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 // 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 --- // --- write_var to register variables ---
// Register scalar variable (non-struct, basic type) // Register scalar variable (non-struct, basic type)
template <typename T> template <typename T>
typename std::enable_if<!VlContainsCustomStruct<T>::value && !IsVlUnpacked<T>::value, typename std::enable_if<!VlContainsCustomStruct<T>::value && !IsVlUnpacked<T>::value,
void>::type 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()) { std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (m_vars.find(name) != m_vars.end()) return; if (m_vars.find(name) != m_vars.end()) return;
// TODO: make_unique once VlRandomizer is per-instance not per-ref // TODO: make_unique once VlRandomizer is per-instance not per-ref
@@ -488,7 +492,7 @@ public:
typename std::enable_if<VlIsCustomStruct<T>::value, void>::type typename std::enable_if<VlIsCustomStruct<T>::value, void>::type
write_var(T& var, int width, const char* name, int dimension, write_var(T& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) { std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
modifyMembers(var, var.memberIndices(), name); modifyMembers(var, var.memberIndices(), name, randmodeIdx);
} }
// Register queue of non-struct types // Register queue of non-struct types
@@ -513,7 +517,7 @@ public:
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
write_var(VlQueue<T, N_MaxSize>& 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()) { std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {}); record_struct_arr(var, name, dimension, {}, {}, randmodeIdx);
} }
// Register unpacked array of non-struct types // Register unpacked array of non-struct types
template <typename T, std::size_t N_Depth> template <typename T, std::size_t N_Depth>
@@ -539,7 +543,7 @@ public:
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
write_var(VlUnpacked<T, N_Depth>& var, int /*width*/, const char* name, int dimension, write_var(VlUnpacked<T, N_Depth>& var, int /*width*/, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) { std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {}); record_struct_arr(var, name, dimension, {}, {}, randmodeIdx);
} }
// Register associative array of non-struct types // Register associative array of non-struct types
@@ -567,7 +571,7 @@ public:
typename std::enable_if<VlContainsCustomStruct<T_Value>::value, void>::type typename std::enable_if<VlContainsCustomStruct<T_Value>::value, void>::type
write_var(VlAssocArray<T_Key, T_Value>& var, int /*width*/, const char* name, int dimension, 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()) { std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {}); record_struct_arr(var, name, dimension, {}, {}, randmodeIdx);
} }
// --- Record Arrays: flat and struct --- // --- Record Arrays: flat and struct ---
@@ -582,10 +586,23 @@ public:
++m_index; ++m_index;
} }
template <typename T>
typename std::enable_if<!std::is_class<T>::value || VlIsVlWide<T>::value, void>::type
update_arr_table(T& var, const std::string& name, int /*dimension*/) {
const std::string key = generateKey(name, m_index++);
const auto it = m_arr_vars.find(key);
assert(it != m_arr_vars.end());
const ArrayInfo& arrayInfo = *it->second;
it->second = std::make_shared<const ArrayInfo>(arrayInfo.m_name, &var, arrayInfo.m_index,
arrayInfo.m_indices, arrayInfo.m_idxWidths);
}
// This is the "Sender" API for the generated code. // This is the "Sender" API for the generated code.
// The elements to make distinct are taken from the array element table at // 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. // 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); } void rand_unique(const std::string& name, const uint32_t key) {
m_uniqueArrays[key].insert(name);
}
// Recursively record all elements in an unpacked array // Recursively record all elements in an unpacked array
template <typename T, std::size_t N_Depth> template <typename T, std::size_t N_Depth>
@@ -603,6 +620,15 @@ public:
} }
} }
// Recursively update pointers to all elements in an unpacked array
template <typename T, std::size_t N_Depth>
void update_arr_table(VlUnpacked<T, N_Depth>& var, const std::string& name, int dimension) {
assert(dimension > 0);
for (size_t i = 0; i < N_Depth; ++i) {
update_arr_table(var[i], name + "[" + std::to_string(i) + "]", dimension - 1);
}
}
// Recursively record all elements in a queue // Recursively record all elements in a queue
template <typename T, size_t N_MaxSize> template <typename T, size_t N_MaxSize>
void record_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension, void record_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
@@ -618,6 +644,15 @@ public:
} }
} }
// Recursively update pointers to all elements in a queue
template <typename T, size_t N_MaxSize>
void update_arr_table(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension) {
assert(dimension > 0);
for (size_t i = 0; i < var.size(); ++i) {
update_arr_table(var.atWrite(i), name + "[" + std::to_string(i) + "]", dimension - 1);
}
}
// Recursively record all elements in an associative array // Recursively record all elements in an associative array
template <typename T_Key, typename T_Value> template <typename T_Key, typename T_Value>
void record_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string& name, void record_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
@@ -648,11 +683,27 @@ public:
} }
} }
// Recursively update pointers to all elements in an associative array
template <typename T_Key, typename T_Value>
void update_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
int dimension) {
assert(dimension > 0);
for (auto it = var.begin(); it != var.end(); ++it) {
const T_Key& key = it->first;
std::string indexed_name;
std::vector<size_t> integral_index;
size_t idx_width = 0;
process_key(key, indexed_name, integral_index, name, idx_width);
update_arr_table(var.atWrite(key), indexed_name, dimension - 1);
}
}
// Register a single structArray element via write_var // Register a single structArray element via write_var
template <typename T> template <typename T>
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
record_struct_arr(T& var, const std::string& name, int /*dimension*/, record_struct_arr(T& var, const std::string& name, int /*dimension*/,
std::vector<IData> indices, std::vector<size_t> idxWidths) { std::vector<IData> indices, std::vector<size_t> idxWidths,
std::uint32_t randmodeIdx) {
std::ostringstream oss; std::ostringstream oss;
for (size_t i = 0; i < indices.size(); ++i) { for (size_t i = 0; i < indices.size(); ++i) {
oss << std::hex << std::setw(int(idxWidths[i] / 4)) << std::setfill('0') oss << std::hex << std::setw(int(idxWidths[i] / 4)) << std::setfill('0')
@@ -660,43 +711,75 @@ public:
if (i < indices.size() - 1) oss << "."; if (i < indices.size() - 1) oss << ".";
} }
write_var(var, 1ULL, write_var(var, 1ULL,
oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL); oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL,
randmodeIdx);
}
template <typename T>
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
update_struct_arr(T& var, const std::string& name) {
update_var(var, name.c_str());
} }
// Recursively process VlUnpacked of structs // Recursively process VlUnpacked of structs
template <typename T, std::size_t N_Depth> template <typename T, std::size_t N_Depth>
void record_struct_arr(VlUnpacked<T, N_Depth>& var, const std::string& name, int dimension, void record_struct_arr(VlUnpacked<T, N_Depth>& var, const std::string& name, int dimension,
std::vector<IData> indices, std::vector<size_t> idxWidths) { std::vector<IData> indices, std::vector<size_t> idxWidths,
std::uint32_t randmodeIdx) {
if (dimension > 0 && N_Depth != 0) { if (dimension > 0 && N_Depth != 0) {
constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1); constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1);
idxWidths.push_back(idx_width); idxWidths.push_back(idx_width);
for (size_t i = 0; i < N_Depth; ++i) { for (size_t i = 0; i < N_Depth; ++i) {
indices.push_back(i); indices.push_back(i);
record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths); record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths,
randmodeIdx);
indices.pop_back(); indices.pop_back();
} }
} }
} }
template <typename T, std::size_t N_Depth>
void update_struct_arr(VlUnpacked<T, N_Depth>& var, const std::string& name) {
constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1);
for (size_t i = 0; i < N_Depth; ++i) {
std::ostringstream oss;
oss << name << "." << std::hex << std::setw(int(idx_width / 4)) << std::setfill('0')
<< static_cast<int>(i);
update_struct_arr(var[i], oss.str());
}
}
// Recursively process VlQueue of structs // Recursively process VlQueue of structs
template <typename T, size_t N_MaxSize> template <typename T, size_t N_MaxSize>
void record_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension, void record_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name, int dimension,
std::vector<IData> indices, std::vector<size_t> idxWidths) { std::vector<IData> indices, std::vector<size_t> idxWidths,
std::uint32_t randmodeIdx) {
if ((dimension > 0) && (var.size() != 0)) { if ((dimension > 0) && (var.size() != 0)) {
idxWidths.push_back(32); idxWidths.push_back(32);
for (size_t i = 0; i < var.size(); ++i) { for (size_t i = 0; i < var.size(); ++i) {
indices.push_back(i); indices.push_back(i);
record_struct_arr(var.atWrite(i), name, dimension - 1, indices, idxWidths); record_struct_arr(var.atWrite(i), name, dimension - 1, indices, idxWidths,
randmodeIdx);
indices.pop_back(); indices.pop_back();
} }
} }
} }
template <typename T, size_t N_MaxSize>
void update_struct_arr(VlQueue<T, N_MaxSize>& var, const std::string& name) {
for (size_t i = 0; i < var.size(); ++i) {
std::ostringstream oss;
oss << name << "." << std::hex << std::setw(8) << std::setfill('0')
<< static_cast<int>(i);
update_struct_arr(var.atWrite(i), oss.str());
}
}
// Recursively process associative arrays of structs // Recursively process associative arrays of structs
template <typename T_Key, typename T_Value> template <typename T_Key, typename T_Value>
void record_struct_arr(VlAssocArray<T_Key, T_Value>& var, const std::string& name, void record_struct_arr(VlAssocArray<T_Key, T_Value>& var, const std::string& name,
int dimension, const std::vector<IData>& indices, int dimension, const std::vector<IData>& indices,
const std::vector<size_t>& idxWidths) { const std::vector<size_t>& idxWidths, std::uint32_t randmodeIdx) {
if ((dimension > 0) && (!var.empty())) { if ((dimension > 0) && (!var.empty())) {
for (auto it = var.begin(); it != var.end(); ++it) { for (auto it = var.begin(); it != var.end(); ++it) {
const T_Key& key = it->first; const T_Key& key = it->first;
@@ -714,23 +797,127 @@ public:
std::string result = oss.str(); std::string result = oss.str();
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0'); result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
record_struct_arr(var.atWrite(key), name + "." + result, dimension - 1, indices, record_struct_arr(var.atWrite(key), name + "." + result, dimension - 1, indices,
idxWidths); idxWidths, randmodeIdx);
} }
} }
} }
template <typename T_Key, typename T_Value>
void update_struct_arr(VlAssocArray<T_Key, T_Value>& var, const std::string& name) {
for (auto it = var.begin(); it != var.end(); ++it) {
const T_Key& key = it->first;
std::string indexed_name;
std::vector<size_t> integral_index;
size_t idx_width = 0;
process_key(key, indexed_name, integral_index, name, idx_width);
std::ostringstream oss;
for (size_t i = 0; i < integral_index.size(); ++i) {
oss << std::hex << static_cast<int>(integral_index[i]);
}
std::string result = oss.str();
result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
update_struct_arr(var.atWrite(key), name + "." + result);
}
}
// --- Helper functions --- // --- Helper functions ---
// Helper: Register all members of a user-defined struct // Helper: Register all members of a user-defined struct
template <typename T, std::size_t... I> template <typename T, std::size_t... I>
void modifyMembers(T& obj, std::index_sequence<I...>, const std::string& baseName) { void modifyMembers(T& obj, std::index_sequence<I...>, const std::string& baseName,
std::uint32_t randmodeIdx) {
// Use the indices to access each member via std::get // Use the indices to access each member via std::get
(void)std::initializer_list<int>{ (void)std::initializer_list<int>{
(write_var(std::get<I>(obj.getMembers(obj)), obj.memberWidth()[I], (write_var(std::get<I>(obj.getMembers(obj)), obj.memberWidth()[I],
(baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I]), (baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I],
randmodeIdx),
0)...}; 0)...};
} }
template <typename T, std::size_t... I>
void updateMembers(T& obj, std::index_sequence<I...>, const std::string& baseName) {
(void)std::initializer_list<int>{
(update_var(std::get<I>(obj.getMembers(obj)),
(baseName + "." + obj.memberNames()[I]).c_str()),
0)...};
}
template <typename T>
typename std::enable_if<!VlContainsCustomStruct<T>::value && !IsVlUnpacked<T>::value,
void>::type
update_var(T& var, const char* name) {
auto it = m_vars.find(name);
assert(it != m_vars.end());
it->second = std::make_shared<const VlRandomVar>(
name, it->second->width(), &var, it->second->dimension(), it->second->randModeIdx());
}
template <typename T>
typename std::enable_if<VlIsCustomStruct<T>::value, void>::type update_var(T& var,
const char* name) {
updateMembers(var, var.memberIndices(), name);
}
template <typename T, size_t N_MaxSize>
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
update_var(VlQueue<T, N_MaxSize>& var, const char* name) {
auto it = m_vars.find(name);
assert(it != m_vars.end());
const int dimension = it->second->dimension();
it->second = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T, N_MaxSize>>>(
name, it->second->width(), &var, dimension, it->second->randModeIdx());
m_index = 0;
update_arr_table(var, name, dimension);
}
template <typename T, size_t N_MaxSize>
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
update_var(VlQueue<T, N_MaxSize>& var, const char* name) {
update_struct_arr(var, name);
}
template <typename T, std::size_t N_Depth>
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
update_var(VlUnpacked<T, N_Depth>& var, const char* name) {
auto it = m_vars.find(name);
assert(it != m_vars.end());
const int dimension = it->second->dimension();
it->second = std::make_shared<const VlRandomArrayVarTemplate<VlUnpacked<T, N_Depth>>>(
name, it->second->width(), &var, dimension, it->second->randModeIdx());
m_index = 0;
update_arr_table(var, name, dimension);
}
template <typename T, std::size_t N_Depth>
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
update_var(VlUnpacked<T, N_Depth>& var, const char* name) {
update_struct_arr(var, name);
}
template <typename T_Key, typename T_Value>
typename std::enable_if<!VlContainsCustomStruct<T_Value>::value, void>::type
update_var(VlAssocArray<T_Key, T_Value>& var, const char* name) {
auto it = m_vars.find(name);
assert(it != m_vars.end());
const int dimension = it->second->dimension();
std::vector<size_t> keyWidths;
VlRandomAssocKeyWidths<VlAssocArray<T_Key, T_Value>>::push(keyWidths);
it->second
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
name, it->second->width(), &var, dimension, it->second->randModeIdx(), keyWidths);
m_index = 0;
update_arr_table(var, name, dimension);
}
template <typename T_Key, typename T_Value>
typename std::enable_if<VlContainsCustomStruct<T_Value>::value, void>::type
update_var(VlAssocArray<T_Key, T_Value>& var, const char* name) {
update_struct_arr(var, name);
}
// Helper: Generate unique variable key from name and index // Helper: Generate unique variable key from name and index
static std::string generateKey(const std::string& name, int idx) { static std::string generateKey(const std::string& name, int idx) {
if (!name.empty() && name[0] == '\\') { if (!name.empty() && name[0] == '\\') {
@@ -765,7 +952,7 @@ public:
void disable_soft(const std::string& varName); void disable_soft(const std::string& varName);
void clearConstraints(); void clearConstraints();
void clearAll(); // Clear both constraints and variables 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, void solveBefore(const std::string& beforeName,
const std::string& afterName); // Register solve-before ordering const std::string& afterName); // Register solve-before ordering
void set_randmode(const VlQueue<CData>& randmode) { m_randmodep = &randmode; } void set_randmode(const VlQueue<CData>& randmode) { m_randmodep = &randmode; }
+56 -46
View File
@@ -99,46 +99,6 @@ package std;
endfunction endfunction
endclass endclass
// IEEE 1800-specified standard "semaphore"
class semaphore;
protected int m_keyCount;
protected int m_nextKeyCount = '1;
protected longint unsigned m_ticket = 0;
protected longint unsigned m_nextTicket = 0;
function new(int keyCount = 0);
m_keyCount = keyCount;
endfunction
function void put(int keyCount = 1);
m_keyCount += keyCount;
endfunction
task get(int keyCount = 1);
`ifdef VERILATOR_TIMING
longint unsigned ticket;
// Fast path: take if keys fit AND either no one is queued, or
// the head still doesn't fit (so we're not stealing its keys).
if (m_keyCount >= keyCount && m_nextKeyCount > m_keyCount) begin
m_keyCount -= keyCount;
return;
end
ticket = m_nextTicket++;
wait (m_ticket == ticket);
m_nextKeyCount = keyCount;
wait (m_keyCount >= keyCount);
m_keyCount -= keyCount;
m_ticket++;
`endif
endtask
function int try_get(int keyCount = 1);
if (m_keyCount < keyCount) return 0;
m_keyCount -= keyCount;
return 1;
endfunction
endclass
// IEEE 1800-specified standard "process" // IEEE 1800-specified standard "process"
class process; class process;
typedef enum { typedef enum {
@@ -243,6 +203,56 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
endfunction endfunction
endclass endclass
// IEEE 1800-specified standard "semaphore"
class semaphore;
typedef struct {
process proc;
int reqKeys;
} procKeys;
procKeys m_queue[$];
protected int m_keyCount;
local task dropKilled;
// Drop killed processes from the queue
`ifdef VERILATOR_TIMING
while (m_queue.size() > 0 && m_queue[0].proc.status() == process::KILLED) m_queue.pop_front();
`endif
endtask
function new(int keyCount = 0);
m_keyCount = keyCount;
endfunction
function void put(int keyCount = 1);
m_keyCount += keyCount;
endfunction
task get(int keyCount = 1);
`ifdef VERILATOR_TIMING
dropKilled();
// Fast path: take if keys fit AND either no one is queued, or
// the head still doesn't fit (so we're not stealing its keys).
if (m_keyCount >= keyCount && (m_queue.size() == 0 || m_queue[0].reqKeys > keyCount)) begin
m_keyCount -= keyCount;
return;
end
m_queue.push_back('{process::self(), keyCount});
while (m_queue[0].proc != process::self()) begin
wait (m_queue[0].proc.status() == process::KILLED || m_queue[0].proc == process::self());
dropKilled();
end
wait (m_keyCount >= keyCount);
m_keyCount -= keyCount;
m_queue.pop_front();
`endif
endtask
function int try_get(int keyCount = 1);
if (m_keyCount < keyCount) return 0;
m_keyCount -= keyCount;
return 1;
endfunction
endclass
// IEEE 1800-specified standard "std::randomize" // IEEE 1800-specified standard "std::randomize"
function int randomize(); function int randomize();
randomize = 0; randomize = 0;
@@ -253,7 +263,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
// them as such currently, so named with a unique prefix // them as such currently, so named with a unique prefix
typedef struct { typedef struct {
string name; string name;
int weight; int weight = 1;
int goal; int goal;
string comment; string comment;
int at_least; int at_least;
@@ -266,7 +276,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
} vl_covergroup_options_t; } vl_covergroup_options_t;
typedef struct { typedef struct {
int weight; int weight = 1;
int goal; int goal;
string comment; string comment;
int at_least; int at_least;
@@ -275,7 +285,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
} vl_coverpoint_options_t; } vl_coverpoint_options_t;
typedef struct { typedef struct {
int weight; int weight = 1;
int goal; int goal;
string comment; string comment;
int at_least; int at_least;
@@ -284,7 +294,7 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
} vl_cross_options_t; } vl_cross_options_t;
typedef struct { typedef struct {
int weight; int weight = 1;
int goal; int goal;
string comment; string comment;
bit strobe; bit strobe;
@@ -294,14 +304,14 @@ inline bool VlClassRef<`systemc_class_name>::operator<(const VlClassRef<`systemc
} vl_covergroup_type_options_t; } vl_covergroup_type_options_t;
typedef struct { typedef struct {
int weight; int weight = 1;
int goal; int goal;
string comment; string comment;
real real_interval; real real_interval;
} vl_coverpoint_type_options_t; } vl_coverpoint_type_options_t;
typedef struct { typedef struct {
int weight; int weight = 1;
int goal; int goal;
string comment; string comment;
} vl_cross_type_options_t; } vl_cross_type_options_t;
+5 -5
View File
@@ -45,11 +45,11 @@ lint_off -rule WIDTHTRUNC `VLT_UVM_FILES
// Context-sensitive waivers // Context-sensitive waivers
lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case ({is_R, is_W})*" lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case ({is_R, is_W})*"
lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case(orig_severity)*" lint_off -rule CASEINCOMPLETE `VLT_UVM_FILES -match "* case(orig_severity)*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_callback*" lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_callback*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_component*" lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_component*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_event*" lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_event*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_report_object*" lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_report_object*"
lint_off -rule CASTCONST `VLT_UVM_FILES -match "*class{}uvm_sequence_item*" lint_off -rule CASTCONST `VLT_UVM_FILES -match "*'class uvm_sequence_item*"
lint_off -rule IGNOREDRETURN `VLT_UVM_FILES -match "* uvm_core_state_callback::add*" lint_off -rule IGNOREDRETURN `VLT_UVM_FILES -match "* uvm_core_state_callback::add*"
lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (abstractions[i])*" lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (abstractions[i])*"
lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (lock_list[i])*" lint_off -rule MISINDENT `VLT_UVM_FILES -match "* foreach (lock_list[i])*"
+20 -5
View File
@@ -38,8 +38,8 @@
// See also V3Ast::VNumRange // See also V3Ast::VNumRange
class VerilatedRange final { class VerilatedRange final {
int m_left = 0; int m_left = 0; // Left side of range (pre-':')
int m_right = 0; int m_right = 0; // Right side of range (post-':')
protected: protected:
friend class VerilatedVarProps; friend class VerilatedVarProps;
@@ -158,7 +158,18 @@ public:
VerilatedVarFlags vldir() const { VerilatedVarFlags vldir() const {
return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR); return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR);
} }
uint32_t entSize() const VL_MT_SAFE; uint32_t entSize() const VL_MT_SAFE {
if (m_entSize) return m_entSize;
switch (vltype()) {
case VLVT_PTR: return sizeof(void*);
case VLVT_UINT8: return sizeof(CData);
case VLVT_UINT16: return sizeof(SData);
case VLVT_UINT32: return sizeof(IData);
case VLVT_UINT64: return sizeof(QData);
case VLVT_WDATA: return VL_WORDS_I(entBits()) * sizeof(IData);
default: return 0; // LCOV_EXCL_LINE
}
}
uint32_t entBits() const VL_MT_SAFE { uint32_t entBits() const VL_MT_SAFE {
uint32_t bits = 1; uint32_t bits = 1;
for (auto it : m_packed) bits *= it.elements(); for (auto it : m_packed) bits *= it.elements();
@@ -214,7 +225,11 @@ public:
: 0; : 0;
} }
// Total size in bytes (note DPI limited to 4GB) // Total size in bytes (note DPI limited to 4GB)
size_t totalSize() const; size_t totalSize() const {
size_t size = entSize();
for (int udim = 0; udim < udims(); ++udim) size *= m_unpacked[udim].elements();
return size;
}
// Adjust a data pointer to access a given array element, NULL if something goes bad // Adjust a data pointer to access a given array element, NULL if something goes bad
void* datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE; void* datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE;
}; };
@@ -268,7 +283,7 @@ class VerilatedVar final : public VerilatedVarProps {
m_forceControlSignals; // Force control signals m_forceControlSignals; // Force control signals
protected: protected:
const bool m_isParam; const bool m_isParam; // From a parameter
friend class VerilatedScope; friend class VerilatedScope;
// CONSTRUCTORS // CONSTRUCTORS
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype, VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
+9
View File
@@ -72,4 +72,13 @@ public:
~VerilatedHierarchyMap() = default; ~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 #endif // Guard
+1 -1
View File
@@ -140,7 +140,7 @@ class VlWorkerThread final {
}; };
// MEMBERS // MEMBERS
mutable VerilatedMutex m_mutex; mutable VerilatedMutex m_mutex; // Protection mutex
std::condition_variable_any m_cv; std::condition_variable_any m_cv;
// Only notify the condition_variable if the worker is waiting // Only notify the condition_variable if the worker is waiting
bool m_waiting VL_GUARDED_BY(m_mutex) = false; bool m_waiting VL_GUARDED_BY(m_mutex) = false;
+5 -2
View File
@@ -296,8 +296,11 @@ void VlProcess::forkSyncOnKillClear(VlForkSyncState* forkSyncp) {
} }
void VlProcess::state(int s) { void VlProcess::state(int s) {
if (s == KILLED && m_state != KILLED && m_state != FINISHED && m_forkSyncOnKillp if (completed()) {
&& !m_forkSyncOnKillDone) { // We can't change the sttaus of completed process
return;
}
if (s == KILLED && m_forkSyncOnKillp && !m_forkSyncOnKillDone) {
m_forkSyncOnKillDone = true; m_forkSyncOnKillDone = true;
m_state = s; m_state = s;
m_forkSyncOnKillp->done(); m_forkSyncOnKillp->done();
+1 -31
View File
@@ -64,36 +64,6 @@
// Placeholder for compiling with --protect-ids // Placeholder for compiling with --protect-ids
#define VL_UNKNOWN "<unknown>" #define VL_UNKNOWN "<unknown>"
//=============================================================================
// VlFileLineDebug stores a SystemVerilog source code location. Used in VlCoroutineHandle for
// debugging purposes.
class VlFileLineDebug final {
// MEMBERS
#ifdef VL_DEBUG
const char* m_filename = nullptr;
int m_lineno = 0;
#endif
public:
// CONSTRUCTORS
// Construct
VlFileLineDebug() = default;
VlFileLineDebug(const char* filename, int lineno)
#ifdef VL_DEBUG
: m_filename{filename}
, m_lineno{lineno}
#endif
{
}
// METHODS
#ifdef VL_DEBUG
const char* filename() const { return m_filename; }
int lineno() const { return m_lineno; }
#endif
};
//============================================================================= //=============================================================================
// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is // VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got // cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
@@ -431,7 +401,7 @@ class VlForkSyncState final {
public: public:
size_t m_counter = 0; // When reaches 0, resume suspended coroutine size_t m_counter = 0; // When reaches 0, resume suspended coroutine
VlCoroutineHandle m_susp; // Coroutine to resume VlCoroutineHandle m_susp; // Coroutine to resume
bool m_inited = false; bool m_inited = false; // Initialization complete
size_t m_pendingDones = 0; // done() calls seen before init() (e.g. early killed branch) 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_inDone = false; // Guard against re-entrant resume recursion from nested kills
bool m_resumePending = false; // Join reached zero again while inside done() bool m_resumePending = false; // Join reached zero again while inside done()
+3 -3
View File
@@ -147,9 +147,9 @@ private:
struct CallbackRecord final { struct CallbackRecord final {
union { // The callback union { // The callback
const initCb_t m_initCb; const initCb_t m_initCb; // Init-callback constructor
const dumpCb_t m_dumpCb; const dumpCb_t m_dumpCb; // Dump-callback constructor
const cleanupCb_t m_cleanupCb; const cleanupCb_t m_cleanupCb; // Cleanup-callback constructor
}; };
const uint32_t m_fidx; // The index of the tracing function const uint32_t m_fidx; // The index of the tracing function
void* const m_userp; // The user pointer to pass to the callback (the symbol table) void* const m_userp; // The user pointer to pass to the callback (the symbol table)
+19 -1
View File
@@ -32,6 +32,7 @@
#include <array> #include <array>
#include <atomic> #include <atomic>
#include <deque> #include <deque>
#include <initializer_list>
#include <map> #include <map>
#include <memory> #include <memory>
#include <set> #include <set>
@@ -231,7 +232,7 @@ extern std::string VL_TO_STRING(SData lhs);
extern std::string VL_TO_STRING(IData lhs); extern std::string VL_TO_STRING(IData lhs);
extern std::string VL_TO_STRING(QData lhs); extern std::string VL_TO_STRING(QData lhs);
extern std::string VL_TO_STRING(double 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> template <std::size_t N_Words>
inline std::string VL_TO_STRING(const VlWide<N_Words>& obj) { inline std::string VL_TO_STRING(const VlWide<N_Words>& obj) {
return VL_TO_STRING_W(N_Words, obj); return VL_TO_STRING_W(N_Words, obj);
@@ -1084,6 +1085,15 @@ public:
// CONSTRUCTORS // CONSTRUCTORS
// m_defaultValue isn't defaulted. Caller's constructor must do it. // m_defaultValue isn't defaulted. Caller's constructor must do it.
VlAssocArray() = default; VlAssocArray() = default;
// Construct with the given entries
explicit VlAssocArray(std::initializer_list<std::pair<const T_Key, T_Value>> init)
: m_map{init}
, m_defaultValue{} {}
// Construct with the given default value and entries
explicit VlAssocArray(const T_Value& defaultValue,
std::initializer_list<std::pair<const T_Key, T_Value>> init)
: m_map{init}
, m_defaultValue{defaultValue} {}
~VlAssocArray() = default; ~VlAssocArray() = default;
VlAssocArray(const VlAssocArray&) = default; VlAssocArray(const VlAssocArray&) = default;
VlAssocArray(VlAssocArray&&) = default; VlAssocArray(VlAssocArray&&) = default;
@@ -1468,6 +1478,14 @@ public:
constexpr std::size_t size() const { return N_Depth; } 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) { void fill(const T_Value& value) {
std::fill(std::begin(m_storage), std::end(m_storage), value); std::fill(std::begin(m_storage), std::end(m_storage), value);
} }
+307 -137
View File
@@ -253,34 +253,34 @@ public:
if (m_fullname.empty()) m_fullname = std::string{m_scopep->name()} + '.' + m_varp->name(); if (m_fullname.empty()) m_fullname = std::string{m_scopep->name()} + '.' + m_varp->name();
return m_fullname.c_str(); return m_fullname.c_str();
} }
virtual void* varDatap() const { return m_varp->datap(); } virtual void* readDatap() const { return m_varp->datap(); }
CData* varCDatap() const { CData* readCDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT8);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT8););
return reinterpret_cast<CData*>(varDatap()); return static_cast<CData*>(readDatap());
} }
SData* varSDatap() const { SData* readSDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT16);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT16););
return reinterpret_cast<SData*>(varDatap()); return static_cast<SData*>(readDatap());
} }
IData* varIDatap() const { IData* readIDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT32);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT32););
return reinterpret_cast<IData*>(varDatap()); return static_cast<IData*>(readDatap());
} }
QData* varQDatap() const { QData* readQDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT64);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT64););
return reinterpret_cast<QData*>(varDatap()); return static_cast<QData*>(readDatap());
} }
EData* varEDatap() const { EData* readEDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_WDATA);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_WDATA););
return reinterpret_cast<EData*>(varDatap()); return static_cast<EData*>(readDatap());
} }
double* varRealDatap() const { double* readRealDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_REAL);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_REAL););
return reinterpret_cast<double*>(varDatap()); return static_cast<double*>(readDatap());
} }
std::string* varStringDatap() const { std::string* readStringDatap() const {
VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_STRING);); VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_STRING););
return reinterpret_cast<std::string*>(varDatap()); return static_cast<std::string*>(readDatap());
} }
virtual uint32_t bitOffset() const { return 0; } virtual uint32_t bitOffset() const { return 0; }
}; };
@@ -363,11 +363,13 @@ protected:
public: public:
explicit VerilatedVpioScope(const VerilatedScope* scopep) explicit VerilatedVpioScope(const VerilatedScope* scopep)
: m_scopep{scopep} { : m_scopep{scopep} {
m_fullname = m_scopep->name(); m_fullname = vpiFullnamep(m_scopep->name());
if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
m_name = m_scopep->identifier(); m_name = m_scopep->identifier();
m_defname = m_scopep->defname(); 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; ~VerilatedVpioScope() override = default;
// cppcheck-suppress duplInheritedMember // cppcheck-suppress duplInheritedMember
static VerilatedVpioScope* castp(vpiHandle h) { static VerilatedVpioScope* castp(vpiHandle h) {
@@ -410,6 +412,9 @@ public:
m_name = name; m_name = name;
m_fullNameOverride = fullname; m_fullNameOverride = fullname;
} }
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep,
const std::string& name, const std::string& fullname)
: VerilatedVpioVar{varp, scopep, varp->datap(), name, fullname} {}
explicit VerilatedVpioVar(const VerilatedVpioVar* vop) explicit VerilatedVpioVar(const VerilatedVpioVar* vop)
: VerilatedVpioVarBase{vop} { : VerilatedVpioVarBase{vop} {
if (vop) { if (vop) {
@@ -515,7 +520,7 @@ public:
const std::string localName = _vl_vpi_member_local_name(memberVarp->name()); const std::string localName = _vl_vpi_member_local_name(memberVarp->name());
return new VerilatedVpioVar{memberVarp, scopep(), return new VerilatedVpioVar{memberVarp, scopep(),
static_cast<uint8_t*>(varDatap()) + offset, localName, static_cast<uint8_t*>(readDatap()) + offset, localName,
std::string{fullname()} + memberName.substr(parentLen)}; std::string{fullname()} + memberName.substr(parentLen)};
} }
uint32_t type() const override { uint32_t type() const override {
@@ -540,7 +545,7 @@ public:
return m_fullname.c_str(); return m_fullname.c_str();
} }
uint8_t* prevDatap() const { return m_prevDatap; } uint8_t* prevDatap() const { return m_prevDatap; }
void* varDatap() const override { return m_varDatap; } void* readDatap() const override { return m_varDatap; }
void createPrevDatap() { void createPrevDatap() {
if (VL_UNLIKELY(!m_prevDatap)) { if (VL_UNLIKELY(!m_prevDatap)) {
m_prevDatap = new uint8_t[entSize()]; m_prevDatap = new uint8_t[entSize()];
@@ -721,6 +726,17 @@ public:
uint32_t type() const override { return vpiModule; } 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 { class VerilatedVpioModuleIter final : public VerilatedVpio {
const std::vector<const VerilatedScope*>* m_vec; const std::vector<const VerilatedScope*>* m_vec;
std::vector<const VerilatedScope*>::const_iterator m_it; std::vector<const VerilatedScope*>::const_iterator m_it;
@@ -778,6 +794,8 @@ public:
return (new VerilatedVpioScope{modp})->castVpiHandle(); return (new VerilatedVpioScope{modp})->castVpiHandle();
} else if (itype == VerilatedScope::SCOPE_MODULE) { } else if (itype == VerilatedScope::SCOPE_MODULE) {
return (new VerilatedVpioModule{modp})->castVpiHandle(); return (new VerilatedVpioModule{modp})->castVpiHandle();
} else if (itype == VerilatedScope::SCOPE_INTERFACE) {
return (new VerilatedVpioInterface{modp})->castVpiHandle();
} }
} }
} }
@@ -803,6 +821,90 @@ public:
uint32_t type() const override { return vpiPackage; } 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 { class VerilatedVpioInstanceIter final : public VerilatedVpio {
const std::vector<const VerilatedScope*>* m_vec; const std::vector<const VerilatedScope*>* m_vec;
std::vector<const VerilatedScope*>::const_iterator m_it; std::vector<const VerilatedScope*>::const_iterator m_it;
@@ -995,22 +1097,13 @@ public:
case vpiOctStrVal: // FALLTHRU case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU case vpiHexStrVal: // FALLTHRU
case vpiStringVal: { case vpiStringVal: // FALLTHRU
if (VL_UNLIKELY(!valuep->value.str)) return false;
break;
}
case vpiScalarVal: // FALLTHRU case vpiScalarVal: // FALLTHRU
case vpiIntVal: // FALLTHRU case vpiIntVal: // FALLTHRU
case vpiRealVal: break; case vpiRealVal: // FALLTHRU
case vpiVectorVal: { case vpiVectorVal: return true;
if (VL_UNLIKELY(!valuep->value.vector)) return false; default: return false;
break;
} }
default: {
return false;
}
}
return true;
} }
}; };
@@ -1192,8 +1285,8 @@ public:
template <typename T> template <typename T>
static bool valueDiffersFromPrev(VerilatedVpioVar* varop) { 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", 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->fullname(), *(static_cast<CData*>(varop->readDatap())),
*(varop->prevDatap()), varop->varDatap(), varop->prevDatap(), *(varop->prevDatap()), varop->readDatap(), varop->prevDatap(),
varop->entSize());); varop->entSize()););
if (varop->bitSize() == 1) { if (varop->bitSize() == 1) {
T* const prevDatap = reinterpret_cast<T*>( T* const prevDatap = reinterpret_cast<T*>(
@@ -1204,7 +1297,7 @@ public:
prevInfo.m_datap = prevDatap; prevInfo.m_datap = prevDatap;
return vl_vpi_get_word_gen(currInfo) != vl_vpi_get_word_gen(prevInfo); return vl_vpi_get_word_gen(currInfo) != vl_vpi_get_word_gen(prevInfo);
} }
return std::memcmp(varop->prevDatap(), varop->varDatap(), varop->entSize()) != 0; return std::memcmp(varop->prevDatap(), varop->readDatap(), varop->entSize()) != 0;
} }
static bool valueDiffersFromPrev(VerilatedVpioVar* varop) { static bool valueDiffersFromPrev(VerilatedVpioVar* varop) {
switch (varop->varp()->vltype()) { switch (varop->varp()->vltype()) {
@@ -1233,9 +1326,9 @@ public:
prevInfo.m_datap = prevDatap; prevInfo.m_datap = prevDatap;
const T currWord = vl_vpi_get_word_gen(currInfo); const T currWord = vl_vpi_get_word_gen(currInfo);
vl_vpi_put_word_gen(prevInfo, currWord); vl_vpi_put_word_gen(prevInfo, currWord);
assert(std::memcmp(varop->prevDatap(), varop->varDatap(), varop->entSize()) == 0); assert(std::memcmp(varop->prevDatap(), varop->readDatap(), varop->entSize()) == 0);
} else { } else {
std::memcpy(varop->prevDatap(), varop->varDatap(), varop->entSize()); std::memcpy(varop->prevDatap(), varop->readDatap(), varop->entSize());
} }
} }
static void updatePrev(const VerilatedVpioVar* const varop) { static void updatePrev(const VerilatedVpioVar* const varop) {
@@ -1276,7 +1369,7 @@ public:
if (valueDiffersFromPrev(varop)) { if (valueDiffersFromPrev(varop)) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n", VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
ho.id(), varop->fullname(), ho.id(), varop->fullname(),
*(static_cast<CData*>(varop->varDatap())));); *(static_cast<CData*>(varop->readDatap()))););
update.insert(varop); update.insert(varop);
vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value); vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
(ho.cb_rtnp())(ho.cb_datap()); (ho.cb_rtnp())(ho.cb_datap());
@@ -1627,9 +1720,9 @@ VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVar* const baseSignal
// assert(forceEnableSignalVop->entSize() == baseSignalVop->entSize()); // assert(forceEnableSignalVop->entSize() == baseSignalVop->entSize());
assert(forceValueSignalVop->entSize() == baseSignalVop->entSize()); assert(forceValueSignalVop->entSize() == baseSignalVop->entSize());
assert(forceReadSignalVop->entSize() == baseSignalVop->entSize()); assert(forceReadSignalVop->entSize() == baseSignalVop->entSize());
assert(forceEnableSignalVop->varDatap() == forceEnableSignalVarp->datap()); assert(forceEnableSignalVop->readDatap() == forceEnableSignalVarp->datap());
assert(forceValueSignalVop->varDatap() == forceValueSignalVarp->datap()); assert(forceValueSignalVop->readDatap() == forceValueSignalVarp->datap());
assert(forceReadSignalVop->varDatap() == forceReadSignalVarp->datap()); assert(forceReadSignalVop->readDatap() == forceReadSignalVarp->datap());
#endif // VL_DEBUG #endif // VL_DEBUG
return VerilatedVpiImp::ForceControlSignalVops{ return VerilatedVpiImp::ForceControlSignalVops{
@@ -1659,9 +1752,9 @@ double VerilatedVpiImp::getReadDataWord(const VerilatedVpioVar* baseSignalVop,
const VerilatedVpioVar* forceEnableSignalVop, const VerilatedVpioVar* forceEnableSignalVop,
const VerilatedVpioVar* forceValueSignalVop, const VerilatedVpioVar* forceValueSignalVop,
size_t /*bitCount*/, size_t /*bitOffset*/) { size_t /*bitCount*/, size_t /*bitOffset*/) {
const double baseSignalData = *baseSignalVop->varRealDatap(); const double baseSignalData = *baseSignalVop->readRealDatap();
const bool forceEnableData = *forceEnableSignalVop->varCDatap(); const bool forceEnableData = *forceEnableSignalVop->readCDatap();
const double forceValueData = *forceValueSignalVop->varRealDatap(); const double forceValueData = *forceValueSignalVop->readRealDatap();
const double readData = forceEnableData ? forceValueData : baseSignalData; const double readData = forceEnableData ? forceValueData : baseSignalData;
return readData; return readData;
} }
@@ -1999,6 +2092,9 @@ const char* VerilatedVpiError::strFromVpiMethod(PLI_INT32 vpiVal) VL_PURE {
"vpiStmt" "vpiStmt"
}; };
// clang-format on // 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*"; if (vpiVal > vpiStmt || vpiVal < vpiCondition) return "*undefined*";
return names[vpiVal - vpiCondition]; return names[vpiVal - vpiCondition];
} }
@@ -2402,6 +2498,8 @@ void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiCondition); SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiCondition);
SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiStmt); 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, cbValueChange);
SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbAtEndOfSimTime); SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbAtEndOfSimTime);
@@ -2701,7 +2799,7 @@ _vl_vpi_handle_indexed_member_from_scope(const VerilatedScope* const scopep,
VerilatedVpioVar* baseVop VerilatedVpioVar* baseVop
= fullnameOverride.empty() = fullnameOverride.empty()
? new VerilatedVpioVar{baseVarp, varScopep} ? new VerilatedVpioVar{baseVarp, varScopep}
: new VerilatedVpioVar{baseVarp, varScopep, baseVarp->datap(), : new VerilatedVpioVar{baseVarp, varScopep,
_vl_vpi_member_local_name(baseVarp->name()), _vl_vpi_member_local_name(baseVarp->name()),
fullnameOverride}; fullnameOverride};
VerilatedVpioVar* vop = _vl_vpi_handle_apply_indices(baseVop, indices); VerilatedVpioVar* vop = _vl_vpi_handle_apply_indices(baseVop, indices);
@@ -2771,6 +2869,10 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope); const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
const VerilatedVpioVar* const voVarp = VerilatedVpioVar::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."))) { if (0 == std::strncmp(scopeAndName.c_str(), "$root.", std::strlen("$root."))) {
scopeAndName.erase(0, std::strlen("$root.")); scopeAndName.erase(0, std::strlen("$root."));
} else if (voScopep) { } else if (voScopep) {
@@ -2798,8 +2900,15 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
if (scopep->type() == VerilatedScope::SCOPE_PACKAGE) { if (scopep->type() == VerilatedScope::SCOPE_PACKAGE) {
return (new VerilatedVpioPackage{scopep})->castVpiHandle(); return (new VerilatedVpioPackage{scopep})->castVpiHandle();
} }
if (scopep->type() == VerilatedScope::SCOPE_INTERFACE) {
return (new VerilatedVpioInterface{scopep})->castVpiHandle();
}
return (new VerilatedVpioScope{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 basename = scopeAndName;
std::string scopename; std::string scopename;
std::string::size_type prevpos = std::string::npos; std::string::size_type prevpos = std::string::npos;
@@ -2862,10 +2971,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
if (varp->isParam()) { if (varp->isParam()) {
resultHandle = (new VerilatedVpioParam{varp, scopep})->castVpiHandle(); resultHandle = (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
} else if (!fullnameOverride.empty()) { } else if (!fullnameOverride.empty()) {
resultHandle resultHandle = (new VerilatedVpioVar{varp, scopep, _vl_vpi_member_local_name(varp->name()),
= (new VerilatedVpioVar{varp, scopep, varp->datap(), fullnameOverride})
_vl_vpi_member_local_name(varp->name()), fullnameOverride}) ->castVpiHandle();
->castVpiHandle();
} else { } else {
resultHandle = (new VerilatedVpioVar{varp, scopep})->castVpiHandle(); resultHandle = (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
} }
@@ -2959,6 +3067,24 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
const int32_t val = vop->index().back(); const int32_t val = vop->index().back();
return (new VerilatedVpioConst{val})->castVpiHandle(); 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: { case vpiScope: {
const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object); const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr; if (VL_UNLIKELY(!vop)) return nullptr;
@@ -3025,6 +3151,15 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
if (it == map->end()) return nullptr; if (it == map->end()) return nullptr;
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle()); 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: { case vpiInternalScope: {
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object); const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap(); const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
@@ -3235,6 +3370,18 @@ bool vl_check_format(const VerilatedVpioVarBase* vop, const p_vpi_value valuep,
return false; return false;
} }
static const char* vl_put_value_null_member(const p_vpi_value valuep) {
switch (valuep->format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: return valuep->value.str ? nullptr : "str";
case vpiVectorVal: return valuep->value.vector ? nullptr : "vector";
default: return nullptr;
}
}
// Get a VPI format that can be used to fully represent a signal of the given type // Get a VPI format that can be used to fully represent a signal of the given type
PLI_INT32 vl_get_vltype_format(VerilatedVarType vlType) { PLI_INT32 vl_get_vltype_format(VerilatedVarType vlType) {
switch (vlType) { switch (vlType) {
@@ -3294,7 +3441,7 @@ VarAccessInfo<T> vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t
bitCount, varBits - addOffset}); bitCount, varBits - addOffset});
VarAccessInfo<T> info; VarAccessInfo<T> info;
info.m_datap = reinterpret_cast<T*>(vop->varDatap()); info.m_datap = static_cast<T*>(vop->readDatap());
if (vop->varp()->vltype() == VLVT_WDATA) { if (vop->varp()->vltype() == VLVT_WDATA) {
assert(sizeof(T) == sizeof(EData)); assert(sizeof(T) == sizeof(EData));
assert(bitCount <= wordBits); assert(bitCount <= wordBits);
@@ -3342,8 +3489,7 @@ T vl_vpi_get_word_gen(VarAccessInfo<T> info) {
template <typename 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(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(vl_vpi_var_access_info<T>(vop, bitCount, addOffset));
return vl_vpi_get_word_gen(info);
} }
template <typename T> template <typename T>
@@ -3361,8 +3507,7 @@ void vl_vpi_put_word_gen(VarAccessInfo<T> info, T word) {
template <typename T> template <typename T>
void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) { 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(vl_vpi_var_access_info<T>(vop, bitCount, addOffset), word);
vl_vpi_put_word_gen(info, word);
} }
// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds // bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds
@@ -3399,7 +3544,7 @@ void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, s
void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
const VerilatedVar* const varp = vop->varp(); const VerilatedVar* const varp = vop->varp();
void* const varDatap = vop->varDatap(); void* const varDatap = vop->readDatap();
if (!vl_check_format(vop, valuep, true)) return; if (!vl_check_format(vop, valuep, true)) return;
// string data type is dynamic and may vary in size during simulation // string data type is dynamic and may vary in size during simulation
@@ -3440,7 +3585,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
return; return;
} else if (valuep->format == vpiBinStrVal) { } else if (valuep->format == vpiBinStrVal) {
t_outDynamicStr.resize(varBits); t_outDynamicStr.resize(varBits);
const CData* datap = reinterpret_cast<CData*>(varDatap); const CData* datap = static_cast<CData*>(varDatap);
for (size_t i = 0; i < varBits; ++i) { for (size_t i = 0; i < varBits; ++i) {
const size_t pos = i + vop->bitOffset(); const size_t pos = i + vop->bitOffset();
const char val = (datap[pos >> 3] >> (pos & 7)) & 1; const char val = (datap[pos >> 3] >> (pos & 7)) & 1;
@@ -3485,10 +3630,10 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
} else if (valuep->format == vpiStringVal) { } else if (valuep->format == vpiStringVal) {
if (varp->vltype() == VLVT_STRING) { if (varp->vltype() == VLVT_STRING) {
if (varp->isParam()) { if (varp->isParam()) {
valuep->value.str = reinterpret_cast<char*>(varDatap); valuep->value.str = static_cast<char*>(varDatap);
return; return;
} }
t_outDynamicStr = *vop->varStringDatap(); t_outDynamicStr = *vop->readStringDatap();
valuep->value.str = const_cast<char*>(t_outDynamicStr.c_str()); valuep->value.str = const_cast<char*>(t_outDynamicStr.c_str());
return; return;
} else { } else {
@@ -3506,7 +3651,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
valuep->value.integer = vl_vpi_get_word(vop, 32, 0); valuep->value.integer = vl_vpi_get_word(vop, 32, 0);
return; return;
} else if (valuep->format == vpiRealVal) { } else if (valuep->format == vpiRealVal) {
valuep->value.real = *(vop->varRealDatap()); valuep->value.real = *(vop->readRealDatap());
return; return;
} else if (valuep->format == vpiScalarVal) { } else if (valuep->format == vpiScalarVal) {
valuep->value.scalar = vl_vpi_get_word(vop, 32, 0) ? vpi1 : vpi0; valuep->value.scalar = vl_vpi_get_word(vop, 32, 0) ? vpi1 : vpi0;
@@ -3597,7 +3742,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
baseSignalVop->fullname(), valuep->format, baseSignalVop->fullname(), valuep->format,
valuep->value.integer); valuep->value.integer);
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n",
baseSignalVop->varp()->datap(), baseSignalVop->varDatap());); baseSignalVop->varp()->datap(), baseSignalVop->readDatap()););
if (VL_UNLIKELY(!baseSignalVop->varp()->isPublicRW())) { if (VL_UNLIKELY(!baseSignalVop->varp()->isPublicRW())) {
VL_VPI_ERROR_(__FILE__, __LINE__, VL_VPI_ERROR_(__FILE__, __LINE__,
@@ -3616,6 +3761,16 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
return nullptr; return nullptr;
} }
if (!vl_check_format(baseSignalVop, valuep, false)) return nullptr; if (!vl_check_format(baseSignalVop, valuep, false)) return nullptr;
// On release valuep is only written
if (forceFlag != vpiReleaseFlag) {
if (const char* const memberp = vl_put_value_null_member(valuep)) {
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Ignoring nullptr value.%s with format %s for '%s'", __func__,
memberp, VerilatedVpiError::strFromVpiVal(valuep->format),
baseSignalVop->fullname());
return nullptr;
}
}
if (delay_mode == vpiInertialDelay) { if (delay_mode == vpiInertialDelay) {
if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) { if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) {
VL_VPI_WARNING_( VL_VPI_WARNING_(
@@ -3675,7 +3830,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
if (baseSignalVop->varp()->vltype() == VLVT_REAL) { if (baseSignalVop->varp()->vltype() == VLVT_REAL) {
const double readData = VerilatedVpiImp::getReadDataWord<double>( const double readData = VerilatedVpiImp::getReadDataWord<double>(
baseSignalVop, forceEnableSignalVop, forceValueSignalVop, 64, 0); baseSignalVop, forceEnableSignalVop, forceValueSignalVop, 64, 0);
*forceReadSignalVop->varRealDatap() = readData; *forceReadSignalVop->readRealDatap() = readData;
return; return;
} }
@@ -3780,7 +3935,6 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
} }
if (valuep->format == vpiVectorVal) { if (valuep->format == vpiVectorVal) {
if (VL_UNLIKELY(!valuep->value.vector)) return nullptr;
if (valueVop->varp()->vltype() == VLVT_WDATA) { if (valueVop->varp()->vltype() == VLVT_WDATA) {
const int words = VL_WORDS_I(varBits); const int words = VL_WORDS_I(varBits);
for (int i = 0; i < words; ++i) for (int i = 0; i < words; ++i)
@@ -3797,7 +3951,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
} }
} else if (valuep->format == vpiBinStrVal) { } else if (valuep->format == vpiBinStrVal) {
const int len = std::strlen(valuep->value.str); const int len = std::strlen(valuep->value.str);
CData* const datap = reinterpret_cast<CData*>(valueVop->varDatap()); CData* const datap = static_cast<CData*>(valueVop->readDatap());
for (int i = 0; i < varBits; ++i) { for (int i = 0; i < varBits; ++i) {
const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1'); const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1');
const size_t pos = valueVop->bitOffset() + i; const size_t pos = valueVop->bitOffset() + i;
@@ -3879,7 +4033,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
} else if (valuep->format == vpiStringVal) { } else if (valuep->format == vpiStringVal) {
if (valueVop->varp()->vltype() == VLVT_STRING) { if (valueVop->varp()->vltype() == VLVT_STRING) {
// Does not use valueVop, because strings are not forceable anyway // Does not use valueVop, because strings are not forceable anyway
*(baseSignalVop->varStringDatap()) = valuep->value.str; *(baseSignalVop->readStringDatap()) = valuep->value.str;
return object; return object;
} }
const int chars = VL_BYTES_I(varBits); const int chars = VL_BYTES_I(varBits);
@@ -3896,7 +4050,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
return object; return object;
} else if (valuep->format == vpiRealVal) { } else if (valuep->format == vpiRealVal) {
if (valueVop->varp()->vltype() == VLVT_REAL) { if (valueVop->varp()->vltype() == VLVT_REAL) {
*(valueVop->varRealDatap()) = valuep->value.real; *(valueVop->readRealDatap()) = valuep->value.real;
if (baseSignalVop->varp()->isForceable()) updateVforceRd(); if (baseSignalVop->varp()->isForceable()) updateVforceRd();
return object; return object;
} }
@@ -3941,7 +4095,7 @@ bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arra
case VLVT_UINT8: case VLVT_UINT8:
case VLVT_UINT16: case VLVT_UINT16:
case VLVT_UINT32: return true; case VLVT_UINT32: return true;
default:; // LCOV_EXCL_LINE default:;
} }
break; break;
case vpiRawTwoStateVal: case vpiRawTwoStateVal:
@@ -3959,7 +4113,7 @@ bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arra
switch (varp->vltype()) { switch (varp->vltype()) {
case VLVT_UINT8: case VLVT_UINT8:
case VLVT_UINT16: return true; case VLVT_UINT16: return true;
default:; // LCOV_EXCL_LINE default:;
} }
break; break;
case vpiLongIntVal: case vpiLongIntVal:
@@ -3968,7 +4122,7 @@ bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arra
case VLVT_UINT16: case VLVT_UINT16:
case VLVT_UINT32: case VLVT_UINT32:
case VLVT_UINT64: return true; case VLVT_UINT64: return true;
default:; // LCOV_EXCL_LINE default:;
} }
break; break;
default:; default:;
@@ -4167,10 +4321,10 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->varCDatap(), shortintsp); vop->readCDatap(), shortintsp);
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->varSDatap(), shortintsp); vop->readSDatap(), shortintsp);
} }
return; return;
@@ -4182,13 +4336,13 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->varCDatap(), integersp); vop->readCDatap(), integersp);
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->varSDatap(), integersp); vop->readSDatap(), integersp);
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->varIDatap(), integersp); vop->readIDatap(), integersp);
} }
return; return;
@@ -4200,16 +4354,16 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->varCDatap(), longintsp); vop->readCDatap(), longintsp);
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->varSDatap(), longintsp); vop->readSDatap(), longintsp);
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->varIDatap(), longintsp); vop->readIDatap(), longintsp);
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
vop->varQDatap(), longintsp); vop->readQDatap(), longintsp);
} }
return; return;
@@ -4221,19 +4375,19 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->varCDatap(), vectorsp); vop->readCDatap(), vectorsp);
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->varSDatap(), vectorsp); vop->readSDatap(), vectorsp);
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->varIDatap(), vectorsp); vop->readIDatap(), vectorsp);
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->varQDatap(), vectorsp); vop->readQDatap(), vectorsp);
} else if (varp->vltype() == VLVT_WDATA) { } else if (varp->vltype() == VLVT_WDATA) {
vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
vop->varEDatap(), vectorsp); vop->readEDatap(), vectorsp);
} }
return; return;
@@ -4245,19 +4399,19 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->varCDatap(), valuep); vop->readCDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->varSDatap(), valuep); vop->readSDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->varIDatap(), valuep); vop->readIDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->varQDatap(), valuep); vop->readQDatap(), valuep);
} else if (varp->vltype() == VLVT_WDATA) { } else if (varp->vltype() == VLVT_WDATA) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
vop->varEDatap(), valuep); vop->readEDatap(), valuep);
} }
return; return;
@@ -4269,19 +4423,19 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->varCDatap(), valuep); vop->readCDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->varSDatap(), valuep); vop->readSDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->varIDatap(), valuep); vop->readIDatap(), valuep);
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->varQDatap(), valuep); vop->readQDatap(), valuep);
} else if (varp->vltype() == VLVT_WDATA) { } else if (varp->vltype() == VLVT_WDATA) {
vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
vop->varEDatap(), valuep); vop->readEDatap(), valuep);
} }
return; return;
@@ -4344,17 +4498,8 @@ void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN
void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const PLI_INT32* index_p, void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const PLI_INT32* index_p,
PLI_UINT32 num) { PLI_UINT32 num) {
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return;
const VerilatedVar* const varp = vop->varp(); const VerilatedVar* const varp = vop->varp();
const int size = vop->size(); const int size = vop->size();
if (VL_UNCOVERABLE(num > size)) {
VL_VPI_ERROR_(__FILE__, __LINE__,
"%s: Requested elements to set (%u) exceed array size (%u)", __func__, num,
size);
return;
}
const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low(); const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low();
const int index const int index
@@ -4366,114 +4511,129 @@ void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const P
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
vop->varCDatap()); vop->readCDatap());
return;
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
vop->varSDatap()); vop->readSDatap());
return;
} }
return;
} else if (arrayvalue_p->format == vpiIntVal) { } else if (arrayvalue_p->format == vpiIntVal) {
const PLI_UINT32* integersp = reinterpret_cast<PLI_UINT32*>(arrayvalue_p->value.integers); const PLI_UINT32* integersp = reinterpret_cast<PLI_UINT32*>(arrayvalue_p->value.integers);
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
vop->varCDatap()); vop->readCDatap());
return;
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
vop->varSDatap()); vop->readSDatap());
return;
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
vop->varIDatap()); vop->readIDatap());
return;
} }
return;
} else if (arrayvalue_p->format == vpiLongIntVal) { } else if (arrayvalue_p->format == vpiLongIntVal) {
const PLI_UINT64* longintsp = reinterpret_cast<PLI_UINT64*>(arrayvalue_p->value.longints); const PLI_UINT64* longintsp = reinterpret_cast<PLI_UINT64*>(arrayvalue_p->value.longints);
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
vop->varCDatap()); vop->readCDatap());
return;
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
vop->varSDatap()); vop->readSDatap());
return;
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
vop->varIDatap()); vop->readIDatap());
return;
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
vop->varQDatap()); vop->readQDatap());
return;
} }
return;
} else if (arrayvalue_p->format == vpiVectorVal) { } else if (arrayvalue_p->format == vpiVectorVal) {
const p_vpi_vecval vectorsp = arrayvalue_p->value.vectors; const p_vpi_vecval vectorsp = arrayvalue_p->value.vectors;
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->varCDatap()); vectorsp, vop->readCDatap());
return;
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->varSDatap()); vectorsp, vop->readSDatap());
return;
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->varIDatap()); vectorsp, vop->readIDatap());
return;
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->varQDatap()); vectorsp, vop->readQDatap());
return;
} else if (varp->vltype() == VLVT_WDATA) { } else if (varp->vltype() == VLVT_WDATA) {
vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
vectorsp, vop->varEDatap()); vectorsp, vop->readEDatap());
return;
} }
return;
} else if (arrayvalue_p->format == vpiRawFourStateVal) { } else if (arrayvalue_p->format == vpiRawFourStateVal) {
const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals); const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals);
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->varCDatap()); vop->readCDatap());
return;
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->varSDatap()); vop->readSDatap());
return;
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->varIDatap()); vop->readIDatap());
return;
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->varQDatap()); vop->readQDatap());
return;
} else if (varp->vltype() == VLVT_WDATA) { } else if (varp->vltype() == VLVT_WDATA) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
vop->varEDatap()); vop->readEDatap());
return;
} }
return;
} else if (arrayvalue_p->format == vpiRawTwoStateVal) { } else if (arrayvalue_p->format == vpiRawTwoStateVal) {
const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals); const PLI_UBYTE8* valuep = reinterpret_cast<PLI_UBYTE8*>(arrayvalue_p->value.rawvals);
if (varp->vltype() == VLVT_UINT8) { if (varp->vltype() == VLVT_UINT8) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->varCDatap()); vop->readCDatap());
return;
} else if (varp->vltype() == VLVT_UINT16) { } else if (varp->vltype() == VLVT_UINT16) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->varSDatap()); vop->readSDatap());
return;
} else if (varp->vltype() == VLVT_UINT32) { } else if (varp->vltype() == VLVT_UINT32) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->varIDatap()); vop->readIDatap());
return;
} else if (varp->vltype() == VLVT_UINT64) { } else if (varp->vltype() == VLVT_UINT64) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->varQDatap()); vop->readQDatap());
return;
} else if (varp->vltype() == VLVT_WDATA) { } else if (varp->vltype() == VLVT_WDATA) {
vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
vop->varEDatap()); vop->readEDatap());
return;
} }
return;
} }
// Reached only if vl_check_array_format and this dispatch drift apart
// LCOV_EXCL_START
VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for '%s'", VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for '%s'",
__func__, VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), __func__, VerilatedVpiError::strFromVpiVal(arrayvalue_p->format),
vop->fullname()); vop->fullname());
// LCOV_EXCL_STOP
} }
void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p,
@@ -4528,6 +4688,16 @@ void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN
return; return;
} }
if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return;
const unsigned size = vop->size();
if (VL_UNLIKELY(num > size)) {
VL_VPI_ERROR_(__FILE__, __LINE__,
"%s: Requested elements to set (%u) exceed array size (%u)", __func__, num,
size);
return;
}
if (num == 0) return;
vl_put_value_array(object, arrayvalue_p, index_p, num); vl_put_value_array(object, arrayvalue_p, index_p, num);
} }
+16 -2
View File
@@ -318,6 +318,17 @@
# define VL_CONSTEXPR_CXX17 # define VL_CONSTEXPR_CXX17
#endif #endif
//=========================================================================
// C++-2020
#if __cplusplus >= 202002L
# define VL_CONSTINIT_CXX20 constinit
# define VL_NO_UNIQUE_ADDRESS_CXX20 [[no_unique_address]]
#else
# define VL_CONSTINIT_CXX20
# define VL_NO_UNIQUE_ADDRESS_CXX20
#endif
//========================================================================= //=========================================================================
// Optimization // Optimization
@@ -452,7 +463,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_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_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_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_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_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 #define VL_VFORMATATTR_TIMEUNIT 'T' // (int timeunit); timeunits passed from V3Emit to runtime
@@ -531,10 +543,12 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#define VL_EUL(n) VL_UL(n) // Make constant number EData sized #define VL_EUL(n) VL_UL(n) // Make constant number EData sized
#define VL_BITWORD_I(bit) ((bit) / VL_IDATASIZE) ///< Word number for sv DPI vectors #define VL_BITWORD_I(bit) ((bit) / VL_IDATASIZE) ///< Word number for sv DPI vectors
#define VL_BITWORD_Q(bit) ((bit) / VL_QUADSIZE) ///< Quadword number for a bit
#define VL_BITWORD_E(bit) ((bit) >> VL_EDATASIZE_LOG2) ///< Word number for a wide quantity #define VL_BITWORD_E(bit) ((bit) >> VL_EDATASIZE_LOG2) ///< Word number for a wide quantity
#define VL_BITBIT_I(bit) ((bit) & VL_SIZEBITS_I) ///< Bit number for a bit in a long #define VL_BITBIT_I(bit) ((bit) & VL_SIZEBITS_I) ///< Bit number for a bit in a long
#define VL_BITBIT_Q(bit) ((bit) & VL_SIZEBITS_Q) ///< Bit number for a bit in a quad #define VL_BITBIT_Q(bit) ((bit) & VL_SIZEBITS_Q) ///< Bit number for a bit in a quad
#define VL_BITBIT_E(bit) ((bit) & VL_SIZEBITS_E) ///< Bit number for a bit in an EData #define VL_BITBIT_E(bit) ((bit) & VL_SIZEBITS_E) ///< Bit number for a bit in an EData
#define VL_BIT_Q(bit) (1ULL << VL_BITBIT_Q(bit)) ///< Quad with only the given bit set
// Return true if data[bit] set; not 0/1 return, but 0/non-zero return. // Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit))) #define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit)))
@@ -736,7 +750,7 @@ namespace vlstd {
template <typename T> template <typename T>
struct reverse_wrapper final { struct reverse_wrapper final {
const T& m_v; const T& m_v; // Pre-wrapped iterator
explicit reverse_wrapper(const T& a_v) explicit reverse_wrapper(const T& a_v)
: m_v(a_v) {} // Need () constructor : m_v(a_v) {} // Need () constructor
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# DESCRIPTION: Verilator: gcov wrapper for fastcov, bounding gcov memory usage
#
# Usage: GCOV=<gcov> nodist/gcov_batch [gcov options] <files>
#
# fastcov passes all coverage files of a worker to a single gcov process, but
# gcov does not free memory between input files, which can exhaust memory with
# many files. This wrapper runs the gcov in $GCOV on batches of at most 16
# files at a time instead. The output is the same as if all files were passed
# to a single gcov.
#
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
gcov="${GCOV:-gcov}"
batch=16
opts=()
files=()
for arg in "$@"; do
case "$arg" in
-*) opts+=("$arg") ;;
*) files+=("$arg") ;;
esac
done
# No files, e.g.: 'gcov -v'
if [ ${#files[@]} -eq 0 ]; then
exec "$gcov" "${opts[@]}"
fi
status=0
for ((i = 0; i < ${#files[@]}; i += batch)); do
"$gcov" "${opts[@]}" "${files[@]:i:batch}" || status=$?
done
exit $status
+3 -1
View File
@@ -25,8 +25,10 @@ def message_section(msg: str) -> int:
return 50 return 50
if re.match(r'^Support', msg, flags=re.IGNORECASE): if re.match(r'^Support', msg, flags=re.IGNORECASE):
return 60 return 60
if re.match(r'^Fix', msg, flags=re.IGNORECASE): if re.match(r'^Optimize', msg, flags=re.IGNORECASE):
return 70 return 70
if re.match(r'^Fix', msg, flags=re.IGNORECASE):
return 80
if re.match(r'^(Internals|CI|Tests)', msg, flags=re.IGNORECASE): if re.match(r'^(Internals|CI|Tests)', msg, flags=re.IGNORECASE):
return -1 return -1
if re.match(r'^Bump.* from .* to .*', msg, flags=re.IGNORECASE): # dependabot if re.match(r'^Bump.* from .* to .*', msg, flags=re.IGNORECASE): # dependabot
+5 -2
View File
@@ -12,20 +12,23 @@
# SPDX-FileCopyrightText: 2026 Wilson Snyder # SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
# These flags tested against verible-v0.0-4080-ga0a8d8eb # These flags tested against verible-v0.0-4192-g682a1d50
verible-verilog-format \ verible-verilog-format \
--inplace \ --inplace \
--wrap_end_else_clauses \ \
--assignment_statement_alignment=flush-left \ --assignment_statement_alignment=flush-left \
--case_items_alignment=flush-left \ --case_items_alignment=flush-left \
--class_member_variable_alignment=flush-left \ --class_member_variable_alignment=flush-left \
--distribution_items_alignment=flush-left \ --distribution_items_alignment=flush-left \
--enum_assignment_statement_alignment=flush-left \ --enum_assignment_statement_alignment=flush-left \
--expand_coverpoints \
--formal_parameters_alignment=flush-left \ --formal_parameters_alignment=flush-left \
--module_net_variable_alignment=flush-left \ --module_net_variable_alignment=flush-left \
--named_parameter_alignment=flush-left \ --named_parameter_alignment=flush-left \
--named_port_alignment=flush-left \ --named_port_alignment=flush-left \
--parameter_declaration_alignment=flush-left \
--port_declarations_alignment=flush-left \ --port_declarations_alignment=flush-left \
--struct_union_members_alignment=flush-left \ --struct_union_members_alignment=flush-left \
--wrap_end_else_clauses \
$* $*
+15 -1
View File
@@ -68,6 +68,7 @@ set(HEADERS
V3Combine.h V3Combine.h
V3Common.h V3Common.h
V3Const.h V3Const.h
V3ConstPool.h
V3Container.h V3Container.h
V3Control.h V3Control.h
V3Coverage.h V3Coverage.h
@@ -114,6 +115,7 @@ set(HEADERS
V3GraphPathChecker.h V3GraphPathChecker.h
V3GraphStream.h V3GraphStream.h
V3Hash.h V3Hash.h
V3HashTable.h
V3Hasher.h V3Hasher.h
V3HierBlock.h V3HierBlock.h
V3Inline.h V3Inline.h
@@ -236,6 +238,7 @@ set(COMMON_SOURCES
V3Clock.cpp V3Clock.cpp
V3Combine.cpp V3Combine.cpp
V3Common.cpp V3Common.cpp
V3ConstPool.cpp
V3Control.cpp V3Control.cpp
V3Const__gen.cpp V3Const__gen.cpp
V3Coverage.cpp V3Coverage.cpp
@@ -292,6 +295,7 @@ set(COMMON_SOURCES
V3GraphPathChecker.cpp V3GraphPathChecker.cpp
V3GraphTest.cpp V3GraphTest.cpp
V3Hash.cpp V3Hash.cpp
V3HashTable.cpp
V3Hasher.cpp V3Hasher.cpp
V3HierBlock.cpp V3HierBlock.cpp
V3Inline.cpp V3Inline.cpp
@@ -345,6 +349,7 @@ set(COMMON_SOURCES
V3Sampled.cpp V3Sampled.cpp
V3Sched.cpp V3Sched.cpp
V3SchedAcyclic.cpp V3SchedAcyclic.cpp
V3SchedCovergroup.cpp
V3SchedPartition.cpp V3SchedPartition.cpp
V3SchedReplicate.cpp V3SchedReplicate.cpp
V3SchedTiming.cpp V3SchedTiming.cpp
@@ -562,9 +567,18 @@ set_target_properties(
MSVC_RUNTIME_LIBRARY MultiThreaded$<IF:$<CONFIG:Release>,,DebugDLL> MSVC_RUNTIME_LIBRARY MultiThreaded$<IF:$<CONFIG:Release>,,DebugDLL>
#JOB_POOL_LINK one_job # Linking takes lots of resources #JOB_POOL_LINK one_job # Linking takes lots of resources
INTERPROCEDURAL_OPTIMIZATION_RELEASE $<IF:MINGW,FALSE,TRUE> INTERPROCEDURAL_OPTIMIZATION_RELEASE $<IF:MINGW,FALSE,TRUE>
INCLUDE_DIRECTORIES ${FLEX_INCLUDE_DIR}
) )
if(MINGW)
# Search MSYS Flex headers after the active toolchain's system headers.
target_compile_options(
${verilator}
PRIVATE -idirafter "${FLEX_INCLUDE_DIR}"
)
else()
target_include_directories(${verilator} PRIVATE "${FLEX_INCLUDE_DIR}")
endif()
add_dependencies( add_dependencies(
${verilator} ${verilator}
V3Lexer_yy_cpp${CMAKE_BUILD_TYPE} V3Lexer_yy_cpp${CMAKE_BUILD_TYPE}
+4 -19
View File
@@ -35,25 +35,10 @@ endif
UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD) UNDER_GIT = $(wildcard ${srcdir}/../.git/logs/HEAD)
ifeq (,$(wildcard obj_dbg/bear.o)) # Use bear if installed, and new enough to support the '--output FILE --' syntax
ifneq (, $(shell which bear 2>/dev/null)) BEAR := $(shell bear=$$(which bear 2>/dev/null) && tmp=$$(mktemp -d 2>/dev/null) \
BEAR := $(shell which bear) && { "$$bear" --output "$$tmp/probe.json" -- true >/dev/null 2>&1 && echo "$$bear"; rm -rf "$$tmp"; })
ifeq (, $(shell $(BEAR) --output obj_dbg/comptest.json -- true)) BEAR_OBJ_OPT := $(if $(BEAR),$(BEAR) --append --output obj_dbg/compile_commands.json --)
$(shell which bear 2>/dev/null >obj_dbg/bear.o)
else
# unsupported version
BEAR :=
endif
endif
else
BEAR := $(shell cat obj_dbg/bear.o)
endif
ifneq ($(BEAR),)
BEAR_OBJ_OPT := $(BEAR) --append --output obj_dbg/compile_commands.json --
else
BEAR_OBJ_OPT :=
endif
#********************************************************************* #*********************************************************************
+8 -5
View File
@@ -187,7 +187,6 @@ maintainer-copy::
#### Top executable #### Top executable
RAW_OBJS = \ RAW_OBJS = \
V3Const__gen.o \
V3Error.o \ V3Error.o \
V3FileLine.o \ V3FileLine.o \
V3Graph.o \ V3Graph.o \
@@ -196,6 +195,7 @@ RAW_OBJS = \
V3GraphPathChecker.o \ V3GraphPathChecker.o \
V3GraphTest.o \ V3GraphTest.o \
V3Hash.o \ V3Hash.o \
V3HashTable.o \
V3OptionParser.o \ V3OptionParser.o \
V3Os.o \ V3Os.o \
V3ParseGrammar.o \ V3ParseGrammar.o \
@@ -212,6 +212,7 @@ RAW_OBJS_PCH_ASTMT = \
V3Ast.o \ V3Ast.o \
V3AstNodes.o \ V3AstNodes.o \
V3Broken.o \ V3Broken.o \
V3ConstPool.o \
V3Control.o \ V3Control.o \
V3EmitCBase.o \ V3EmitCBase.o \
V3EmitCConstPool.o \ V3EmitCConstPool.o \
@@ -252,6 +253,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Clock.o \ V3Clock.o \
V3Combine.o \ V3Combine.o \
V3Common.o \ V3Common.o \
V3Const__gen.o \
V3Coverage.o \ V3Coverage.o \
V3Covergroup.o \ V3Covergroup.o \
V3CoverageJoin.o \ V3CoverageJoin.o \
@@ -330,6 +332,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Sampled.o \ V3Sampled.o \
V3Sched.o \ V3Sched.o \
V3SchedAcyclic.o \ V3SchedAcyclic.o \
V3SchedCovergroup.o \
V3SchedPartition.o \ V3SchedPartition.o \
V3SchedReplicate.o \ V3SchedReplicate.o \
V3SchedTiming.o \ V3SchedTiming.o \
@@ -408,8 +411,6 @@ $(TGT): $(PREDEP_H) $(OBJS)
%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS) $(DFG_DEFS) %__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS) $(DFG_DEFS)
$(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) $*.cpp $(PYTHON3) $(ASTGEN) $(ASTGENFLAGS) $*.cpp
.SECONDARY:
%.gch: % %.gch: %
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH} -c $< -o $@ $(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH} -c $< -o $@
%.o: %.cpp %.o: %.cpp
@@ -429,7 +430,7 @@ V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
define CXX_ASTMT_template define CXX_ASTMT_template
$(1): $(basename $(1)).cpp V3PchAstMT.h.gch $(1): $(basename $(1)).cpp V3PchAstMT.h.gch
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstMT.h${CFG_GCH_IF_CLANG} -c $(srcdir)/$(basename $(1)).cpp -o $(1) $(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstMT.h${CFG_GCH_IF_CLANG} -c $$< -o $(1)
endef endef
@@ -437,7 +438,7 @@ $(foreach obj,$(RAW_OBJS_PCH_ASTMT),$(eval $(call CXX_ASTMT_template,$(obj))))
define CXX_ASTNOMT_template define CXX_ASTNOMT_template
$(1): $(basename $(1)).cpp V3PchAstNoMT.h.gch $(1): $(basename $(1)).cpp V3PchAstNoMT.h.gch
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstNoMT.h${CFG_GCH_IF_CLANG} -c $(srcdir)/$(basename $(1)).cpp -o $(1) $(OBJCACHE) ${CXX} ${CXXFLAGS} ${CXXFLAGSOBJ} ${CPPFLAGSWALL} ${CFG_CXXFLAGS_PCH_I} V3PchAstNoMT.h${CFG_GCH_IF_CLANG} -c $$< -o $(1)
endef endef
@@ -487,6 +488,8 @@ header_cc: $(addsuffix __header_cc.o, $(basename $(HEADER_CC_H)))
%__header_cc.cpp: %.h %__header_cc.cpp: %.h
$(PYTHON3) $(srcdir)/../bin/verilator_includer -DVL_MT_DISABLED_CODE_UNIT=1 $^ > $@ $(PYTHON3) $(srcdir)/../bin/verilator_includer -DVL_MT_DISABLED_CODE_UNIT=1 $^ > $@
.SECONDARY: V3Const__gen.cpp $(addsuffix __header_cc.cpp, $(basename $(HEADER_CC_H)))
.SUFFIXES: .SUFFIXES:
###################################################################### ######################################################################
+2 -2
View File
@@ -689,8 +689,8 @@ class CovergroupEventBindVisitor final : public VNVisitor {
public: public:
CovergroupEventBindVisitor(AstSenTree* eventp, AstClass* classp, AstVarScope* instancep) CovergroupEventBindVisitor(AstSenTree* eventp, AstClass* classp, AstVarScope* instancep)
: m_instancep{instancep} { : m_instancep{instancep} {
classp->foreachMember([&](AstClass* const, AstVar* const varp) { classp->foreachMember([&](AstClass* const, AstVar* const varp) { //
if (!varp->isStatic()) m_memberps.emplace(varp); m_memberps.emplace(varp);
}); });
iterate(eventp); iterate(eventp);
} }
+28 -22
View File
@@ -154,6 +154,13 @@ void V3AssertCommon::lowerSequenceEvents(AstNetlist* nodep) {
V3Global::dumpCheckGlobalTree("assertseqevent", 0, dumpTreeEitherLevel() >= 3); V3Global::dumpCheckGlobalTree("assertseqevent", 0, dumpTreeEitherLevel() >= 3);
} }
string V3AssertCommon::assertCtlGetCall(const char* query, VAssertType type,
VAssertDirectiveType directiveType) {
// Template arguments let the runtime build the type/directive mask at C++ compile time
return "vlSymsp->_vm_contextp__->assertCtlGet<"s + std::to_string(type) + ", "s
+ std::to_string(directiveType) + ">(VerilatedAssertCtlQuery::"s + query + ")"s;
}
//###################################################################### //######################################################################
// AssertDeFutureVisitor // AssertDeFutureVisitor
// If any AstFuture, then move all non-future varrefs to be one cycle behind, // If any AstFuture, then move all non-future varrefs to be one cycle behind,
@@ -324,6 +331,7 @@ class AssertVisitor final : public VNVisitor {
VDouble0 m_statLiftedCaseExprs; // Count of purified case expressions VDouble0 m_statLiftedCaseExprs; // Count of purified case expressions
AstNodeFTask* m_ftaskp = nullptr; // Current function/task AstNodeFTask* m_ftaskp = nullptr; // Current function/task
V3UniqueNames m_caseTempNames{"__VCase"}; V3UniqueNames m_caseTempNames{"__VCase"};
V3UniqueNames m_matchCountNames{"__VnfaRemainingMatchCount"}; // Match replay counter names
// Maps from (expression, senTree) to the AstAlways that computes its delayed values. // Maps from (expression, senTree) to the AstAlways that computes its delayed values.
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>> std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
m_modExpr2Sen2DelayedAlwaysp; m_modExpr2Sen2DelayedAlwaysp;
@@ -334,11 +342,6 @@ class AssertVisitor final : public VNVisitor {
std::unordered_map<const AstAlways*, AstVar*> m_delayedAlways2TickTimep; std::unordered_map<const AstAlways*, AstVar*> m_delayedAlways2TickTimep;
// METHODS // METHODS
static string assertCtlGetCall(const char* query, VAssertType type,
VAssertDirectiveType directiveType) {
return "vlSymsp->_vm_contextp__->assertCtlGet(VerilatedAssertCtlQuery::"s + query + ", "s
+ std::to_string(type) + ", "s + std::to_string(directiveType) + ")"s;
}
static const char* assertPassOnQuery(bool vacuous) { static const char* assertPassOnQuery(bool vacuous) {
static constexpr const char* queries[2] static constexpr const char* queries[2]
= {"ASSERT_CTL_PASS_ON_NONVACUOUS", "ASSERT_CTL_PASS_ON_VACUOUS"}; = {"ASSERT_CTL_PASS_ON_NONVACUOUS", "ASSERT_CTL_PASS_ON_VACUOUS"};
@@ -362,8 +365,9 @@ class AssertVisitor final : public VNVisitor {
case VAssertDirectiveType::COVER: case VAssertDirectiveType::COVER:
case VAssertDirectiveType::ASSUME: { case VAssertDirectiveType::ASSUME: {
if (v3Global.opt.assertOn()) { if (v3Global.opt.assertOn()) {
return new AstCExpr{fl, AstCExpr::Pure{}, return new AstCExpr{
assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1}; fl, AstCExpr::Pure{},
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_ON", type, directiveType), 1};
} }
return new AstConst{fl, AstConst::BitFalse{}}; return new AstConst{fl, AstConst::BitFalse{}};
} }
@@ -390,15 +394,17 @@ class AssertVisitor final : public VNVisitor {
VAssertDirectiveType directiveType, bool vacuous) { VAssertDirectiveType directiveType, bool vacuous) {
if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}}; if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}};
if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}}; if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}};
return new AstCExpr{fl, AstCExpr::Pure{}, return new AstCExpr{
assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType), 1}; fl, AstCExpr::Pure{},
V3AssertCommon::assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType), 1};
} }
static AstNodeExpr* assertFailOnCond(FileLine* fl, VAssertType type, static AstNodeExpr* assertFailOnCond(FileLine* fl, VAssertType type,
VAssertDirectiveType directiveType) { VAssertDirectiveType directiveType) {
if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}}; if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}};
if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}}; if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}};
return new AstCExpr{fl, AstCExpr::Pure{}, return new AstCExpr{
assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType), 1}; fl, AstCExpr::Pure{},
V3AssertCommon::assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType), 1};
} }
string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message, string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message,
VDisplayType severity) { VDisplayType severity) {
@@ -433,20 +439,20 @@ class AssertVisitor final : public VNVisitor {
if (!m_monitorNumVarp) { if (!m_monitorNumVarp) {
m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum", m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum",
nodep->findUInt64DType()}; nodep->findUInt64DType()};
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorNumVarp); v3Global.rootp()->dollarUnitPkgp()->addStmtsp(m_monitorNumVarp);
} }
AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorNumVarp, access}; AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorNumVarp, access};
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp()); varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgp());
return varrefp; return varrefp;
} }
AstVarRef* newMonitorOffVarRefp(const AstNode* nodep, VAccess access) { AstVarRef* newMonitorOffVarRefp(const AstNode* nodep, VAccess access) {
if (!m_monitorOffVarp) { if (!m_monitorOffVarp) {
m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff", m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff",
nodep->findBitDType()}; nodep->findBitDType()};
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorOffVarp); v3Global.rootp()->dollarUnitPkgp()->addStmtsp(m_monitorOffVarp);
} }
AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorOffVarp, access}; AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorOffVarp, access};
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp()); varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgp());
return varrefp; return varrefp;
} }
static AstIf* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType, static AstIf* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
@@ -749,11 +755,11 @@ class AssertVisitor final : public VNVisitor {
// reaches zero. // reaches zero.
matchCountp->unlinkFrBack(); matchCountp->unlinkFrBack();
AstVar* const remainingp = new AstVar{ AstVar* const remainingp = new AstVar{
flp, VVarType::BLOCKTEMP, "__VnfaRemainingMatchCount", matchCountp->dtypep()}; flp, VVarType::MODULETEMP, m_matchCountNames.get(""), matchCountp->dtypep()};
remainingp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); remainingp->lifetime(VLifetime::STATIC_EXPLICIT);
AstBegin* const replayp = new AstBegin{flp, "", remainingp, true}; m_modp->addStmtsp(remainingp);
replayp->addStmtsp( AstNode* const replaysp
new AstAssign{flp, new AstVarRef{flp, remainingp, VAccess::WRITE}, matchCountp}); = new AstAssign{flp, new AstVarRef{flp, remainingp, VAccess::WRITE}, matchCountp};
AstLoop* const loopp = new AstLoop{flp}; AstLoop* const loopp = new AstLoop{flp};
loopp->addStmtsp( loopp->addStmtsp(
new AstLoopTest{flp, loopp, new AstVarRef{flp, remainingp, VAccess::READ}}); new AstLoopTest{flp, loopp, new AstVarRef{flp, remainingp, VAccess::READ}});
@@ -763,8 +769,8 @@ class AssertVisitor final : public VNVisitor {
new AstSub{flp, new AstVarRef{flp, remainingp, VAccess::READ}, new AstSub{flp, new AstVarRef{flp, remainingp, VAccess::READ},
new AstConst{flp, AstConst::WidthedValue{}, new AstConst{flp, AstConst::WidthedValue{},
remainingp->dtypep()->width(), 1}}}); remainingp->dtypep()->width(), 1}}});
replayp->addStmtsp(loopp); replaysp->addNext(loopp);
passsp = replayp; passsp = replaysp;
} }
AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp); AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp}; if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp};
+2
View File
@@ -28,6 +28,8 @@ class V3AssertCommon final {
public: public:
static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED; static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED;
static void lowerSequenceEvents(AstNetlist* nodep) VL_MT_DISABLED; static void lowerSequenceEvents(AstNetlist* nodep) VL_MT_DISABLED;
static string assertCtlGetCall(const char* query, VAssertType type,
VAssertDirectiveType directiveType) VL_MT_DISABLED;
}; };
class V3Assert final { class V3Assert final {
+438 -228
View File
File diff suppressed because it is too large Load Diff
+262 -60
View File
@@ -32,6 +32,36 @@
VL_DEFINE_DEBUG_FUNCTIONS; VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Clocking blocks of clocking items
class AssertPreClockingVisitor final : public VNVisitorConst {
// Captures the clocking block of each clocking item before AssertPreVisitor, which needs it
// at synchronous drives, often visited before the clocking block, e.g. in a parent module
// NODE STATE
// AstClockingItem::user3p() // AstClocking*. Clocking block of the item, user3 claimed by
// // AssertPreVisitor
// VISITORS
void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); }
void visit(AstNodeModule* nodep) override { iterateAndNextConstNull(nodep->stmtsp()); }
void visit(AstGenBlock* nodep) override { iterateAndNextConstNull(nodep->itemsp()); }
void visit(AstClocking* nodep) override {
for (AstNode* itemp = nodep->itemsp(); itemp; itemp = itemp->nextp()) {
if (AstClockingItem* const citemp = VN_CAST(itemp, ClockingItem)) {
citemp->user3p(nodep);
}
}
}
// Clocking blocks are only in modules and their generate blocks
void visit(AstNode*) override {}
public:
// CONSTRUCTORS
explicit AssertPreClockingVisitor(AstNetlist* nodep) { iterateConst(nodep); }
~AssertPreClockingVisitor() override = default;
};
//###################################################################### //######################################################################
// Assert class functions // Assert class functions
@@ -40,10 +70,21 @@ class AssertPreVisitor final : public VNVisitor {
// Eventually inlines calls to sequences, properties, etc. // Eventually inlines calls to sequences, properties, etc.
// We're not parsing the tree, or anything more complicated. // We're not parsing the tree, or anything more complicated.
private: private:
// TYPES
struct SynchDrive final {
AstClockingItem* itemp; // Clocking item of the driven clockvar
AstNodeExpr* refp; // Reference to the driven clockvar
};
// NODE STATE // NODE STATE
// AstClockingItem::user1p() // AstVar*. varp() of ClockingItem after unlink // AstClockingItem::user1p() // AstVar*. varp() of ClockingItem after unlink
// AstClockingItem::user2p() // AstVar*. Flag set by drives of output clockvar
// AstClockingItem::user3p() // AstClocking*. Clocking block of the item, captured
// // by AssertPreClockingVisitor
// AstPExpr::user1() // bool. Created from AstUntil // AstPExpr::user1() // bool. Created from AstUntil
const VNUser1InUse m_inuser1; const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
const VNUser3InUse m_inuser3;
// STATE // STATE
// Current context: // Current context:
AstNetlist* const m_netlistp = nullptr; // Current netlist AstNetlist* const m_netlistp = nullptr; // Current netlist
@@ -68,17 +109,123 @@ private:
V3UniqueNames m_gotoRepNames{"__VgotoRep"}; // Goto repetition counter name generator V3UniqueNames m_gotoRepNames{"__VgotoRep"}; // Goto repetition counter name generator
V3UniqueNames m_nonConsRepNames{"__VnonConsRep"}; // Nonconsecutive rep name generator V3UniqueNames m_nonConsRepNames{"__VnonConsRep"}; // Nonconsecutive rep name generator
V3UniqueNames m_disableCntNames{"__VdisableCnt"}; // Disable condition counter name generator V3UniqueNames m_disableCntNames{"__VdisableCnt"}; // Disable condition counter name generator
V3UniqueNames m_blockNames{"__VassertBlock"}; // Names of blocks with temporaries
V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable name generator V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable name generator
V3UniqueNames m_activeNames{"__VassertsActive"}; // Active asserts map name generator V3UniqueNames m_activeNames{"__VassertsActive"}; // Active asserts map name generator
V3UniqueNames m_drivenNames{"__VclockingDriven"}; // Clockvar drive flag name generator
bool m_inAssign = false; // True if in an AssignNode bool m_inAssign = false; // True if in an AssignNode
bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS
bool m_inSynchDrive = false; // True if in synchronous drive bool m_inSynchDrive = false; // True if in synchronous drive
bool m_inConcatAssign = false; // True if in assignment to a concatenation
std::vector<SynchDrive> m_drives; // Clockvars written by the synchronous drive
bool m_hasCycleDelay = false; // True if node has cycle delay beneath bool m_hasCycleDelay = false; // True if node has cycle delay beneath
std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup
std::vector<AstSequence*> m_seqsToCleanup; // Sequences to clean up after traversal std::vector<AstSequence*> m_seqsToCleanup; // Sequences to clean up after traversal
// METHODS // METHODS
// Flag set by each drive of an output clockvar, added to the module with its clocking item
AstVar* getCreateDrivenVarp(AstClockingItem* itemp) {
if (!itemp->user2p()) {
AstVar* const varp = new AstVar{itemp->fileline(), VVarType::MODULETEMP,
m_drivenNames.get(itemp), itemp->findBitDType()};
varp->lifetime(VLifetime::STATIC_EXPLICIT);
itemp->user2p(varp);
}
return VN_AS(itemp->user2p(), Var);
}
// Reference to a variable in the scope of a driven clockvar, made like the clockvar's
static AstNodeExpr* newDriveRefp(FileLine* flp, const SynchDrive& drive, AstVar* varp,
const VAccess& access) {
if (const AstVarXRef* const xrefp = VN_CAST(drive.refp, VarXRef)) {
return new AstVarXRef{flp, varp, xrefp->dotted(), access};
} else if (const AstMemberSel* const selp = VN_CAST(drive.refp, MemberSel)) {
// The interface expression is evaluated again, so it must not have side effects;
// cloneTreePure warns if it has any
AstMemberSel* const newSelp
= new AstMemberSel{flp, selp->fromp()->cloneTreePure(false), varp};
newSelp->access(access);
return newSelp;
}
return new AstVarRef{flp, varp, access};
}
// Assignment setting the drive flag of a driven clockvar
AstAssign* newDrivenSetp(FileLine* flp, const SynchDrive& drive) {
AstVar* const varp = getCreateDrivenVarp(drive.itemp);
AstAssign* const setp = new AstAssign{flp, newDriveRefp(flp, drive, varp, VAccess::WRITE),
new AstConst{flp, AstConst::BitTrue{}}};
setp->user1(true);
return setp;
}
// Clocking block of a clocking item
static AstClocking* clockingOf(const AstClockingItem* itemp) {
UASSERT_OBJ(itemp->user3p(), itemp, "Clocking item not captured in a clocking block");
return VN_AS(itemp->user3p(), Clocking);
}
// Clocking event of a driven clockvar, referenced from the drive like the clockvar, or
// nullptr if unsupported, which is reported on the drive's cycle delay
AstSenItem* newDriveSensesp(AstDelay* delayp, const SynchDrive& drive) {
AstSenItem* const origp = clockingOf(drive.itemp)->sensesp();
if (VN_IS(drive.refp, VarRef)) return origp->cloneTree(false);
if (origp->exists([](const AstVarXRef*) { return true; })) {
// The reference is relative to the clocking block
delayp->v3warn(E_UNSUPPORTED, "Unsupported: cycle delay in synchronous drive to"
" clockvar in another scope, whose clocking event"
" has a hierarchical reference");
return nullptr;
}
const AstMemberSel* const selp = VN_CAST(drive.refp, MemberSel);
// Processes outside classes evaluate their events also when not waiting on them,
// possibly before the interface reference is set, so the event is guarded against a
// null reference, which needs an integral value; scheduling does not guard it
const bool guardNull = selp && !VN_IS(m_modp, Class);
if (guardNull && !origp->sensp()->dtypep()->skipRefp()->isIntegralOrPacked()) {
delayp->v3warn(E_UNSUPPORTED, "Unsupported: cycle delay in synchronous drive through"
" an interface reference outside a class, to clocking"
" block with non-integral clocking event");
return nullptr;
}
// Interface declaring the clocking block, if driven through an interface reference
AstIface* const ifacep
= selp ? VN_AS(selp->fromp()->dtypep()->skipRefp(), IfaceRefDType)->ifaceViaCellp()
: nullptr;
AstSenItem* const sensesp = origp->cloneTree(false);
std::vector<AstVarRef*> refps;
sensesp->foreach([&](AstVarRef* refp) {
if (!refp->classOrPackagep()) refps.push_back(refp);
});
for (AstVarRef* const refp : refps) {
AstVar* const varp = refp->varp();
if (ifacep) {
// Mark the variable as read through an interface reference, as V3Width does for
// a member select. The mark is the interface declaring the variable, whose
// instances all share it, so drives through references to any instances agree.
UASSERT_OBJ(!varp->sensIfacep() || varp->sensIfacep() == ifacep, refp,
"Variable read through references to different interfaces");
varp->sensIfacep(ifacep);
}
refp->replaceWith(newDriveRefp(refp->fileline(), drive, varp, refp->access()));
VL_DO_DANGLING(pushDeletep(refp), refp);
}
if (guardNull) guardNullIface(sensesp, selp->fromp());
return sensesp;
}
// Make an integral clocking event through an interface reference false while the reference
// is null
static void guardNullIface(AstSenItem* sensesp, AstNodeExpr* fromp) {
FileLine* const flp = sensesp->fileline();
AstNodeExpr* const sensp = sensesp->sensp()->unlinkFrBack();
sensesp->sensp(new AstCond{flp, newNotNullp(flp, fromp), sensp,
new AstConst{flp, AstConst::DTyped{}, sensp->dtypep()}});
if (AstNodeExpr* const condp = sensesp->condp()) {
condp->unlinkFrBack();
sensesp->condp(new AstLogAnd{flp, newNotNullp(flp, fromp), condp});
}
}
static AstNodeExpr* newNotNullp(FileLine* flp, AstNodeExpr* fromp) {
return new AstNeq{flp, fromp->cloneTreePure(false), new AstConst{flp, AstConst::Null{}}};
}
static void checkSamplingFuncDType(AstNodeExpr* nodep, const AstNode* exprp) { static void checkSamplingFuncDType(AstNodeExpr* nodep, const AstNode* exprp) {
const AstNodeDType* const dtypep = exprp->dtypep()->skipRefp(); const AstNodeDType* const dtypep = exprp->dtypep()->skipRefp();
if (!dtypep->isIntegralOrPacked()) { if (!dtypep->isIntegralOrPacked()) {
@@ -272,13 +419,21 @@ private:
// It has to be converted to a list of ModportClockingVarRefs, // It has to be converted to a list of ModportClockingVarRefs,
// because clocking blocks are removed in this pass // because clocking blocks are removed in this pass
for (AstNode* itemp = nodep->clockingp()->itemsp(); itemp; itemp = itemp->nextp()) { for (AstNode* itemp = nodep->clockingp()->itemsp(); itemp; itemp = itemp->nextp()) {
if (const AstClockingItem* citemp = VN_CAST(itemp, ClockingItem)) { if (AstClockingItem* const citemp = VN_CAST(itemp, ClockingItem)) {
if (AstVar* const varp if (AstVar* const varp
= citemp->varp() ? citemp->varp() : VN_AS(citemp->user1p(), Var)) { = citemp->varp() ? citemp->varp() : VN_AS(citemp->user1p(), Var)) {
AstModportVarRef* const modVarp = new AstModportVarRef{ AstModportVarRef* const modVarp = new AstModportVarRef{
nodep->fileline(), varp->name(), citemp->direction()}; nodep->fileline(), varp->name(), citemp->direction()};
modVarp->varp(varp); modVarp->varp(varp);
nodep->addNextHere(modVarp); nodep->addNextHere(modVarp);
if (citemp->direction().isOutput()) {
// Drives through the modport set the drive flag
AstVar* const drivenVarp = getCreateDrivenVarp(citemp);
AstModportVarRef* const drivenRefp = new AstModportVarRef{
nodep->fileline(), drivenVarp->name(), VDirection::OUTPUT};
drivenRefp->varp(drivenVarp);
nodep->addNextHere(drivenRefp);
}
} }
} }
} }
@@ -291,6 +446,8 @@ private:
// Unused item // Unused item
return; return;
} }
// Flag set by drives of an output clockvar, possibly visited before this item
if (nodep->direction().isOutput()) m_modp->addStmtsp(getCreateDrivenVarp(nodep));
FileLine* const flp = nodep->fileline(); FileLine* const flp = nodep->fileline();
V3Const::constifyEdit(nodep->skewp()); V3Const::constifyEdit(nodep->skewp());
if (!VN_IS(nodep->skewp(), Const)) { if (!VN_IS(nodep->skewp(), Const)) {
@@ -317,32 +474,26 @@ private:
AstInitialStatic* const initClockvarp = new AstInitialStatic{ AstInitialStatic* const initClockvarp = new AstInitialStatic{
flp, new AstAssign{flp, skewedWriteRefp, exprp->cloneTreePure(false)}}; flp, new AstAssign{flp, skewedWriteRefp, exprp->cloneTreePure(false)}};
m_modp->addStmtsp(initClockvarp); m_modp->addStmtsp(initClockvarp);
// A var to keep the previous value of the clockvar // Each drive sets a flag, so the signal is also assigned when driven with the same
AstVar* const prevVarp = new AstVar{ // value again, e.g. after another assignment to the signal (IEEE 1800-2023 14.16.2)
flp, VVarType::MODULETEMP, "__Vclocking_prev__" + varp->name(), exprp->dtypep()}; AstVar* const drivenVarp = getCreateDrivenVarp(nodep);
prevVarp->lifetime(VLifetime::STATIC_EXPLICIT); m_modp->addStmtsp(new AstInitialStatic{
AstInitialStatic* const initPrevClockvarp = new AstInitialStatic{ flp, new AstAssign{flp, new AstVarRef{flp, drivenVarp, VAccess::WRITE},
flp, new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE}, new AstConst{flp, AstConst::BitFalse{}}}});
skewedReadRefp->cloneTreePure(false)}}; // Assign the clockvar to the actual var if it was driven
m_modp->addStmtsp(prevVarp);
m_modp->addStmtsp(initPrevClockvarp);
// Assign the clockvar to the actual var; only do it if the clockvar's value has
// changed
AstAssign* const assignp AstAssign* const assignp
= new AstAssign{flp, exprp->cloneTreePure(false), skewedReadRefp}; = new AstAssign{flp, exprp->cloneTreePure(false), skewedReadRefp};
AstIf* const ifp AstIf* const ifp
= new AstIf{flp, = new AstIf{flp, new AstVarRef{flp, drivenVarp, VAccess::READ},
new AstNeq{flp, new AstVarRef{flp, prevVarp, VAccess::READ}, new AstAssign{flp, new AstVarRef{flp, drivenVarp, VAccess::WRITE},
skewedReadRefp->cloneTreePure(false)}, new AstConst{flp, AstConst::BitFalse{}}}};
assignp}; ifp->addThensp(assignp);
ifp->addThensp(new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
skewedReadRefp->cloneTree(false)});
if (skewp->isZero()) { if (skewp->isZero()) {
// Drive the var in Re-NBA (IEEE 1800-2023 14.16) // Drive the var in Re-NBA (IEEE 1800-2023 14.16)
AstSenTree* senTreep AstSenTree* senTreep
= new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}; = new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)};
senTreep->addSensesp( senTreep->addSensesp(new AstSenItem{
new AstSenItem{flp, VEdgeType::ET_CHANGED, skewedReadRefp->cloneTree(false)}); flp, VEdgeType::ET_CHANGED, new AstVarRef{flp, drivenVarp, VAccess::READ}});
AstCMethodHard* const trigp = new AstCMethodHard{ AstCMethodHard* const trigp = new AstCMethodHard{
nodep->fileline(), nodep->fileline(),
new AstVarRef{flp, m_clockingp->ensureEventp(), VAccess::READ}, new AstVarRef{flp, m_clockingp->ensureEventp(), VAccess::READ},
@@ -414,6 +565,7 @@ private:
flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, VCMethod::DYN_POP, flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, VCMethod::DYN_POP,
new AstTime{nodep->fileline(), m_modp->timeunit()}}; new AstTime{nodep->fileline(), m_modp->timeunit()}};
popp->addPinsp(skewp->unlinkFrBack()); popp->addPinsp(skewp->unlinkFrBack());
refp->access(VAccess::READWRITE); // Only conditionally assigned
popp->addPinsp(refp); popp->addPinsp(refp);
popp->dtypeSetVoid(); popp->dtypeSetVoid();
m_clockingp->addNextHere( m_clockingp->addNextHere(
@@ -446,12 +598,12 @@ private:
FileLine* const flp = nodep->fileline(); FileLine* const flp = nodep->fileline();
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack()); AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
const AstConst* const constp = VN_CAST(valuep, Const); const AstConst* const constp = VN_CAST(valuep, Const);
if (!constp) { if (!constp && !m_inSynchDrive) {
// V3AssertNfa handles non-const delays before this pass and // V3AssertNfa handles non-const delays before this pass and
// replaces the property; this branch should never be reached. // replaces the property; this branch should never be reached.
nodep->v3fatalSrc("Non-constant cycle delay in assertion: " nodep->v3fatalSrc("Non-constant cycle delay in assertion: "
"should have been caught by V3AssertNfa"); "should have been caught by V3AssertNfa");
} else if (constp->isZero()) { } else if (constp && constp->isZero()) {
VL_DO_DANGLING(pushDeletep(valuep), valuep); VL_DO_DANGLING(pushDeletep(valuep), valuep);
if (m_inSynchDrive) { if (m_inSynchDrive) {
// ##0 has no effect in synchronous drives (IEEE 1800-2023 14.11) // ##0 has no effect in synchronous drives (IEEE 1800-2023 14.11)
@@ -488,20 +640,36 @@ private:
return; return;
} }
AstSenItem* sensesp = nullptr; AstSenItem* sensesp = nullptr;
if (!m_defaultClockingp) { if (!m_drives.empty()) {
if (m_inConcatAssign) {
// Also the clockvars may be of different clocking blocks
nodep->v3error("Cycle delays cannot be used in synchronous drives to a"
" concatenation (IEEE 1800-2023 14.16)");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
VL_DO_DANGLING(pushDeletep(valuep), valuep);
return;
}
// Count the cycles of the driven clockvar's clocking block (IEEE 1800-2023 14.16)
sensesp = newDriveSensesp(nodep, m_drives.front());
if (!sensesp) {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
VL_DO_DANGLING(pushDeletep(valuep), valuep);
return;
}
} else if (!m_defaultClockingp) {
if (!m_pexprp) { if (!m_pexprp) {
nodep->v3error("Usage of cycle delays requires default clocking" nodep->v3error("Usage of cycle delays requires default clocking"
" (IEEE 1800-2023 14.11)"); " (IEEE 1800-2023 14.11)");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
VL_DO_DANGLING(valuep->deleteTree(), valuep); VL_DO_DANGLING(pushDeletep(valuep), valuep);
return; return;
} }
sensesp = m_senip; sensesp = m_senip->cloneTree(false);
} else { } else {
sensesp = m_defaultClockingp->sensesp(); sensesp = m_defaultClockingp->sensesp()->cloneTree(false);
} }
AstEventControl* const controlp = new AstEventControl{ AstEventControl* const controlp
nodep->fileline(), new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr}; = new AstEventControl{nodep->fileline(), new AstSenTree{flp, sensesp}, nullptr};
const std::string delayName = m_cycleDlyNames.get(nodep); const std::string delayName = m_cycleDlyNames.get(nodep);
AstNodeExpr* throughoutp AstNodeExpr* throughoutp
= nodep->throughoutp() ? nodep->throughoutp()->unlinkFrBack() : nullptr; = nodep->throughoutp() ? nodep->throughoutp()->unlinkFrBack() : nullptr;
@@ -510,6 +678,15 @@ private:
nodep->findBasicDType(VBasicDTypeKwd::UINT32)}; nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true}; AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true};
// A non-constant delay of a synchronous drive is counted at runtime, waiting for none if
// zero, like a constant '##0' in a synchronous drive. The count is integral of 64 bits,
// as V3Width makes delays, or real, which is rounded (IEEE 1800-2023 6.12.1)
if (valuep->isDouble()) {
valuep = new AstRToIRoundS{flp, valuep};
valuep->dtypeFrom(cntVarp);
} else {
valuep = new AstSel{flp, valuep, 0, cntVarp->width()};
}
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep}); beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
// Throughout: create flag tracking whether condition held every tick // Throughout: create flag tracking whether condition held every tick
@@ -561,8 +738,7 @@ private:
} }
if (m_disableSeqIfp && remainp) { if (m_disableSeqIfp && remainp) {
AstIf* const disableSeqIfp = m_disableSeqIfp->cloneTree(false); AstIf* const disableSeqIfp = m_disableSeqIfp->cloneTree(false);
// Keep continuation statements in a proper statement-list container. disableSeqIfp->addThensp(remainp);
disableSeqIfp->addThensp(new AstBegin{flp, "", remainp, true});
remainp = disableSeqIfp; remainp = disableSeqIfp;
} }
if (remainp) { if (remainp) {
@@ -619,7 +795,10 @@ private:
nodep->v3error("Only non-blocking assignments can write " nodep->v3error("Only non-blocking assignments can write "
"to clockvars (IEEE 1800-2023 14.16)"); "to clockvars (IEEE 1800-2023 14.16)");
} }
if (m_inAssign) m_inSynchDrive = true; if (m_inAssign) {
m_inSynchDrive = true;
m_drives.push_back({itemp, nodep});
}
} else if (itemp->direction() == VDirection::INPUT) { } else if (itemp->direction() == VDirection::INPUT) {
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)"); nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
} }
@@ -640,7 +819,10 @@ private:
nodep->v3error("Only non-blocking assignments can write " nodep->v3error("Only non-blocking assignments can write "
"to clockvars (IEEE 1800-2023 14.16)"); "to clockvars (IEEE 1800-2023 14.16)");
} }
if (m_inAssign) m_inSynchDrive = true; if (m_inAssign) {
m_inSynchDrive = true;
m_drives.push_back({itemp, nodep});
}
} else if (itemp->direction() == VDirection::INPUT) { } else if (itemp->direction() == VDirection::INPUT) {
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)"); nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
} }
@@ -651,22 +833,44 @@ private:
if (nodep->user1()) return; if (nodep->user1()) return;
VL_RESTORER(m_inAssign); VL_RESTORER(m_inAssign);
VL_RESTORER(m_inSynchDrive); VL_RESTORER(m_inSynchDrive);
VL_RESTORER(m_inConcatAssign);
VL_RESTORER_CLEAR(m_drives);
m_inAssign = true; m_inAssign = true;
m_inSynchDrive = false; m_inSynchDrive = false;
{
// The braces of a concatenation are a replication, by one
const AstNodeExpr* lhsp = nodep->lhsp();
if (const AstReplicate* const repp = VN_CAST(lhsp, Replicate)) lhsp = repp->srcp();
m_inConcatAssign = VN_IS(lhsp, Concat);
}
{ {
VL_RESTORER(m_inAssignDlyLhs); VL_RESTORER(m_inAssignDlyLhs);
m_inAssignDlyLhs = VN_IS(nodep, AssignDly); m_inAssignDlyLhs = VN_IS(nodep, AssignDly);
iterate(nodep->lhsp()); iterate(nodep->lhsp());
} }
iterate(nodep->rhsp()); iterate(nodep->rhsp());
if (nodep->timingControlp()) { if (nodep->timingControlp()) iterate(nodep->timingControlp());
iterate(nodep->timingControlp()); // A drive without a cycle delay, also after removing '##0', updates the clockvar now
} else if (m_inSynchDrive) { if (m_inSynchDrive && !nodep->timingControlp()) {
AstAssign* const assignp = new AstAssign{ AstAssign* const assignp = new AstAssign{
nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()}; nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()};
assignp->user1(true); assignp->user1(true);
nodep->replaceWith(assignp); nodep->replaceWith(assignp);
VL_DO_DANGLING(nodep->deleteTree(), nodep); VL_DO_DANGLING(nodep->deleteTree(), nodep);
nodep = assignp;
}
// Note the drives when they take effect, after the cycle delay if any. Each clockvar
// of the LHS is driven, e.g. in a concatenation which other simulators accept, although
// IEEE 1800-2023 14.16 does not allow it
AstNode* setsp = nullptr;
for (const SynchDrive& drive : m_drives) {
setsp = AstNode::addNext(setsp, newDrivenSetp(nodep->fileline(), drive));
}
if (!setsp) return;
if (AstBegin* const beginp = VN_CAST(nodep->timingControlp(), Begin)) {
beginp->addStmtsp(setsp);
} else {
nodep->addNextHere(setsp);
} }
} }
void visit(AstAlways* nodep) override { void visit(AstAlways* nodep) override {
@@ -877,7 +1081,7 @@ private:
= getProcessAssocArrayDelete(new AstVarRef{flp, activep, VAccess::WRITE}); = getProcessAssocArrayDelete(new AstVarRef{flp, activep, VAccess::WRITE});
// Main assertion block // Main assertion block
AstBegin* const bodyp = new AstBegin{flp, "", nullptr, true}; AstBegin* const bodyp = new AstBegin{flp, m_blockNames.get(""), nullptr, true};
bodyp->addStmtsp(incrementp); bodyp->addStmtsp(incrementp);
bodyp->addStmtsp(loopp); bodyp->addStmtsp(loopp);
bodyp->addStmtsp(clausep); bodyp->addStmtsp(clausep);
@@ -906,7 +1110,7 @@ private:
new AstConst{flp, 1}}}); new AstConst{flp, 1}}});
// Final assertion block // Final assertion block
AstBegin* const finalp = new AstBegin{flp, "", nullptr, true}; AstBegin* const finalp = new AstBegin{flp, m_blockNames.get(""), nullptr, true};
finalp->addStmtsp(activeCountp); finalp->addStmtsp(activeCountp);
finalp->addStmtsp(initActiveCountp); finalp->addStmtsp(initActiveCountp);
finalp->addStmtsp(finalLoopp); finalp->addStmtsp(finalLoopp);
@@ -1072,15 +1276,13 @@ private:
windowp->addStmtsp(new AstLoopTest{ windowp->addStmtsp(new AstLoopTest{
flp, windowp, new AstNot{flp, new AstVarRef{flp, doneVarp, VAccess::READ}}}); flp, windowp, new AstNot{flp, new AstVarRef{flp, doneVarp, VAccess::READ}}});
// if (expr) { fail; done = 1; } -- window closed, expr true again // if (expr) { fail; done = 1; } -- window closed, expr true again
AstBegin* const failBlockp = new AstBegin{flp, "", nullptr, true}; AstNode* const failsp = new AstPExprClause{flp, false};
failBlockp->addStmtsp(new AstPExprClause{flp, false}); failsp->addNext(setDone());
failBlockp->addStmtsp(setDone()); windowp->addStmtsp(new AstIf{flp, exprp->cloneTreePure(false), failsp});
windowp->addStmtsp(new AstIf{flp, exprp->cloneTreePure(false), failBlockp});
// if (rhs) { pass; done = 1; } -- consequent true at this !expr endpoint // if (rhs) { pass; done = 1; } -- consequent true at this !expr endpoint
AstBegin* const passBlockp = new AstBegin{flp, "", nullptr, true}; AstNode* const passsp = new AstPExprClause{flp, true};
passBlockp->addStmtsp(new AstPExprClause{flp, true}); passsp->addNext(setDone());
passBlockp->addStmtsp(setDone()); windowp->addStmtsp(new AstIf{flp, rhsp, passsp});
windowp->addStmtsp(new AstIf{flp, rhsp, passBlockp});
// @(clk) -- advance to next cycle in window // @(clk) -- advance to next cycle in window
windowp->addStmtsp( windowp->addStmtsp(
new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr}); new AstEventControl{flp, new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr});
@@ -1294,7 +1496,7 @@ private:
// */ } // */ }
AstBegin* const bodyp = pexprp->bodyp(); AstBegin* const bodyp = pexprp->bodyp();
AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext(); AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext();
AstIf* const guardp = new AstIf{flp, condp, new AstBegin{flp, "", origStmtsp, true}}; AstIf* const guardp = new AstIf{flp, condp, origStmtsp};
bodyp->addStmtsp(guardp); bodyp->addStmtsp(guardp);
nodep->replaceWith(pexprp); nodep->replaceWith(pexprp);
// Don't iterate pexprp here -- it was already iterated when created // Don't iterate pexprp here -- it was already iterated when created
@@ -1368,10 +1570,9 @@ private:
loopp->addStmtsp(new AstLoopTest{ loopp->addStmtsp(new AstLoopTest{
flp, loopp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}}); flp, loopp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}});
{ {
AstBegin* const passp = new AstBegin{flp, "", nullptr, true}; AstNode* const passp = new AstPExprClause{flp};
passp->addStmtsp(new AstPExprClause{flp}); passp->addNext(decrementVar);
passp->addStmtsp(decrementVar); passp->addNext(setDone());
passp->addStmtsp(setDone());
AstNodeExpr* passCondp = rhsp; AstNodeExpr* passCondp = rhsp;
if (nodep->isOverlapping()) { if (nodep->isOverlapping()) {
passCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), passCondp}; passCondp = new AstLogAnd{flp, lhsp->cloneTreePure(false), passCondp};
@@ -1379,10 +1580,9 @@ private:
loopp->addStmtsp(new AstIf{flp, passCondp, passp}); loopp->addStmtsp(new AstIf{flp, passCondp, passp});
} }
{ {
AstBegin* const failp = new AstBegin{flp, "", nullptr, true}; AstNode* const failp = new AstPExprClause{flp, false};
failp->addStmtsp(new AstPExprClause{flp, false}); failp->addNext(decrementVar->cloneTree(false));
failp->addStmtsp(decrementVar->cloneTree(false)); failp->addNext(setDone());
failp->addStmtsp(setDone());
loopp->addStmtsp(new AstIf{ loopp->addStmtsp(new AstIf{
flp, flp,
new AstLogAnd{flp, new AstLogAnd{flp,
@@ -1396,7 +1596,7 @@ private:
flp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}, delayp}); flp, new AstLogNot{flp, new AstVarRef{flp, donep, VAccess::READ}}, delayp});
// Main assertion block // Main assertion block
AstBegin* const bodyp = new AstBegin{flp, "", nullptr, true}; AstBegin* const bodyp = new AstBegin{flp, m_blockNames.get(""), nullptr, true};
bodyp->addStmtsp(donep); bodyp->addStmtsp(donep);
bodyp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, donep, VAccess::WRITE}, bodyp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, donep, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}}); new AstConst{flp, AstConst::BitFalse{}}});
@@ -1428,7 +1628,7 @@ private:
new AstConst{flp, 1}}}); new AstConst{flp, 1}}});
// Final assertion block // Final assertion block
AstBegin* const finalp = new AstBegin{flp, "", nullptr, true}; AstBegin* const finalp = new AstBegin{flp, m_blockNames.get(""), nullptr, true};
finalp->addStmtsp(activeCountp); finalp->addStmtsp(activeCountp);
finalp->addStmtsp(initActiveCountp); finalp->addStmtsp(initActiveCountp);
finalp->addStmtsp(finalLoopp); finalp->addStmtsp(finalLoopp);
@@ -1459,7 +1659,7 @@ private:
AstNodeExpr* const passCondp AstNodeExpr* const passCondp
= nodep->isOverlapping() ? new AstLogAnd{flp, lhsp->cloneTreePure(false), rhsCopyp} = nodep->isOverlapping() ? new AstLogAnd{flp, lhsp->cloneTreePure(false), rhsCopyp}
: rhsCopyp; : rhsCopyp;
AstBegin* const beginp = new AstBegin{flp, "", loopp, true}; AstBegin* const beginp = new AstBegin{flp, m_blockNames.get(""), loopp, true};
beginp->addStmtsp( beginp->addStmtsp(
new AstIf{flp, passCondp, new AstPExprClause{flp}, new AstPExprClause{flp, false}}); new AstIf{flp, passCondp, new AstPExprClause{flp}, new AstPExprClause{flp, false}});
@@ -1548,7 +1748,7 @@ private:
"Unsupported: $sampled inside disabled condition of a sequence"); "Unsupported: $sampled inside disabled condition of a sequence");
m_disablep = new AstConst{m_disablep->fileline(), AstConst::BitFalse{}}; m_disablep = new AstConst{m_disablep->fileline(), AstConst::BitFalse{}};
// always a copy is used, so remove it now // always a copy is used, so remove it now
pushDeletep(m_disablep); pushDeletep(m_disablep); // m_disablep used later
} }
FileLine* const flp = nodep->fileline(); FileLine* const flp = nodep->fileline();
// Add counter which counts times the condition turned true // Add counter which counts times the condition turned true
@@ -1577,6 +1777,7 @@ private:
= new AstAssign{flp, new AstVarRef{flp, initialCntp, VAccess::WRITE}, = new AstAssign{flp, new AstVarRef{flp, initialCntp, VAccess::WRITE},
readCntRefp->cloneTree(false)}; readCntRefp->cloneTree(false)};
// Prepend to the sequence body to keep statement list structure valid. // Prepend to the sequence body to keep statement list structure valid.
UASSERT_OBJ(!bodyp->name().empty(), bodyp, "Sequence body should be named");
AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext(); AstNode* const origStmtsp = bodyp->stmtsp()->unlinkFrBackWithNext();
bodyp->addStmtsp(initialCntp); bodyp->addStmtsp(initialCntp);
initialCntp->addNextHere(assignp); initialCntp->addNextHere(assignp);
@@ -1586,7 +1787,7 @@ private:
= new AstIf{flp, new AstEq{flp, new AstVarRef{flp, initialCntp, VAccess::READ}, = new AstIf{flp, new AstEq{flp, new AstVarRef{flp, initialCntp, VAccess::READ},
readCntRefp->cloneTree(false)}}; readCntRefp->cloneTree(false)}};
// Delete it, because it is always copied before insetion to the AST // Delete it, because it is always copied before insetion to the AST
pushDeletep(m_disableSeqIfp); pushDeletep(m_disableSeqIfp); // m_disableSeqIfp used later
} }
iterateChildren(nodep); iterateChildren(nodep);
} }
@@ -1622,6 +1823,7 @@ public:
// CONSTRUCTORS // CONSTRUCTORS
explicit AssertPreVisitor(AstNetlist* nodep) explicit AssertPreVisitor(AstNetlist* nodep)
: m_netlistp{nodep} { : m_netlistp{nodep} {
{ AssertPreClockingVisitor{nodep}; }
// Process // Process
iterate(nodep); iterate(nodep);
// Fix up varref names // Fix up varref names
+6 -2
View File
@@ -66,7 +66,7 @@ VCMethod VCMethod::arrayMethod(const string& name) {
std::string VNUser::dumpStr(std::string (*fmtAddrp)(const void*)) const { std::string VNUser::dumpStr(std::string (*fmtAddrp)(const void*)) const {
#ifdef VL_USER_TYPE_CHECKS #ifdef VL_USER_TYPE_CHECKS
if (const int* const uip = std::get_if<int>(&m_u)) return "#"s + cvtToStr(*uip); 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); if (void* const* const upp = std::get_if<void*>(&m_u)) return fmtAddrp(*upp);
return ""; return "";
#else #else
@@ -208,6 +208,10 @@ string AstNode::dedotName(const string& namein) {
return pretty; return pretty;
} }
string AstNode::nameNoArray(const string& namein) {
return namein.substr(0, namein.find("__BRA__"));
}
string AstNode::vcdName(const string& namein) { string AstNode::vcdName(const string& namein) {
// VCD tracing expects space to separate hierarchy // VCD tracing expects space to separate hierarchy
// Dots are reserved for dots the user put in the name // Dots are reserved for dots the user put in the name
@@ -361,7 +365,7 @@ string AstNode::vpiName(const string& namein) {
string AstNode::prettyTypeName() const { string AstNode::prettyTypeName() const {
if (name() == "") return typeName(); if (name() == "") return typeName();
return std::string{typeName()} + " '" + prettyName() + "'"; return std::string{typeName()} + " '" + prettyNameMsg() + "'";
} }
//###################################################################### //######################################################################
+45 -41
View File
@@ -56,6 +56,7 @@
// Forward declarations // Forward declarations
class V3Graph; class V3Graph;
class ExecMTask; class ExecMTask;
class VIfaceCaptureTag;
//###################################################################### //######################################################################
@@ -151,11 +152,11 @@ class VNUser final {
#ifdef VL_USER_TYPE_CHECKS #ifdef VL_USER_TYPE_CHECKS
// monostate is an unwritten / cleared slot. It can be read as either form // monostate is an unwritten / cleared slot. It can be read as either form
// and yields nullptr/0. // and yields nullptr/0.
std::variant<std::monostate, int, void*> m_u; std::variant<std::monostate, uint64_t, void*> m_u;
#else #else
union { union {
void* up; void* up;
int ui; uint64_t uq;
} m_u; } m_u;
#endif #endif
@@ -164,7 +165,7 @@ public:
VNUser() = default; VNUser() = default;
// non-explicit: // non-explicit:
// cppcheck-suppress noExplicitConstructor // cppcheck-suppress noExplicitConstructor
VNUser(int i) { VNUser(uint64_t i) {
// VNUser{0} represents the monostate // VNUser{0} represents the monostate
if (i) m_u = i; if (i) m_u = i;
} }
@@ -178,22 +179,22 @@ public:
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE { 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; if (std::holds_alternative<std::monostate>(m_u)) return nullptr;
void* const* const upp = std::get_if<void*>(&m_u); void* const* const upp = std::get_if<void*>(&m_u);
UASSERT_STATIC(upp, "AstNode user() slot written as int, read as pointer"); UASSERT_STATIC(upp, "AstNode user() slot written as uint64_t, read as pointer");
return reinterpret_cast<T>(*upp); return reinterpret_cast<T>(*upp);
} }
int toInt() const { uint64_t toUQuad() const {
if (std::holds_alternative<std::monostate>(m_u)) return 0; if (std::holds_alternative<std::monostate>(m_u)) return 0;
const int* const uip = std::get_if<int>(&m_u); const uint64_t* const uip = std::get_if<uint64_t>(&m_u);
UASSERT_STATIC(uip, "AstNode user() slot written as pointer, read as int"); UASSERT_STATIC(uip, "AstNode user() slot written as pointer, read as uint64_t");
return *uip; return *uip;
} }
#else #else
VNUser() = default; VNUser() = default;
// non-explicit: // non-explicit:
// cppcheck-suppress noExplicitConstructor // cppcheck-suppress noExplicitConstructor
VNUser(int i) { VNUser(uint64_t i) {
m_u.up = nullptr; m_u.up = nullptr;
m_u.ui = i; m_u.uq = i;
} }
explicit VNUser(void* p) { m_u.up = p; } explicit VNUser(void* p) { m_u.up = p; }
~VNUser() = default; ~VNUser() = default;
@@ -202,7 +203,7 @@ public:
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE { typename std::enable_if<std::is_pointer<T>::value, T>::type to() const VL_MT_SAFE {
return reinterpret_cast<T>(m_u.up); return reinterpret_cast<T>(m_u.up);
} }
int toInt() const { return m_u.ui; } uint64_t toUQuad() const { return m_u.uq; }
#endif #endif
VSymEnt* toSymEnt() const { return to<VSymEnt*>(); } VSymEnt* toSymEnt() const { return to<VSymEnt*>(); }
AstNode* toNodep() const VL_MT_SAFE { return to<AstNode*>(); } AstNode* toNodep() const VL_MT_SAFE { return to<AstNode*>(); }
@@ -391,7 +392,7 @@ protected:
RELINK_OP4 RELINK_OP4
}; };
AstNode* m_oldp = nullptr; // The old node that was linked to this point in the tree AstNode* m_oldp = nullptr; // The old node that was linked to this point in the tree
AstNode* m_backp = nullptr; AstNode* m_backp = nullptr; // Saved AstNode::m_backp of the unlinked node, to relink
AstNode** m_iterpp = nullptr; AstNode** m_iterpp = nullptr;
RelinkWhatEn m_chg = RELINK_BAD; RelinkWhatEn m_chg = RELINK_BAD;
@@ -492,14 +493,14 @@ class AstNode VL_NOT_FINAL {
// This member ordering both allows 64 bit alignment and puts associated data together // This member ordering both allows 64 bit alignment and puts associated data together
// (under VL_USER_TYPE_CHECKS a VNUser is larger than 64 bits, so this packing no // (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) // longer holds; that build trades node size for catching int/pointer confusion)
VNUser m_user1u{0}; // Contains any information the user iteration routine wants 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_user1Cnt = 0; // Mark of when userp was set
uint32_t m_user2Cnt = 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_user2u{nullptr}; // Contains any information the user iteration routine wants
VNUser m_user3u{0}; // 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_user3Cnt = 0; // Mark of when userp was set
uint32_t m_user4Cnt = 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 // METHODS
void op1p(AstNode* nodep) { void op1p(AstNode* nodep) {
@@ -658,6 +659,7 @@ public:
string origNameProtect() const; // origName with --protect-id applied string origNameProtect() const; // origName with --protect-id applied
string shortName() const; // Name with __PVT__ removed for concatenating scopes string shortName() const; // Name with __PVT__ removed for concatenating scopes
static string dedotName(const string& namein); // Name with dots removed static string dedotName(const string& namein); // Name with dots removed
static string nameNoArray(const string& namein); // Name with any array index removed
static string prettyName(const string& namein) VL_PURE; // Name for printing out to the user static string prettyName(const string& namein) VL_PURE; // Name for printing out to the user
static string vpiName(const string& namein); // Name for vpi access static string vpiName(const string& namein); // Name for vpi access
static string prettyNameQ(const string& namein) { // Quoted pretty name (for errors) static string prettyNameQ(const string& namein) { // Quoted pretty name (for errors)
@@ -668,7 +670,10 @@ public:
static string encodeNumber(int64_t num); // Encode number into internal C representation static string encodeNumber(int64_t num); // Encode number into internal C representation
static string vcdName(const string& namein); // Name for printing out to vcd files static string vcdName(const string& namein); // Name for printing out to vcd files
string prettyName() const { return prettyName(name()); } string prettyName() const { return prettyName(name()); }
string prettyNameQ() const { return prettyNameQ(name()); } // Name for messages to the user, by default prettyName(), but friendlier for some nodes,
// such as a specialized class, whose name() is internal
virtual string prettyNameMsg() const { return prettyName(); }
string prettyNameQ() const { return "'"s + prettyNameMsg() + "'"; } // Quoted, for messages
string verilogName() const { return vpiName(origName()); } // Decoded original Verilog name string verilogName() const { return vpiName(origName()); } // Decoded original Verilog name
// "VARREF" for error messages (NOT dtype's pretty name) // "VARREF" for error messages (NOT dtype's pretty name)
string prettyTypeName() const; string prettyTypeName() const;
@@ -708,61 +713,61 @@ public:
VNUser user1u() const VL_MT_STABLE { VNUser user1u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default // Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser1InUse::s_userBusy, "user1p used without AstUserInUse"); //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(); } AstNode* user1p() const VL_MT_STABLE { return user1u().toNodep(); }
void user1u(const VNUser& user) { m_user1u = user; m_user1Cnt = VNUser1InUse::s_userCntGbl; } void user1u(const VNUser& user) { m_user1u = user; m_user1Cnt = VNUser1InUse::s_userCntGbl; }
void user1p(void* userp) { user1u(VNUser{userp}); } void user1p(void* userp) { user1u(VNUser{userp}); }
void user1(int val) { user1u(VNUser{val}); } void user1(uint64_t val) { user1u(VNUser{val}); }
int user1() const { return user1u().toInt(); } uint64_t user1() const { return user1u().toUQuad(); }
int user1Inc(int val = 1) { const int v = user1(); user1(v + val); return v; } uint64_t user1Inc(uint64_t val = 1) { const uint64_t v = user1(); user1(v + val); return v; }
int user1Or(int val) { const int v = user1(); user1(v | val); return v; } uint64_t user1Or(uint64_t val) { const uint64_t v = user1(); user1(v | val); return v; }
int user1SetOnce() { const int v = user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc() 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 static void user1ClearTree() { VNUser1InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user2u() const VL_MT_STABLE { VNUser user2u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default // Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser2InUse::s_userBusy, "user2p used without AstUserInUse"); //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(); } AstNode* user2p() const VL_MT_STABLE { return user2u().toNodep(); }
void user2u(const VNUser& user) { m_user2u = user; m_user2Cnt = VNUser2InUse::s_userCntGbl; } void user2u(const VNUser& user) { m_user2u = user; m_user2Cnt = VNUser2InUse::s_userCntGbl; }
void user2p(void* userp) { user2u(VNUser{userp}); } void user2p(void* userp) { user2u(VNUser{userp}); }
void user2(int val) { user2u(VNUser{val}); } void user2(uint64_t val) { user2u(VNUser{val}); }
int user2() const { return user2u().toInt(); } uint64_t user2() const { return user2u().toUQuad(); }
int user2Inc(int val = 1) { const int v = user2(); user2(v + val); return v; } uint64_t user2Inc(uint64_t val = 1) { const uint64_t v = user2(); user2(v + val); return v; }
int user2Or(int val) { const int v = user2(); user2(v | val); return v; } uint64_t user2Or(uint64_t val) { const uint64_t v = user2(); user2(v | val); return v; }
int user2SetOnce() { const int v = user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc() 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 static void user2ClearTree() { VNUser2InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user3u() const VL_MT_STABLE { VNUser user3u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default // Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser3InUse::s_userBusy, "user3p used without AstUserInUse"); //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(); } AstNode* user3p() const VL_MT_STABLE { return user3u().toNodep(); }
void user3u(const VNUser& user) { m_user3u = user; m_user3Cnt = VNUser3InUse::s_userCntGbl; } void user3u(const VNUser& user) { m_user3u = user; m_user3Cnt = VNUser3InUse::s_userCntGbl; }
void user3p(void* userp) { user3u(VNUser{userp}); } void user3p(void* userp) { user3u(VNUser{userp}); }
void user3(int val) { user3u(VNUser{val}); } void user3(uint64_t val) { user3u(VNUser{val}); }
int user3() const { return user3u().toInt(); } uint64_t user3() const { return user3u().toUQuad(); }
int user3Inc(int val = 1) { const int v = user3(); user3(v + val); return v; } uint64_t user3Inc(uint64_t val = 1) { const uint64_t v = user3(); user3(v + val); return v; }
int user3Or(int val) { const int v = user3(); user3(v | val); return v; } uint64_t user3Or(uint64_t val) { const uint64_t v = user3(); user3(v | val); return v; }
int user3SetOnce() { const int v = user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc() 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 static void user3ClearTree() { VNUser3InUse::clear(); } // Clear userp()'s across the entire tree
VNUser user4u() const VL_MT_STABLE { VNUser user4u() const VL_MT_STABLE {
// Slows things down measurably, so disabled by default // Slows things down measurably, so disabled by default
//UASSERT_STATIC(VNUser4InUse::s_userBusy, "user4p used without AstUserInUse"); //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(); } AstNode* user4p() const VL_MT_STABLE { return user4u().toNodep(); }
void user4u(const VNUser& user) { m_user4u = user; m_user4Cnt = VNUser4InUse::s_userCntGbl; } void user4u(const VNUser& user) { m_user4u = user; m_user4Cnt = VNUser4InUse::s_userCntGbl; }
void user4p(void* userp) { user4u(VNUser{userp}); } void user4p(void* userp) { user4u(VNUser{userp}); }
void user4(int val) { user4u(VNUser{val}); } void user4(uint64_t val) { user4u(VNUser{val}); }
int user4() const { return user4u().toInt(); } uint64_t user4() const { return user4u().toUQuad(); }
int user4Or(int val) { const int v = user4(); user4(v | val); return v; } uint64_t user4Or(uint64_t val) { const uint64_t v = user4(); user4(v | val); return v; }
int user4Inc(int val = 1) { const int v = user4(); user4(v + val); return v; } uint64_t user4Inc(uint64_t val = 1) { const uint64_t v = user4(); user4(v + val); return v; }
int user4SetOnce() { const int v = user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc() 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 static void user4ClearTree() { VNUser4InUse::clear(); } // Clear userp()'s across the entire tree
// clang-format on // clang-format on
@@ -1035,7 +1040,6 @@ protected:
AstNode* iterateSubtreeReturnEdits(VNVisitor& v); AstNode* iterateSubtreeReturnEdits(VNVisitor& v);
static void dumpJsonNum(std::ostream& os, const std::string& name, int64_t val); 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 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 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); static void dumpJsonPtr(std::ostream& os, const std::string& name, const AstNode* const valp);
+437 -265
View File
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -149,6 +149,9 @@ bool AstActive::hasCombo() const { return m_sentreep->hasCombo(); }
bool AstActive::hasInitial() const { return m_sentreep->hasInitial(); } bool AstActive::hasInitial() const { return m_sentreep->hasInitial(); }
bool AstActive::hasStatic() const { return m_sentreep->hasStatic(); } bool AstActive::hasStatic() const { return m_sentreep->hasStatic(); }
bool AstNodeModule::isDollarUnit() const { return this == v3Global.rootp()->dollarUnitPkgp(); }
bool AstNodeModule::isConstPool() const { return this == v3Global.rootp()->constPoolPkgp(); }
AstAlways::AstAlways(AstAssignW* assignp) AstAlways::AstAlways(AstAssignW* assignp)
: ASTGEN_SUPER_Always(assignp->fileline(), assignp) : ASTGEN_SUPER_Always(assignp->fileline(), assignp)
, m_keyword{VAlwaysKwd::CONT_ASSIGN} {} , m_keyword{VAlwaysKwd::CONT_ASSIGN} {}
@@ -165,10 +168,10 @@ bool AstVar::sameNode(const AstNode* samep) const {
&& covergroupRefMember() == asamep->covergroupRefMember(); && covergroupRefMember() == asamep->covergroupRefMember();
} }
AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp) AstMatchMasked::AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarRef* matchp)
: ASTGEN_SUPER_MatchMasked(fl) { : ASTGEN_SUPER_MatchMasked(fl) {
this->lhsp(lhsp); this->lhsp(lhsp);
this->matchp(new AstVarRef{fl, matchp, VAccess::READ}); this->matchp(matchp);
dtypeSetUInt32(); dtypeSetUInt32();
} }
+125 -23
View File
@@ -34,10 +34,14 @@ class AstNodeDType VL_NOT_FINAL : public AstNode {
// Ideally width() would migrate to BasicDType as that's where it makes sense, // Ideally width() would migrate to BasicDType as that's where it makes sense,
// but it's currently so prevalent in the code we leave it here. // but it's currently so prevalent in the code we leave it here.
// Note the below members are included in AstTypeTable::Key lookups // Note the below members are included in AstTypeTable::Key lookups
// dist-ast-dump-suppress // Part of dumpSmall
int m_width = 0; // (also in AstTypeTable::Key) Bit width of operation int m_width = 0; // (also in AstTypeTable::Key) Bit width of operation
int m_widthMin // (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation
= 0; // (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation // dist-ast-dump-suppress // Part of dumpSmall
int m_widthMin = 0;
// dist-ast-dump-suppress // Part of dumpSmall
VSigning m_numeric; // (also in AstTypeTable::Key) Node is signed VSigning m_numeric; // (also in AstTypeTable::Key) Node is signed
// dist-ast-dump-suppress // Part of dumpSmall
bool m_generic = false; // Simple globally referenced type, don't garbage collect bool m_generic = false; // Simple globally referenced type, don't garbage collect
// Unique number assigned to each dtype during creation for IEEE matching // Unique number assigned to each dtype during creation for IEEE matching
static int s_uniqueNum; static int s_uniqueNum;
@@ -99,6 +103,9 @@ public:
return const_cast<AstNodeDType*>( return const_cast<AstNodeDType*>(
static_cast<const AstNodeDType*>(this)->skipRefIterp(true, false)); static_cast<const AstNodeDType*>(this)->skipRefIterp(true, false));
} }
// (Slow) Recurse over MemberDType|ParamTypeDType|RefDType|ConstDType to EnumDType,
// Returns null if not resolved
const AstNodeDType* skipRefToEnumOrNullp() const { return skipRefIterp(true, false, false); }
// (Slow) Recurse over MemberDType|ParamTypeDType|RefDType to other type // (Slow) Recurse over MemberDType|ParamTypeDType|RefDType to other type
const AstNodeDType* skipRefToNonRefp() const { return skipRefIterp(false, false); } const AstNodeDType* skipRefToNonRefp() const { return skipRefIterp(false, false); }
AstNodeDType* skipRefToNonRefp() { AstNodeDType* skipRefToNonRefp() {
@@ -246,8 +253,9 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType {
// //
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Package emitted with // @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Package emitted with
string m_name; // Name from upper typedef, if any string m_name; // Name from upper typedef, if any
const int m_uniqueNum; string m_typedefName; // Typedef's dtypeName() once moved off the typedef declaring it
bool m_packed; const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
bool m_packed; // Packed struct/union, else unpacked
bool m_isFourstate = false; // V3Width computes; true if any member is 4-state bool m_isFourstate = false; // V3Width computes; true if any member is 4-state
bool m_constrainedRand = false; // True if struct has constraint expression bool m_constrainedRand = false; // True if struct has constraint expression
bool m_emitToString = false; // Generate to_string() for this struct/union if set bool m_emitToString = false; // Generate to_string() for this struct/union if set
@@ -263,6 +271,7 @@ protected:
AstNodeUOrStructDType(const AstNodeUOrStructDType& other) AstNodeUOrStructDType(const AstNodeUOrStructDType& other)
: AstNodeDType{other} : AstNodeDType{other}
, m_name{other.m_name} , m_name{other.m_name}
, m_typedefName{other.m_typedefName}
, m_uniqueNum{uniqueNumInc()} , m_uniqueNum{uniqueNumInc()}
, m_packed{other.m_packed} , m_packed{other.m_packed}
, m_isFourstate{other.m_isFourstate} {} , m_isFourstate{other.m_isFourstate} {}
@@ -290,6 +299,8 @@ public:
bool similarDTypeNode(const AstNodeDType* samep) const override; bool similarDTypeNode(const AstNodeDType* samep) const override;
string name() const override VL_MT_STABLE { return m_name; } string name() const override VL_MT_STABLE { return m_name; }
void name(const string& flag) override { m_name = flag; } void name(const string& flag) override { m_name = flag; }
// Keep the name the typedef declaring it gives, as it moves off it to the type table
void typedefName(const string& name) { m_typedefName = name; }
bool packed() const VL_MT_SAFE { return m_packed; } bool packed() const VL_MT_SAFE { return m_packed; }
void packed(bool flag) { m_packed = flag; } void packed(bool flag) { m_packed = flag; }
// packed() but as don't support unpacked, presently all structs // packed() but as don't support unpacked, presently all structs
@@ -315,7 +326,7 @@ public:
class AstEnumItem final : public AstNode { class AstEnumItem final : public AstNode {
// @astgen op1 := rangep : Optional[AstRange] // Range for name appending // @astgen op1 := rangep : Optional[AstRange] // Range for name appending
// @astgen op2 := valuep : Optional[AstNodeExpr] // @astgen op2 := valuep : Optional[AstNodeExpr]
string m_name; string m_name; // Name of item
public: public:
// Parents: ENUM // Parents: ENUM
@@ -405,6 +416,7 @@ class AstBasicDType final : public AstNodeDType {
// @astgen op1 := rangep : Optional[AstRange] // Range of variable // @astgen op1 := rangep : Optional[AstRange] // Range of variable
struct Members final { struct Members final {
VBasicDTypeKwd m_keyword; // (also in VBasicTypeKey) What keyword created basic type VBasicDTypeKwd m_keyword; // (also in VBasicTypeKey) What keyword created basic type
// dist-ast-dump-suppress // Part of dumpSmall
VNumRange m_nrange; // (also in VBasicTypeKey) Numeric msb/lsb (if non-opaque keyword) VNumRange m_nrange; // (also in VBasicTypeKey) Numeric msb/lsb (if non-opaque keyword)
bool operator==(const Members& rhs) const { bool operator==(const Members& rhs) const {
return rhs.m_keyword == m_keyword && rhs.m_nrange == m_nrange; return rhs.m_keyword == m_keyword && rhs.m_nrange == m_nrange;
@@ -493,6 +505,9 @@ public:
bool isStdRandomGenerator() const VL_MT_SAFE { bool isStdRandomGenerator() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::RANDOM_STDGENERATOR; return keyword() == VBasicDTypeKwd::RANDOM_STDGENERATOR;
} }
bool isCovergroupInstHandle() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::COVERGROUP_INSTHANDLE;
}
bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); } bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); }
bool isString() const VL_MT_STABLE { return keyword().isString(); } bool isString() const VL_MT_STABLE { return keyword().isString(); }
bool isZeroInit() const { return keyword().isZeroInit(); } bool isZeroInit() const { return keyword().isZeroInit(); }
@@ -603,6 +618,7 @@ public:
void dumpJson(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override; string prettyDTypeName(bool full) const override;
string prettyNameMsg() const override;
string name() const override VL_MT_STABLE; string name() const override VL_MT_STABLE;
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
int widthAlignBytes() const override { return 0; } int widthAlignBytes() const override { return 0; }
@@ -682,13 +698,89 @@ public:
int widthTotalBytes() const override { return 1; } int widthTotalBytes() const override { return 1; }
bool isCompound() const override { return false; } bool isCompound() const override { return false; }
}; };
class AstCoverCrossDType final : public AstNodeDType {
// Borrowed pointer to VlCoverCrossT<...> or construction-time VlCoverCrossDyn.
const uint32_t m_dimensions;
const uint32_t m_tuples; // Fixed capacity number of cross tuples (VlCoverCrossT's Tuples)
const uint32_t m_bins; // Fixed capacity number of explicit bins (VlCoverCrossT's Bins)
const uint32_t m_autoBins; // Fixed capacity number of auto bins (VlCoverCrossT's AutoBins)
const uint64_t m_binWords; // Fixed capacity selection words (VlCoverCrossT's BinWords)
const bool m_dynamic; // Bin layout is determined at covergroup construction
public:
AstCoverCrossDType(FileLine* fl, uint32_t dimensions, uint32_t tuples, uint32_t bins,
uint32_t autoBins, uint64_t binWords, bool dynamic = false)
: ASTGEN_SUPER_CoverCrossDType(fl)
, m_dimensions{dimensions}
, m_tuples{tuples}
, m_bins{bins}
, m_autoBins{autoBins}
, m_binWords{binWords}
, m_dynamic{dynamic} {
dtypep(this);
}
ASTGEN_MEMBERS_AstCoverCrossDType;
const char* broken() const override {
BROKEN_RTN(m_dimensions == 0);
return nullptr;
}
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
const AstCoverCrossDType* const sp = VN_DBG_AS(samep, CoverCrossDType);
return dimensions() == sp->dimensions() && tuples() == sp->tuples() && bins() == sp->bins()
&& autoBins() == sp->autoBins() && binWords() == sp->binWords()
&& isDynamic() == sp->isDynamic();
} // LCOV_EXCL_STOP
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
void dumpSmall(std::ostream& str) const override;
uint32_t dimensions() const { return m_dimensions; }
uint32_t tuples() const { return m_tuples; }
uint32_t bins() const { return m_bins; }
uint32_t autoBins() const { return m_autoBins; }
uint64_t binWords() const { return m_binWords; }
bool isDynamic() const { return m_dynamic; }
string cppTemplateArgs() const;
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
int widthAlignBytes() const override { return sizeof(void*); }
int widthTotalBytes() const override { return sizeof(void*); }
bool isCompound() const override { return true; }
};
class AstCoverpointDType final : public AstNodeDType {
// Borrowed pointer to a covergroup coverpoint runtime, 'VlCoverpointT<hitBound>*'.
// Follows pattern of AstQueueDType in capturing the compile-time max bin overlap
// template argument.
uint32_t m_hitBound; // VlCoverpointT<> template argument; hit list size, >= 1
public:
AstCoverpointDType(FileLine* fl, uint32_t hitBound)
: ASTGEN_SUPER_CoverpointDType(fl)
, m_hitBound{hitBound} {
dtypep(this);
}
ASTGEN_MEMBERS_AstCoverpointDType;
const char* broken() const override {
BROKEN_RTN(m_hitBound < 1);
return nullptr;
}
// V3Covergroup interns these one-per-hitBound into the type table, so identity is
// equality; there is never a second node with the same bound to compare against.
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; }
void dumpSmall(std::ostream& str) const override;
// ACCESSORS
uint32_t hitBound() const { return m_hitBound; }
// METHODS
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
int widthAlignBytes() const override { return sizeof(void*); }
int widthTotalBytes() const override { return sizeof(void*); }
bool isCompound() const override { return true; }
};
class AstDefImplicitDType final : public AstNodeDType { class AstDefImplicitDType final : public AstNodeDType {
// For parsing enum/struct/unions that are declared with a variable rather than typedef // For parsing enum/struct/unions that are declared with a variable rather than typedef
// This allows "var enum {...} a,b" to share the enum definition for both variables // This allows "var enum {...} a,b" to share the enum definition for both variables
// After link, these become typedefs // After link, these become typedefs
// @astgen op1 := childDTypep : Optional[AstNodeDType] // @astgen op1 := childDTypep : Optional[AstNodeDType]
string m_name; string m_name; // Data type name
const int m_uniqueNum; const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
public: public:
AstDefImplicitDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp) AstDefImplicitDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp)
@@ -798,13 +890,9 @@ class AstEnumDType final : public AstNodeDType {
// @astgen op2 := itemsp : List[AstEnumItem] // @astgen op2 := itemsp : List[AstEnumItem]
// //
// @astgen ptr := m_refDTypep : Optional[AstNodeDType] // Elements of this type (post-width) // @astgen ptr := m_refDTypep : Optional[AstNodeDType] // Elements of this type (post-width)
public:
using TableMap = std::map<VAttrType, AstVar*>;
private:
string m_name; // Name from upper typedef, if any string m_name; // Name from upper typedef, if any
const int m_uniqueNum; string m_typedefName; // Typedef's dtypeName() once moved off the typedef declaring it
TableMap m_tableMap; // Created table for V3Width only to remove duplicates const int m_uniqueNum; // Unique ID distinguishing this dtype instance, for hashing/naming
public: public:
AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstEnumItem* itemsp) AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstEnumItem* itemsp)
@@ -819,6 +907,7 @@ public:
AstEnumDType(const AstEnumDType& other) AstEnumDType(const AstEnumDType& other)
: AstNodeDType{other} : AstNodeDType{other}
, m_name{other.m_name} , m_name{other.m_name}
, m_typedefName{other.m_typedefName}
, m_uniqueNum{uniqueNumInc()} {} , m_uniqueNum{uniqueNumInc()} {}
ASTGEN_MEMBERS_AstEnumDType; ASTGEN_MEMBERS_AstEnumDType;
@@ -838,6 +927,8 @@ public:
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
string name() const override VL_MT_STABLE { return m_name; } string name() const override VL_MT_STABLE { return m_name; }
void name(const string& flag) override { m_name = flag; } void name(const string& flag) override { m_name = flag; }
// Keep the name the typedef declaring it gives, as it moves off it to the type table
void typedefName(const string& name) { m_typedefName = name; }
void dump(std::ostream& str = std::cout) const override; void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
@@ -852,13 +943,12 @@ public:
return count; return count;
} }
bool isCompound() const override { return false; } bool isCompound() const override { return false; }
TableMap& tableMap() { return m_tableMap; }
const TableMap& tableMap() const { return m_tableMap; }
}; };
class AstIfaceGenericDType final : public AstNodeDType { class AstIfaceGenericDType final : public AstNodeDType {
// Generic interface that will be replaced with AstIfaceRefDType // Generic interface that will be replaced with AstIfaceRefDType
FileLine* m_modportFileline; // Where modport token was FileLine* m_modportFileline; // Where modport token was
// dist-ast-dump-suppress // Part of name()
string m_modportName; // "" = no modport string m_modportName; // "" = no modport
public: public:
explicit AstIfaceGenericDType(FileLine* fl) explicit AstIfaceGenericDType(FileLine* fl)
@@ -872,8 +962,6 @@ public:
dtypep(this); dtypep(this);
} }
ASTGEN_MEMBERS_AstIfaceGenericDType; ASTGEN_MEMBERS_AstIfaceGenericDType;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
bool hasDType() const override VL_MT_SAFE { return true; } bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; } bool maybePointedTo() const override VL_MT_SAFE { return true; }
@@ -903,7 +991,6 @@ class AstIfaceRefDType final : public AstNodeDType {
string m_cellName; // "" = no cell, such as when connects to 'input' iface string m_cellName; // "" = no cell, such as when connects to 'input' iface
string m_ifaceName; // Interface name string m_ifaceName; // Interface name
string m_modportName; // "" = no modport string m_modportName; // "" = no modport
bool m_portDecl = false; // Interface_port_declaration
bool m_virtual = false; // True if virtual interface bool m_virtual = false; // True if virtual interface
public: public:
AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName) AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName)
@@ -934,12 +1021,18 @@ public:
void dump(std::ostream& str = std::cout) const override; void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; } bool similarDTypeNode(const AstNodeDType* samep) const override {
// Each occurrence of a virtual interface type parses to its own node,
// so pointer identity is insufficient; compare the referenced
// interface instead, same equivalence as AstNode::computeCastable uses.
const AstIfaceRefDType* const asamep = VN_DBG_AS(samep, IfaceRefDType);
return ifaceViaCellp() && ifaceViaCellp() == asamep->ifaceViaCellp()
&& modportName() == asamep->modportName() && isVirtual() == asamep->isVirtual();
}
int widthAlignBytes() const override { return 0; } int widthAlignBytes() const override { return 0; }
int widthTotalBytes() const override { return 0; } int widthTotalBytes() const override { return 0; }
bool isPortDecl() const { return m_portDecl; }
void isPortDecl(bool flag) { m_portDecl = flag; }
bool isVirtual() const { return m_virtual; } bool isVirtual() const { return m_virtual; }
void isVirtual(bool flag) { void isVirtual(bool flag) {
m_virtual = flag; m_virtual = flag;
@@ -974,7 +1067,7 @@ class AstMemberDType final : public AstNodeDType {
string m_name; // Name of variable string m_name; // Name of variable
string m_tag; // Holds the string of the verilator tag -- used in JSON output. string m_tag; // Holds the string of the verilator tag -- used in JSON output.
int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member
bool m_constrainedRand = false; bool m_constrainedRand = false; // Member has a constraint expression
VRandAttr m_rand; // Randomizability of this member (rand, randc, etc) VRandAttr m_rand; // Randomizability of this member (rand, randc, etc)
public: public:
AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp, AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp,
@@ -998,6 +1091,7 @@ public:
void dump(std::ostream& str = std::cout) const override; void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name string name() const override VL_MT_STABLE { return m_name; } // * = Var name
bool hasDType() const override VL_MT_SAFE { return true; } bool hasDType() const override VL_MT_SAFE { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return true; } bool maybePointedTo() const override VL_MT_SAFE { return true; }
@@ -1081,6 +1175,7 @@ public:
ASTGEN_MEMBERS_AstParamTypeDType; ASTGEN_MEMBERS_AstParamTypeDType;
void dump(std::ostream& str = std::cout) const override; void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override;
string prettyDTypeName(bool full) const override;
AstNodeDType* getChildDTypep() const override { return childDTypep(); } AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* subDTypep() const override VL_MT_STABLE { AstNodeDType* subDTypep() const override VL_MT_STABLE {
return dtypep() ? dtypep() : childDTypep(); return dtypep() ? dtypep() : childDTypep();
@@ -1195,6 +1290,9 @@ class AstRefDType final : public AstNodeDType {
// @astgen ptr := m_refDTypep : Optional[AstNodeDType] // Data type references // @astgen ptr := m_refDTypep : Optional[AstNodeDType] // Data type references
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in // @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in
string m_name; // Name of an AstTypedef string m_name; // Name of an AstTypedef
// Interface capture tag, owned by V3LinkDotIfaceCapture; cloning copies it
const VIfaceCaptureTag* m_captureTagp = nullptr;
public: public:
AstRefDType(FileLine* fl, const string& name) AstRefDType(FileLine* fl, const string& name)
: ASTGEN_SUPER_RefDType(fl) : ASTGEN_SUPER_RefDType(fl)
@@ -1221,12 +1319,13 @@ public:
bool similarDTypeNode(const AstNodeDType* samep) const override { bool similarDTypeNode(const AstNodeDType* samep) const override {
return subDTypep()->similarDType(samep->subDTypep()); return subDTypep()->similarDType(samep->subDTypep());
} }
const char* broken() const override;
void dump(std::ostream& str = std::cout) const override; void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override; void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; } string name() const override VL_MT_STABLE { return m_name; }
string prettyDTypeName(bool full) const override { string prettyDTypeName(bool full) const override {
return subDTypep() ? prettyName(subDTypep()->prettyDTypeName(full)) : prettyName(); return subDTypep() ? subDTypep()->prettyDTypeName(full) : prettyName();
} }
AstBasicDType* basicp() const override VL_MT_STABLE { AstBasicDType* basicp() const override VL_MT_STABLE {
return subDTypep() ? subDTypep()->basicp() : nullptr; return subDTypep() ? subDTypep()->basicp() : nullptr;
@@ -1245,6 +1344,8 @@ public:
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; } AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; } void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
const VIfaceCaptureTag* captureTagp() const { return m_captureTagp; }
void captureTagp(const VIfaceCaptureTag* tagp) { m_captureTagp = tagp; }
bool isCompound() const override { bool isCompound() const override {
v3fatalSrc("call isCompound on subdata type, not reference"); v3fatalSrc("call isCompound on subdata type, not reference");
return false; return false;
@@ -1416,6 +1517,7 @@ public:
bool sameNode(const AstNode* samep) const override; bool sameNode(const AstNode* samep) const override;
bool similarDTypeNode(const AstNodeDType* samep) const override; bool similarDTypeNode(const AstNodeDType* samep) const override;
void dumpSmall(std::ostream& str) const override; void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
AstNodeDType* getChildDTypep() const override { return childDTypep(); } AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* subDTypep() const override VL_MT_STABLE { AstNodeDType* subDTypep() const override VL_MT_STABLE {
return m_refDTypep ? m_refDTypep : childDTypep(); return m_refDTypep ? m_refDTypep : childDTypep();
+57 -17
View File
@@ -66,7 +66,18 @@ public:
bool isOpaque() const { return VN_IS(this, CvtPackString); } bool isOpaque() const { return VN_IS(this, CvtPackString); }
// True for SVA multi-cycle sequence nodes (SExpr, SConsRep, etc.) // True for SVA multi-cycle sequence nodes (SExpr, SConsRep, etc.)
virtual bool isMultiCycleSva() const { return false; } virtual bool isMultiCycleSva() const { return false; }
bool isLValue() const; const AstNodeExpr* getVAccessTargetRecurse() const;
AstNodeExpr* getVAccessTargetRecurse() {
return const_cast<AstNodeExpr*>( // casting constness away is safe since this function is
// non-const itself therefore, caller guarantees that
// this object is non-const
static_cast<const AstNodeExpr*>(this)->getVAccessTargetRecurse());
}
VAccess getVAccessRecurse() const;
// TODO: this actually means it's a write or RW, not that it's an LValue
bool isLValue() const { return getVAccessRecurse().isWriteOrRW(); }
// Return base var (or const) nodep dereferences
AstNode* baseFromp(bool overMembers);
// Wrap This expression into an AstStmtExpr to denote it occurs in statement position // Wrap This expression into an AstStmtExpr to denote it occurs in statement position
inline AstStmtExpr* makeStmt(); inline AstStmtExpr* makeStmt();
@@ -189,6 +200,7 @@ class AstNodeCCall VL_NOT_FINAL : public AstNodeExpr {
// @astgen op2 := argsp : List[AstNodeExpr] // Note: op1 used by some sub-types only // @astgen op2 := argsp : List[AstNodeExpr] // Note: op1 used by some sub-types only
// //
// @astgen ptr := m_funcp : AstCFunc // Function being called // @astgen ptr := m_funcp : AstCFunc // Function being called
// dist-ast-dump-suppress // Too verbose
string m_argTypes; string m_argTypes;
bool m_superReference = false; // Called with super reference bool m_superReference = false; // Called with super reference
@@ -216,7 +228,7 @@ public:
void funcp(AstCFunc* funcp) { m_funcp = funcp; } void funcp(AstCFunc* funcp) { m_funcp = funcp; }
string argTypes() const { return m_argTypes; } string argTypes() const { return m_argTypes; }
void argTypes(const string& str) { m_argTypes = str; } void argTypes(const string& str) { m_argTypes = str; }
bool isUnlikely() const override { return m_funcp && m_funcp->isUnlikely(); }
string emitVerilog() final override { V3ERROR_NA_RETURN(""); } string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
string emitC() final override { V3ERROR_NA_RETURN(""); } string emitC() final override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const final override { return true; } bool cleanOut() const final override { return true; }
@@ -271,6 +283,7 @@ public:
bool superReference() const { return m_superReference; } bool superReference() const { return m_superReference; }
void superReference(bool flag) { m_superReference = flag; } void superReference(bool flag) { m_superReference = flag; }
bool isPure() override; bool isPure() override;
bool isUnlikely() const override { return m_taskp && m_taskp->isUnlikely(); }
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
const AstNodeFTaskRef* const asamep = VN_DBG_AS(samep, NodeFTaskRef); const AstNodeFTaskRef* const asamep = VN_DBG_AS(samep, NodeFTaskRef);
return taskp() == asamep->taskp() // return taskp() == asamep->taskp() //
@@ -519,7 +532,6 @@ public:
} }
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; } AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; } void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
static AstNodeVarRef* varRefLValueRecurse(AstNode* nodep);
}; };
// === Concrete node types ===================================================== // === Concrete node types =====================================================
@@ -556,6 +568,7 @@ class AstWith final : public AstNode {
private: private:
// 'with (identifier_list) {...}' restricted form (IEEE 1800-2023 18.7). // 'with (identifier_list) {...}' restricted form (IEEE 1800-2023 18.7).
bool m_restricted = false; bool m_restricted = false;
// dist-ast-dump-suppress // V3LinkDot temporary use only
bool m_validated = false; // identifier_list typo / unused checks already run bool m_validated = false; // identifier_list typo / unused checks already run
std::set<std::string> m_restrictedNames; std::set<std::string> m_restrictedNames;
@@ -739,7 +752,7 @@ class AstCMethodHard final : public AstNodeExpr {
// @astgen op2 := pinsp : List[AstNodeExpr] // Arguments // @astgen op2 := pinsp : List[AstNodeExpr] // Arguments
// @astgen op3 := withp : Optional[AstWith] // With clause // @astgen op3 := withp : Optional[AstWith] // With clause
VCMethod m_method; // Which method to call VCMethod m_method; // Which method to call
bool m_pure = false; // Pure optimizable VIsCached m_purity; // Pure optimizable
bool m_usePtr = false; // Use '->' not '.' bool m_usePtr = false; // Use '->' not '.'
public: public:
AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VCMethod method, AstNodeExpr* pinsp = nullptr) AstCMethodHard(FileLine* fl, AstNodeExpr* fromp, VCMethod method, AstNodeExpr* pinsp = nullptr)
@@ -747,15 +760,19 @@ public:
, m_method{method} { , m_method{method} {
this->fromp(fromp); this->fromp(fromp);
addPinsp(pinsp); addPinsp(pinsp);
setPurity();
} }
ASTGEN_MEMBERS_AstCMethodHard; ASTGEN_MEMBERS_AstCMethodHard;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return method().ascii(); } string name() const override VL_MT_STABLE { return method().ascii(); }
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
const AstCMethodHard* const asamep = VN_DBG_AS(samep, CMethodHard); const AstCMethodHard* const asamep = VN_DBG_AS(samep, CMethodHard);
return (m_method == asamep->m_method); return (m_method == asamep->m_method);
} }
bool isPure() override { return m_pure; } bool isPure() override {
if (!m_purity.isCached()) m_purity.set(getPurity());
return m_purity.get();
}
int instrCount() const override; int instrCount() const override;
string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); }
@@ -766,7 +783,7 @@ public:
void method(VCMethod value) { m_method = value; } void method(VCMethod value) { m_method = value; }
private: private:
void setPurity(); bool getPurity();
}; };
class AstCReset final : public AstNodeExpr { class AstCReset final : public AstNodeExpr {
// Reset variable at startup // Reset variable at startup
@@ -1236,6 +1253,10 @@ public:
int instrCount() const override { return widthInstrs(); } int instrCount() const override { return widthInstrs(); }
bool isEqAllOnes() const { return num().isEqAllOnes(width()); } bool isEqAllOnes() const { return num().isEqAllOnes(width()); }
bool isEqAllOnesV() const { return num().isEqAllOnes(widthMinV()); } bool isEqAllOnesV() const { return num().isEqAllOnes(widthMinV()); }
// Whether a parameter declared without a data type takes the same type from this value
// as from 'samep': both real, both string, or both integral with the same width and
// signedness. Unlike AstNodeDType::similarDType, ignores whether two-state or four-state.
bool sameValueType(const AstConst* samep) const;
// Parse string and create appropriate type of AstConst. // Parse string and create appropriate type of AstConst.
// May return nullptr on parse failure. // May return nullptr on parse failure.
static AstConst* parseParamLiteral(FileLine* fl, const string& literal); static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
@@ -1425,6 +1446,8 @@ class AstEnumItemRef final : public AstNodeExpr {
// @astgen ptr := m_itemp : Optional[AstEnumItem] // [AfterLink] Pointer to item // @astgen ptr := m_itemp : Optional[AstEnumItem] // [AfterLink] Pointer to item
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in // @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in
string m_name; // Name of enum (for param relink) string m_name; // Name of enum (for param relink)
string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
bool m_containsGenBlock = false; // Contains gen block reference
public: public:
AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep) AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep)
: ASTGEN_SUPER_EnumItemRef(fl) : ASTGEN_SUPER_EnumItemRef(fl)
@@ -1441,10 +1464,15 @@ public:
int instrCount() const override { return 0; } int instrCount() const override { return 0; }
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
const AstEnumItemRef* const sp = VN_DBG_AS(samep, EnumItemRef); const AstEnumItemRef* const sp = VN_DBG_AS(samep, EnumItemRef);
return itemp() == sp->itemp(); return itemp() == sp->itemp() && dotted() == sp->dotted()
&& containsGenBlock() == sp->containsGenBlock();
} }
AstEnumItem* itemp() const VL_MT_STABLE { return m_itemp; } AstEnumItem* itemp() const VL_MT_STABLE { return m_itemp; }
void itemp(AstEnumItem* nodep) { m_itemp = nodep; } void itemp(AstEnumItem* nodep) { m_itemp = nodep; }
string dotted() const { return m_dotted; }
void dotted(const string& dotted) { m_dotted = dotted; }
bool containsGenBlock() const { return m_containsGenBlock; }
void containsGenBlock(const bool flag) { m_containsGenBlock = flag; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; } bool cleanOut() const override { return true; }
@@ -1458,7 +1486,7 @@ class AstExprStmt final : public AstNodeExpr {
// @astgen op1 := stmtsp : List[AstNode] // @astgen op1 := stmtsp : List[AstNode]
// @astgen op2 := resultp : AstNodeExpr // @astgen op2 := resultp : AstNodeExpr
private: private:
bool m_hasResult = true; bool m_hasResult = true; // Returns result via resultp()
public: public:
AstExprStmt(FileLine* fl, AstNode* stmtsp, AstNodeExpr* resultp) AstExprStmt(FileLine* fl, AstNode* stmtsp, AstNodeExpr* resultp)
@@ -1469,6 +1497,8 @@ public:
} }
ASTGEN_MEMBERS_AstExprStmt; ASTGEN_MEMBERS_AstExprStmt;
// METHODS // METHODS
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; } bool cleanOut() const override { return true; }
@@ -1746,7 +1776,7 @@ public:
bool sameNode(const AstNode* /*samep*/) const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; }
}; };
class AstImplication final : public AstNodeExpr { class AstImplication final : public AstNodeExpr {
// Implication |-> |=> (IEEE 1800-2023 16.12.6) and followed-by #-# #=# // Implication |-> |=> (IEEE 1800-2023 16.12.7) and followed-by #-# #=#
// (IEEE 1800-2023 16.12.9). Antecedent-miss is vacuous-pass for implication // (IEEE 1800-2023 16.12.9). Antecedent-miss is vacuous-pass for implication
// and non-vacuous-fail for followed-by, hence the separate flag. // and non-vacuous-fail for followed-by, hence the separate flag.
// @astgen op1 := lhsp : AstNodeExpr // @astgen op1 := lhsp : AstNodeExpr
@@ -1774,7 +1804,7 @@ public:
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); } string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
int instrCount() const override { return widthInstrs(); } int instrCount() const override { return widthInstrs(); }
bool isMultiCycleSva() const override { return m_isFollowedBy; } bool isMultiCycleSva() const override { return m_isFollowedBy || !m_isOverlapped; }
bool isOverlapped() const { return m_isOverlapped; } bool isOverlapped() const { return m_isOverlapped; }
bool isFollowedBy() const { return m_isFollowedBy; } bool isFollowedBy() const { return m_isFollowedBy; }
}; };
@@ -1793,6 +1823,7 @@ public:
using KeyItemMap = std::map<uint64_t, AstInitItem*>; using KeyItemMap = std::map<uint64_t, AstInitItem*>;
private: private:
// dist-ast-dump-suppress // Dumped using dumpInitList
KeyItemMap m_map; // Node value for each array index KeyItemMap m_map; // Node value for each array index
// METHODS // METHODS
void dumpInitList(std::ostream& str) const; void dumpInitList(std::ostream& str) const;
@@ -1866,6 +1897,8 @@ public:
, m_name{name} , m_name{name}
, m_index{index} {} , m_index{index} {}
ASTGEN_MEMBERS_AstLambdaArgRef; ASTGEN_MEMBERS_AstLambdaArgRef;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool sameNode(const AstNode* /*samep*/) const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; }
string emitVerilog() override { return name(); } string emitVerilog() override { return name(); }
string emitC() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); }
@@ -1885,7 +1918,7 @@ class AstMatchMasked final : public AstNodeExpr {
// @astgen op1 := lhsp : AstNodeExpr // @astgen op1 := lhsp : AstNodeExpr
// @astgen op2 := matchp : AstVarRef // @astgen op2 := matchp : AstVarRef
public: public:
inline AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarScope* matchp); inline AstMatchMasked(FileLine* fl, AstNodeExpr* lhsp, AstVarRef* matchp);
ASTGEN_MEMBERS_AstMatchMasked; ASTGEN_MEMBERS_AstMatchMasked;
string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { return "VL_MATCHMASKED_%lq(%lw, %li, %ri)"; } string emitC() override { return "VL_MATCHMASKED_%lq(%lw, %li, %ri)"; }
@@ -2013,7 +2046,7 @@ class AstParseRef final : public AstNodeExpr {
// @astgen op1 := lhsp : Optional[AstNodeExpr] // @astgen op1 := lhsp : Optional[AstNodeExpr]
// @astgen op2 := ftaskrefp : Optional[AstNodeFTaskRef] // @astgen op2 := ftaskrefp : Optional[AstNodeFTaskRef]
string m_name; string m_name; // Name of the variable/function/task
public: public:
AstParseRef(FileLine* fl, const string& name, AstNodeExpr* lhsp = nullptr, AstParseRef(FileLine* fl, const string& name, AstNodeExpr* lhsp = nullptr,
@@ -2386,6 +2419,7 @@ class AstSFormatArg final : public AstNodeExpr {
// used to pass to (potentially) runtime decoding of format arguments // used to pass to (potentially) runtime decoding of format arguments
// PARENT: SFormatF (or next list of expressions) // PARENT: SFormatF (or next list of expressions)
// @astgen op1 := exprp : AstNodeExpr // @astgen op1 := exprp : AstNodeExpr
// @astgen op2 := namep : Optional[AstNodeExpr] // Runtime enum name lookup
VFormatAttr m_formatAttr; // How to format expression VFormatAttr m_formatAttr; // How to format expression
public: public:
@@ -2408,6 +2442,7 @@ public:
bool cleanOut() const override { return true; } bool cleanOut() const override { return true; }
const char* broken() const override { const char* broken() const override {
BROKEN_RTN(!VN_IS(backp(), SFormatF) && firstAbovep()); // In list under SFormatF BROKEN_RTN(!VN_IS(backp(), SFormatF) && firstAbovep()); // In list under SFormatF
BROKEN_RTN(formatAttr().isEnum() != static_cast<bool>(namep()));
return nullptr; return nullptr;
} }
VFormatAttr formatAttr() const { return m_formatAttr; } VFormatAttr formatAttr() const { return m_formatAttr; }
@@ -2613,7 +2648,7 @@ class AstScopeName final : public AstNodeExpr {
// For display %m and DPI context imports // For display %m and DPI context imports
// Parents: AstSFormatF, AstNodeFTaskRef, AstNodeFTask // Parents: AstSFormatF, AstNodeFTaskRef, AstNodeFTask
std::string m_scopeAttr; std::string m_scopeAttr;
std::string m_scopeEntr; std::string m_scopeEntr; // Scope path for the DPI import/export context name
bool m_dpiExport = false; // Is for dpiExport bool m_dpiExport = false; // Is for dpiExport
const bool m_forFormat; // Is for a format %m const bool m_forFormat; // Is for a format %m
static std::string scopeNameFormatter(const std::string& text); static std::string scopeNameFormatter(const std::string& text);
@@ -3035,6 +3070,8 @@ public:
addConstraintsp(constraintsp); addConstraintsp(constraintsp);
} }
ASTGEN_MEMBERS_AstWithParse; ASTGEN_MEMBERS_AstWithParse;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool sameNode(const AstNode* /*samep*/) const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool restricted() const { return m_restricted; } bool restricted() const { return m_restricted; }
void restricted(bool flag) { m_restricted = flag; } void restricted(bool flag) { m_restricted = flag; }
@@ -4911,9 +4948,6 @@ public:
bool isPredictOptimizable() const override { return true; } bool isPredictOptimizable() const override { return true; }
bool sameNode(const AstNode* /*samep*/) const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; }
int instrCount() const override { return widthInstrs(); } int instrCount() const override { return widthInstrs(); }
// Special operators
// Return base var (or const) nodep dereferences
static AstNode* baseFromp(AstNode* nodep, bool overMembers);
}; };
class AstAssocSel final : public AstNodeSel { class AstAssocSel final : public AstNodeSel {
void init(const AstNode* fromp) { void init(const AstNode* fromp) {
@@ -5166,6 +5200,8 @@ public:
"not coded to create after dtypes resolved"); "not coded to create after dtypes resolved");
} }
ASTGEN_MEMBERS_AstSelBit; ASTGEN_MEMBERS_AstSelBit;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
VAccess access() const { return m_access; } VAccess access() const { return m_access; }
void access(const VAccess& flag) { m_access = flag; } void access(const VAccess& flag) { m_access = flag; }
}; };
@@ -5430,6 +5466,7 @@ public:
enum FmtType : int { ATOI = 10, ATOHEX = 16, ATOOCT = 8, ATOBIN = 2, ATOREAL = -1 }; enum FmtType : int { ATOI = 10, ATOHEX = 16, ATOOCT = 8, ATOBIN = 2, ATOREAL = -1 };
private: private:
// dist-ast-dump-suppress // Part of name()
const FmtType m_fmtType; // Operation type const FmtType m_fmtType; // Operation type
public: public:
AstAtoN(FileLine* fl, AstNodeExpr* lhsp, FmtType fmtType) AstAtoN(FileLine* fl, AstNodeExpr* lhsp, FmtType fmtType)
@@ -6438,6 +6475,7 @@ class AstVarXRef final : public AstNodeVarRef {
string m_dotted; // Dotted part of scope the name()'ed reference is under or "" string m_dotted; // Dotted part of scope the name()'ed reference is under or ""
string m_inlinedDots; // Dotted hierarchy flattened out string m_inlinedDots; // Dotted hierarchy flattened out
bool m_containsGenBlock = false; // Contains gen block reference bool m_containsGenBlock = false; // Contains gen block reference
bool m_readOnlyModport = false; // Linked via an input-only modport, until V3LinkLValue
public: public:
AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access) AstVarXRef(FileLine* fl, const string& name, const string& dotted, const VAccess& access)
: ASTGEN_SUPER_VarXRef(fl, nullptr, access) : ASTGEN_SUPER_VarXRef(fl, nullptr, access)
@@ -6455,6 +6493,8 @@ public:
void inlinedDots(const string& flag) { m_inlinedDots = flag; } void inlinedDots(const string& flag) { m_inlinedDots = flag; }
bool containsGenBlock() const { return m_containsGenBlock; } bool containsGenBlock() const { return m_containsGenBlock; }
void containsGenBlock(const bool flag) { m_containsGenBlock = flag; } void containsGenBlock(const bool flag) { m_containsGenBlock = flag; }
bool readOnlyModport() const { return m_readOnlyModport; }
void readOnlyModport(const bool flag) { m_readOnlyModport = flag; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; } bool cleanOut() const override { return true; }
+273 -90
View File
@@ -44,6 +44,7 @@ class AstNodeCoverDecl VL_NOT_FINAL : public AstNode {
// Coverage counters are emitted in each module object, so duplicate // Coverage counters are emitted in each module object, so duplicate
// no-inline instances can keep independent counts for forcePerInstance. // no-inline instances can keep independent counts for forcePerInstance.
int m_localBinNum = 0; // Per-module coverage bin offset int m_localBinNum = 0; // Per-module coverage bin offset
bool m_perInstance = false; // Don't clone during inlining, already per instance
public: public:
AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment) AstNodeCoverDecl(VNType t, FileLine* fl, const string& page, const string& comment)
: AstNode(t, fl) : AstNode(t, fl)
@@ -65,6 +66,8 @@ public:
void binNum(int flag) { m_binNum = flag; } void binNum(int flag) { m_binNum = flag; }
int localBinNum() const { return m_localBinNum; } int localBinNum() const { return m_localBinNum; }
void localBinNum(int flag) { m_localBinNum = flag; } void localBinNum(int flag) { m_localBinNum = flag; }
bool perInstance() const { return m_perInstance; }
void perInstance(bool flag) { m_perInstance = flag; }
virtual int size() const = 0; virtual int size() const = 0;
const string& comment() const { return m_text; } // text to insert in code const string& comment() const { return m_text; } // text to insert in code
const string& page() const { return m_page; } const string& page() const { return m_page; }
@@ -74,7 +77,8 @@ public:
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl); const AstNodeCoverDecl* const asamep = VN_DBG_AS(samep, NodeCoverDecl);
return (fileline() == asamep->fileline() && hier() == asamep->hier() return (fileline() == asamep->fileline() && hier() == asamep->hier()
&& comment() == asamep->comment() && page() == asamep->page()); && comment() == asamep->comment() && page() == asamep->page()
&& perInstance() == asamep->perInstance());
} }
bool isPredictOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; }
void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; } void dataDeclp(AstNodeCoverDecl* nodep) { m_dataDeclp = nodep; }
@@ -94,6 +98,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
// @astgen op4 := scopeNamep : Optional[AstScopeName] // @astgen op4 := scopeNamep : Optional[AstScopeName]
string m_name; // Name of task string m_name; // Name of task
string m_cname; // Name of task if DPI import string m_cname; // Name of task if DPI import
// dist-ast-dump-suppress // Not dumped due to verbosity
string m_dpiCDecl; // Custom DPI-C function declaration string m_dpiCDecl; // Custom DPI-C function declaration
string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8) string m_ifacePortName; // Interface port name for out-of-block definition (IEEE 25.8)
uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees uint64_t m_dpiOpenParent = 0; // DPI import open array, if !=0, how many callees
@@ -112,6 +117,8 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
bool m_isHideLocal : 1; // Verilog local bool m_isHideLocal : 1; // Verilog local
bool m_isHideProtected : 1; // Verilog protected bool m_isHideProtected : 1; // Verilog protected
bool m_dpiPure : 1; // DPI import pure (vs. virtual pure) bool m_dpiPure : 1; // DPI import pure (vs. virtual pure)
bool m_keepAlive : 1; // Disable dead function elimination
bool m_needsSyms : 1; // Requires vlSymsp argument
bool m_pureVirtual : 1; // Pure virtual bool m_pureVirtual : 1; // Pure virtual
bool m_recursive : 1; // Recursive or part of recursion bool m_recursive : 1; // Recursive or part of recursion
bool m_static : 1; // Static method in class bool m_static : 1; // Static method in class
@@ -144,6 +151,8 @@ protected:
, m_isHideLocal{false} , m_isHideLocal{false}
, m_isHideProtected{false} , m_isHideProtected{false}
, m_dpiPure{false} , m_dpiPure{false}
, m_keepAlive{false}
, m_needsSyms{true}
, m_pureVirtual{false} , m_pureVirtual{false}
, m_recursive{false} , m_recursive{false}
, m_static{false} , m_static{false}
@@ -211,6 +220,10 @@ public:
void isHideProtected(bool flag) { m_isHideProtected = flag; } void isHideProtected(bool flag) { m_isHideProtected = flag; }
bool dpiPure() const { return m_dpiPure; } bool dpiPure() const { return m_dpiPure; }
void dpiPure(bool flag) { m_dpiPure = flag; } void dpiPure(bool flag) { m_dpiPure = flag; }
bool keepAlive() const { return m_keepAlive; }
void keepAlive(bool flag) { m_keepAlive = flag; }
bool needsSyms() const { return m_needsSyms; }
void needsSyms(bool flag) { m_needsSyms = flag; }
bool pureVirtual() const { return m_pureVirtual; } bool pureVirtual() const { return m_pureVirtual; }
void pureVirtual(bool flag) { m_pureVirtual = flag; } void pureVirtual(bool flag) { m_pureVirtual = flag; }
bool recursive() const { return m_recursive; } bool recursive() const { return m_recursive; }
@@ -293,8 +306,8 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
// @astgen op2 := stmtsp : List[AstNode] // @astgen op2 := stmtsp : List[AstNode]
string m_name; // Name of the module string m_name; // Name of the module
const string m_origName; // Name of the module, ignoring name() changes, for dot lookup const string m_origName; // Name of the module, ignoring name() changes, for dot lookup
// dist-ast-dump-suppress // For some user errors messages only, visible where used
string m_someInstanceName; // Hierarchical name of some arbitrary instance of this module. string m_someInstanceName; // Hierarchical name of some arbitrary instance of this module.
// Used for user messages only.
string m_libname; // Work library string m_libname; // Work library
int m_depth = 0; // 1=top module, 2=cell off top, shared things low, for -depth options int m_depth = 0; // 1=top module, 2=cell off top, shared things low, for -depth options
int m_level = 0; // 1=top module, 2=cell off top, shared things have high number int m_level = 0; // 1=top module, 2=cell off top, shared things have high number
@@ -312,7 +325,6 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
bool m_hasParameterList : 1; // Has #() for parameter declaration bool m_hasParameterList : 1; // Has #() for parameter declaration
bool m_hierBlock : 1; // Hierarchical Block marked by HIER_BLOCK pragma bool m_hierBlock : 1; // Hierarchical Block marked by HIER_BLOCK pragma
bool m_hierParams : 1; // Block containing params for parameterized hier blocks bool m_hierParams : 1; // Block containing params for parameterized hier blocks
bool m_internal : 1; // Internally created
bool m_recursive : 1; // Recursive module bool m_recursive : 1; // Recursive module
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
bool m_parameterizedTemplate : 1; // True when at least one specialized clone exists; bool m_parameterizedTemplate : 1; // True when at least one specialized clone exists;
@@ -334,7 +346,6 @@ protected:
, m_hasParameterList{false} , m_hasParameterList{false}
, m_hierBlock{false} , m_hierBlock{false}
, m_hierParams{false} , m_hierParams{false}
, m_internal{false}
, m_recursive{false} , m_recursive{false}
, m_recursiveClone{false} , m_recursiveClone{false}
, m_parameterizedTemplate{false} , m_parameterizedTemplate{false}
@@ -347,6 +358,8 @@ public:
bool maybePointedTo() const override VL_MT_SAFE { return true; } bool maybePointedTo() const override VL_MT_SAFE { return true; }
string name() const override VL_MT_STABLE { return m_name; } string name() const override VL_MT_STABLE { return m_name; }
virtual bool timescaleMatters() const = 0; virtual bool timescaleMatters() const = 0;
inline bool isDollarUnit() const; // Is the $unit package
inline bool isConstPool() const; // Is the constant pool package
// ACCESSORS // ACCESSORS
void name(const string& name) override { m_name = name; } void name(const string& name) override { m_name = name; }
string origName() const override { return m_origName; } string origName() const override { return m_origName; }
@@ -378,8 +391,6 @@ public:
void hierBlock(bool flag) { m_hierBlock = flag; } void hierBlock(bool flag) { m_hierBlock = flag; }
bool hierParams() const { return m_hierParams; } bool hierParams() const { return m_hierParams; }
void hierParams(bool flag) { m_hierParams = flag; } void hierParams(bool flag) { m_hierParams = flag; }
bool internal() const { return m_internal; }
void internal(bool flag) { m_internal = flag; }
bool recursive() const { return m_recursive; } bool recursive() const { return m_recursive; }
void recursive(bool flag) { m_recursive = flag; } void recursive(bool flag) { m_recursive = flag; }
void recursiveClone(bool flag) { m_recursiveClone = flag; } void recursiveClone(bool flag) { m_recursiveClone = flag; }
@@ -543,6 +554,7 @@ class AstCFunc final : public AstNode {
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
bool m_needProcess : 1; // Needs access to VlProcess of the caller bool m_needProcess : 1; // Needs access to VlProcess of the caller
bool m_recursive : 1; // Recursive or part of recursion bool m_recursive : 1; // Recursive or part of recursion
bool m_unlikely : 1; // Unlikely to get called (though still optimize unlike slow())
bool m_noLife : 1; // Disable V3Life on this function - has multiple calls, and reads Syms bool m_noLife : 1; // Disable V3Life on this function - has multiple calls, and reads Syms
// state // state
bool m_isCovergroupSample : 1; // Automatic covergroup sample() function bool m_isCovergroupSample : 1; // Automatic covergroup sample() function
@@ -575,6 +587,7 @@ public:
m_dpiImportPrototype = false; m_dpiImportPrototype = false;
m_dpiImportWrapper = false; m_dpiImportWrapper = false;
m_recursive = false; m_recursive = false;
m_unlikely = false;
m_noLife = false; m_noLife = false;
m_isCovergroupSample = false; m_isCovergroupSample = false;
m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost
@@ -653,6 +666,8 @@ public:
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; } bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
void recursive(bool flag) { m_recursive = flag; } void recursive(bool flag) { m_recursive = flag; }
bool recursive() const { return m_recursive; } bool recursive() const { return m_recursive; }
void unlikely(bool flag) { m_unlikely = flag; }
bool isUnlikely() const override { return m_unlikely; } // Note virtual override
void noLife(bool flag) { m_noLife = flag; } void noLife(bool flag) { m_noLife = flag; }
bool noLife() const { return m_noLife; } bool noLife() const { return m_noLife; }
bool isCovergroupSample() const { return m_isCovergroupSample; } bool isCovergroupSample() const { return m_isCovergroupSample; }
@@ -685,15 +700,18 @@ class AstCell final : public AstNode {
// A instantiation cell or interface call (don't know which until link) // A instantiation cell or interface call (don't know which until link)
// @astgen op1 := pinsp : List[AstPin] // List of port assignments // @astgen op1 := pinsp : List[AstPin] // List of port assignments
// @astgen op2 := paramsp : List[AstPin] // List of parameter assignments // @astgen op2 := paramsp : List[AstPin] // List of parameter assignments
// @astgen op3 := rangep : List[AstRange] // Range(s) for arrayed instances; multi-dim chains // Pre V3Param: Range(s) for arrayed instances.
// via nextp() // From V3Param to V3Width: ranges of the arrayed cell this element cell was expanded from
// @astgen op4 := intfRefsp : List[AstIntfRef] // List of interface references, for tracing // After V3Width: nullptr
// @astgen op3 := rangep : List[AstRange]
// @astgen op4 := intfRefsp : List[AstIntfRef] // List of interface references, for tracing/VPI
// //
// @astgen ptr := m_modp : Optional[AstNodeModule] // [AfterLink] Pointer to module instanced // @astgen ptr := m_modp : Optional[AstNodeModule] // [AfterLink] Pointer to module instanced
FileLine* m_modNameFileline; // Where module the cell instances token was FileLine* m_modNameFileline; // Where module the cell instances token was
string m_name; // Cell name string m_name; // Cell name
string m_origName; // Original name before dot addition string m_origName; // Original name before dot addition
string m_modName; // Module the cell instances string m_modName; // Module the cell instances
int m_arrayIdx = -1; // Row-major position in instance array, from the left (V3Param-V3Width)
bool m_hasIfaceVar : 1; // True if a Var has been created for this cell bool m_hasIfaceVar : 1; // True if a Var has been created for this cell
bool m_recursive : 1; // Self-recursive module bool m_recursive : 1; // Self-recursive module
bool m_trace : 1; // Trace this cell bool m_trace : 1; // Trace this cell
@@ -728,6 +746,8 @@ public:
FileLine* modNameFileline() const { return m_modNameFileline; } FileLine* modNameFileline() const { return m_modNameFileline; }
AstNodeModule* modp() const { return m_modp; } // [AfterLink] = Pointer to module instantiated AstNodeModule* modp() const { return m_modp; } // [AfterLink] = Pointer to module instantiated
void modp(AstNodeModule* nodep) { m_modp = nodep; } void modp(AstNodeModule* nodep) { m_modp = nodep; }
int arrayIdx() const { return m_arrayIdx; }
void arrayIdx(int idx) { m_arrayIdx = idx; }
bool hasIfaceVar() const { return m_hasIfaceVar; } bool hasIfaceVar() const { return m_hasIfaceVar; }
void hasIfaceVar(bool flag) { m_hasIfaceVar = flag; } void hasIfaceVar(bool flag) { m_hasIfaceVar = flag; }
void trace(bool flag) { m_trace = flag; } void trace(bool flag) { m_trace = flag; }
@@ -781,6 +801,9 @@ public:
string name() const override VL_MT_STABLE { return m_cellp->name(); } string name() const override VL_MT_STABLE { return m_cellp->name(); }
bool maybePointedTo() const override VL_MT_SAFE { return true; } bool maybePointedTo() const override VL_MT_SAFE { return true; }
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
void scopep(AstScope* nodep) { m_scopep = nodep; }
AstCellInline* cellp() const VL_MT_STABLE { return m_cellp; } // Pointer to the CellInline
void cellp(AstCellInline* nodep) { m_cellp = nodep; }
string origModName() const { string origModName() const {
return m_cellp->origModName(); return m_cellp->origModName();
} // * = modp()->origName() before inlining } // * = modp()->origName() before inlining
@@ -889,6 +912,8 @@ public:
} }
} }
ASTGEN_MEMBERS_AstClockingItem; ASTGEN_MEMBERS_AstClockingItem;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
VDirection direction() const { return m_direction; } VDirection direction() const { return m_direction; }
AstClockingItem* outputp() const { return m_outputp; } AstClockingItem* outputp() const { return m_outputp; }
void outputp(AstClockingItem* outputp) { m_outputp = outputp; } void outputp(AstClockingItem* outputp) { m_outputp = outputp; }
@@ -907,11 +932,11 @@ public:
, m_libname{libname} , m_libname{libname}
, m_configname{cellname} {} , m_configname{cellname} {}
ASTGEN_MEMBERS_AstConfig; ASTGEN_MEMBERS_AstConfig;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
std::string name() const override VL_MT_STABLE { return m_libname + "." + m_configname; } std::string name() const override VL_MT_STABLE { return m_libname + "." + m_configname; }
std::string libname() const VL_MT_STABLE { return m_libname; } std::string libname() const VL_MT_STABLE { return m_libname; }
std::string configname() const VL_MT_STABLE { return m_configname; } std::string configname() const VL_MT_STABLE { return m_configname; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
}; };
class AstConfigCell final : public AstNode { class AstConfigCell final : public AstNode {
// Parents: CONFIGRULE // Parents: CONFIGRULE
@@ -970,40 +995,6 @@ public:
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override; void dumpJson(std::ostream& str) const override;
}; };
class AstConstPool final : public AstNode {
// Container for const static data
// @astgen op1 := modulep : AstModule // m_modp below TODO: fix this mess
//
// @astgen ptr := m_modp : AstModule // The Module holding the Scope below ...
// @astgen ptr := m_scopep : AstScope // Scope holding the constant variables
std::unordered_multimap<uint32_t, AstVarScope*> m_tables; // Constant tables (unpacked arrays)
std::unordered_multimap<uint32_t, AstVarScope*> m_consts; // Constant tables (scalars)
AstVarScope* createNewEntry(const string& name, AstNodeExpr* initp);
public:
explicit AstConstPool(FileLine* fl);
ASTGEN_MEMBERS_AstConstPool;
bool maybePointedTo() const override VL_MT_SAFE { return true; }
void cloneRelink() override { V3ERROR_NA; } // Not cloneable
AstModule* modp() const { return m_modp; }
AstScope* scopep() const { return m_scopep; }
// Find a table (unpacked array) within the constant pool which is initialized with the
// given value, or create one if one does not already exists. The returned VarScope *might*
// have a different dtype than the given initp->dtypep(), including a different element type,
// but it will always have the same size and element width. In contexts where this matters,
// the caller must handle the dtype difference as appropriate.
AstVarScope* findTable(AstInitArray* initp);
// Find a constant within the constant pool which is initialized with the given value, or
// create one if one does not already exists. If 'mergeDType' is true, then the returned
// VarScope *might* have a different type than the given initp->dtypep(). In contexts where
// this matters, the caller must handle the dtype difference as appropriate. If 'mergeDType' is
// false, the returned VarScope will have _->dtypep()->sameTree(initp->dtypep()) return true.
AstVarScope* findConst(AstConst* initp, bool mergeDType);
// Rebuild hashes and missing variable scopes after potential removals
void rebuildVarScopesAndCache();
};
class AstConstraint final : public AstNode { class AstConstraint final : public AstNode {
// Constraint // Constraint
// @astgen op1 := itemsp : List[AstNode] // @astgen op1 := itemsp : List[AstNode]
@@ -1051,12 +1042,13 @@ class AstCoverBin final : public AstNode {
// @astgen op4 := transp : List[AstCoverTransSet] // @astgen op4 := transp : List[AstCoverTransSet]
const string m_name; // Base name of the bin const string m_name; // Base name of the bin
const VCoverBinsType m_binsType; // Bin type (eg AUTO, IGNORE, ILLEGAL) const VCoverBinsType m_binsType; // Bin type (eg AUTO, IGNORE, ILLEGAL)
bool m_isArray = false; // Bin is either an auto-sized array of values or transitions bool m_isArray = false; // Bin is an array: of a bin per value or transition, or of
// arraySizep() bins
bool m_isWildcard = false; // Bin uses wildcard matching (independent of ignore/illegal) bool m_isWildcard = false; // Bin uses wildcard matching (independent of ignore/illegal)
public: public:
AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal, AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal,
bool isWildcard = false) bool isWildcard = false, AstNodeExpr* iffp = nullptr)
: ASTGEN_SUPER_CoverBin(fl) : ASTGEN_SUPER_CoverBin(fl)
, m_name{name} , m_name{name}
, m_binsType{isIllegal ? VCoverBinsType::BINS_ILLEGAL , m_binsType{isIllegal ? VCoverBinsType::BINS_ILLEGAL
@@ -1065,12 +1057,14 @@ public:
: VCoverBinsType::BINS_USER))} : VCoverBinsType::BINS_USER))}
, m_isWildcard{isWildcard} { , m_isWildcard{isWildcard} {
addRangesp(rangesp); addRangesp(rangesp);
this->iffp(iffp);
} }
// Constructor for automatic bins // Constructor for automatic bins
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep) AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep,
VCoverBinsType type = VCoverBinsType::BINS_AUTO)
: ASTGEN_SUPER_CoverBin(fl) : ASTGEN_SUPER_CoverBin(fl)
, m_name{name} , m_name{name}
, m_binsType{VCoverBinsType::BINS_AUTO} , m_binsType{type}
, m_isArray{true} { , m_isArray{true} {
this->arraySizep(arraySizep); this->arraySizep(arraySizep);
} }
@@ -1098,21 +1092,138 @@ public:
bool isArray() const { return m_isArray; } bool isArray() const { return m_isArray; }
void isArray(bool flag) { m_isArray = flag; } void isArray(bool flag) { m_isArray = flag; }
}; };
class AstCoverBinsof final : public AstNode {
// A binsof selection of a coverpoint or one of its named bins
// @astgen op1 := pointp : AstCoverpointRef
// @astgen op2 := rangesp : List[AstNode] // Optional intersect value ranges
string m_name; // Selected bin name, or empty for all bins of the coverpoint
const bool m_isNegated; // Complement the selection within the cross product
public:
AstCoverBinsof(FileLine* fl, AstCoverpointRef* pointp, bool isNegated = false,
AstNode* rangesp = nullptr)
: ASTGEN_SUPER_CoverBinsof(fl)
, m_isNegated{isNegated} {
this->pointp(pointp);
addRangesp(rangesp);
}
ASTGEN_MEMBERS_AstCoverBinsof;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& name) override { m_name = name; }
bool isNegated() const { return m_isNegated; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
const AstCoverBinsof* const asamep = VN_DBG_AS(samep, CoverBinsof);
return m_name == asamep->m_name && m_isNegated == asamep->m_isNegated;
} // LCOV_EXCL_STOP
};
class AstCoverCrossBin final : public AstNode {
// A named cross bin and its selection expression
// @astgen op1 := selectp : Optional[AstNode] // Null for unsupported selections
// @astgen op2 := iffp : Optional[AstNodeExpr]
const string m_name; // Declared cross bin name
// dist-ast-dump-suppress // Bin kind is shown by verilogKwd() in emitted Verilog.
const VCoverBinsType m_binsType; // Normal, ignore, or illegal bin
public:
AstCoverCrossBin(FileLine* fl, const string& name, AstNode* selectp, AstNodeExpr* iffp,
VCoverBinsType binsType = VCoverBinsType::BINS_USER)
: ASTGEN_SUPER_CoverCrossBin(fl)
, m_name{name}
, m_binsType{binsType} {
this->selectp(selectp);
this->iffp(iffp);
}
ASTGEN_MEMBERS_AstCoverCrossBin;
string name() const override VL_MT_STABLE { return m_name; }
string verilogKwd() const override;
VCoverBinsType binsType() const { return m_binsType; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
const AstCoverCrossBin* const asamep = VN_DBG_AS(samep, CoverCrossBin);
return m_name == asamep->m_name && m_binsType.m_e == asamep->m_binsType.m_e;
} // LCOV_EXCL_STOP
};
class AstCoverCrossRef final : public AstNode {
// The complete bin-tuple space of the enclosing cross
const string m_name; // Referenced cross name
public:
AstCoverCrossRef(FileLine* fl, const string& name)
: ASTGEN_SUPER_CoverCrossRef(fl)
, m_name{name} {}
ASTGEN_MEMBERS_AstCoverCrossRef;
string name() const override VL_MT_STABLE { return m_name; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
return m_name == VN_DBG_AS(samep, CoverCrossRef)->m_name;
} // LCOV_EXCL_STOP
};
class AstCoverCrossSelect final : public AstNode {
// Intersection or union of two cross-bin selections
// @astgen op1 := lhsp : Optional[AstNode] // Null for an unsupported selection
// @astgen op2 := rhsp : Optional[AstNode] // Null for an unsupported selection
const bool m_isOr; // Union (||), rather than intersection (&&)
public:
AstCoverCrossSelect(FileLine* fl, AstNode* lhsp, AstNode* rhsp, bool isOr)
: ASTGEN_SUPER_CoverCrossSelect(fl)
, m_isOr{isOr} {
this->lhsp(lhsp);
this->rhsp(rhsp);
}
ASTGEN_MEMBERS_AstCoverCrossSelect;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
bool isOr() const { return m_isOr; }
string verilogKwd() const override { return isOr() ? "||" : "&&"; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
return m_isOr == VN_DBG_AS(samep, CoverCrossSelect)->m_isOr;
} // LCOV_EXCL_STOP
};
class AstCoverOption final : public AstNode { class AstCoverOption final : public AstNode {
// Coverage-option assignment // Coverage-option assignment
// @astgen op1 := valuep : AstNodeExpr // @astgen op1 := valuep : AstNodeExpr
const VCoverOptionType m_optType; // Option being assigned const VCoverOptionType m_optType; // Option being assigned
const bool m_typeOption; // type_option (vs option)
public: public:
AstCoverOption(FileLine* fl, VCoverOptionType optType, AstNodeExpr* valuep) AstCoverOption(FileLine* fl, bool typeOption, VCoverOptionType optType, AstNodeExpr* valuep)
: ASTGEN_SUPER_CoverOption(fl) : ASTGEN_SUPER_CoverOption(fl)
, m_optType{optType} { , m_optType{optType}
, m_typeOption{typeOption} {
this->valuep(valuep); this->valuep(valuep);
} }
ASTGEN_MEMBERS_AstCoverOption; ASTGEN_MEMBERS_AstCoverOption;
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override; void dumpJson(std::ostream& str) const override;
VCoverOptionType optType() const { return m_optType; } VCoverOptionType optType() const { return m_optType; }
bool typeOption() const { return m_typeOption; }
bool sameNode(const AstNode* samep) const override { // LCOV_EXCL_START
const AstCoverOption* const asamep = VN_DBG_AS(samep, CoverOption);
return m_optType.m_e == asamep->m_optType.m_e && m_typeOption == asamep->m_typeOption;
} // LCOV_EXCL_STOP
};
class AstCoverToggle final : public AstNode {
// Toggle analysis of given signal
// Parents: MODULE
// @astgen op1 := incp : AstCoverInc
// @astgen op2 := origp : AstNodeExpr
// @astgen op3 := changep : AstNodeExpr
public:
AstCoverToggle(FileLine* fl, AstCoverInc* incp, AstNodeExpr* origp, AstNodeExpr* changep)
: ASTGEN_SUPER_CoverToggle(fl) {
this->incp(incp);
this->origp(origp);
this->changep(changep);
}
ASTGEN_MEMBERS_AstCoverToggle;
int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return true; }
bool isOutputter() override {
return false; // Though the AstCoverInc under this is an outputter
}
}; };
class AstCoverTransItem final : public AstNode { class AstCoverTransItem final : public AstNode {
// Represents a single transition item: value or value[*N] or value[->N] or value[=N] // Represents a single transition item: value or value[*N] or value[->N] or value[=N]
@@ -1143,6 +1254,20 @@ public:
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override; void dumpJson(std::ostream& str) const override;
}; };
class AstCoverWith final : public AstNode {
// The values of a coverpoint bin for which a 'with' filter is true (IEEE 1800-2023
// 19.5.1.1), the only range list element of its AstCoverBin
// @astgen op1 := subp : List[AstNode] // Range list, or AstCoverpointRef for all values
// @astgen op2 := filterp : AstNodeExpr
// @astgen op3 := itemp : Optional[AstVar] // Candidate value 'item', from V3LinkParse
public:
AstCoverWith(FileLine* fl, AstNode* subp, AstNodeExpr* filterp)
: ASTGEN_SUPER_CoverWith(fl) {
addSubp(subp);
this->filterp(filterp);
}
ASTGEN_MEMBERS_AstCoverWith;
};
class AstCovergroup final : public AstNode { class AstCovergroup final : public AstNode {
// Represents a covergroup declaration. V3LinkParse transforms this // Represents a covergroup declaration. V3LinkParse transforms this
// into an AstClass with isCovergroup==true and attaches the clocking // into an AstClass with isCovergroup==true and attaches the clocking
@@ -1258,6 +1383,8 @@ public:
BROKEN_RTN(!fmtp()); BROKEN_RTN(!fmtp());
return nullptr; return nullptr;
} }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
string verilogKwd() const override { return "$"s + string{displayType().ascii()}; } string verilogKwd() const override { return "$"s + string{displayType().ascii()}; }
bool isGateOptimizable() const override { return false; } bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; }
@@ -1320,13 +1447,21 @@ public:
}; };
class AstIntfRef final : public AstNode { class AstIntfRef final : public AstNode {
// An interface reference // An interface reference
string m_name; // Name of the reference string m_name; // Hierarchical path of the reference
string m_baseName; // Final component of m_name, i.e. the reference port name
string m_modportName; // "" = no modport, else name of the modport referenced
public: public:
AstIntfRef(FileLine* fl, const string& name) AstIntfRef(FileLine* fl, const string& name, const string& baseName, const string& modportName)
: ASTGEN_SUPER_IntfRef(fl) : ASTGEN_SUPER_IntfRef(fl)
, m_name{name} {} , m_name{name}
string name() const override VL_MT_STABLE { return m_name; } , m_baseName{baseName}
, m_modportName{modportName} {}
ASTGEN_MEMBERS_AstIntfRef; ASTGEN_MEMBERS_AstIntfRef;
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
string name() const override VL_MT_STABLE { return m_name; }
string baseName() const { return m_baseName; }
string modportName() const { return m_modportName; }
}; };
class AstLibrary final : public AstNode { class AstLibrary final : public AstNode {
// Parents: NETLIST // Parents: NETLIST
@@ -1440,7 +1575,7 @@ class AstNetlist final : public AstNode {
// @astgen op3 := miscsp : List[AstNode] // @astgen op3 := miscsp : List[AstNode]
// //
// @astgen ptr := m_typeTablep : AstTypeTable // Reference to type table, for faster lookup // @astgen ptr := m_typeTablep : AstTypeTable // Reference to type table, for faster lookup
// @astgen ptr := m_constPoolp : AstConstPool // Reference to constant pool, for faster lookup // @astgen ptr := m_constPoolPkgp : AstPackage // Constant pool package
// @astgen ptr := m_dollarUnitPkgp : Optional[AstPackage] // $unit // @astgen ptr := m_dollarUnitPkgp : Optional[AstPackage] // $unit
// @astgen ptr := m_stdPackagep : Optional[AstPackage] // SystemVerilog std package // @astgen ptr := m_stdPackagep : Optional[AstPackage] // SystemVerilog std package
// @astgen ptr := m_stdPackageProcessp : Optional[AstClass] // SystemVerilog std process class // @astgen ptr := m_stdPackageProcessp : Optional[AstClass] // SystemVerilog std process class
@@ -1461,6 +1596,8 @@ class AstNetlist final : public AstNode {
// AstConst itself, as AstConst is a very common node and only a small fraction carry this // AstConst itself, as AstConst is a very common node and only a small fraction carry this
// name. // name.
std::unordered_map<const AstConst*, string> m_constOrigParamNames; std::unordered_map<const AstConst*, string> m_constOrigParamNames;
// Module each node is in, only good while the tree holds still
std::unordered_map<const AstNode*, const AstNodeModule*> m_containingModules;
// The model's evaluation entry point functions // The model's evaluation entry point functions
std::array<AstCFunc*, VEval::_ENUM_END> m_evalFuncps{}; std::array<AstCFunc*, VEval::_ENUM_END> m_evalFuncps{};
// The trigger dump function of each region if exists, otherwise nullptr // The trigger dump function of each region if exists, otherwise nullptr
@@ -1482,12 +1619,16 @@ public:
return modulesp(); // First one in the list, for now return modulesp(); // First one in the list, for now
} }
AstTypeTable* typeTablep() { return m_typeTablep; } AstTypeTable* typeTablep() { return m_typeTablep; }
AstConstPool* constPoolp() { return m_constPoolp; }
string astConstOrigParamName(const AstConst* nodep) const; string astConstOrigParamName(const AstConst* nodep) const;
void astConstOrigParamName(const AstConst* nodep, const string& name); void astConstOrigParamName(const AstConst* nodep, const string& name);
void astConstOrigParamNameErase(const AstConst* nodep); void astConstOrigParamNameErase(const AstConst* nodep);
// Find the module a node is in, remembering what it passed on the way.
const AstNodeModule* containingModule(const AstNode* nodep);
// Forget remembered modules, as the tree has moved.
void clearContainingModules() { m_containingModules.clear(); }
AstPackage* constPoolPkgp() const { return m_constPoolPkgp; }
AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; } AstPackage* dollarUnitPkgp() const { return m_dollarUnitPkgp; }
AstPackage* dollarUnitPkgAddp(); void dollarUnitPkgp(AstPackage* const packagep) { m_dollarUnitPkgp = packagep; }
AstCFunc* evalFuncp(VEval eval) const { return m_evalFuncps[eval]; } AstCFunc* evalFuncp(VEval eval) const { return m_evalFuncps[eval]; }
void evalFuncp(VEval eval, AstCFunc* funcp) { m_evalFuncps[eval] = funcp; } void evalFuncp(VEval eval, AstCFunc* funcp) { m_evalFuncps[eval] = funcp; }
AstCFunc* dumpTriggersFuncp(VEval eval) const { return m_dumpTriggersFuncps[eval]; } AstCFunc* dumpTriggersFuncp(VEval eval) const { return m_dumpTriggersFuncps[eval]; }
@@ -1751,6 +1892,7 @@ public:
bool maybePointedTo() const override VL_MT_SAFE { return true; } bool maybePointedTo() const override VL_MT_SAFE { return true; }
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
void name(const string& name) override { m_name = name; } void name(const string& name) override { m_name = name; }
string prettyNameMsg() const override;
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override; void dumpJson(std::ostream& str) const override;
bool sameNode(const AstNode* samep) const override; bool sameNode(const AstNode* samep) const override;
@@ -1758,7 +1900,9 @@ public:
AstNodeModule* modp() const { return m_modp; } AstNodeModule* modp() const { return m_modp; }
// //
AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; } AstScope* aboveScopep() const VL_MT_SAFE { return m_aboveScopep; }
void aboveScopep(AstScope* nodep) { m_aboveScopep = nodep; }
AstCell* aboveCellp() const { return m_aboveCellp; } AstCell* aboveCellp() const { return m_aboveCellp; }
void aboveCellp(AstCell* nodep) { m_aboveCellp = nodep; }
bool isTop() const VL_MT_SAFE { return aboveScopep() == nullptr; } // At top of hierarchy bool isTop() const VL_MT_SAFE { return aboveScopep() == nullptr; } // At top of hierarchy
// Create new MODULETEMP variable under this scope // Create new MODULETEMP variable under this scope
AstVarScope* createTemp(const string& name, unsigned width); AstVarScope* createTemp(const string& name, unsigned width);
@@ -1915,8 +2059,11 @@ public:
class AstTextBlock final : public AstNode { class AstTextBlock final : public AstNode {
// Text block emitted into output, with some arbitrary nodes interspersed // Text block emitted into output, with some arbitrary nodes interspersed
// @astgen op1 := nodesp : List[AstNode] // Nodes to print // @astgen op1 := nodesp : List[AstNode] // Nodes to print
// dist-ast-dump-suppress // Omitting text blocks due to verbosity
const std::string m_prefix; // Prefix to print before first element in 'nodesp' const std::string m_prefix; // Prefix to print before first element in 'nodesp'
// dist-ast-dump-suppress // Omitting text blocks due to verbosity
const std::string m_separator; // Separator to print between each element in 'nodesp' const std::string m_separator; // Separator to print between each element in 'nodesp'
// dist-ast-dump-suppress // Omitting text blocks due to verbosity
const std::string m_suffix; // Suffix to pring after last element in 'nodesp' const std::string m_suffix; // Suffix to pring after last element in 'nodesp'
public: public:
explicit AstTextBlock(FileLine* fl, // explicit AstTextBlock(FileLine* fl, //
@@ -1965,6 +2112,7 @@ class AstTypeTable final : public AstNode {
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{}; AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{};
// //
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>; using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
// dist-ast-dump-suppress // Link to other nodes
DetailedMap m_detailedMap; DetailedMap m_detailedMap;
public: public:
@@ -1993,10 +2141,10 @@ class AstTypedef final : public AstNode {
// @astgen op1 := childDTypep : Optional[AstNodeDType] // @astgen op1 := childDTypep : Optional[AstNodeDType]
// @astgen op4 := attrsp : List[AstNode] // Attributes during early parse // @astgen op4 := attrsp : List[AstNode] // Attributes during early parse
string m_name; string m_name; // Name of the typedef
string m_tag; // Holds the string of the verilator tag -- used in JSON output. string m_tag; // Holds the string of the verilator tag -- used in JSON output.
uint32_t m_declTokenNum; // Declaration token number uint32_t m_declTokenNum; // Declaration token number
bool m_attrPublic = false; bool m_attrPublic = false; // Marked with public; keep even if unused
bool m_isHideLocal : 1; // Verilog local bool m_isHideLocal : 1; // Verilog local
bool m_isHideProtected : 1; // Verilog protected bool m_isHideProtected : 1; // Verilog protected
bool m_isUnderClass : 1; // Underneath class bool m_isUnderClass : 1; // Underneath class
@@ -2026,6 +2174,9 @@ public:
ASTGEN_MEMBERS_AstTypedef; ASTGEN_MEMBERS_AstTypedef;
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override; void dumpJson(std::ostream& str) const override;
// Name of the type it declares, with the scope declaring it, so with the values of the
// parameters of the scope, e.g. '$unit::Cls#(8)::s_t'
string dtypeName() const;
AstNodeDType* getChildDTypep() const override { return childDTypep(); } AstNodeDType* getChildDTypep() const override { return childDTypep(); }
virtual AstNodeDType* subDTypep() const VL_MT_STABLE { virtual AstNodeDType* subDTypep() const VL_MT_STABLE {
return dtypep() ? dtypep() : childDTypep(); return dtypep() ? dtypep() : childDTypep();
@@ -2162,11 +2313,11 @@ class AstVar final : public AstNode {
bool m_attrSFormat : 1; // User sformat attribute bool m_attrSFormat : 1; // User sformat attribute
bool m_attrSplitVar : 1; // declared with split_var metacomment bool m_attrSplitVar : 1; // declared with split_var metacomment
bool m_attrFsmState : 1; // declared with fsm_state metacomment bool m_attrFsmState : 1; // declared with fsm_state metacomment
bool m_attrFsmRegisterWrapper : 1; // connected to an fsm_register_wrapper instance
bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment
bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment
bool m_constPoolEntry : 1; // Constant pool variable bool m_constPoolEntry : 1; // Constant pool variable
bool m_covergroupRefMember : 1; // Persistent covergroup ref/const ref argument bool m_covergroupRefMember : 1; // Persistent covergroup ref/const ref argument
bool m_embeddedCovergroup : 1; // Instance variable an embedded covergroup declares
bool m_attrFileDescr : 1; // File descriptor bool m_attrFileDescr : 1; // File descriptor
bool m_gotNansiType : 1; // Linker saw Non-ANSI type declaration bool m_gotNansiType : 1; // Linker saw Non-ANSI type declaration
bool m_icoMaybeWritten : 1; // Design might write this input signal - for ico change detect bool m_icoMaybeWritten : 1; // Design might write this input signal - for ico change detect
@@ -2174,10 +2325,11 @@ class AstVar final : public AstNode {
bool m_isContinuously : 1; // Ever assigned continuously (for force/release) bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier
bool m_hasUserInit : 1; // Has initial assignment by user at parse time bool m_hasUserInit : 1; // Has initial assignment by user at parse time
bool m_isStatic : 1; // Static C variable (for Verilog see instead lifetime())
bool m_isPulldown : 1; // Tri0 bool m_isPulldown : 1; // Tri0
bool m_isPullup : 1; // Tri1 bool m_isPullup : 1; // Tri1
bool m_isIfaceParent : 1; // dtype is reference to interface present in this module bool m_isIfaceParent : 1; // dtype is reference to interface present in this module
bool m_isIfaceArraySplit : 1; // Interface array that was split into element variables by
// V3Param, until V3Width removes it
bool m_isInternal : 1; // Internal state, don't add to method pinter bool m_isInternal : 1; // Internal state, don't add to method pinter
bool m_isIfaceParam : 1; // Parameter belongs to an interface/modport bool m_isIfaceParam : 1; // Parameter belongs to an interface/modport
bool m_isDpiOpenArray : 1; // DPI import open array bool m_isDpiOpenArray : 1; // DPI import open array
@@ -2227,11 +2379,11 @@ class AstVar final : public AstNode {
m_attrSFormat = false; m_attrSFormat = false;
m_attrSplitVar = false; m_attrSplitVar = false;
m_attrFsmState = false; m_attrFsmState = false;
m_attrFsmRegisterWrapper = false;
m_attrFsmResetArc = false; m_attrFsmResetArc = false;
m_attrFsmArcInclCond = false; m_attrFsmArcInclCond = false;
m_constPoolEntry = false; m_constPoolEntry = false;
m_covergroupRefMember = false; m_covergroupRefMember = false;
m_embeddedCovergroup = false;
m_attrFileDescr = false; m_attrFileDescr = false;
m_gotNansiType = false; m_gotNansiType = false;
m_icoMaybeWritten = false; m_icoMaybeWritten = false;
@@ -2239,10 +2391,10 @@ class AstVar final : public AstNode {
m_isContinuously = false; m_isContinuously = false;
m_hasStrengthAssignment = false; m_hasStrengthAssignment = false;
m_hasUserInit = false; m_hasUserInit = false;
m_isStatic = false;
m_isPulldown = false; m_isPulldown = false;
m_isPullup = false; m_isPullup = false;
m_isIfaceParent = false; m_isIfaceParent = false;
m_isIfaceArraySplit = false;
m_isInternal = false; m_isInternal = false;
m_isIfaceParam = false; m_isIfaceParam = false;
m_isDpiOpenArray = false; m_isDpiOpenArray = false;
@@ -2355,10 +2507,10 @@ public:
string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument
string dpiTmpVarType(const string& varName) const; string dpiTmpVarType(const string& varName) const;
// Return Verilator internal type for argument: CData, SData, IData, WData // Return Verilator internal type for argument: CData, SData, IData, WData
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "", string vlArgType(bool named, bool forReturn, bool forFunc, bool asRef = false,
bool asRef = false, bool constRef = false) const; bool constRef = false) const;
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc string vlEnumDir(bool forMember = false) const; // Return VerilatorVarDir: VLVD_INOUT, etc
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
void combineType(VVarType type); void combineType(VVarType type);
AstNodeDType* getChildDTypep() const override { return childDTypep(); } AstNodeDType* getChildDTypep() const override { return childDTypep(); }
@@ -2378,13 +2530,16 @@ public:
void attrSFormat(bool flag) { m_attrSFormat = flag; } void attrSFormat(bool flag) { m_attrSFormat = flag; }
void attrSplitVar(bool flag) { m_attrSplitVar = flag; } void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
void attrFsmState(bool flag) { m_attrFsmState = flag; } void attrFsmState(bool flag) { m_attrFsmState = flag; }
void attrFsmRegisterWrapper(bool flag) { m_attrFsmRegisterWrapper = flag; }
void attrFsmResetArc(bool flag) { m_attrFsmResetArc = flag; } void attrFsmResetArc(bool flag) { m_attrFsmResetArc = flag; }
void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; } void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; }
bool constPoolEntry() const { return m_constPoolEntry; } bool constPoolEntry() const { return m_constPoolEntry; }
void setConstPoolEntry() { m_constPoolEntry = true; } void setConstPoolEntry() { m_constPoolEntry = true; }
bool covergroupRefMember() const { return m_covergroupRefMember; } bool covergroupRefMember() const { return m_covergroupRefMember; }
void covergroupRefMember(bool flag) { m_covergroupRefMember = flag; } void covergroupRefMember(bool flag) { m_covergroupRefMember = flag; }
// Instance variable an embedded covergroup declares, of the covergroup's anonymous type
// (IEEE 1800-2023 19.4)
bool embeddedCovergroup() const { return m_embeddedCovergroup; }
void embeddedCovergroup(bool flag) { m_embeddedCovergroup = flag; }
void rand(const VRandAttr flag) { m_rand = flag; } void rand(const VRandAttr flag) { m_rand = flag; }
void usedParam(bool flag) { m_usedParam = flag; } void usedParam(bool flag) { m_usedParam = flag; }
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; } void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
@@ -2403,8 +2558,8 @@ public:
void primaryIO(bool flag) { m_primaryIO = flag; } void primaryIO(bool flag) { m_primaryIO = flag; }
void isConst(bool flag) { m_isConst = flag; } void isConst(bool flag) { m_isConst = flag; }
void isContinuously(bool flag) { m_isContinuously = flag; } void isContinuously(bool flag) { m_isContinuously = flag; }
void isStatic(bool flag) { m_isStatic = flag; }
void isIfaceParent(bool flag) { m_isIfaceParent = flag; } void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
void isIfaceArraySplit(bool flag) { m_isIfaceArraySplit = flag; }
void isInternal(bool flag) { m_isInternal = flag; } void isInternal(bool flag) { m_isInternal = flag; }
void isIfaceParam(bool flag) { m_isIfaceParam = flag; } void isIfaceParam(bool flag) { m_isIfaceParam = flag; }
void funcLocal(bool flag) { void funcLocal(bool flag) {
@@ -2492,6 +2647,7 @@ public:
m_varType = VVarType::IFACEREF; m_varType = VVarType::IFACEREF;
} }
bool isIfaceParent() const { return m_isIfaceParent; } bool isIfaceParent() const { return m_isIfaceParent; }
bool isIfaceArraySplit() const { return m_isIfaceArraySplit; }
bool isInternal() const { return m_isInternal; } bool isInternal() const { return m_isInternal; }
bool isIfaceParam() const { return m_isIfaceParam; } bool isIfaceParam() const { return m_isIfaceParam; }
bool isSignal() const { return varType().isSignal(); } bool isSignal() const { return varType().isSignal(); }
@@ -2534,7 +2690,6 @@ public:
bool isRand() const { return m_rand.isRand(); } bool isRand() const { return m_rand.isRand(); }
bool isRandC() const { return m_rand.isRandC(); } bool isRandC() const { return m_rand.isRandC(); }
bool isConst() const VL_MT_SAFE { return m_isConst; } bool isConst() const VL_MT_SAFE { return m_isConst; }
bool isStatic() const VL_MT_SAFE { return m_isStatic; }
bool isLatched() const { return m_isLatched; } bool isLatched() const { return m_isLatched; }
bool isFuncLocal() const { return m_funcLocal; } bool isFuncLocal() const { return m_funcLocal; }
bool isFuncLocalSticky() const { return m_funcLocalSticky; } bool isFuncLocalSticky() const { return m_funcLocalSticky; }
@@ -2547,7 +2702,6 @@ public:
bool attrSFormat() const { return m_attrSFormat; } bool attrSFormat() const { return m_attrSFormat; }
bool attrSplitVar() const { return m_attrSplitVar; } bool attrSplitVar() const { return m_attrSplitVar; }
bool attrFsmState() const { return m_attrFsmState; } bool attrFsmState() const { return m_attrFsmState; }
bool attrFsmRegisterWrapper() const { return m_attrFsmRegisterWrapper; }
bool attrFsmResetArc() const { return m_attrFsmResetArc; } bool attrFsmResetArc() const { return m_attrFsmResetArc; }
bool attrFsmArcInclCond() const { return m_attrFsmArcInclCond; } bool attrFsmArcInclCond() const { return m_attrFsmArcInclCond; }
AstIface* sensIfacep() const { return m_sensIfacep; } AstIface* sensIfacep() const { return m_sensIfacep; }
@@ -2571,13 +2725,6 @@ public:
lifetime(fromp->lifetime()); lifetime(fromp->lifetime());
} }
void combineType(const AstVar* otherp); void combineType(const AstVar* otherp);
void inlineAttrReset(const string& name) {
if (direction() == VDirection::INOUT && varType() == VVarType::WIRE) {
m_varType = VVarType::TRIWIRE;
}
m_direction = VDirection::NONE;
m_name = name;
}
bool needsCReset() const { bool needsCReset() const {
return !isIfaceParent() && !isIfaceRef() && !noReset() && !isParam() && !isStatementTemp() return !isIfaceParent() && !isIfaceRef() && !noReset() && !isParam() && !isStatementTemp()
&& !noCReset() && !(basicp() && basicp()->isEvent()); && !noCReset() && !(basicp() && basicp()->isEvent());
@@ -2620,6 +2767,7 @@ public:
bool sameNode(const AstNode* samep) const override; bool sameNode(const AstNode* samep) const override;
bool hasDType() const override VL_MT_SAFE { return true; } bool hasDType() const override VL_MT_SAFE { return true; }
AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable AstVar* varp() const VL_MT_STABLE { return m_varp; } // [After Link] Pointer to variable
void varp(AstVar* nodep) { m_varp = nodep; }
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
void scopep(AstScope* nodep) { m_scopep = nodep; } void scopep(AstScope* nodep) { m_scopep = nodep; }
bool isTrace() const { return m_trace; } bool isTrace() const { return m_trace; }
@@ -2633,10 +2781,10 @@ class AstCoverOtherDecl final : public AstNodeCoverDecl {
// Coverage analysis point declaration // Coverage analysis point declaration
// Used for other than toggle types of coverage // Used for other than toggle types of coverage
string m_linescov; string m_linescov;
string m_fsmVar; string m_fsmVar; // FSM state variable name
string m_fsmFrom; string m_fsmFrom; // FSM source state label
string m_fsmTo; string m_fsmTo; // FSM destination state label
string m_fsmTag; string m_fsmTag; // FSM arc kind tag (e.g. reset, reset_include, default)
int m_offset; // Offset column numbers to uniq-ify IFs int m_offset; // Offset column numbers to uniq-ify IFs
public: public:
AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment, AstCoverOtherDecl(FileLine* fl, const string& page, const string& comment,
@@ -2662,7 +2810,7 @@ public:
const string& fsmTag() const { return m_fsmTag; } const string& fsmTag() const { return m_fsmTag; }
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl); const AstCoverOtherDecl* const asamep = VN_DBG_AS(samep, CoverOtherDecl);
return AstNodeCoverDecl::sameNode(samep) && linescov() == asamep->linescov(); return Super::sameNode(samep) && linescov() == asamep->linescov();
} }
}; };
class AstCoverToggleDecl final : public AstNodeCoverDecl { class AstCoverToggleDecl final : public AstNodeCoverDecl {
@@ -2684,7 +2832,7 @@ public:
const VNumRange& range() const { return m_range; } const VNumRange& range() const { return m_range; }
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl); const AstCoverToggleDecl* const asamep = VN_DBG_AS(samep, CoverToggleDecl);
return AstNodeCoverDecl::sameNode(samep) && range() == asamep->range(); return Super::sameNode(samep) && range() == asamep->range();
} }
}; };
@@ -2802,8 +2950,8 @@ public:
class AstCoverCross final : public AstNodeFuncCovItem { class AstCoverCross final : public AstNodeFuncCovItem {
// @astgen op1 := itemsp : List[AstCoverpointRef] // @astgen op1 := itemsp : List[AstCoverpointRef]
// @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only // @astgen op2 := optionsp : List[AstCoverOption] // post-LinkParse only
// @astgen op3 := rawBodyp : List[AstNode] // Parse: raw cross_body items; // @astgen op3 := binsp : List[AstNode] // Parse: mixed cross bins/options;
// // post-LinkParse: empty // // post-LinkParse: AstCoverCrossBin only
// @astgen op4 := iffp : Optional[AstNodeExpr] // Conditional sampling guard // @astgen op4 := iffp : Optional[AstNodeExpr] // Conditional sampling guard
public: public:
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp, AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp,
@@ -2913,8 +3061,12 @@ class AstClass final : public AstNodeModule {
// MEMBERS // MEMBERS
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with // @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
// @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class // @astgen ptr := m_covergroupEnclosingClassp : Optional[AstClass] // Lexical enclosing class
string m_dtypeNameFull; // dtypeName(true) once frozen, as parameters may then be gone
string m_dtypeNameShort; // dtypeName(false) once frozen, as parameters may then be gone
uint32_t m_declTokenNum; // Declaration token number uint32_t m_declTokenNum; // Declaration token number
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends) VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
bool m_hasRandVarsUpdate = false; // Has updateRandVars method,
// which updates pointers to rand variables in clone()
bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?) bool m_covergroup = false; // Is covergroup (TODO perhaps make a new Ast node type for CG?)
bool m_extended = false; // Is extension or extended by other classes bool m_extended = false; // Is extension or extended by other classes
bool m_interfaceClass = false; // Interface class bool m_interfaceClass = false; // Interface class
@@ -2922,9 +3074,14 @@ class AstClass final : public AstNodeModule {
bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class bool m_useVirtualPublic = false; // Subclasses need virtual public as uses interface class
bool m_virtual = false; // Virtual class bool m_virtual = false; // Virtual class
bool m_printedFrom = false; // This class is printed from i.e. is used as format arg. bool m_printedFrom = false; // This class is printed from i.e. is used as format arg.
// Mutable, as only a recursion guard of the const dtypeName(), so also not dumped
mutable bool m_dtypeNameBusy = false; // In dtypeName(), which a parameter may lead back to
// Covergroup options (when m_covergroup is true) // Covergroup options (when m_covergroup is true)
int m_cgAutoBinMax = -1; // option.auto_bin_max value (-1 = not set, use default 64) int m_cgAutoBinMax = -1; // option.auto_bin_max value (-1 = not set, use default 64)
string dtypeNameCalc(bool full) const; // dtypeName() as computed from the tree
string dtypeNameScope() const; // Prefix of dtypeName(true) for the scope declaring the class
public: public:
AstClass(FileLine* fl, const string& name, const string& libname) AstClass(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_Class(fl, name, libname) : ASTGEN_SUPER_Class(fl, name, libname)
@@ -2943,6 +3100,8 @@ public:
void covergroupEnclosingClassp(AstClass* classp) { m_covergroupEnclosingClassp = classp; } void covergroupEnclosingClassp(AstClass* classp) { m_covergroupEnclosingClassp = classp; }
AstNode* membersp() const VL_MT_STABLE { return stmtsp(); } AstNode* membersp() const VL_MT_STABLE { return stmtsp(); }
void addMembersp(AstNode* nodep) { addStmtsp(nodep); } void addMembersp(AstNode* nodep) { addStmtsp(nodep); }
bool hasRandVarsUpdate() const { return m_hasRandVarsUpdate; }
void hasRandVarsUpdate(bool flag) { m_hasRandVarsUpdate = flag; }
bool isCovergroup() const { return m_covergroup; } bool isCovergroup() const { return m_covergroup; }
void isCovergroup(bool flag) { m_covergroup = flag; } void isCovergroup(bool flag) { m_covergroup = flag; }
bool isExtended() const { return m_extended; } bool isExtended() const { return m_extended; }
@@ -2957,6 +3116,16 @@ public:
void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; } void useVirtualPublic(bool flag) { m_useVirtualPublic = flag; }
void markPrintedFrom() { m_printedFrom = true; } void markPrintedFrom() { m_printedFrom = true; }
bool isPrintedFrom() const { return m_printedFrom; } bool isPrintedFrom() const { return m_printedFrom; }
// Name of the class as a data type, with the values of its parameters, e.g. 'Cls#(int,5)'.
// With 'full', as for $typename (IEEE 1800-2023 20.6.1), prefixed with the scope declaring
// the class, e.g. '$unit::Cls#(int,5)', and with the types of parameters in full.
string dtypeName(bool full) const;
// Fix dtypeName(), as V3WidthCommit moves parameter types to the type table
void dtypeNameFreeze();
// Whether dtypeName() is fixed, as for a class elaborated from the design
bool dtypeNameFrozen() const { return !m_dtypeNameFull.empty(); }
// Named by dtypeName(), as name() is internal for a specialization, unless still a template
string prettyNameMsg() const override { return hasGParam() ? prettyName() : dtypeName(false); }
// Covergroup options accessors // Covergroup options accessors
int cgAutoBinMax() const { return m_cgAutoBinMax; } int cgAutoBinMax() const { return m_cgAutoBinMax; }
void cgAutoBinMax(int value) { m_cgAutoBinMax = value; } void cgAutoBinMax(int value) { m_cgAutoBinMax = value; }
@@ -3026,17 +3195,25 @@ public:
}; };
class AstIface final : public AstNodeModule { class AstIface final : public AstNodeModule {
// An interface declaration // An interface declaration
string m_dtypeNameFull; // dtypeName(true) once frozen, as parameter types may then be gone
string m_dtypeNameShort; // dtypeName(false) once frozen, as parameter types may then be gone
bool m_hasVirtualRef = false; // There exists a virtual interface reference for this interface bool m_hasVirtualRef = false; // There exists a virtual interface reference for this interface
public: public:
AstIface(FileLine* fl, const string& name, const string& libname) AstIface(FileLine* fl, const string& name, const string& libname)
: ASTGEN_SUPER_Iface(fl, name, libname) {} : ASTGEN_SUPER_Iface(fl, name, libname) {}
ASTGEN_MEMBERS_AstIface; ASTGEN_MEMBERS_AstIface;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string verilogKwd() const override { return "interface"; }
// Interfaces have `timescale applicability but lots of code seems to // Interfaces have `timescale applicability but lots of code seems to
// get false warnings if we enable this // get false warnings if we enable this
string verilogKwd() const override { return "interface"; }
bool timescaleMatters() const override { return false; } bool timescaleMatters() const override { return false; }
bool hasVirtualRef() const { return m_hasVirtualRef; } bool hasVirtualRef() const { return m_hasVirtualRef; }
void setHasVirtualRef() { m_hasVirtualRef = true; } void setHasVirtualRef() { m_hasVirtualRef = true; }
// Name of the interface as a data type, with the values of its parameters, e.g. 'ifc#(8)'
string dtypeName(bool full) const;
// Fix dtypeName(), as V3WidthCommit moves parameter types to the type table
void dtypeNameFreeze();
}; };
class AstModule final : public AstNodeModule { class AstModule final : public AstNodeModule {
// A module declaration // A module declaration
@@ -3087,9 +3264,7 @@ public:
: ASTGEN_SUPER_Package(fl, name, libname) {} : ASTGEN_SUPER_Package(fl, name, libname) {}
ASTGEN_MEMBERS_AstPackage; ASTGEN_MEMBERS_AstPackage;
string verilogKwd() const override { return "package"; } string verilogKwd() const override { return "package"; }
bool timescaleMatters() const override { return !isDollarUnit(); } bool timescaleMatters() const override { return !isDollarUnit() && !isConstPool(); }
static string dollarUnitName() { return AstNode::encodeName("$unit"); }
bool isDollarUnit() const { return name() == dollarUnitName(); }
}; };
class AstPrimitive final : public AstNodeModule { class AstPrimitive final : public AstNodeModule {
// A primitive declaration // A primitive declaration
@@ -3241,6 +3416,14 @@ public:
} }
int elementsConst() const VL_MT_STABLE { return hiConst() - loConst() + 1; } int elementsConst() const VL_MT_STABLE { return hiConst() - loConst() + 1; }
bool ascending() const { return leftConst() < rightConst(); } bool ascending() const { return leftConst() < rightConst(); }
// Iterate indices from left to right inclusive, see VNumRange::seqLeftToRight()
VNumRange::Sequence seqLeftToRight() const {
return VNumRange{leftConst(), rightConst()}.seqLeftToRight();
}
// Iterate indices from right to left inclusive, see VNumRange::seqRightToLeft()
VNumRange::Sequence seqRightToLeft() const {
return VNumRange{leftConst(), rightConst()}.seqRightToLeft();
}
void dump(std::ostream& str) const override; void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override; void dumpJson(std::ostream& str) const override;
virtual string emitC() { V3ERROR_NA_RETURN(""); } virtual string emitC() { V3ERROR_NA_RETURN(""); }
+13 -41
View File
@@ -413,16 +413,16 @@ class AstCase final : public AstNodeStmt {
// @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression // @astgen op1 := exprp : AstNodeExpr // Condition (scurtinee) expression
// @astgen op2 := itemsp : List[AstCaseItem] // @astgen op2 := itemsp : List[AstCaseItem]
// @astgen op3 := notParallelp : List[AstNode] // assertion code for non-full case's // @astgen op3 := notParallelp : List[AstNode] // assertion code for non-full case's
VCaseType m_casex; // 0=case, 1=casex, 2=casez VCaseType m_caseType; // 0=case, 1=casex, 2=casez
bool m_fullPragma = false; // Synthesis full_case bool m_fullPragma = false; // Synthesis full_case
bool m_parallelPragma = false; // Synthesis parallel_case bool m_parallelPragma = false; // Synthesis parallel_case
bool m_uniquePragma = false; // unique case bool m_uniquePragma = false; // unique case
bool m_unique0Pragma = false; // unique0 case bool m_unique0Pragma = false; // unique0 case
bool m_priorityPragma = false; // priority case bool m_priorityPragma = false; // priority case
public: public:
AstCase(FileLine* fl, VCaseType casex, AstNodeExpr* exprp, AstCaseItem* itemsp) AstCase(FileLine* fl, VCaseType caseType, AstNodeExpr* exprp, AstCaseItem* itemsp)
: ASTGEN_SUPER_Case(fl) : ASTGEN_SUPER_Case(fl)
, m_casex{casex} { , m_caseType{caseType} {
this->exprp(exprp); this->exprp(exprp);
addItemsp(itemsp); addItemsp(itemsp);
} }
@@ -432,15 +432,15 @@ public:
int instrCount() const override { return INSTR_COUNT_BRANCH; } int instrCount() const override { return INSTR_COUNT_BRANCH; }
string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; } string verilogKwd() const override { return casez() ? "casez" : casex() ? "casex" : "case"; }
bool sameNode(const AstNode* samep) const override { bool sameNode(const AstNode* samep) const override {
return m_casex == VN_DBG_AS(samep, Case)->m_casex; return m_caseType == VN_DBG_AS(samep, Case)->m_caseType;
} }
bool casex() const { return m_casex == VCaseType::CT_CASEX; } bool casex() const { return m_caseType == VCaseType::CT_CASEX; }
bool casez() const { return m_casex == VCaseType::CT_CASEZ; } bool casez() const { return m_caseType == VCaseType::CT_CASEZ; }
bool caseInside() const { return m_casex == VCaseType::CT_CASEINSIDE; } bool caseInside() const { return m_caseType == VCaseType::CT_CASEINSIDE; }
bool caseMatches() const { return m_casex == VCaseType::CT_CASEMATCHES; } bool caseMatches() const { return m_caseType == VCaseType::CT_CASEMATCHES; }
bool caseSimple() const { return m_casex == VCaseType::CT_CASE; } bool caseSimple() const { return m_caseType == VCaseType::CT_CASE; }
void caseInsideSet() { m_casex = VCaseType::CT_CASEINSIDE; } void caseInsideSet() { m_caseType = VCaseType::CT_CASEINSIDE; }
void caseMatchesSet() { m_casex = VCaseType::CT_CASEMATCHES; } void caseMatchesSet() { m_caseType = VCaseType::CT_CASEMATCHES; }
bool fullPragma() const { return m_fullPragma; } bool fullPragma() const { return m_fullPragma; }
void fullPragma(bool flag) { m_fullPragma = flag; } void fullPragma(bool flag) { m_fullPragma = flag; }
bool parallelPragma() const { return m_parallelPragma; } bool parallelPragma() const { return m_parallelPragma; }
@@ -549,29 +549,7 @@ public:
bool isOutputter() override { return true; } bool isOutputter() override { return true; }
bool isPure() override { return false; } bool isPure() override { return false; }
AstNodeCoverDecl* declp() const { return m_declp; } // Where defined AstNodeCoverDecl* declp() const { return m_declp; } // Where defined
}; void declp(AstNodeCoverDecl* nodep) { m_declp = nodep; }
class AstCoverToggle final : public AstNodeStmt {
// Toggle analysis of given signal
// Parents: MODULE
// @astgen op1 := incp : AstCoverInc
// @astgen op2 := origp : AstNodeExpr
// @astgen op3 := changep : AstNodeExpr
public:
AstCoverToggle(FileLine* fl, AstCoverInc* incp, AstNodeExpr* origp, AstNodeExpr* changep)
: ASTGEN_SUPER_CoverToggle(fl) {
this->incp(incp);
this->origp(origp);
this->changep(changep);
}
ASTGEN_MEMBERS_AstCoverToggle;
int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return true; }
bool isOutputter() override {
return false; // Though the AstCoverInc under this is an outputter
}
// but isPure() true
}; };
class AstDeassign final : public AstNodeStmt { class AstDeassign final : public AstNodeStmt {
// Procedural 'deassign' statement // Procedural 'deassign' statement
@@ -1221,13 +1199,6 @@ public:
ASTGEN_MEMBERS_AstSetuphold; ASTGEN_MEMBERS_AstSetuphold;
bool sameNode(const AstNode* /*samep*/) const override { return true; } bool sameNode(const AstNode* /*samep*/) const override { return true; }
}; };
class AstSplitPlaceholder final : public AstNodeStmt {
public:
// Dummy node used within V3Split; never exists outside of V3Split.
explicit AstSplitPlaceholder(FileLine* fl)
: ASTGEN_SUPER_SplitPlaceholder(fl) {}
ASTGEN_MEMBERS_AstSplitPlaceholder;
};
class AstStackTraceT final : public AstNodeStmt { class AstStackTraceT final : public AstNodeStmt {
// $stacktrace used as task // $stacktrace used as task
public: public:
@@ -1340,6 +1311,7 @@ class AstTraceDecl final : public AstNodeStmt {
const VVarType m_varType; // Type of variable (for localparam vs. param) const VVarType m_varType; // Type of variable (for localparam vs. param)
const VDirection m_declDirection; // Declared direction input/output etc const VDirection m_declDirection; // Declared direction input/output etc
const bool m_inDtypeFunc; // Trace decl inside type init function const bool m_inDtypeFunc; // Trace decl inside type init function
// dist-ast-dump-suppress // Not stable and of low value
int m_codeInc{0}; // Code increment for type int m_codeInc{0}; // Code increment for type
public: public:
AstTraceDecl(FileLine* fl, const string& showname, AstTraceDecl(FileLine* fl, const string& showname,
+3451 -3280
View File
File diff suppressed because it is too large Load Diff
+46
View File
@@ -26,6 +26,8 @@
#include "V3Broken.h" #include "V3Broken.h"
#include "V3ConstPool.h"
#include <unordered_set> #include <unordered_set>
VL_DEFINE_DEBUG_FUNCTIONS; VL_DEFINE_DEBUG_FUNCTIONS;
@@ -227,6 +229,45 @@ private:
} }
return false; return false;
} }
static void checkArgRefs(AstNodeExpr* nodep, const char* descrp, AstNodeExpr* argsp) {
if (!std::strcmp(descrp, "TODO")) return; // Skip if not yet checked
// Check each argument
const char* dp = descrp;
for (AstNodeExpr* argp = argsp; argp; argp = VN_AS(argp->nextp(), NodeExpr)) {
if (argp->fileline()->erroringOn()) return; // Intentionally skip all checks
const AstNodeExpr* const lvalp = argp->getVAccessTargetRecurse();
const VAccess access
= lvalp ? lvalp->getVAccessRecurse() // Since we call it on a result of above call
// there should be no recursion at all
: VAccess{VAccess::READ};
if (dp[0] == '+') --dp; // Repeats the entry before it
switch (dp[0]) {
case 'r':
UASSERT_OBJ(access.isReadOnly(), argp,
"Input argument of library call is not a read-only expression");
break;
case 'w':
UASSERT_OBJ(lvalp, argp, //
"Output argument of library call is not a valid C++ LValue");
UASSERT_OBJ(access.isWriteOnly(), argp,
"Output argument of library call is not write-only");
break;
case 'm':
UASSERT_OBJ(lvalp, argp, //
"Inout argument of library call is not a valid C++ LValue");
UASSERT_OBJ(access.isRW(), argp,
"Inout argument of library call is not read-write");
break;
default:
UASSERT_OBJ(dp[0], argp, "Unexpected trailing arguments to library call");
break; // LCOV_EXCL_LINE
}
++dp;
}
UASSERT_OBJ(!dp[0] || dp[0] == '+', nodep, "Insufficient arguments to library call");
}
// VISITORS // VISITORS
void visit(AstNodeAssign* nodep) override { void visit(AstNodeAssign* nodep) override {
processEnter(nodep); processEnter(nodep);
@@ -291,6 +332,7 @@ private:
void visit(AstCMethodHard* nodep) override { void visit(AstCMethodHard* nodep) override {
++m_nCalls; ++m_nCalls;
processAndIterate(nodep); processAndIterate(nodep);
checkArgRefs(nodep, nodep->method().args(), nodep->pinsp());
} }
void visit(AstNodeFTaskRef* nodep) override { void visit(AstNodeFTaskRef* nodep) override {
++m_nCalls; ++m_nCalls;
@@ -392,6 +434,10 @@ void V3Broken::brokenAll(AstNetlist* nodep) {
// Check every node in tree // Check every node in tree
const BrokenCheckVisitor cvisitor{nodep}; const BrokenCheckVisitor cvisitor{nodep};
// Check the constant pool lookup cache refers only to nodes in the tree
const char* const whyp = V3ConstPool::broken();
UASSERT_OBJ(!whyp, nodep, "Broken constant pool cache: " << whyp);
s_allocTable.checkForLeaks(); s_allocTable.checkForLeaks();
s_linkableTable.clear(); s_linkableTable.clear();
s_brokenCntGlobal.inc(); s_brokenCntGlobal.inc();
+3 -3
View File
@@ -152,6 +152,7 @@ class CCtorsVisitor final : public VNVisitor {
// VISITORS // VISITORS
void visit(AstNodeModule* nodep) override { void visit(AstNodeModule* nodep) override {
if (nodep->isConstPool()) return; // Special emit rules
if (const AstClass* const classp = VN_CAST(nodep, Class)) { if (const AstClass* const classp = VN_CAST(nodep, Class)) {
// Interface class may only have pure virtuals and params which do not need cctor reset // Interface class may only have pure virtuals and params which do not need cctor reset
if (classp->isInterfaceClass()) return; if (classp->isInterfaceClass()) return;
@@ -167,12 +168,12 @@ class CCtorsVisitor final : public VNVisitor {
if (v3Global.opt.coverage()) { if (v3Global.opt.coverage()) {
V3CCtorsBuilder configure_coverage{nodep, "_configure_coverage", VCtorType::COVERAGE}; V3CCtorsBuilder configure_coverage{nodep, "_configure_coverage", VCtorType::COVERAGE};
for (AstNode* np = nodep->stmtsp(); np; np = np->nextp()) { for (AstNode *np = nodep->stmtsp(), *nextp; np; np = nextp) {
nextp = np->nextp();
if (AstNodeCoverDecl* const coverp = VN_CAST(np, NodeCoverDecl)) { if (AstNodeCoverDecl* const coverp = VN_CAST(np, NodeCoverDecl)) {
// ... else we don't have a static VlSym to be able to coverage insert // ... else we don't have a static VlSym to be able to coverage insert
UASSERT_OBJ(!VN_IS(nodep, Class), coverp, UASSERT_OBJ(!VN_IS(nodep, Class), coverp,
"NodeCoverDecl should be in class's package, not class itself"); "NodeCoverDecl should be in class's package, not class itself");
np = coverp->backp();
configure_coverage.add(coverp->unlinkFrBack()); configure_coverage.add(coverp->unlinkFrBack());
} }
} }
@@ -219,7 +220,6 @@ class CCtorsVisitor final : public VNVisitor {
} }
} }
void visit(AstConstPool*) override {}
void visit(AstNode* nodep) override { iterateChildren(nodep); } void visit(AstNode* nodep) override { iterateChildren(nodep); }
public: public:
+2
View File
@@ -86,6 +86,7 @@ class CUseVisitor final : public VNVisitorConst {
} }
void visit(AstCell* nodep) override { void visit(AstCell* nodep) override {
if (nodep->user1SetOnce()) return; // Process once if (nodep->user1SetOnce()) return; // Process once
if (nodep->modp()->isConstPool()) return; // Special emit rules
// Currently no IMP_INCLUDE because we include __Syms which has them all // Currently no IMP_INCLUDE because we include __Syms which has them all
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name()); addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name());
iterateChildrenConst(nodep); iterateChildrenConst(nodep);
@@ -115,6 +116,7 @@ void V3CUse::cUseAll() {
// Call visitor separately for each module, so visitor state is cleared // Call visitor separately for each module, so visitor state is cleared
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp;
modp = VN_AS(modp->nextp(), NodeModule)) { modp = VN_AS(modp->nextp(), NodeModule)) {
if (modp->isConstPool()) continue; // Special emit rules
// Insert under this module; someday we should e.g. make Ast // Insert under this module; someday we should e.g. make Ast
// for each output file and put under that // for each output file and put under that
CUseVisitor{modp}; CUseVisitor{modp};
+24 -16
View File
@@ -38,6 +38,7 @@
#include "V3Case.h" #include "V3Case.h"
#include "V3ConstPool.h"
#include "V3Stats.h" #include "V3Stats.h"
VL_DEFINE_DEBUG_FUNCTIONS; VL_DEFINE_DEBUG_FUNCTIONS;
@@ -738,18 +739,15 @@ class CaseVisitor final : public VNVisitor {
} }
} }
// Create the table in the constant pool, unless using an inline table // Create the lookup table reference, in the constant pool unless using an inline table
AstVarScope* const tableVscp = [&]() -> AstVarScope* { AstNodeExpr* const tableRefp = [&]() -> AstNodeExpr* {
if (isTinyTable) return nullptr; if (isTinyTable) return tablep;
AstVarScope* vscp = v3Global.rootp()->constPoolp()->findConst(tablep, true); AstVarRef* const refp = V3ConstPool::findConst(tablep);
VL_DO_DANGLING(tablep->deleteTree(), tablep); // findConst clones VL_DO_DANGLING(tablep->deleteTree(), tablep); // findConst clones
return vscp; return refp;
}(); }();
// Create the lookup table reference and index // Create the lookup index
AstNodeExpr* const tableRefp
= tableVscp ? static_cast<AstNodeExpr*>(new AstVarRef{flp, tableVscp, VAccess::READ})
: static_cast<AstNodeExpr*>(tablep);
AstNodeExpr* const caseExprp AstNodeExpr* const caseExprp
= new AstExtend{flp, nodep->exprp()->cloneTreePure(false), 32}; = new AstExtend{flp, nodep->exprp()->cloneTreePure(false), 32};
AstNodeExpr* const scalep = new AstConst{flp, entryWidth}; AstNodeExpr* const scalep = new AstConst{flp, entryWidth};
@@ -855,15 +853,14 @@ class CaseVisitor final : public VNVisitor {
} }
// Create the tables // Create the tables
AstVarScope* const matchVscp = v3Global.rootp()->constPoolp()->findConst(matchp, true); AstVarRef* const matchRefp = V3ConstPool::findConst(matchp);
AstVarScope* const tableVscp = v3Global.rootp()->constPoolp()->findTable(tablep); AstVarRef* const tableRefp = V3ConstPool::findTable(tablep);
VL_DO_DANGLING(matchp->deleteTree(), matchp); VL_DO_DANGLING(matchp->deleteTree(), matchp);
VL_DO_DANGLING(tablep->deleteTree(), tablep); VL_DO_DANGLING(tablep->deleteTree(), tablep);
// AstMatchMasked produces the index of the matching entry // AstMatchMasked produces the index of the matching entry
AstNodeExpr* const tableRefp = new AstVarRef{flp, tableVscp, VAccess::READ};
AstNodeExpr* const caseExprp = nodep->exprp()->cloneTreePure(false); AstNodeExpr* const caseExprp = nodep->exprp()->cloneTreePure(false);
AstMatchMasked* const indexp = new AstMatchMasked{flp, caseExprp, matchVscp}; AstMatchMasked* const indexp = new AstMatchMasked{flp, caseExprp, matchRefp};
AstNodeExpr* const entryp = new AstArraySel{flp, tableRefp, indexp}; AstNodeExpr* const entryp = new AstArraySel{flp, tableRefp, indexp};
return connectDecoderOutputs(nodep, entryp, "__VcaseDecoderOut"); return connectDecoderOutputs(nodep, entryp, "__VcaseDecoderOut");
@@ -1004,11 +1001,22 @@ class CaseVisitor final : public VNVisitor {
itemp->addCondsp(newCondp); itemp->addCondsp(newCondp);
} }
// If there was no default, add a empty one, this greatly simplifies below code // If there was no default, make one up, this greatly simplifies below code
// and constant propagation will just eliminate it for us later. // and constant propagation will just eliminate it for us later.
if (!hasDefault) { if (!hasDefault) {
nodep->addItemsp(new AstCaseItem{ if (m_caseDetailsValid && m_caseDetails.exhaustive
nodep->fileline(), new AstConst{nodep->fileline(), AstConst::BitTrue{}}, nullptr}); && !m_caseDetails.exhaustiveOverEnumOnly) {
// Case is proven exhaustive, so the last is reached only when its test holds,
// make it unconditional
AstCaseItem* const lastp = VN_AS(nodep->itemsp()->lastp(), CaseItem);
pushDeletep(lastp->condsp()->unlinkFrBackWithNext());
lastp->addCondsp(new AstConst{lastp->fileline(), AstConst::BitTrue{}});
} else {
// Otherwise needs an explicit empty default
nodep->addItemsp(new AstCaseItem{
nodep->fileline(), new AstConst{nodep->fileline(), AstConst::BitTrue{}},
nullptr});
}
} }
// Now build the IF statement tree // Now build the IF statement tree
+21 -12
View File
@@ -18,7 +18,8 @@
// Each module: // Each module:
// For each expression, if it requires a clean operand, // For each expression, if it requires a clean operand,
// and the operand is dirty, insert a CLEAN node. // and the operand is dirty, insert a CLEAN node.
// Resize operands (but not variables or variable selects) to C++ 32/64/wide types. // Resize operands (but not variables, variable selects, or formatting metadata)
// to C++ 32/64/wide types.
// Copy all width() values to widthMin() so RANGE, etc can still see orig widths // Copy all width() values to widthMin() so RANGE, etc can still see orig widths
// //
//************************************************************************* //*************************************************************************
@@ -84,6 +85,7 @@ class CleanVisitor final : public VNVisitor {
|| VN_IS(nodep, ConsPackMember) // || VN_IS(nodep, ConsPackMember) //
|| VN_IS(nodep, NodeDType) // Don't want to change variable widths! || VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|| VN_IS(nodep, NodeSel) // Array selects should reflect variable widths || VN_IS(nodep, NodeSel) // Array selects should reflect variable widths
|| VN_IS(nodep, SFormatArg) // Retain the logical argument type, not storage width
|| VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays || VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays
|| VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
@@ -91,6 +93,8 @@ class CleanVisitor final : public VNVisitor {
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType) || VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|| VN_IS(nodep->dtypep()->skipRefp(), StreamDType) || VN_IS(nodep->dtypep()->skipRefp(), StreamDType)
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), CoverCrossDType)
|| VN_IS(nodep->dtypep()->skipRefp(), CoverpointDType)
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) { || VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
} else { } else {
const AstNodeUOrStructDType* const dtypep const AstNodeUOrStructDType* const dtypep
@@ -138,12 +142,12 @@ class CleanVisitor final : public VNVisitor {
computeCppWidth(nodep); computeCppWidth(nodep);
if (!isClean(nodep)) insertClean(nodep); if (!isClean(nodep)) insertClean(nodep);
} }
void ensureCleanAndNext(AstNodeExpr* nodep) { void ensureCleanAndNext(AstNode* nodep) {
// Editing list, careful looping! // Editing list, careful looping!
for (AstNodeExpr* exprp = nodep; exprp;) { for (AstNode* argp = nodep; argp;) {
AstNodeExpr* const nextp = VN_AS(exprp->nextp(), NodeExpr); AstNode* const nextp = argp->nextp();
ensureClean(exprp); if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
exprp = nextp; argp = nextp;
} }
} }
@@ -247,9 +251,7 @@ class CleanVisitor final : public VNVisitor {
setClean(nodep, false); setClean(nodep, false);
// We always clean, as we don't trust those pesky users. // We always clean, as we don't trust those pesky users.
if (!VN_IS(nodep->backp(), And)) insertClean(nodep); if (!VN_IS(nodep->backp(), And)) insertClean(nodep);
for (AstNode* argp = nodep->nodesp(); argp; argp = argp->nextp()) { ensureCleanAndNext(nodep->nodesp());
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
}
} }
void visit(AstTraceDecl* nodep) override {} // Nothing to do here void visit(AstTraceDecl* nodep) override {} // Nothing to do here
void visit(AstTraceInc* nodep) override { void visit(AstTraceInc* nodep) override {
@@ -280,16 +282,23 @@ class CleanVisitor final : public VNVisitor {
iterateChildren(nodep); iterateChildren(nodep);
ensureClean(nodep->condp()); ensureClean(nodep->condp());
} }
void visit(AstSFormatArg* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->exprp());
setClean(nodep, true);
}
void visit(AstSFormatF* nodep) override { void visit(AstSFormatF* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
ensureCleanAndNext(nodep->exprsp()); ensureCleanAndNext(nodep->exprsp());
setClean(nodep, true); // generates a string, so not relevant setClean(nodep, true); // generates a string, so not relevant
} }
void visit(AstCStmt* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->nodesp());
}
void visit(AstCStmtUser* nodep) override { void visit(AstCStmtUser* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
for (AstNode* argp = nodep->nodesp(); argp; argp = argp->nextp()) { ensureCleanAndNext(nodep->nodesp());
if (AstNodeExpr* const exprp = VN_CAST(argp, NodeExpr)) ensureClean(exprp);
}
} }
void visit(AstNodeCCall* nodep) override { void visit(AstNodeCCall* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
+1 -1
View File
@@ -97,7 +97,7 @@ class ClockVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->deleteTree(), nodep); VL_DO_DANGLING(nodep->deleteTree(), nodep);
} }
void visit(AstSenTree* nodep) override { void visit(AstSenTree* nodep) override {
pushDeletep(nodep->unlinkFrBack()); // No longer needed VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); // No longer needed
} }
//========== Move sampled assignments //========== Move sampled assignments
+2 -2
View File
@@ -44,8 +44,8 @@ class CombineVisitor final : VNVisitor {
// TYPES // TYPES
using funcit_t = std::list<AstCFunc*>::iterator; using funcit_t = std::list<AstCFunc*>::iterator;
struct CFuncs final { struct CFuncs final {
std::list<AstCFunc*> m_fast; std::list<AstCFunc*> m_fast; // Functions that are not slow
std::list<AstCFunc*> m_slow; std::list<AstCFunc*> m_slow; // Functions marked for slow files
}; };
// STATE // STATE
-1
View File
@@ -318,7 +318,6 @@ class ToStringVisitor final : public VNVisitorConst {
} }
} }
} }
void visit(AstConstPool*) override {} // Accelerate
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
public: public:
+27 -23
View File
@@ -110,12 +110,12 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
class LeafInfo final { // Leaf node (either AstConst or AstVarRef) class LeafInfo final { // Leaf node (either AstConst or AstVarRef)
// MEMBERS // MEMBERS
bool m_polarity = true; bool m_polarity = true; // Invert result due to NOT
int m_lsb = 0; // LSB of actually used bit of m_refp->varp() int m_lsb = 0; // LSB of actually used bit of m_refp->varp()
int m_msb = 0; // MSB of actually used bit of m_refp->varp() int m_msb = 0; // MSB of actually used bit of m_refp->varp()
int m_wordIdx = -1; // -1 means AstWordSel is not used. int m_wordIdx = -1; // -1 means AstWordSel is not used.
AstVarRef* m_refp = nullptr; AstVarRef* m_refp = nullptr; // Leaf's variable reference
const AstConst* m_constp = nullptr; const AstConst* m_constp = nullptr; // Leaf's constant
public: public:
// CONSTRUCTORS // CONSTRUCTORS
@@ -187,9 +187,9 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
}; };
struct BitPolarityEntry final { // Found bit polarity during iterate() struct BitPolarityEntry final { // Found bit polarity during iterate()
LeafInfo m_info; LeafInfo m_info; // Leaf (variable or constant) bit polarity was found on
bool m_polarity = false; bool m_polarity = false; // Polarity the bit must have to match
int m_bit = 0; int m_bit = 0; // Bit index within the leaf that was tested
BitPolarityEntry(const LeafInfo& info, bool pol, int bit) BitPolarityEntry(const LeafInfo& info, bool pol, int bit)
: m_info{info} : m_info{info}
, m_polarity{pol} , m_polarity{pol}
@@ -198,8 +198,8 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
}; };
struct FrozenNodeInfo final { // Context when a frozen node is found struct FrozenNodeInfo final { // Context when a frozen node is found
bool m_polarity; bool m_polarity; // Polarity the frozen node must match
int m_lsb; int m_lsb; // LSB position of the frozen node
bool operator<(const FrozenNodeInfo& other) const { bool operator<(const FrozenNodeInfo& other) const {
if (m_lsb != other.m_lsb) return m_lsb < other.m_lsb; if (m_lsb != other.m_lsb) return m_lsb < other.m_lsb;
return m_polarity < other.m_polarity; return m_polarity < other.m_polarity;
@@ -207,12 +207,12 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
}; };
class Restorer final { // Restore the original state unless disableRestore() is called class Restorer final { // Restore the original state unless disableRestore() is called
ConstBitOpTreeVisitor& m_visitor; ConstBitOpTreeVisitor& m_visitor; // Visitor whose state is saved and restored
const size_t m_polaritiesSize; const size_t m_polaritiesSize; // Saved m_visitor.m_bitPolarities size to truncate back to
const size_t m_frozenSize; const size_t m_frozenSize; // Saved m_visitor.m_frozenNodes size to truncate back to
const unsigned m_ops; const unsigned m_ops; // Saved m_visitor.m_ops to restore
const bool m_polarity; const bool m_polarity; // Saved m_visitor.m_polarity to restore
bool m_restore = true; bool m_restore = true; // Whether the destructor still needs to restore state
public: public:
explicit Restorer(ConstBitOpTreeVisitor& visitor) explicit Restorer(ConstBitOpTreeVisitor& visitor)
@@ -1015,14 +1015,10 @@ class ConstVisitor final : public VNVisitor {
const bool packReal) { const bool packReal) {
const AstNodeDType* const dtypep = fromp->dtypep()->skipRefp(); const AstNodeDType* const dtypep = fromp->dtypep()->skipRefp();
if (const AstUnpackArrayDType* const unpackDtypep = VN_CAST(dtypep, UnpackArrayDType)) { if (const AstUnpackArrayDType* const unpackDtypep = VN_CAST(dtypep, UnpackArrayDType)) {
const int left = unpackDtypep->left(); for (const int idx : unpackDtypep->declRange().seqLeftToRight()) {
const int right = unpackDtypep->right();
const int step = left <= right ? 1 : -1;
for (int idx = left;; idx += step) {
AstArraySel* const selp AstArraySel* const selp
= new AstArraySel{fromp->fileline(), fromp->cloneTreePure(false), idx}; = new AstArraySel{fromp->fileline(), fromp->cloneTreePure(false), idx};
collectFixedAggregateTerms(selp, termps, packReal); collectFixedAggregateTerms(selp, termps, packReal);
if (idx == right) break;
} }
VL_DO_DANGLING(pushDeletep(fromp), fromp); VL_DO_DANGLING(pushDeletep(fromp), fromp);
} else if (const AstNodeUOrStructDType* const sdtypep } else if (const AstNodeUOrStructDType* const sdtypep
@@ -1463,11 +1459,12 @@ class ConstVisitor final : public VNVisitor {
} }
if (const AstShiftR* const shiftp = VN_CAST(rhsp, ShiftR)) { if (const AstShiftR* const shiftp = VN_CAST(rhsp, ShiftR)) {
// 'a >> S' forces the high S bits to zero. Check against the width of the shifted // 'a >> S' forces the high S bits to zero. Check against the width of the shifted
// operand, V3Expand can create shifts wider than their inputs // operand, V3Expand can create shifts wider than their inputs. Must use width(),
// not widthMin(), as bits above widthMin() are not guaranteed to be zero.
if (AstConst* const scp = VN_CAST(shiftp->rhsp(), Const)) { if (AstConst* const scp = VN_CAST(shiftp->rhsp(), Const)) {
return scp->num().fitsInUInt() return scp->num().fitsInUInt()
&& (lsb + scp->num().toUInt() && (lsb + scp->num().toUInt()
>= static_cast<uint32_t>(shiftp->lhsp()->widthMin())); >= static_cast<uint32_t>(shiftp->lhsp()->width()));
} }
} }
if (const AstMul* const mulp = VN_CAST(rhsp, Mul)) { if (const AstMul* const mulp = VN_CAST(rhsp, Mul)) {
@@ -3969,7 +3966,12 @@ class ConstVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return true; return true;
} }
void visit(AstSFormatArg* nodep) override { iterateChildren(nodep); } void visit(AstSFormatArg* nodep) override {
// Skip namep(): requiring its runtime lookup to be constant can reject valid
// enum-valued constant-function calls in parameters. displayedEnum() resolves
// the name from the folded exprp() value and enum dtype instead.
iterateAndNextNull(nodep->exprp());
}
void visit(AstSFormatF* nodep) override { void visit(AstSFormatF* nodep) override {
// Substitute constants into displays. The main point of this is to // Substitute constants into displays. The main point of this is to
// simplify assertion methodologies which call functions with display's. // simplify assertion methodologies which call functions with display's.
@@ -4033,7 +4035,9 @@ class ConstVisitor final : public VNVisitor {
: VFormatAttr{}; : VFormatAttr{};
if (VN_IS(subargp, Const)) { // Convert it if (VN_IS(subargp, Const)) { // Convert it
const string out const string out
= constNumV(subargp).displayed(nodep, fmt, formatAttr); = formatAttr.isEnum()
? constNumV(subargp).displayedEnum(fargp, fmt)
: constNumV(subargp).displayed(nodep, fmt, formatAttr);
UINFO(9, " DispConst: " << fmt << " -> " << out << " for " UINFO(9, " DispConst: " << fmt << " -> " << out << " for "
<< subargp); << subargp);
// fmt = out w/ replace % with %% as it must later when // fmt = out w/ replace % with %% as it must later when
+219
View File
@@ -0,0 +1,219 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Constant pool
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of either the GNU Lesser General Public License Version 3
// or the Perl Artistic License Version 2.0.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#include "V3PchAstMT.h"
#include "V3ConstPool.h"
//######################################################################
// V3ConstPool
bool V3ConstPool::equalMap(const AstInitArray* ap, const AstInitArray* bp) {
// Associative array initializers must have equivalent types, defaults, and entries.
// As in 'equalTable', compare by value, rather than by tree structure.
const AstAssocArrayDType* const aDTypep = VN_AS(ap->dtypep(), AssocArrayDType);
const AstAssocArrayDType* const bDTypep = VN_AS(bp->dtypep(), AssocArrayDType);
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) return false;
if (!aDTypep->keyDTypep()->sameTree(bDTypep->keyDTypep())) return false;
// The default is optional, but must be the same if present
const AstNode* const aDefaultp = ap->defaultp();
const AstNode* const bDefaultp = bp->defaultp();
UASSERT_OBJ(!aDefaultp || VN_IS(aDefaultp, Const), ap, "Const pool map default not Const");
UASSERT_OBJ(!bDefaultp || VN_IS(bDefaultp, Const), bp, "Const pool map default not Const");
if (!aDefaultp != !bDefaultp) return false;
if (aDefaultp && !aDefaultp->sameTree(bDefaultp)) return false;
// Compare the entries, which are ordered by key. Note an entry explicitly set to the
// default value makes maps compare different, which is safe, but misses merging them.
const AstInitArray::KeyItemMap& aMap = ap->map();
const AstInitArray::KeyItemMap& bMap = bp->map();
if (aMap.size() != bMap.size()) return false;
auto bIt = bMap.cbegin();
for (const auto& aItem : aMap) {
const AstNode* const aValuep = aItem.second->valuep();
const AstNode* const bValuep = bIt->second->valuep();
UASSERT_OBJ(VN_IS(aValuep, Const), aValuep, "Const pool map item not Const");
UASSERT_OBJ(VN_IS(bValuep, Const), bValuep, "Const pool map item not Const");
if (aItem.first != bIt->first) return false;
if (!aValuep->sameTree(bValuep)) return false;
++bIt;
}
return true;
}
bool V3ConstPool::equalTable(const AstInitArray* ap, const AstInitArray* bp) {
// Unpacked array initializers must have equivalent values
// Note, sadly we can't just call ap->sameTree(pb), because both:
// - the dtypes might be different instances
// - the default/inititem children might be in different order yet still yield the same table
// See note in AstInitArray::same about the same. This function instead compares by initializer
// value, rather than by tree structure.
const AstUnpackArrayDType* const aDTypep = VN_AS(ap->dtypep(), UnpackArrayDType);
const AstUnpackArrayDType* const bDTypep = VN_AS(bp->dtypep(), UnpackArrayDType);
if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) return false;
// Compare the range by value, as the bounds might be differently typed constants
if (!(aDTypep->declRange() == bDTypep->declRange())) return false;
// Compare initializer arrays by value. Note this is only called when they hash the same.
const uint64_t size = aDTypep->elementsConst();
for (uint64_t n = 0; n < size; ++n) {
const AstNode* const valAp = ap->getIndexDefaultedValuep(n);
const AstNode* const valBp = bp->getIndexDefaultedValuep(n);
UASSERT_OBJ(VN_IS(valAp, Const), valAp, "Const pool table item not Const");
UASSERT_OBJ(VN_IS(valBp, Const), valBp, "Const pool table item not Const");
if (!valAp->sameTree(valBp)) return false;
}
return true;
}
size_t V3ConstPool::hashTable(const AstInitArray* initp) {
// Hash what 'equalTable' compares: the element type, the range by value, and the values
const AstUnpackArrayDType* const dtypep = VN_AS(initp->dtypep(), UnpackArrayDType);
V3Hash hash = V3Hasher::uncachedHash(dtypep->subDTypep());
hash += dtypep->left();
hash += dtypep->right();
const uint64_t size = dtypep->elementsConst();
for (uint64_t n = 0; n < size; ++n) {
hash += V3Hasher::uncachedHash(initp->getIndexDefaultedValuep(n));
}
return hash.value();
}
template <typename T_Set, typename T_Init>
AstVarRef* V3ConstPool::findOrCreate(T_Set& set, uint32_t& nextr, const char* prefixp,
T_Init* initp) {
// Rebuild the cache if invalidated
if (VL_UNLIKELY(!m_cacheValid)) {
m_cacheValid = true;
m_scopep = nullptr;
m_consts.clear();
m_maps.clear();
m_tables.clear();
m_varScopes.clear();
AstPackage* const pkgp = v3Global.rootp()->constPoolPkgp();
for (AstNode* nodep = pkgp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (AstScope* const scopep = VN_CAST(nodep, Scope)) {
if (m_stage == Stage::SCOPED) m_scopep = scopep;
continue;
}
// Otherwise must be a variable
AstVar* const varp = VN_AS(nodep, Var);
UASSERT_OBJ(varp->constPoolEntry(), varp, "Unmarked variable in constant pool");
if (VN_IS(varp->valuep(), Const)) {
m_consts.insert(varp);
} else if (const AstInitArray* const arrayp = VN_CAST(varp->valuep(), InitArray)) {
if (VN_IS(arrayp->dtypep(), AssocArrayDType)) {
m_maps.insert(varp);
} else {
m_tables.insert(varp);
}
}
}
if (m_scopep) {
for (AstVarScope* vscp = m_scopep->varsp(); vscp;
vscp = VN_AS(vscp->nextp(), VarScope)) {
m_varScopes.insert({vscp->varp(), vscp});
}
}
}
FileLine* const flp = initp->fileline();
AstNodeDType* const dtypep = initp->dtypep();
// Look up/create the variable with this value
const auto pair = set.insertLazy(initp, [&]() {
const std::string name = prefixp + std::to_string(nextr++);
AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, dtypep};
varp->setConstPoolEntry();
varp->lifetime(VLifetime::STATIC_EXPLICIT);
varp->isConst(true);
varp->valuep(initp->cloneTree(false));
v3Global.rootp()->constPoolPkgp()->addStmtsp(varp);
return varp;
});
AstVar* const varp = *pair.first;
// Create a read reference to the entry, of the right form for the current stage
AstVarRef* refp = nullptr;
switch (m_stage) {
case Stage::UNSCOPED: {
// Before V3Scope, reference via the package, as any other package variable
AstPackage* const pkgp = v3Global.rootp()->constPoolPkgp();
refp = new AstVarRef{flp, pkgp, varp, VAccess::READ};
break;
}
case Stage::SCOPED: {
// While scoped, reference the VarScope, creating it for a new entry, or if removed
const auto vscpPair = m_varScopes.insertLazy(varp, [&]() {
AstVarScope* const vscp = new AstVarScope{varp->fileline(), m_scopep, varp};
m_scopep->addVarsp(vscp);
return std::pair<const AstVar*, AstVarScope*>{varp, vscp};
});
refp = new AstVarRef{flp, vscpPair.first->second, VAccess::READ};
break;
}
case Stage::DESCOPED: {
// After V3Descope, no VarScope is needed
refp = new AstVarRef{flp, varp, VAccess::READ};
break;
}
}
// The entry might have been created with a different, but compatible dtype
refp->dtypep(dtypep);
return refp;
}
AstVarRef* V3ConstPool::findConst(AstConst* initp) {
V3ConstPool& self = instance();
return self.findOrCreate(self.m_consts, self.m_nextConst, "CONST_", initp);
}
AstVarRef* V3ConstPool::findMap(AstInitArray* initp) {
UASSERT_OBJ(VN_IS(initp->dtypep(), AssocArrayDType), initp,
"Const pool map must have associative array dtype");
V3ConstPool& self = instance();
return self.findOrCreate(self.m_maps, self.m_nextMap, "MAP_", initp);
}
AstVarRef* V3ConstPool::findTable(AstInitArray* initp) {
UASSERT_OBJ(VN_IS(initp->dtypep(), UnpackArrayDType), initp,
"Const pool table must have unpacked array dtype");
V3ConstPool& self = instance();
return self.findOrCreate(self.m_tables, self.m_nextTable, "TABLE_", initp);
}
AstVarRef* V3ConstPool::find(AstNodeExpr* initp) {
if (AstConst* const constp = VN_CAST(initp, Const)) return findConst(constp);
if (AstInitArray* const arrayp = VN_CAST(initp, InitArray)) {
if (VN_IS(arrayp->dtypep(), AssocArrayDType)) return findMap(arrayp);
if (VN_IS(arrayp->dtypep(), UnpackArrayDType)) return findTable(arrayp);
}
initp->v3fatalSrc("Unhandled constant pool initializer");
return nullptr; // LCOV_EXCL_LINE
}
const char* V3ConstPool::broken() {
const V3ConstPool& self = instance();
if (!self.m_cacheValid) return nullptr;
BROKEN_RTN(self.m_scopep && !self.m_scopep->brokeExists());
for (const AstVar* const varp : self.m_consts) BROKEN_RTN(!varp->brokeExists());
for (const AstVar* const varp : self.m_maps) BROKEN_RTN(!varp->brokeExists());
for (const AstVar* const varp : self.m_tables) BROKEN_RTN(!varp->brokeExists());
for (const auto& pair : self.m_varScopes) {
BROKEN_RTN(!pair.first->brokeExists());
BROKEN_RTN(!pair.second->brokeExists());
}
return nullptr;
}
+163
View File
@@ -0,0 +1,163 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Constant pool
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of either the GNU Lesser General Public License Version 3
// or the Perl Artistic License Version 2.0.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// V3ConstPool finds or creates entries in the constant pool package
// (AstNetlist::constPoolPkgp()), which holds read-only static data, i.e.:
// constants, maps and tables, shared across the whole design. Entries are
// ordinary package variables, deduplicated by value.
//
//*************************************************************************
#ifndef VERILATOR_V3CONSTPOOL_H_
#define VERILATOR_V3CONSTPOOL_H_
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3HashTable.h"
#include "V3Hasher.h"
//============================================================================
class V3ConstPool final {
friend class V3Global; // Owns the instance, needs constructor access
// TYPES
// Scoping stage of the design, which determines how entries are referenced
enum class Stage : uint8_t {
UNSCOPED, // Before V3Scope: reference via the package
SCOPED, // Between V3Scope and V3Descope: reference via the VarScope
DESCOPED // After V3Descope: plain reference
};
// Hash and equality of constant entries in 'm_consts'
struct ConstHash final {
size_t operator()(const AstVar* varp) const {
return (*this)(VN_AS(varp->valuep(), Const));
}
size_t operator()(const AstConst* initp) const { return initp->num().toHash().value(); }
};
struct ConstEqual final {
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
return (*this)(ap, VN_AS(bp->valuep(), Const)); // Only on collision while rebuild
} // LCOV_EXCL_STOP
bool operator()(const AstVar* varp, const AstConst* initp) const {
// Compare by value, which also checks the width. The dtype is ignored.
return VN_AS(varp->valuep(), Const)->num().isCaseEq(initp->num());
}
};
// Hash and equality of associative array entries in 'm_maps'
struct MapHash final {
size_t operator()(const AstVar* varp) const {
return (*this)(VN_AS(varp->valuep(), InitArray));
}
size_t operator()(const AstInitArray* initp) const {
return V3Hasher::uncachedHash(initp).value();
}
};
struct MapEqual final {
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
return (*this)(ap, VN_AS(bp->valuep(), InitArray)); // Only on collision while rebuild
} // LCOV_EXCL_STOP
bool operator()(const AstVar* varp, const AstInitArray* initp) const {
return equalMap(VN_AS(varp->valuep(), InitArray), initp);
}
};
// Hash and equality of unpacked array entries in 'm_tables'
struct TableHash final {
size_t operator()(const AstVar* varp) const {
return (*this)(VN_AS(varp->valuep(), InitArray));
}
size_t operator()(const AstInitArray* initp) const { //
return hashTable(initp);
}
};
struct TableEqual final {
bool operator()(const AstVar* ap, const AstVar* bp) const { // LCOV_EXCL_START
return (*this)(ap, VN_AS(bp->valuep(), InitArray)); // Only on collision while rebuild
} // LCOV_EXCL_STOP
bool operator()(const AstVar* varp, const AstInitArray* initp) const {
return equalTable(VN_AS(varp->valuep(), InitArray), initp);
}
};
// MEMBERS
Stage m_stage = Stage::UNSCOPED; // Scoping stage of the design, see setScoped()/setDescoped()
bool m_cacheValid = false; // Cache below is up to date
AstScope* m_scopep = nullptr; // Scope of the constant pool, between V3Scope and V3Descope
V3HashSet<AstVar*, ConstHash, ConstEqual> m_consts; // Packed constants
V3HashSet<AstVar*, MapHash, MapEqual> m_maps; // Associative array constants (maps)
V3HashSet<AstVar*, TableHash, TableEqual> m_tables; // Unpacked array constants (tables)
V3HashMap<const AstVar*, AstVarScope*> m_varScopes; // VarScope of each entry iff m_scopep
uint32_t m_nextConst = 0; // Sequence number for naming
uint32_t m_nextMap = 0; // Sequence number for naming
uint32_t m_nextTable = 0; // Sequence number for naming
// METHODS
static V3ConstPool& instance() { return *v3Global.constPoolp(); }
static bool equalMap(const AstInitArray* ap, const AstInitArray* bp);
static bool equalTable(const AstInitArray* ap, const AstInitArray* bp);
static size_t hashTable(const AstInitArray* initp);
// Find an entry equal to the given initializer in the given set, or create one named from
// the given prefix and sequence number, and return a read reference to it
template <typename T_Set, typename T_Init>
AstVarRef* findOrCreate(T_Set& set, uint32_t& nextr, const char* prefixp, T_Init* initp);
// CONSTRUCTORS - only V3Global creates and deletes the instance
V3ConstPool() = default;
~V3ConstPool() = default;
VL_UNCOPYABLE(V3ConstPool);
VL_UNMOVABLE(V3ConstPool);
public:
// STATIC METHODS
// Find a constant packed variable with the given value, or create one if one does not
// already exist, and return a read reference to it.
// The reference has the same dtype as initp.
// The referenced variable *might* have a different, but compatible dtype.
static AstVarRef* findConst(AstConst* initp);
// Find a constant associative array (map) with the given value, or create one if one does
// not already exist, and return a read reference to it.
// The reference has the same dtype as initp.
// The referenced variable *might* have a different, but compatible dtype.
static AstVarRef* findMap(AstInitArray* initp);
// Find a constant unpacked array (table) with the given value, or create one if one does
// not already exist, and return a read reference to it.
// The reference has the same dtype as initp.
// The referenced variable *might* have a different, but compatible dtype.
static AstVarRef* findTable(AstInitArray* initp);
// Dispatch to the above based on the kind of initializer.
static AstVarRef* find(AstNodeExpr* initp);
// Invalidate the lookup cache. This must be called after deleting any Vars from the pool.
static void invalidateCache() { instance().m_cacheValid = false; }
// Notify that the design has been scoped, called by V3Scope
static void setScoped() {
V3ConstPool& self = instance();
UASSERT(self.m_stage == Stage::UNSCOPED, "Constant pool scoped twice");
self.m_stage = Stage::SCOPED;
invalidateCache();
}
// Notify that the design has been descoped, called by V3Descope
static void setDescoped() {
V3ConstPool& self = instance();
UASSERT(self.m_stage == Stage::SCOPED, "Constant pool descoped when not scoped");
self.m_stage = Stage::DESCOPED;
invalidateCache();
}
// Check the lookup cache refers only to existing nodes, for V3Broken
static const char* broken();
};
#endif // Guard
+10
View File
@@ -41,6 +41,15 @@ public:
if (itFoundPair.second) m_keys.push_back(key); if (itFoundPair.second) m_keys.push_back(key);
return itFoundPair.second; return itFoundPair.second;
} }
template <typename T>
void insert(T begin, const T end) {
static_assert(std::is_same<typename std::iterator_traits<T>::value_type, T_Key>::value,
"T must be an iterator with value type T_Key");
while (begin != end) {
if (m_keySet.insert(*begin).second) m_keys.push_back(*begin);
++begin;
}
}
void clear() { void clear() {
m_keys.clear(); m_keys.clear();
m_keySet.clear(); m_keySet.clear();
@@ -48,6 +57,7 @@ public:
// ACCESSORS // ACCESSORS
bool empty() const { return m_keys.empty(); } bool empty() const { return m_keys.empty(); }
size_t size() const { return m_keys.size(); }
bool exists(const T_Key& key) const { return m_keySet.find(key) != m_keySet.end(); } bool exists(const T_Key& key) const { return m_keySet.find(key) != m_keySet.end(); }
// ITERATORS // ITERATORS
+36 -36
View File
@@ -491,41 +491,41 @@ public:
IgnIndices results; IgnIndices results;
int nextChange = 0; int nextChange = 0;
tree.find(0, results, nextChange); tree.find(0, results, nextChange);
UASSERT_SELFTEST(const size_t, results.size(), 0); UASSERT_SELFTEST(results.size(), 0);
UASSERT_SELFTEST(const int, nextChange, 10); UASSERT_SELFTEST(nextChange, 10);
tree.find(10, results, nextChange); tree.find(10, results, nextChange);
UASSERT_SELFTEST(const size_t, results.size(), 2); UASSERT_SELFTEST(results.size(), 2);
UASSERT_SELFTEST(const int, results[0], 0); UASSERT_SELFTEST(results[0], 0);
UASSERT_SELFTEST(const int, results[1], 3); UASSERT_SELFTEST(results[1], 3);
UASSERT_SELFTEST(const int, nextChange, 11); UASSERT_SELFTEST(nextChange, 11);
tree.find(11, results, nextChange); tree.find(11, results, nextChange);
UASSERT_SELFTEST(const size_t, results.size(), 1); UASSERT_SELFTEST(results.size(), 1);
UASSERT_SELFTEST(const int, results[0], 3); UASSERT_SELFTEST(results[0], 3);
UASSERT_SELFTEST(const int, nextChange, 15); // Center, or would be 20 UASSERT_SELFTEST(nextChange, 15); // Center, or would be 20
tree.find(20, results, nextChange); tree.find(20, results, nextChange);
UASSERT_SELFTEST(const size_t, results.size(), 3); UASSERT_SELFTEST(results.size(), 3);
UASSERT_SELFTEST(const int, results[0], 1); UASSERT_SELFTEST(results[0], 1);
UASSERT_SELFTEST(const int, results[1], 3); UASSERT_SELFTEST(results[1], 3);
UASSERT_SELFTEST(const int, results[2], 4); UASSERT_SELFTEST(results[2], 4);
UASSERT_SELFTEST(const int, nextChange, 21); UASSERT_SELFTEST(nextChange, 21);
tree.find(21, results, nextChange); tree.find(21, results, nextChange);
UASSERT_SELFTEST(const size_t, results.size(), 2); UASSERT_SELFTEST(results.size(), 2);
UASSERT_SELFTEST(const int, results[0], 3); UASSERT_SELFTEST(results[0], 3);
UASSERT_SELFTEST(const int, results[1], 4); UASSERT_SELFTEST(results[1], 4);
UASSERT_SELFTEST(const int, nextChange, 25); // Center, or would be 30 UASSERT_SELFTEST(nextChange, 25); // Center, or would be 30
tree.find(30, results, nextChange); tree.find(30, results, nextChange);
UASSERT_SELFTEST(const size_t, results.size(), 2); UASSERT_SELFTEST(results.size(), 2);
UASSERT_SELFTEST(const int, results[0], 3); UASSERT_SELFTEST(results[0], 3);
UASSERT_SELFTEST(const int, results[1], 4); UASSERT_SELFTEST(results[1], 4);
UASSERT_SELFTEST(const int, nextChange, 31); UASSERT_SELFTEST(nextChange, 31);
tree.find(40, results, nextChange); tree.find(40, results, nextChange);
UASSERT_SELFTEST(const size_t, results.size(), 2); UASSERT_SELFTEST(results.size(), 2);
UASSERT_SELFTEST(const int, results[0], 2); UASSERT_SELFTEST(results[0], 2);
UASSERT_SELFTEST(const int, results[1], 4); UASSERT_SELFTEST(results[1], 4);
UASSERT_SELFTEST(const int, nextChange, 41); UASSERT_SELFTEST(nextChange, 41);
tree.find(41, results, nextChange); tree.find(41, results, nextChange);
UASSERT_SELFTEST(const size_t, results.size(), 0); UASSERT_SELFTEST(results.size(), 0);
UASSERT_SELFTEST(const int, nextChange, std::numeric_limits<int>::max()); UASSERT_SELFTEST(nextChange, std::numeric_limits<int>::max());
// //
points = {{0, 0}}; points = {{0, 0}};
for (const auto& it : points) { for (const auto& it : points) {
@@ -536,8 +536,8 @@ public:
tree.build(data); tree.build(data);
// //
tree.find(50, results, nextChange); tree.find(50, results, nextChange);
UASSERT_SELFTEST(const size_t, results.size(), 1); UASSERT_SELFTEST(results.size(), 1);
UASSERT_SELFTEST(const int, results[0], 5); UASSERT_SELFTEST(results[0], 5);
} }
}; };
@@ -709,8 +709,8 @@ public:
// Tracks what matches are known to hit against V3ControlScopeTraceEntries // Tracks what matches are known to hit against V3ControlScopeTraceEntries
class V3ControlScopeTraceEntryMatch final { class V3ControlScopeTraceEntryMatch final {
public: public:
const V3ControlScopeTraceEntry* m_entryp; const V3ControlScopeTraceEntry* m_entryp; // Entry the match refers to
const string m_scopepart; const string m_scopepart; // Scope prefix being matched against the entry
V3ControlScopeTraceEntryMatch(const V3ControlScopeTraceEntry* entryp, const string& scopepart) V3ControlScopeTraceEntryMatch(const V3ControlScopeTraceEntry* entryp, const string& scopepart)
: m_entryp{entryp} : m_entryp{entryp}
, m_scopepart{scopepart} {} , m_scopepart{scopepart} {}
@@ -780,8 +780,8 @@ public:
// Resolve modules and files in the design // Resolve modules and files in the design
class V3ControlResolverHierWorkerEntry final { class V3ControlResolverHierWorkerEntry final {
const int m_workers; const int m_workers; // Number of workers requested for the hierarchical block
FileLine* const m_flp; FileLine* const m_flp; // Location the worker count was specified at
public: public:
explicit V3ControlResolverHierWorkerEntry(int workers, FileLine* flp) explicit V3ControlResolverHierWorkerEntry(int workers, FileLine* flp)
@@ -798,9 +798,9 @@ class V3ControlResolver final {
V3ControlScopeTraceResolver m_scopeTraces; // Regexp to trace enables V3ControlScopeTraceResolver m_scopeTraces; // Regexp to trace enables
std::unordered_map<string, std::unordered_map<string, uint64_t>> std::unordered_map<string, std::unordered_map<string, uint64_t>>
m_profileData; // Access to profile_data records m_profileData; // Access to profile_data records
uint8_t m_mode = NONE; uint8_t m_mode = NONE; // Kind of profile_data records is currently active
std::unordered_map<string, V3ControlResolverHierWorkerEntry> m_hierWorkers; std::unordered_map<string, V3ControlResolverHierWorkerEntry> m_hierWorkers;
FileLine* m_profileFileLine = nullptr; FileLine* m_profileFileLine = nullptr; // Location profile_data was read from
V3ControlResolver() = default; V3ControlResolver() = default;
~V3ControlResolver() = default; ~V3ControlResolver() = default;
+15 -4
View File
@@ -199,6 +199,7 @@ class CoverageVisitor final : public VNVisitor {
AstVar* const varp = new AstVar{fl_nowarn, VVarType::MODULETEMP, trace_var_name, AstVar* const varp = new AstVar{fl_nowarn, VVarType::MODULETEMP, trace_var_name,
incp->findUInt32DType()}; incp->findUInt32DType()};
varp->setIgnoreSchedWrite(); // Ignore the increment output, so no UNOPTFLAT varp->setIgnoreSchedWrite(); // Ignore the increment output, so no UNOPTFLAT
varp->lifetime(VLifetime::STATIC_IMPLICIT);
varp->trace(true); varp->trace(true);
m_modp->addStmtsp(varp); m_modp->addStmtsp(varp);
UINFO(5, "New coverage trace: " << varp); UINFO(5, "New coverage trace: " << varp);
@@ -283,7 +284,7 @@ class CoverageVisitor final : public VNVisitor {
VL_RESTORER_COPY(m_funcTemps); VL_RESTORER_COPY(m_funcTemps);
createHandle(nodep); createHandle(nodep);
m_modp = nodep; m_modp = nodep;
m_state.m_inModOff = false; // Haven't made top shell, so tops are real tops m_state.m_inModOff = nodep->isTop(); // Already made top shell, no coverage for it
if (!origModp) { if (!origModp) {
// No blocks cross (non-nested) modules, so save some memory // No blocks cross (non-nested) modules, so save some memory
m_varnames.clear(); m_varnames.clear();
@@ -460,7 +461,7 @@ class CoverageVisitor final : public VNVisitor {
if (const AstBasicDType* const basicp = VN_CAST(dtypep, BasicDType)) { if (const AstBasicDType* const basicp = VN_CAST(dtypep, BasicDType)) {
toggleVarBottom(above, varp, basicp->nrange()); toggleVarBottom(above, varp, basicp->nrange());
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) { } else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) { for (const int index_docs : adtypep->declRange().seqLoToHi()) {
const int index_code = index_docs - adtypep->lo(); const int index_code = index_docs - adtypep->lo();
ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]", ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]",
new AstArraySel{varp->fileline(), new AstArraySel{varp->fileline(),
@@ -471,7 +472,7 @@ class CoverageVisitor final : public VNVisitor {
newent.cleanup(); newent.cleanup();
} }
} else if (const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType)) { } else if (const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType)) {
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) { for (const int index_docs : adtypep->declRange().seqLoToHi()) {
const AstNodeDType* const subtypep = adtypep->subDTypep()->skipRefp(); const AstNodeDType* const subtypep = adtypep->subDTypep()->skipRefp();
const int index_code = index_docs - adtypep->lo(); const int index_code = index_docs - adtypep->lo();
ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]", ToggleEnt newent{above.m_comment + "["s + cvtToStr(index_docs) + "]",
@@ -1053,7 +1054,7 @@ class CoverageVisitor final : public VNVisitor {
unrolledp = new T_Oper{fl, selp, unrolledp}; unrolledp = new T_Oper{fl, selp, unrolledp};
} }
iterate(unrolledp); iterate(unrolledp);
pushDeletep(unrolledp); VL_DO_DANGLING(pushDeletep(unrolledp), unrolledp);
} else { } else {
iterateChildren(nodep); iterateChildren(nodep);
lineTrack(nodep); lineTrack(nodep);
@@ -1114,6 +1115,16 @@ class CoverageVisitor final : public VNVisitor {
} }
// VISITORS - BOTH // VISITORS - BOTH
void visit(AstProperty* nodep) override {
VL_RESTORER(m_state);
m_state.m_on = false;
iterateChildren(nodep);
}
void visit(AstSequence* nodep) override {
VL_RESTORER(m_state);
m_state.m_on = false;
iterateChildren(nodep);
}
void visit(AstNode* nodep) override { void visit(AstNode* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
lineTrack(nodep); lineTrack(nodep);
+2907 -574
View File
File diff suppressed because it is too large Load Diff
+328 -72
View File
@@ -37,6 +37,8 @@
#include "V3Dead.h" #include "V3Dead.h"
#include "V3ConstPool.h"
#include "V3Graph.h"
#include "V3Stats.h" #include "V3Stats.h"
#include <queue> #include <queue>
@@ -44,6 +46,199 @@
VL_DEFINE_DEBUG_FUNCTIONS; VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Dead tracking graph
class DeadVertex final : public V3GraphVertex {
VL_RTTI_IMPL(DeadVertex, V3GraphVertex)
AstNode* const m_nodep; // Node that created vertex
uint64_t m_workPos = 0; // Position on DeadWorkList, 0 = not on list
bool m_removable = false; // Subject to dead removal
bool m_isVirtual = false; // Virtual node, indirection
public:
DeadVertex(V3Graph* graphp, AstNode* nodep)
: V3GraphVertex{graphp}
, m_nodep{nodep} {}
~DeadVertex() override = default;
string dotShape() const override {
return isVirtual() ? "diamond" : removable() ? "rectangle" : "ellipse";
}
AstNode* nodep() const VL_MT_STABLE { return m_nodep; }
string name() const override VL_MT_STABLE {
return (isVirtual() ? "[VIRT] "
: removable() ? "[R] "s
: "[NR] ")
+ nodep()->typeName() + ' ' + cvtToHex(nodep()) + ' ' + nodep()->name();
}
bool removable() const { return m_removable; }
void removable(bool flag) { m_removable = flag; }
bool isVirtual() const { return m_isVirtual; }
void isVirtual(bool flag) { m_isVirtual = flag; }
uint64_t workPos() const { return m_workPos; }
void workPos(uint64_t value) { m_workPos = value; }
};
// Work list which keeps ordering of elements (so stable), adds always to
// the end of the queue, and allows arbitrary removal
class DeadWorkList final {
// MEMBERS
static uint64_t s_sequence; // Sequence number, inc each push
std::map<uint64_t, DeadVertex*> m_works; // Work list by sequence
public:
// METHODS
bool empty() const { return m_works.empty(); }
void push(DeadVertex* vtxp) {
if (vtxp->workPos()) return; // Already on list
const uint64_t id = ++s_sequence;
vtxp->workPos(id);
m_works.emplace(id, vtxp);
UINFO(9, "Worklist.push " << vtxp);
}
void erase(DeadVertex* vtxp) {
const uint64_t id = vtxp->workPos();
if (!id) return;
UINFO(9, "Worklist.erase " << vtxp);
const auto it = m_works.find(id);
UASSERT_OBJ(it != m_works.end(), vtxp->nodep(),
"vertex thought to be on work list but not");
m_works.erase(it);
vtxp->workPos(0);
}
DeadVertex* getPopFront() {
UDEBUGONLY(UASSERT(!empty(), "Front invalid to call on empty list"););
const auto it = m_works.begin();
DeadVertex* const vtxp = it->second;
m_works.erase(it);
vtxp->workPos(0);
UINFO(9, "Worklist.getPopFront " << vtxp);
return vtxp;
}
};
uint64_t DeadWorkList::s_sequence = 0;
class DeadGraph final : public V3Graph {
// NODE STATE
// AstNodeFTask::user2p() -> DeadVertex* for this node
// See const VNUser2InUse m_inuser2; inside DeadVisitor
// MEMBERS
DeadWorkList m_funcs; // Functions eligble for deletion
// Each virtual vertex by the ftask name()
std::unordered_map<std::string, DeadVertex*> m_virtualVtxsp;
void newEdge(DeadVertex* fromp, DeadVertex* top) {
new V3GraphEdge{this, fromp, top, 1, false};
if (top->removable()) m_funcs.erase(top); // Now has an input edge
}
void pushWorkMaybe(DeadVertex* vtxp, bool allowSize1) {
if (vtxp->removable()) {
if (VN_IS(vtxp->nodep(), NodeFTask)
&& (vtxp->inEmpty() || (allowSize1 && vtxp->inSize1())))
m_funcs.push(vtxp);
}
}
public:
// METHODS
DeadGraph() = default;
~DeadGraph() override = default;
DeadVertex* findNewVertex(AstNode* nodep) {
DeadVertex* vtxp = nodep->user2u().to<DeadVertex*>();
if (!vtxp) {
vtxp = new DeadVertex{this, nodep};
nodep->user2p(vtxp);
pushWorkMaybe(vtxp, false);
}
UASSERT_OBJ(vtxp->nodep() == nodep, nodep, "Vertex points at different node");
return vtxp;
}
DeadVertex* findVirtualVertex(AstNode* nodep) {
const auto it = m_virtualVtxsp.find(nodep->name());
DeadVertex* vtxp;
if (it != m_virtualVtxsp.end()) {
vtxp = it->second;
} else {
vtxp = new DeadVertex{this, nodep};
vtxp->removable(true);
vtxp->isVirtual(true);
pushWorkMaybe(vtxp, false);
m_virtualVtxsp.emplace(nodep->name(), vtxp);
}
return vtxp;
}
void findNewRemovableVertex(AstNode* nodep, bool removable) {
DeadVertex* const vtxp = findNewVertex(nodep);
// Wasn't removable before (due to earlier insert), make removable now
if (removable && !vtxp->removable()) {
vtxp->removable(true);
pushWorkMaybe(vtxp, false);
}
}
void deleteNodeVertex(AstNode* nodep) {
if (DeadVertex* const vtxp = nodep->user2u().to<DeadVertex*>()) deleteVertex(vtxp);
}
void deleteVertex(DeadVertex* vtxp) {
UINFO(9, "Delete vertex " << vtxp->name());
// Mark all about-to-empty downstream vertices onto worklist
for (const V3GraphEdge& oedge : vtxp->outEdges()) {
DeadVertex* const toVtxp = static_cast<DeadVertex*>(oedge.top());
pushWorkMaybe(toVtxp, true); // size1 ok as about to delete below
}
// Node shouldn't be looking at user2p later as node being deleting, but in case
if (!vtxp->isVirtual()) vtxp->nodep()->user2p(nullptr);
if (vtxp->removable()) m_funcs.erase(vtxp);
VL_DO_DANGLING(vtxp->unlinkDelete(this), vtxp);
}
// This only tracks usage dependancy, not "containership",
// When all needs disappear the related node is eligble for deletion
void needs(AstNode* nodep, AstNode* parentp) {
if (parentp == nodep) return; // No need for tracking needs itself (recursion)
UINFO(9, "Edge node " << nodep << " -> " << parentp);
DeadVertex* const parentVtxp = findNewVertex(parentp);
DeadVertex* const nodeVtxp = findNewVertex(nodep);
UINFO(9, "Edge need " << parentVtxp << " -> " << nodeVtxp);
newEdge(parentVtxp, nodeVtxp);
}
void needsVirtual(AstNodeFTask* nodep, AstNode* parentp) {
// Virtual call can be to any function in the call hierarchy.
// For simplicity rather than tracking possible multiple base
// classes (due to 'implements' classes there can be more than
// one), we simply assume all virtual functions of the same name
// can call any other virtual function of the same name
// Track via an intermediate node.
// All calling parents' verticies -> Virtual Vertex -> all ftasks verticies
if (parentp == nodep) return; // No need for tracking needs itself (recursion)
DeadVertex* const parentVtxp = findNewVertex(parentp);
DeadVertex* const virtualVtxp = findVirtualVertex(nodep);
UINFO(9, "Edge needVirtual " << parentVtxp << " -> " << virtualVtxp);
newEdge(parentVtxp, virtualVtxp);
}
void funcVirtual(AstNodeFTask* nodep) {
// Virtual Vertex -> all ftasks verticies
DeadVertex* const virtualVtxp = findVirtualVertex(nodep);
DeadVertex* const nodeVtxp = findNewVertex(nodep);
UINFO(9, "Edge funcvirtual " << virtualVtxp << " -> " << nodeVtxp);
newEdge(virtualVtxp, nodeVtxp);
}
bool funcsEmpty() const { return m_funcs.empty(); }
AstNode* funcsGetPopFront() {
DeadVertex* const vtxp = m_funcs.getPopFront();
UASSERT_OBJ(vtxp->inEmpty(), vtxp->nodep(), "Non-empty node on work list");
UASSERT_OBJ(vtxp->removable(), vtxp->nodep(), "Non-removable node on work list");
if (vtxp->isVirtual()) {
// Emptied (no inbound edge) virtual wrapper; all
// destinations verticies are now unused too (e.g. all virtual
// functions of this name may be deleted)
UINFO(9, "Removing virtual " << vtxp);
deleteVertex(vtxp);
return nullptr; // Caller will search again
}
AstNode* const nodep = vtxp->nodep();
return nodep;
}
};
//###################################################################### //######################################################################
// Dead state, as a visitor of each AstNode // Dead state, as a visitor of each AstNode
@@ -52,10 +247,11 @@ class DeadVisitor final : public VNVisitor {
// Entire Netlist: // Entire Netlist:
// AstNodeModule::user1() -> int. Count of number of cells referencing this module. // AstNodeModule::user1() -> int. Count of number of cells referencing this module.
// AstVar::user1() -> int. Count of number of references // AstVar::user1() -> int. Count of number of references
// AstVar::user2() -> bool. Is a formal function argument
// AstVarScope::user1() -> int. Count of number of references // AstVarScope::user1() -> int. Count of number of references
// AstNodeDType::user1() -> int. Count of number of references // AstNodeDType::user1() -> int. Count of number of references
// AstNodeFTask::user1() -> int. Count of number of references (via AstNodeFTaskRefs)
const VNUser1InUse m_inuser1; const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2; // For usage information see DeadGraph
// TYPES // TYPES
using AssignMap = std::multimap<AstVarScope*, AstNodeAssign*>; using AssignMap = std::multimap<AstVarScope*, AstNodeAssign*>;
@@ -64,6 +260,9 @@ class DeadVisitor final : public VNVisitor {
const bool m_elimUserVars; // Allow removal of user's vars const bool m_elimUserVars; // Allow removal of user's vars
const bool m_elimDTypes; // Allow removal of DTypes const bool m_elimDTypes; // Allow removal of DTypes
const bool m_elimCells; // Allow removal of Cells const bool m_elimCells; // Allow removal of Cells
DeadGraph m_graph; // Tracking graph
// List of all encountered to avoid another loop through tree // List of all encountered to avoid another loop through tree
std::vector<AstVar*> m_varsp; std::vector<AstVar*> m_varsp;
std::vector<AstNode*> m_dtypeElimsp; // Data types might eliminate std::vector<AstNode*> m_dtypeElimsp; // Data types might eliminate
@@ -73,7 +272,6 @@ class DeadVisitor final : public VNVisitor {
std::vector<AstCell*> m_cellsp; std::vector<AstCell*> m_cellsp;
std::vector<AstClass*> m_classesp; std::vector<AstClass*> m_classesp;
std::vector<AstTypedef*> m_typedefsp; std::vector<AstTypedef*> m_typedefsp;
std::queue<AstNodeFTask*> m_tasksp; // All the tasks that could be removed if not called
AssignMap m_assignMap; // List of all simple assignments for each variable AssignMap m_assignMap; // List of all simple assignments for each variable
bool m_sideEffect = false; // Side effects discovered in assign RHS bool m_sideEffect = false; // Side effects discovered in assign RHS
@@ -82,14 +280,20 @@ class DeadVisitor final : public VNVisitor {
AstNodeDType* m_curDTypep = nullptr; // Current NodeDType AstNodeDType* m_curDTypep = nullptr; // Current NodeDType
AstNodeModule* m_modp = nullptr; // Current module AstNodeModule* m_modp = nullptr; // Current module
AstForeachHeader* m_foreachHeaderp = nullptr; // Current foreach header AstForeachHeader* m_foreachHeaderp = nullptr; // Current foreach header
AstNode* m_containingFTaskRefp = nullptr; // Parent of ftaskref (e.g. task/module)
bool m_inFTask = false; // Under an AstNodeFTask
// STATE - Statistic tracking // STATE - Statistic tracking
VDouble0 m_statFTasksDeadified; VDouble0 m_statFTasksDemoted;
VDouble0 m_statFTasksMDeadified;
VDouble0 m_statFTasksNMDeadified;
VDouble0 m_statFTasksVirtDeadified;
// METHODS // METHODS
void deleting(AstNode* nodep) { void deleting(AstNode* nodep) {
UINFO(9, " deleting " << nodep); UINFO(9, " deleting " << nodep);
m_graph.deleteNodeVertex(nodep);
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
} }
@@ -115,15 +319,31 @@ class DeadVisitor final : public VNVisitor {
m_dtypeElimsp.push_back(nodep); m_dtypeElimsp.push_back(nodep);
} }
if (VN_IS(m_modp, Package) || VN_IS(m_modp, Class)) m_dtypePkgsp.emplace(nodep, m_modp); if (VN_IS(m_modp, Package) || VN_IS(m_modp, Class)) m_dtypePkgsp.emplace(nodep, m_modp);
// A struct type moved to the type table refers to its package via classOrPackagep
if (const AstNodeUOrStructDType* const sdtypep = VN_CAST(nodep, NodeUOrStructDType)) {
if (sdtypep->classOrPackagep() && sdtypep->classOrPackagep() != m_modp) {
m_dtypePkgsp.emplace(nodep, sdtypep->classOrPackagep());
}
}
if (AstNode* const subnodep = nodep->virtRefDTypep()) subnodep->user1Inc(); if (AstNode* const subnodep = nodep->virtRefDTypep()) subnodep->user1Inc();
if (AstNode* const subnodep = nodep->virtRefDType2p()) subnodep->user1Inc(); if (AstNode* const subnodep = nodep->virtRefDType2p()) subnodep->user1Inc();
} }
void needsTask(AstNodeFTask* taskp, AstNode* containerp) {
if (!taskp) return; // Unlinked
if (taskp->isVirtual()) {
m_graph.needsVirtual(taskp, containerp);
} else {
m_graph.needs(taskp, containerp);
}
}
// VISITORS // VISITORS
void visit(AstNodeModule* nodep) override { void visit(AstNodeModule* nodep) override {
if (m_modp) m_modp->user1Inc(); // e.g. Class under Package if (m_modp) m_modp->user1Inc(); // e.g. Class under Package
VL_RESTORER(m_modp); VL_RESTORER(m_modp);
m_modp = nodep; m_modp = nodep;
VL_RESTORER(m_containingFTaskRefp);
m_containingFTaskRefp = nodep;
if (nodep->dead()) return; if (nodep->dead()) return;
if (nodep->modPublic()) m_modp->user1Inc(); if (nodep->modPublic()) m_modp->user1Inc();
iterateChildren(nodep); iterateChildren(nodep);
@@ -140,6 +360,16 @@ class DeadVisitor final : public VNVisitor {
iterateChildren(nodep); iterateChildren(nodep);
checkAll(nodep); checkAll(nodep);
if (nodep->scopep()) nodep->scopep()->user1Inc(); if (nodep->scopep()) nodep->scopep()->user1Inc();
// Keep formal arguments
for (AstVar* varp = nodep->argsp(); varp; varp = VN_AS(varp->nextp(), Var)) {
varp->user1Inc();
varp->user2(1);
}
}
void visit(AstPin* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->modVarp()) nodep->modVarp()->user1Inc();
} }
void visit(AstScope* nodep) override { void visit(AstScope* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
@@ -148,6 +378,8 @@ class DeadVisitor final : public VNVisitor {
// Class packages might have no children, but need to remain as // Class packages might have no children, but need to remain as
// long as the class they refer to is needed // long as the class they refer to is needed
if (VN_IS(m_modp, Class) || VN_IS(m_modp, ClassPackage)) nodep->user1Inc(); if (VN_IS(m_modp, Class) || VN_IS(m_modp, ClassPackage)) nodep->user1Inc();
// The constant pool scope must remain for entries created later
if (m_modp->isConstPool()) nodep->user1Inc();
if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp()) { if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp()) {
m_scopesp.push_back(nodep); m_scopesp.push_back(nodep);
} }
@@ -175,7 +407,7 @@ class DeadVisitor final : public VNVisitor {
iterateChildren(nodep); iterateChildren(nodep);
if (!m_sideEffect && !nodep->isPure()) m_sideEffect = true; if (!m_sideEffect && !nodep->isPure()) m_sideEffect = true;
checkAll(nodep); checkAll(nodep);
if (nodep->taskp()) nodep->taskp()->user1Inc(); needsTask(nodep->taskp(), m_containingFTaskRefp);
if (nodep->classOrPackagep()) { if (nodep->classOrPackagep()) {
if (m_elimCells) { if (m_elimCells) {
nodep->classOrPackagep(nullptr); nodep->classOrPackagep(nullptr);
@@ -187,7 +419,12 @@ class DeadVisitor final : public VNVisitor {
void visit(AstModportFTaskRef* nodep) override { void visit(AstModportFTaskRef* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
checkAll(nodep); checkAll(nodep);
if (nodep->ftaskp()) nodep->ftaskp()->user1Inc(); needsTask(nodep->ftaskp(), m_containingFTaskRefp);
}
void visit(AstModportVarRef* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->varp()) nodep->varp()->user1Inc();
} }
void visit(AstRefDType* nodep) override { void visit(AstRefDType* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
@@ -294,6 +531,11 @@ class DeadVisitor final : public VNVisitor {
iterateChildren(nodep); iterateChildren(nodep);
checkAll(nodep); checkAll(nodep);
if (m_foreachHeaderp) nodep->user1Inc(); if (m_foreachHeaderp) nodep->user1Inc();
// Keep formal arguments
if (m_inFTask && nodep->isIO()) {
nodep->user1Inc();
nodep->user2(1);
}
if (mightElimVar(nodep)) { if (mightElimVar(nodep)) {
m_varsp.push_back(nodep); m_varsp.push_back(nodep);
} else { } else {
@@ -327,13 +569,21 @@ class DeadVisitor final : public VNVisitor {
if (assignInAssign) m_sideEffect = true; // Parent assign shouldn't optimize if (assignInAssign) m_sideEffect = true; // Parent assign shouldn't optimize
} }
void visit(AstNodeFTask* nodep) override { void visit(AstNodeFTask* nodep) override {
const bool removable = !(nodep->taskPublic() || nodep->dpiExport() || nodep->dpiImport()
|| nodep->keepAlive() || nodep->isConstructor()
|| (!v3Global.opt.fDeadMethods() && nodep->classMethod()));
m_graph.findNewRemovableVertex(nodep, removable);
//
VL_RESTORER(m_containingFTaskRefp);
VL_RESTORER(m_inFTask);
m_containingFTaskRefp = nodep;
m_inFTask = true;
iterateChildren(nodep); iterateChildren(nodep);
checkAll(nodep); checkAll(nodep);
if (nodep->taskPublic() || nodep->dpiExport() || nodep->dpiImport()) { if (nodep->isVirtual()) m_graph.funcVirtual(nodep);
if (!removable) {
if (m_modp && !m_modp->dead() && !m_modp->verilatorLib()) if (m_modp && !m_modp->dead() && !m_modp->verilatorLib())
m_modp->user1Inc(); // Keep container m_modp->user1Inc(); // Keep container
} else {
m_tasksp.push(nodep);
} }
if (nodep->classOrPackagep()) { if (nodep->classOrPackagep()) {
if (m_elimCells) { if (m_elimCells) {
@@ -374,19 +624,41 @@ class DeadVisitor final : public VNVisitor {
} }
void deadCheckTasks() { void deadCheckTasks() {
while (!m_tasksp.empty()) { while (!m_graph.funcsEmpty()) {
AstNodeFTask* taskp = m_tasksp.front(); AstNode* const nodep = m_graph.funcsGetPopFront();
m_tasksp.pop(); if (!nodep) continue;
if (taskp->user1() == 0 && !taskp->classMethod()) { UINFO(9, "Dead " << nodep);
taskp->foreach([this](AstNodeFTaskRef* ftaskrefp) { if (AstNodeFTask* const taskp = VN_CAST(nodep, NodeFTask)) {
AstNodeFTask* task2p = ftaskrefp->taskp(); if (taskp->isVirtual()) {
if (!task2p) return; ++m_statFTasksVirtDeadified;
task2p->user1Inc(-1); } else if (taskp->classMethod()) {
if (task2p->user1() == 0) m_tasksp.push(task2p); ++m_statFTasksMDeadified;
}); } else {
taskp->user1(-1); // we don't want to try deleting twice ++m_statFTasksNMDeadified;
deleting(taskp); }
++m_statFTasksDeadified; }
deleting(nodep);
}
}
void deadCheckDemote() {
for (V3GraphVertex& gvtx : m_graph.vertices()) {
DeadVertex* const vtxp = gvtx.cast<DeadVertex>();
// A isVirtual vertex with single out means there's only one target virtual function
// that can be virtually called, so can make it non-virtual for faster execution
// (UVM benefits from this)
if (!vtxp->outSize1()) continue;
if (!vtxp->isVirtual()) continue;
for (V3GraphEdge& edge : vtxp->outEdges()) { // Always a single one
AstNode* const nodep = edge.top()->as<DeadVertex>()->nodep();
AstNodeFTask* const funcp = VN_AS(nodep, NodeFTask);
UASSERT_OBJ(funcp->isVirtual(), funcp,
"Only virtual ftasks should be under DeadVirtualVertex");
if (v3Global.opt.fDeadMethods()) {
funcp->isVirtual(false);
UINFO(9, "Demote to non-virtual " << funcp);
++m_statFTasksDemoted;
}
} }
} }
} }
@@ -400,7 +672,11 @@ class DeadVisitor final : public VNVisitor {
AstNodeModule* nextmodp; AstNodeModule* nextmodp;
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp = nextmodp) { for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp = nextmodp) {
nextmodp = VN_AS(modp->nextp(), NodeModule); nextmodp = VN_AS(modp->nextp(), NodeModule);
if (modp->dead() || (!modp->isTop() && modp->user1() == 0 && !modp->internal())) { // Keep $unit until m_elimCells stages. Note v3Global.opt.serializeOnly()
// won't reach this stage, and will always have an empty $unit. That's ok.
// The constant pool is always kept, entries might be created later.
const bool keep = (!m_elimCells && modp->isDollarUnit()) || modp->isConstPool();
if (modp->dead() || (!modp->isTop() && modp->user1() == 0 && !keep)) {
// > 2 because L1 is the wrapper, L2 is the top user module // > 2 because L1 is the wrapper, L2 is the top user module
UINFO(4, " Dead module " << modp); UINFO(4, " Dead module " << modp);
// And its children may now be killable too; correct counts // And its children may now be killable too; correct counts
@@ -410,6 +686,7 @@ class DeadVisitor final : public VNVisitor {
cellp->modp()->user1Inc(-1); cellp->modp()->user1Inc(-1);
}); });
} }
if (modp->isDollarUnit()) v3Global.rootp()->dollarUnitPkgp(nullptr);
deleting(modp); deleting(modp);
retry = true; retry = true;
} }
@@ -418,7 +695,7 @@ class DeadVisitor final : public VNVisitor {
} }
bool mightElimVar(const AstVar* nodep) const { bool mightElimVar(const AstVar* nodep) const {
if (nodep->isSigPublic()) return false; // Can't elim publics! if (nodep->isSigPublic()) return false; // Can't elim publics!
if (nodep->isIO() || nodep->isClassMember() || nodep->sensIfacep()) return false; if (nodep->isPrimaryIO() || nodep->isClassMember() || nodep->sensIfacep()) return false;
if (nodep->isTemp() && !nodep->isTrace()) return true; if (nodep->isTemp() && !nodep->isTrace()) return true;
return m_elimUserVars; // Post-Trace can kill most anything return m_elimUserVars; // Post-Trace can kill most anything
} }
@@ -470,6 +747,8 @@ class DeadVisitor final : public VNVisitor {
void deadCheckVar() { void deadCheckVar() {
// Delete any unused varscopes // Delete any unused varscopes
for (AstVarScope* vscp : m_vscsp) { for (AstVarScope* vscp : m_vscsp) {
// Keep formal arguments
if (vscp->varp()->user2()) continue;
if (vscp->user1() == 0) { if (vscp->user1() == 0) {
UINFO(4, " Dead " << vscp); UINFO(4, " Dead " << vscp);
const std::pair<AssignMap::iterator, AssignMap::iterator> eqrange const std::pair<AssignMap::iterator, AssignMap::iterator> eqrange
@@ -522,53 +801,17 @@ class DeadVisitor final : public VNVisitor {
} }
} }
// cppcheck-suppress constParameterPointer
void preserveTopIfaces(AstNetlist* rootp) {
// cppcheck-suppress constVariablePointer
for (AstNodeModule* modp = rootp->modulesp(); modp && modp->isTop();
modp = VN_AS(modp->nextp(), NodeModule)) {
for (AstNode* subnodep = modp->stmtsp(); subnodep; subnodep = subnodep->nextp()) {
if (AstVar* const varp = VN_CAST(subnodep, Var)) {
if (varp->isIfaceRef()) {
const AstNodeDType* const subtypep = varp->subDTypep();
const AstIfaceRefDType* ifacerefp = nullptr;
if (VN_IS(subtypep, IfaceRefDType)) {
ifacerefp = VN_AS(varp->subDTypep(), IfaceRefDType);
} else if (VN_IS(subtypep, BracketArrayDType)) {
const AstBracketArrayDType* const arrp
= VN_AS(subtypep, BracketArrayDType);
const AstNodeDType* const arrsubtypep = arrp->subDTypep();
if (VN_IS(arrsubtypep, IfaceRefDType)) {
ifacerefp = VN_AS(arrsubtypep, IfaceRefDType);
}
} else if (VN_IS(subtypep, UnpackArrayDType)) {
const AstUnpackArrayDType* const arrp
= VN_AS(subtypep, UnpackArrayDType);
const AstNodeDType* const arrsubtypep = arrp->subDTypep();
if (VN_IS(arrsubtypep, IfaceRefDType)) {
ifacerefp = VN_AS(arrsubtypep, IfaceRefDType);
}
}
if (ifacerefp && !ifacerefp->cellp()
&& (ifacerefp->ifacep()->user1() == 0)) {
ifacerefp->ifacep()->user1(1);
}
}
}
}
}
}
public: public:
// CONSTRUCTORS // CONSTRUCTORS
DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, bool elimScopes, DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, bool elimScopes,
bool elimCells, bool elimTopIfaces, bool elimTasks) bool elimCells, bool elimTasks)
: m_elimUserVars{elimUserVars} : m_elimUserVars{elimUserVars}
, m_elimDTypes{elimDTypes} , m_elimDTypes{elimDTypes}
, m_elimCells{elimCells} { , m_elimCells{elimCells}
, m_containingFTaskRefp{nodep} {
// Prepare to remove some datatypes // Prepare to remove some datatypes
nodep->typeTablep()->clearCache(); nodep->typeTablep()->clearCache();
// Operate on whole netlist // Operate on whole netlist
iterate(nodep); iterate(nodep);
@@ -582,6 +825,11 @@ public:
if (itr.first->user1()) itr.second->user1Inc(); if (itr.first->user1()) itr.second->user1Inc();
} }
// Simplify redundant edges (e.g. function calls another function many times)
m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
if (dumpGraphLevel() >= 9 || debug() >= 9)
m_graph.dumpDotFilePrefixed("dead_graph", false);
if (elimTasks) deadCheckTasks(); if (elimTasks) deadCheckTasks();
deadCheckTypedefs(); deadCheckTypedefs();
deadCheckVar(); deadCheckVar();
@@ -591,16 +839,24 @@ public:
if (elimCells) deadCheckCells(); if (elimCells) deadCheckCells();
deadCheckClasses(); deadCheckClasses();
// Modules after vars, because might be vars we delete inside a mod we delete // Modules after vars, because might be vars we delete inside a mod we delete
if (!elimTopIfaces) preserveTopIfaces(nodep);
deadCheckMod(); deadCheckMod();
// After deleting as much as can, demote some virtual functions
if (elimTasks) deadCheckDemote();
// We may have removed some datatypes, cleanup // We may have removed some datatypes, cleanup
nodep->typeTablep()->repairCache(); nodep->typeTablep()->repairCache();
VIsCached::clearCacheTree(); // Removing assignments may affect isPure VIsCached::clearCacheTree(); // Removing assignments may affect isPure
nodep->constPoolp()->rebuildVarScopesAndCache(); V3ConstPool::invalidateCache(); // Might have deleted constant pool entries
} }
~DeadVisitor() override { ~DeadVisitor() override {
V3Stats::addStatSum("Optimizations, deadified FTasks", m_statFTasksDeadified); V3Stats::addStatSum("Optimizations, FTasks, virtual-to-nonvirtual demotion",
m_statFTasksDemoted);
V3Stats::addStatSum("Optimizations, FTasks, deadified, methods", m_statFTasksMDeadified);
V3Stats::addStatSum("Optimizations, FTasks, deadified, non-methods",
m_statFTasksNMDeadified);
V3Stats::addStatSum("Optimizations, FTasks, deadified, virtual",
m_statFTasksVirtDeadified);
}; };
}; };
@@ -609,32 +865,32 @@ public:
void V3Dead::deadifyModules(AstNetlist* nodep) { void V3Dead::deadifyModules(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
{ // node, elimUserVars, elimDTypes, elimScopes, elimCells, elimTopIfaces { // node, elimUserVars, elimDTypes, elimScopes, elimCells, elimTasks
DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported(), false}; DeadVisitor{nodep, false, false, false, false, false};
} // Destruct before checking } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTreeEitherLevel() >= 6); V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTreeEitherLevel() >= 6);
} }
void V3Dead::deadifyDTypes(AstNetlist* nodep) { void V3Dead::deadifyDTypes(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
{ DeadVisitor{nodep, false, true, false, false, false, true}; } // Destruct before checking { DeadVisitor{nodep, false, true, false, false, true}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTreeEitherLevel() >= 3); V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTreeEitherLevel() >= 3);
} }
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) { void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
{ DeadVisitor{nodep, false, true, true, false, false, false}; } // Destruct before checking { DeadVisitor{nodep, false, true, true, false, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTreeEitherLevel() >= 3); V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTreeEitherLevel() >= 3);
} }
void V3Dead::deadifyAll(AstNetlist* nodep) { void V3Dead::deadifyAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
{ DeadVisitor{nodep, true, true, false, true, false, true}; } // Destruct before checking { DeadVisitor{nodep, true, true, false, true, true}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTreeEitherLevel() >= 3); V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTreeEitherLevel() >= 3);
} }
void V3Dead::deadifyAllScoped(AstNetlist* nodep) { void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
{ DeadVisitor{nodep, true, true, true, true, false, true}; } // Destruct before checking { DeadVisitor{nodep, true, true, true, true, true}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTreeEitherLevel() >= 3); V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTreeEitherLevel() >= 3);
} }
+141 -23
View File
@@ -118,6 +118,7 @@
#include "V3Delayed.h" #include "V3Delayed.h"
#include "V3AstUserAllocator.h" #include "V3AstUserAllocator.h"
#include "V3ClassGraph.h"
#include "V3Const.h" #include "V3Const.h"
#include "V3Stats.h" #include "V3Stats.h"
@@ -250,6 +251,7 @@ class DelayedVisitor final : public VNVisitor {
// AstVar::user1() -> bool. Set true if already issued MULTIDRIVEN warning // AstVar::user1() -> bool. Set true if already issued MULTIDRIVEN warning
// AstVarRef::user1() -> bool. Set true if target of NBA // AstVarRef::user1() -> bool. Set true if target of NBA
// AstAssignDly::user1() -> bool. Set true if already visited // AstAssignDly::user1() -> bool. Set true if already visited
// AstCFunc::user1() -> AstUser1Allocator. See `m_cfuncsCache` below
// AstAssignDly::user2p() -> AstVarScope*: Scope this AstAssignDelay is under // AstAssignDly::user2p() -> AstVarScope*: Scope this AstAssignDelay is under
// AstNodeModule::user1p() -> std::unorded_map<std::string, AstVar*> temp map via m_varMap // AstNodeModule::user1p() -> std::unorded_map<std::string, AstVar*> temp map via m_varMap
// AstScope::user1() -> int: Temporary counter for this scope // AstScope::user1() -> int: Temporary counter for this scope
@@ -259,6 +261,24 @@ class DelayedVisitor final : public VNVisitor {
const VNUser1InUse m_user1InUse; const VNUser1InUse m_user1InUse;
const VNUser2InUse m_user2InUse; const VNUser2InUse m_user2InUse;
const VNUser3InUse m_user3InUse; const VNUser3InUse m_user3InUse;
struct CFuncCache final {
VInsertionSet<AstSenTree*> m_timingDomains; // What shall be added to m_timingDomains
std::set<AstCFunc*>
m_includes; // CFuncs whose CFuncCache shall be included into this - this is used to
// break cycles: A->B->A (instead of visiting A while it is still begin
// visited B just marks that it includes A)
enum State : uint8_t {
UNINITIALIZED = 0, // Not initialized members are empty
VISITING, // Visiting - needed for breaking recursion
INITIALIZED, // Members contains correct values
} m_state // Current state of Cache
= UNINITIALIZED;
};
// Caches what should be added to m_timingDomains because of calls to the AstCFunc (with
// recursive check of other AstCFuncs called from inside)
AstUser1Allocator<AstCFunc, CFuncCache> m_cfuncsCache;
AstUser1Allocator<AstNodeModule, std::unordered_map<std::string, AstVar*>> m_varMap; AstUser1Allocator<AstNodeModule, std::unordered_map<std::string, AstVar*>> m_varMap;
AstUser1Allocator<AstVarScope, VarScopeInfo> m_vscpInfo; AstUser1Allocator<AstVarScope, VarScopeInfo> m_vscpInfo;
AstUser3Allocator<AstVarScope, std::vector<WriteReference>> m_writeRefs; AstUser3Allocator<AstVarScope, std::vector<WriteReference>> m_writeRefs;
@@ -266,6 +286,10 @@ class DelayedVisitor final : public VNVisitor {
// STATE - across all visitors // STATE - across all visitors
VInsertionSet<AstSenTree*> m_timingDomains; // Timing resume domains VInsertionSet<AstSenTree*> m_timingDomains; // Timing resume domains
const std::unique_ptr<V3ClassGraph>
m_classGraphp; // class graph to get possibly called functions from a virtual call
std::vector<const AstCFunc*> m_callStack; // Current callstack of AstCFuncs
// STATE - for current visit position (use VL_RESTORER) // STATE - for current visit position (use VL_RESTORER)
AstActive* m_activep = nullptr; // Current activate AstActive* m_activep = nullptr; // Current activate
const AstCFunc* m_cfuncp = nullptr; // Current public C Function const AstCFunc* m_cfuncp = nullptr; // Current public C Function
@@ -276,6 +300,7 @@ class DelayedVisitor final : public VNVisitor {
bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK
bool m_inNonCombLogic = false; // We are in non-combinational logic bool m_inNonCombLogic = false; // We are in non-combinational logic
bool m_needsInitialTrigger = false; // Whether a NodeProcedure needs a initial trigger bool m_needsInitialTrigger = false; // Whether a NodeProcedure needs a initial trigger
std::vector<AstSenTree*> m_nbaEventSenTreeps; // Sensitivities of '->>' in the process
AstVarRef* m_currNbaLhsRefp = nullptr; // Current NBA LHS variable reference AstVarRef* m_currNbaLhsRefp = nullptr; // Current NBA LHS variable reference
// STATE - during NBA conversion (after visit) // STATE - during NBA conversion (after visit)
@@ -293,6 +318,7 @@ class DelayedVisitor final : public VNVisitor {
VDouble0 m_nSchemeValueQueuesPartial; // Number of variables using Scheme::ValueQueuePartial VDouble0 m_nSchemeValueQueuesPartial; // Number of variables using Scheme::ValueQueuePartial
VDouble0 m_nSharedSetFlags; // "Set" flags actually shared by Scheme::FlagShared variables VDouble0 m_nSharedSetFlags; // "Set" flags actually shared by Scheme::FlagShared variables
VDouble0 m_nInitialNBA; // Number of procedural blocks with initial NBA VDouble0 m_nInitialNBA; // Number of procedural blocks with initial NBA
VDouble0 m_nonInlinedCAwaitsWithSenTree; // Count uses of not inlined co_awaits
// METHODS // METHODS
@@ -563,6 +589,17 @@ class DelayedVisitor final : public VNVisitor {
return ss.str(); return ss.str();
} }
void addCFuncCachedValues(const AstCFunc* const cfuncp,
std::unordered_set<const AstCFunc*>& visited) {
if (!visited.insert(cfuncp).second) return;
CFuncCache& value = m_cfuncsCache(cfuncp);
m_timingDomains.insert(value.m_timingDomains.begin(), value.m_timingDomains.end());
m_nonInlinedCAwaitsWithSenTree += value.m_timingDomains.size();
for (const AstCFunc* const includedp : value.m_includes) {
addCFuncCachedValues(includedp, visited);
}
}
// Create a temporary variable in the given 'scopep', with the given 'name', and with 'dtypep' // Create a temporary variable in the given 'scopep', with the given 'name', and with 'dtypep'
// type, with the bits selected by 'sLsbp'/'sWidthp' set to 'valuep', other bits set to zero. // type, with the bits selected by 'sLsbp'/'sWidthp' set to 'valuep', other bits set to zero.
// Insert new statements before 'insertp'. // Insert new statements before 'insertp'.
@@ -784,7 +821,7 @@ class DelayedVisitor final : public VNVisitor {
m_prevVscp = vscp; m_prevVscp = vscp;
// Delete original NBA // Delete original NBA
pushDeletep(nodep->unlinkFrBack()); VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
} }
// Scheme::FlagUnique // Scheme::FlagUnique
@@ -834,7 +871,7 @@ class DelayedVisitor final : public VNVisitor {
ifp->addThensp(new AstAssign{flp, capturedLhsp, capturedRhsp}); ifp->addThensp(new AstAssign{flp, capturedLhsp, capturedRhsp});
// Delete original NBA // Delete original NBA
pushDeletep(nodep->unlinkFrBack()); VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
} }
// Scheme::ValueQueuePartial/Scheme::ValueQueueWhole // Scheme::ValueQueuePartial/Scheme::ValueQueueWhole
@@ -865,8 +902,9 @@ class DelayedVisitor final : public VNVisitor {
activep->addStmtsp(postp); activep->addStmtsp(postp);
// Add the commit // Add the commit
AstCMethodHard* const callp = new AstCMethodHard{ AstCMethodHard* const callp = new AstCMethodHard{
flp, new AstVarRef{flp, queueVscp, VAccess::READWRITE}, VCMethod::SCHED_COMMIT}; flp, new AstVarRef{flp, queueVscp, VAccess::READWRITE}, VCMethod::NBA_COMMIT};
callp->dtypeSetVoid(); callp->dtypeSetVoid();
// TODO: this is a partial update, so must be READWRITE, but that breaks scheduling
callp->addPinsp(new AstVarRef{flp, vscp, VAccess::WRITE}); callp->addPinsp(new AstVarRef{flp, vscp, VAccess::WRITE});
postp->addStmtsp(callp->makeStmt()); postp->addStmtsp(callp->makeStmt());
} }
@@ -975,7 +1013,7 @@ class DelayedVisitor final : public VNVisitor {
// Enqueue the update at the site of the original NBA // Enqueue the update at the site of the original NBA
AstCMethodHard* const callp = new AstCMethodHard{ AstCMethodHard* const callp = new AstCMethodHard{
flp, new AstVarRef{flp, vscpInfo.valueQueueKit().vscp, VAccess::READWRITE}, flp, new AstVarRef{flp, vscpInfo.valueQueueKit().vscp, VAccess::READWRITE},
VCMethod::SCHED_ENQUEUE}; VCMethod::NBA_ENQUEUE};
callp->dtypeSetVoid(); callp->dtypeSetVoid();
callp->addPinsp(valuep); callp->addPinsp(valuep);
if (partial) callp->addPinsp(maskp); if (partial) callp->addPinsp(maskp);
@@ -983,7 +1021,7 @@ class DelayedVisitor final : public VNVisitor {
nodep->addHereThisAsNext(callp->makeStmt()); nodep->addHereThisAsNext(callp->makeStmt());
// Delete original NBA // Delete original NBA
pushDeletep(nodep->unlinkFrBack()); VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
} }
// Record where a variable is assigned // Record where a variable is assigned
@@ -1007,6 +1045,43 @@ class DelayedVisitor final : public VNVisitor {
return procedurep && procedurep->sentreep(); return procedurep && procedurep->sentreep();
} }
// Visit AstCFunc from a AstNodeCCall - this is made into a separate quasi-visitor because
// AstCFunc that is not called from the code (e.g.: DPI exports) does not need to be visited
// this way. Also, not visiting such AstCFuncs allows to avoid caching results for them which
// this function does - which could lead to excessive memory usage
void visitCalledCFunc(AstCFunc* const nodep) {
CFuncCache& value = m_cfuncsCache(nodep);
switch (value.m_state) {
case CFuncCache::UNINITIALIZED: {
// Save current state
VL_RESTORER_CLEAR(m_timingDomains);
// Visit
value.m_state = CFuncCache::VISITING;
m_callStack.push_back(nodep);
{
VL_RESTORER(m_cfuncp);
m_cfuncp = nodep;
iterateChildren(nodep);
}
m_callStack.pop_back();
value.m_state = CFuncCache::INITIALIZED;
// Save a cache
std::swap(m_timingDomains, value.m_timingDomains);
} break;
case CFuncCache::VISITING: {
for (size_t i = m_callStack.size() - 1; m_callStack.at(i) != nodep; --i) {
m_cfuncsCache(m_callStack[i]).m_includes.insert(nodep);
}
return; // Break recursion
}
case CFuncCache::INITIALIZED: break;
}
std::unordered_set<const AstCFunc*> visited;
addCFuncCachedValues(nodep, visited);
}
// VISITORS // VISITORS
void visit(AstNetlist* nodep) override { void visit(AstNetlist* nodep) override {
iterateChildren(nodep); iterateChildren(nodep);
@@ -1103,11 +1178,6 @@ class DelayedVisitor final : public VNVisitor {
m_scopep = nodep; m_scopep = nodep;
iterateChildren(nodep); iterateChildren(nodep);
} }
void visit(AstCFunc* nodep) override {
VL_RESTORER(m_cfuncp);
m_cfuncp = nodep;
iterateChildren(nodep);
}
void visit(AstActive* nodep) override { void visit(AstActive* nodep) override {
UASSERT_OBJ(!m_activep, nodep, "Should not nest"); UASSERT_OBJ(!m_activep, nodep, "Should not nest");
VL_RESTORER(m_activep); VL_RESTORER(m_activep);
@@ -1121,6 +1191,7 @@ class DelayedVisitor final : public VNVisitor {
} }
void visit(AstNodeProcedure* nodep) override { void visit(AstNodeProcedure* nodep) override {
VL_RESTORER(m_needsInitialTrigger); VL_RESTORER(m_needsInitialTrigger);
VL_RESTORER_CLEAR(m_nbaEventSenTreeps);
const size_t firstNBAAddedIndex = m_nbas.size(); const size_t firstNBAAddedIndex = m_nbas.size();
{ {
VL_RESTORER(m_inSuspendableOrFork); VL_RESTORER(m_inSuspendableOrFork);
@@ -1172,6 +1243,8 @@ class DelayedVisitor final : public VNVisitor {
for (size_t i = firstNBAAddedIndex; i < m_nbas.size(); ++i) { for (size_t i = firstNBAAddedIndex; i < m_nbas.size(); ++i) {
m_vscpInfo(m_nbas[i].vscp).addSensitivity(senItemp); m_vscpInfo(m_nbas[i].vscp).addSensitivity(senItemp);
} }
for (AstSenTree* const senTreep : m_nbaEventSenTreeps)
senTreep->addSensesp(senItemp->cloneTree(true));
// Done with these // Done with these
VL_DO_DANGLING(senItemp->deleteTree(), senItemp); VL_DO_DANGLING(senItemp->deleteTree(), senItemp);
} }
@@ -1182,6 +1255,7 @@ class DelayedVisitor final : public VNVisitor {
} }
void visit(AstCAwait* nodep) override { void visit(AstCAwait* nodep) override {
if (nodep->sentreep()) m_timingDomains.insert(nodep->sentreep()); if (nodep->sentreep()) m_timingDomains.insert(nodep->sentreep());
iterateChildren(nodep);
} }
void visit(AstFireEvent* nodep) override { void visit(AstFireEvent* nodep) override {
UASSERT_OBJ(v3Global.hasEvents(), nodep, "Inconsistent"); UASSERT_OBJ(v3Global.hasEvents(), nodep, "Inconsistent");
@@ -1196,8 +1270,15 @@ class DelayedVisitor final : public VNVisitor {
cstmtp->add(");"); cstmtp->add(");");
AstNode* newp = cstmtp; AstNode* newp = cstmtp;
if (nodep->isDelayed()) { const AstVarRef* const vrefp = VN_CAST(eventp, VarRef);
const AstVarRef* const vrefp = VN_AS(eventp, VarRef); if (nodep->isDelayed() && (m_cfuncp || !vrefp)) {
// V3Timing converts these with --timing
nodep->v3warn(E_NOTIMING, "Nonblocking event trigger "
<< (m_cfuncp ? "in a non-inlined function/task"
: "of a class or interface member or "
"array element")
<< " requires --timing");
} else if (nodep->isDelayed()) {
const std::string newvarname = "__Vdly__" + vrefp->varp()->shortName(); const std::string newvarname = "__Vdly__" + vrefp->varp()->shortName();
AstVarScope* const dlyvscp AstVarScope* const dlyvscp
= createTemp(flp, vrefp->varScopep()->scopep(), newvarname, 1); = createTemp(flp, vrefp->varScopep()->scopep(), newvarname, 1);
@@ -1206,20 +1287,35 @@ class DelayedVisitor final : public VNVisitor {
return new AstVarRef{flp, dlyvscp, access}; return new AstVarRef{flp, dlyvscp, access};
}; };
AstAlwaysPre* const prep = new AstAlwaysPre{flp};
prep->addStmtsp(new AstAssign{flp, dlyRef(VAccess::WRITE),
new AstConst{flp, AstConst::BitFalse{}}});
AstAlwaysPost* const postp = new AstAlwaysPost{flp}; AstAlwaysPost* const postp = new AstAlwaysPost{flp};
{ AstIf* const ifp = new AstIf{flp, dlyRef(VAccess::READ)};
AstIf* const ifp = new AstIf{flp, dlyRef(VAccess::READ)}; postp->addStmtsp(ifp);
postp->addStmtsp(ifp);
ifp->addThensp(newp);
}
UASSERT_OBJ(m_activep, nodep, "No active to handle FireEvent"); UASSERT_OBJ(m_activep, nodep, "No active to handle FireEvent");
AstActive* const activep = new AstActive{flp, "nba-event", m_activep->sentreep()}; AstSenTree* senTreep = m_activep->sentreep();
AstActive* activep = nullptr;
if (m_inSuspendableOrFork && (senTreep->hasInitial() || senTreep->hasClocked())) {
// Suspendable code can trigger whenever it resumes, so fire when its NBAs are
// committed (see visit(AstNodeProcedure*)), clearing the flag like
// Scheme::FlagUnique
senTreep = new AstSenTree{
flp, senTreep->hasClocked() ? senTreep->sensesp()->cloneTree(true) : nullptr};
m_nbaEventSenTreeps.push_back(senTreep);
m_needsInitialTrigger |= m_timingDomains.empty();
dlyvscp->varp()->setIgnorePostWrite();
ifp->addThensp(new AstAssign{flp, dlyRef(VAccess::WRITE),
new AstConst{flp, AstConst::BitFalse{}}});
activep = new AstActive{flp, "nba-event", senTreep};
activep->senTreeStorep(senTreep);
} else {
activep = new AstActive{flp, "nba-event", senTreep};
AstAlwaysPre* const prep = new AstAlwaysPre{flp};
prep->addStmtsp(new AstAssign{flp, dlyRef(VAccess::WRITE),
new AstConst{flp, AstConst::BitFalse{}}});
activep->addStmtsp(prep);
}
ifp->addThensp(newp);
m_activep->addNextHere(activep); m_activep->addNextHere(activep);
activep->addStmtsp(prep);
activep->addStmtsp(postp); activep->addStmtsp(postp);
newp = new AstAssign{flp, dlyRef(VAccess::WRITE), newp = new AstAssign{flp, dlyRef(VAccess::WRITE),
@@ -1337,6 +1433,24 @@ class DelayedVisitor final : public VNVisitor {
m_inLoop = true; m_inLoop = true;
iterateChildren(nodep); iterateChildren(nodep);
} }
void visit(AstNodeCCall* const nodep) override {
iterateChildren(nodep);
// We need to visit bodies of non-inlined functions
const auto& cfuncps = m_classGraphp->getCallPossibleCFuncs(nodep);
if (cfuncps.empty()) {
visitCalledCFunc(nodep->funcp());
} else {
for (AstCFunc* const cfuncp : cfuncps) visitCalledCFunc(cfuncp);
}
}
void visit(AstCFunc* const nodep) override {
const auto& value = m_cfuncsCache(nodep);
// Check whether it was already visited by visitCalledCFunc()
if (value.m_state != CFuncCache::UNINITIALIZED) return;
VL_RESTORER(m_cfuncp);
m_cfuncp = nodep;
iterateChildren(nodep);
}
// Pre/Post logic are created here and their content need no further changes, so ignore. // Pre/Post logic are created here and their content need no further changes, so ignore.
void visit(AstAlwaysPre*) override {} void visit(AstAlwaysPre*) override {}
@@ -1347,7 +1461,10 @@ class DelayedVisitor final : public VNVisitor {
public: public:
// CONSTRUCTORS // CONSTRUCTORS
explicit DelayedVisitor(AstNetlist* nodep) { iterate(nodep); } explicit DelayedVisitor(AstNetlist* nodep)
: m_classGraphp{V3ClassGraph::build(nodep)} {
iterate(nodep);
}
~DelayedVisitor() override { ~DelayedVisitor() override {
V3Stats::addStat("NBA, variables using ShadowVar scheme", m_nSchemeShadowVar); V3Stats::addStat("NBA, variables using ShadowVar scheme", m_nSchemeShadowVar);
V3Stats::addStat("NBA, variables using ShadowVarMasked scheme", m_nSchemeShadowVarMasked); V3Stats::addStat("NBA, variables using ShadowVarMasked scheme", m_nSchemeShadowVarMasked);
@@ -1358,6 +1475,7 @@ public:
m_nSchemeValueQueuesPartial); m_nSchemeValueQueuesPartial);
V3Stats::addStat("Optimizations, NBA flags shared", m_nSharedSetFlags); V3Stats::addStat("Optimizations, NBA flags shared", m_nSharedSetFlags);
V3Stats::addStat("Procedures needing initial NBA trigger", m_nInitialNBA); V3Stats::addStat("Procedures needing initial NBA trigger", m_nInitialNBA);
V3Stats::addStat("Non-inlined co_awaits with SenTree", m_nonInlinedCAwaitsWithSenTree);
} }
}; };
+4 -1
View File
@@ -110,7 +110,10 @@ class DepthVisitor final : public VNVisitor {
// (Here instead of new visitor after V3Descope just to avoid another visitor) // (Here instead of new visitor after V3Descope just to avoid another visitor)
void needNonStaticFunc(AstNode* nodep) { void needNonStaticFunc(AstNode* nodep) {
UASSERT_OBJ(m_cfuncp, nodep, "Non-static accessor not under a function"); UASSERT_OBJ(m_cfuncp, nodep, "Non-static accessor not under a function");
if (m_cfuncp->isStatic()) { // DPI export implementations are always invoked via the generic DPI dispatcher,
// which can only ever supply the symbol table pointer, never a scope-specific
// "this", so they must remain static regardless of what they contain.
if (m_cfuncp->isStatic() && !m_cfuncp->dpiExportImpl()) {
UINFO(5, "Mark non-public due to " << nodep); UINFO(5, "Mark non-public due to " << nodep);
m_cfuncp->isStatic(false); m_cfuncp->isStatic(false);
} }
+4 -3
View File
@@ -26,6 +26,7 @@
#include "V3Descope.h" #include "V3Descope.h"
#include "V3ConstPool.h"
#include "V3EmitCBase.h" #include "V3EmitCBase.h"
VL_DEFINE_DEBUG_FUNCTIONS; VL_DEFINE_DEBUG_FUNCTIONS;
@@ -159,8 +160,7 @@ class DescopeVisitor final : public VNVisitor {
if (AstVar* const portp = VN_CAST(stmtp, Var)) { if (AstVar* const portp = VN_CAST(stmtp, Var)) {
if (portp->isIO() && !portp->isFuncReturn()) { if (portp->isIO() && !portp->isFuncReturn()) {
AstVarRef* const newp = new AstVarRef{ AstVarRef* const newp = new AstVarRef{
portp->fileline(), portp, portp->fileline(), portp, portp->direction().pinAccess()};
portp->isWritable() ? VAccess::WRITE : VAccess::READ};
argsp = AstNode::addNext(argsp, newp); argsp = AstNode::addNext(argsp, newp);
} }
} }
@@ -236,7 +236,7 @@ class DescopeVisitor final : public VNVisitor {
if (varp->isFuncLocal()) { if (varp->isFuncLocal()) {
// Reference to function locals need no self pointer // Reference to function locals need no self pointer
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()}); nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
} else if (scopep->modp() == v3Global.rootp()->constPoolp()->modp()) { } else if (scopep->modp()->isConstPool()) {
// Reference to constant pool value need no self pointer // Reference to constant pool value need no self pointer
nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()}); nodep->selfPointer(VSelfPointerText{VSelfPointerText::Empty()});
} else { } else {
@@ -295,5 +295,6 @@ public:
void V3Descope::descopeAll(AstNetlist* nodep) { void V3Descope::descopeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":"); UINFO(2, __FUNCTION__ << ":");
{ DescopeVisitor{nodep}; } // Destruct before checking { DescopeVisitor{nodep}; } // Destruct before checking
V3ConstPool::setDescoped();
V3Global::dumpCheckGlobalTree("descope", 0, dumpTreeEitherLevel() >= 3); V3Global::dumpCheckGlobalTree("descope", 0, dumpTreeEitherLevel() >= 3);
} }

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