Compare commits

..
252 Commits
Author SHA1 Message Date
Wilson Snyder 8ca45df9c7 Version bump 2024-08-21 08:40:24 -04:00
Wilson Snyder 8db9c1d227 Commentary: Changes update 2024-08-21 08:40:14 -04:00
Krzysztof Bieganski 930f35acc9 Support constraint_mode (#5338) 2024-08-21 06:16:44 -04:00
Krzysztof Bieganski 13e0fc7c27 Fix virtual interface null checks (#5391) 2024-08-21 05:40:52 -04:00
Bartłomiej Chmiel a730daabef Support 'parameter type' in hierarchical blocks (#5309) (#5333) 2024-08-21 05:30:59 -04:00
Andrew Nolte 3c28b72897 Tests: Add info files to golden (#5390) 2024-08-21 06:44:02 +03:00
Wilson Snyder 05e5f1955e Update include/gtkwave from upstream 2024-08-20 15:23:53 -04:00
Wilson Snyder 378800ee4a Commentary: Changes update 2024-08-20 15:13:44 -04:00
Wilson Snyder 43a57da950 Add quotes to not-found filenames 2024-08-20 14:47:43 -04:00
James BaileyandJames Bailey 99a43b7695 Fix +: and -: unpacked array slicing when array has nonzero low index (#5345) (#5387)
Co-authored-by: James Bailey <[email protected]>
2024-08-20 14:20:48 -04:00
Krzysztof Bieganski 59d2eea302 Fix capturing fields from superclass in randomize() with (#5389)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-08-20 13:25:58 -04:00
Kaleb Barrett 6f3f64e63f Add --timing flag as Verilated cc define (#5383) 2024-08-18 20:02:54 +03:00
Drew Ranck 48c71ef76c Support default value on module input (#5358) (#5373) 2024-08-15 10:04:07 -04:00
Ryszard Rozak 563faeb33f Internals: Fix removing nodes in V3Life (#5365) 2024-08-14 09:23:24 +02:00
Wilson Snyder 9c5c77c69c Favor use of more general idAny in parser 2024-08-13 15:25:07 -04:00
Wilson Snyder 4573e04488 Fix segfault after unsupported member call (#5359). 2024-08-13 14:39:37 -04:00
Krzysztof Bieganski 6cb0a41857 Support inline random variable control (#5317)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-08-13 14:20:31 -04:00
Geza Lore 124e3463fc Fix cache config file resolution performance (#5369)
V3ConfigWildcardResolver::resolve used to only cache the result of a
name lookup if there were any directives applied to the queried name.

This can be a performance problem. E.g. the module resolver is invoked
for every variable to check for attributes. If you had 'n' variables in
the design, and you also had 'k' directives that apply to specific
modules not involving most of the 'n' variables, then checking the
attributes involved O(n*k) operations. This can be very expensive
for large 'n' and 'k'.

In this patch we resolve every name exactly once, so the same above is
now O(n) in the worst case, and often a lot smaller due to repeat
lookups.
2024-08-13 19:52:23 +02:00
Ethan Sifferman 1b15509a9c Fix lint_off on Errors (#5351) (#5363) 2024-08-11 21:04:46 +02:00
Wilson Snyder eb3f10399a Internal: Rename tokenPipeScanTypeEq. No functional change. 2024-08-11 12:15:20 -04:00
Wilson Snyder 4660806c8f Internals: Remove wrong = new grammar rule. 2024-08-11 11:03:28 -04:00
Wilson Snyder 6f5d1037b4 Commentary 2024-08-11 10:33:04 -04:00
Wilson Snyder 24b097b228 Improve new class error (#5359) 2024-08-11 10:09:05 -04:00
Wilson Snyder a0ea96b355 Favor use of more general idAny in parser 2024-08-09 18:00:36 -04:00
Wilson Snyder a663da7393 Internals: Split idType from simple_type. No functional change intended 2024-08-09 17:45:54 -04:00
Tim Hutt c812a9b18f Fix shortened module names when searching for files (#5196) (#5246) 2024-08-09 17:23:00 -04:00
Arkadiusz Kozdra 367249ec84 Add parsing but otherwise ignore std::randomize (#5354) 2024-08-09 17:21:32 -04:00
Wilson Snyder f4acc59b82 Tests: Check for wrong quotes in docs (#5355) 2024-08-09 17:18:59 -04:00
Luca Colagrande d1da1664f0 Docs: Fix typos in :vlopt: command usage in docs (#5355) (#5356) 2024-08-09 14:17:36 +01:00
Krzysztof Bieganski b100615726 Internals: Relax requirements for AstClass iteration methods (#5335)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-08-09 10:05:52 +01:00
Krzysztof Bieganski ec0815e9ac Fix NBAs in suspendables (#5348)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-08-08 22:12:00 +01:00
Krzysztof Bieganski 97e9996f0b Fix optimized-out sentrees with --timing (#5080) (#5349)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-08-08 21:57:12 +01:00
Krzysztof Bieganski b7af859ba3 Fix forks capturing non-input ports in tasks (#5237) (#5343)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-08-08 21:55:46 +01:00
Krzysztof Bieganski 701fa5438a Fix output clockvar overwriting signal (#5320) (#5347)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-08-08 21:48:25 +01:00
Geza Lore 004865a8b2 Fix tracing_{on,off} in the presence of non-inlined modules (#5346)
Previously "*.foo.*" failed to match non-inlined instances called 'foo'.
2024-08-08 17:16:54 +01:00
Arkadiusz Kozdra 3e5859e5da Support constraining AstSel (#5344)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-08-08 15:37:08 +01:00
Arkadiusz Kozdra 78555b683c Fix missing type coercion in 'inside {array}' (#5340) 2024-08-08 03:46:41 -04:00
Wilson Snyder c2e44dbd99 Fix $sformat with array arguments (#5330). 2024-08-08 03:32:07 -04:00
Wilson Snyder 18fc3e6089 Internals: Favor idAny in typedefs. 2024-08-07 18:45:53 -04:00
Wilson Snyder f4f8ea0dcf Internals: Change cast fileline to point to cast operator 2024-08-07 18:45:53 -04:00
Arkadiusz Kozdra f78c4e8490 Fix compilation error on unreachable disable fork / wait fork (#5339)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-08-07 18:44:02 -04:00
Arkadiusz Kozdra e6fe367bdb Support streams to/from arrays of wide data (#5334) 2024-08-06 16:18:16 +01:00
Ryszard Rozak 3426ee5170 Fix purity of functions with AstJumpBlock or AstStmtExpr (#5332) 2024-08-06 16:07:38 +01:00
Arkadiusz Kozdra a32b8d80f9 Support streaming operator on arrays and wide data (#5326)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-08-06 08:48:46 -04:00
Bartłomiej Chmiel 3b81654c72 Fix -Wunused-but-set-variable clang warning (#5331)
Signed-off-by: Bartłomiej Chmiel <[email protected]>
2024-08-06 08:48:12 -04:00
Wilson Snyder 6abd556f5a Tests: Fix GCC 11.4.0 hang 2024-08-06 08:47:28 -04:00
Varun Koyyalagunta 31c1df638b Fix assert on wide expression (#5319) (#5324) 2024-08-06 01:45:57 -04:00
Krzysztof Bieganski f4cb2c8cf2 Add more rand_mode unsupported errors (#5329)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-08-05 17:56:03 -04:00
Bartłomiej Chmiel 7d5e19365e Support assertcontrol directive type (#5310)
Signed-off-by: Bartłomiej Chmiel <[email protected]>
2024-08-05 17:54:13 -04:00
Wilson Snyder 6fa1ade4e0 Commentary: Changes update 2024-08-05 04:03:21 -04:00
Bartłomiej Chmiel 99c619a4c9 Error on static variable initializers using automatic variables in procedures (#5296) 2024-08-05 08:39:43 +01:00
Krzysztof Bieganski 37a4002098 Fix stringify in nested preprocessor macros (#5323) 2024-08-02 11:46:02 -04:00
Krzysztof Boroński 45ee949cc4 Internals: Disambiguate variable references under with clauses of randomize() methods (#5277) 2024-08-02 11:45:17 -04:00
Arkadiusz Kozdra 54f9f4b6a9 Support foreach constraints (#5302)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-08-02 10:03:55 -04:00
Arkadiusz Kozdra a61178bd89 Internals: Convert foreach loops in V3Begin as well as V3Width (#5283) 2024-08-02 08:29:05 -04:00
Krzysztof Bieganski 2f5c58b345 Support rand_mode (#5273)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-07-31 22:30:48 +01:00
Wilson Snyder 403a197e23 Python lint fix 2024-07-28 14:57:29 -04:00
Wilson Snyder 3a659d460d Fix ==? and !=? with X values. 2024-07-28 14:40:55 -04:00
Wilson Snyder 8707c88787 Tests: Close misc internal code coverage holes 2024-07-28 14:18:24 -04:00
github action 167cba723e Apply 'make format' 2024-07-28 17:59:13 +00:00
Wilson Snyder 7f40dd8b5b CI: Fix codecov upload 2024-07-28 13:58:26 -04:00
Wilson Snyder 1609c23fea Fix cputime being zero 2024-07-28 13:14:10 -04:00
Wilson Snyder 216fc8a212 CI: Fix codecov upload 2024-07-27 07:18:50 -04:00
Wilson Snyder 5136ea1082 CI: Fix codecov upload 2024-07-27 07:02:18 -04:00
Wilson Snyder 5b931faf2b CI: Fix codecov upload 2024-07-27 06:31:20 -04:00
Wilson Snyder a2496e041e CI: Change to action-based codecov upload 2024-07-26 14:07:53 -04:00
Wilson Snyder fab268f772 CI: Change to action-based codecov upload 2024-07-26 10:39:08 -04:00
Wilson Snyder 550d47b7f4 CI: Change to action-based codecov upload 2024-07-26 10:27:00 -04:00
Markus Krause a01a21db86 Fix make flows to pass PYTHON3 (like perl) (#5307) (#5308)
Fixes #5307
2024-07-26 09:35:37 -04:00
Wilson Snyder 2082f1aa9e Internals: Update fastcov.py from upstream 2024-07-26 08:48:12 -04:00
Wilson Snyder 50a5a1ff5b Tests: Fix some verilog.y parser coverage holes 2024-07-26 07:29:15 -04:00
Arkadiusz Kozdra 1600cc01a4 Internals: clean up begin blocks and lifetimes in foreach loops (#5306)
According to IEEE 1800-2023 12.7.3, the loop creates an implicit block
around it, containing declarations of the loop variables with automatic
lifetime.

No functional change intended.

Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-07-26 05:50:29 -04:00
Ryszard Rozak b9e1d55262 Fix handling of rand fields not referenced in constraints (#5305)
Signed-off-by: Ryszard Rozak <[email protected]>
2024-07-26 05:46:30 -04:00
Geza Lore 0d43c14b93 Internals: Remove redundant hash lookup (#5276 partial) (#5303)
Minor help for #5276
2024-07-25 16:07:58 -04:00
Ricardo Barbedo 87d1c10fa7 Fix compiler coroutine check (#5190) (#5300)
The necessary options to support C++ coroutines vary greatly between
compilers, its versions, and the standard library being used. This
patch makes the check for coroutine support more robust by adding a
declaration of a coroutine variable, instead of just including the
header. It also makes sure that the HAVE_COROUTINE and
CFG_CXXFLAGS_COROUTINES flags are always set together, and only when
coroutine support is detected.
2024-07-24 17:21:14 -04:00
Udaya Raj Subedi a2506a6f96 Fix monitor block sensitivity items (#4400) (#5294) 2024-07-24 07:18:57 -04:00
Bartłomiej Chmiel 20dba7464d Support--compiler-include headers in user-supplied cpp files (#5271)
Signed-off-by: Bartłomiej Chmiel <[email protected]>
2024-07-24 06:40:39 -04:00
Wilson Snyder cb5d03ff0b Fix WIDTHEXPAND on left shift of intuitive amount (#5284). 2024-07-24 06:39:27 -04:00
github action 2727c33219 Apply 'make format' 2024-07-24 10:07:59 +00:00
Wilson Snyder 0419ed0430 Fix initializing static array in dynamic arrays and queues (#5287). 2024-07-24 06:06:57 -04:00
Wilson Snyder 1e3c7a5496 Fix inline function ref port persistence 2024-07-23 18:47:04 -04:00
Wilson Snyder d2dfcbca53 Internals: Add log_changes for updating Changes file 2024-07-23 18:36:57 -04:00
Wilson Snyder e779934443 Commentary: Changes update 2024-07-22 17:22:37 -04:00
Arkadiusz Kozdra a3b255b054 Internal: Suppress new clang-tidy messages (#5293)
The two most useless warning classes are llvmlibc-* on virtually every
identifier use that it does not resolve to __llvm_libc:: namespace, and
altera-* ones that warn on virtually every loop statement that it could
be unrolled with a pragma.  No functional change.

Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-07-22 13:11:50 -04:00
Krzysztof Bieganski f5caa4b7dc Fix randomizing current object with rand class instance member (#5292)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-07-22 14:41:12 +02:00
Wilson Snyder 8f4490628f Commentary: Clarify some warning messages. 2024-07-20 17:50:14 -04:00
Wilson Snyder df566bdf4d Improve PINMISSING to show related port 2024-07-20 17:37:33 -04:00
Wilson Snyder 981d3ce782 Add suggestions on misspelled PLI functions. 2024-07-20 07:20:10 -04:00
Wilson Snyder b7345eb5d5 Tests: Add checkp macro. No test functionality change. 2024-07-20 06:51:50 -04:00
Krzysztof Bieganski 2bd2b9324f Fix inline constraints creating class random generator (#5280) 2024-07-19 13:03:48 -04:00
Arkadiusz Kozdra 298faa84ee Fix elaborating foreach loops (#5285) 2024-07-19 14:56:30 +02:00
Arkadiusz Kozdra 43377ed8b0 Add support for this.randomize with (#5282)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-07-19 09:14:56 +02:00
Krzysztof Bieganski b18037b7e7 Internals: Move non-visit methods to the proper place (#5279) 2024-07-18 08:57:39 -04:00
Yutetsu TAKATSUKASA 095b1ccb67 Fix incorrect result of width mismatch (#5186) (#5189) 2024-07-17 11:54:58 +02:00
Krzysztof Bieganski 2a30a87580 Fix randomization when used with inheritance (#5268)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-07-17 08:21:45 +02:00
Krzysztof Bieganski e11bc6beeb Internals: Refactor function argument traits (#5269)
No functional change intended.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-07-16 20:55:04 +02:00
Arkadiusz Kozdra 0a9b31bb30 Add warning on dist in constraints (#5264) 2024-07-15 21:01:33 -04:00
Krzysztof Bieganski 34e37d7bd0 Keep one member map for randomize method creation (#5263)
No functional change intended.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-07-15 16:42:41 +02:00
Yangyu Chen 164a7a7c5d Improve emitted code to use a reference for VlSelf (#5254) 2024-07-15 09:44:01 -04:00
Krzysztof Obłonczek 67ea819d82 Fix toggle coverage aggregation on same line (#5248)
Documentation states that minimum of all reported coverage of all signals in a line should be taken.
Previous logic would break if there were any signals with zero coverage followed by signals with
nonzero coverage - a minimum from those nonzero toggle count would be taken, disregarding zero
coverage of previous signals.

Internal-tag: [#62193]

Signed-off-by: Krzysztof Obłonczek <[email protected]>
2024-07-14 17:05:58 -04:00
Andrew Nolte 60f9e21d8c Fix --x-assign to be independent from +verilator+rand+reset (#5214) 2024-07-14 17:04:00 -04:00
Wilson Snyder dc037db1cd Internals: Make upcase clean. No functional change. 2024-07-14 15:28:00 -04:00
Wilson Snyder 0658af90f5 Fix classes/modules of case-similar names (#5109). 2024-07-14 13:57:16 -04:00
Wilson Snyder 131623de34 Internals: Favor s string literals. No functional change. 2024-07-14 11:39:45 -04:00
Wilson Snyder 74ae879c2f Internals: Remove duplicate filenameNonExt. No functional change. 2024-07-14 11:31:32 -04:00
Wilson Snyder 8c3b754bac Internals: Rename some filename functions. No functional change. 2024-07-14 09:34:54 -04:00
Wilson Snyder e080f5c0cb Fix tracing with --main-top-name - (#5261). 2024-07-14 07:35:26 -04:00
Wilson Snyder 2284cf595d Commentary: Changes update 2024-07-14 07:34:36 -04:00
Wilson Snyder b1cc0c54c1 Tests: Fail with test.pl-file-needs-have_solver()-call if forget have_solver() 2024-07-13 08:55:06 -04:00
Geza Lore 4cf017d7fe Tests: Skip t_randomize_method_constraints when no solver is installed (#5260) 2024-07-13 08:31:16 -04:00
Geza Lore 25f5db4b5f DFG: Allow inlining of variabels driven from forced vars (#5259)
Not sure why this was disabled before, but it seems legal to me to
change

'forced A' -> 'B' -> 'C'

into

'forced A' -> 'B',
'forced A' -> 'C'

Fixes #5249
2024-07-13 12:35:09 +01:00
Krzysztof Boroński 3cf9606ea9 Support inline constraints for class randomization methods (#5234)
Signed-off-by: Krzysztof Boronski <[email protected]>
2024-07-12 16:18:18 +02:00
Wilson Snyder 6882f8c55e Commentary: Surfer (#5253) 2024-07-11 18:33:35 -04:00
Krzysztof Bieganski 2696a9a5bd Fix unconstrained randomization of unpacked structs (#5252)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-07-11 16:43:56 +02:00
Krzysztof Bieganski abb46c9cb2 Refactor AstMemberSel creation with AstVar (#5251)
No functional change intended.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-07-11 16:42:58 +02:00
Wilson Snyder bbb223f217 Fix error on empty generate with -O0 (#5250). 2024-07-11 06:59:15 -04:00
Arkadiusz Kozdra 570e1bc35a Support conditional constraints (#5245) 2024-07-10 11:30:18 -04:00
11da07d3b9 Support $assertcontrol assertion_type (#5236)
Signed-off-by: Bartłomiej Chmiel <[email protected]>
Signed-off-by: Ryszard Rozak <[email protected]>
Co-authored-by: Ryszard Rozak <[email protected]>
Co-authored-by: Wilson Snyder <[email protected]>
2024-07-10 05:06:13 -04:00
Arkadiusz Kozdra 2cfec0ecc3 Support clocking blocks in virtual interfaces (#5235) 2024-07-09 18:31:58 -04:00
Wilson Snyder e3e7b3f2b5 Fix errors on using string in incorrect format (#5240). 2024-07-09 08:30:20 -04:00
Wilson Snyder fe0b210a0c Fix errors on using string in incorrect format (#5340). 2024-07-09 08:29:22 -04:00
Chris Bachhuber c383f4868f Commentary: Mention docker hub explicitly (#5241) 2024-07-09 06:59:43 -04:00
Wilson Snyder b66fdd7421 Fix unknown conversion on queues (#5220). 2024-07-08 10:19:51 -04:00
Wilson Snyder 83878325a7 Commentary: Update git issue templates. 2024-07-08 08:12:58 -04:00
github action 490bc63eb5 Apply 'make format' 2024-07-06 12:13:35 +00:00
Ryan Ziegler 947b6fd23f Add --emit-accessors (#5182) (#5227) 2024-07-06 13:12:53 +01:00
Wilson Snyder de44ca8df3 Fix top-level unpacked structure resets (#5221). 2024-07-05 17:22:26 -04:00
Wilson Snyder 3cee54dfdf Commentary: Changes update 2024-07-05 17:17:29 -04:00
Geza Lore dace2c0a9a Tests: Skip t_constraint_state when no solver is installed (#5224) 2024-07-04 11:42:53 +01:00
Liam Braun ca4858eb7f Fix concurrency for mailbox and semaphores (#5222) 2024-07-03 22:29:32 +01:00
Wilson SnyderandSeth Pellegrino a6d438d111 Fix mis-removing $value$plusargs calls (#5127) (#5137). [Seth Pellegrino]
Co-authored-by: Seth Pellegrino <[email protected]>
2024-07-02 18:46:58 -04:00
Bartłomiej Chmiel 955ed3f193 Fix splitting if statements with impure conditions (#5219) 2024-07-02 15:17:10 -04:00
Wilson Snyder 8bffb8c391 Fix table optimizing logic with side effect (#5137 prep) 2024-07-02 09:22:56 -04:00
Arkadiusz Kozdra 85356f464f State-dependent constraints (#5217)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-07-01 20:19:15 +02:00
Arkadiusz Kozdra 72993ec3dd Support cross-module clockvars access (#5184) 2024-06-30 15:19:02 -04:00
404allen404 d6a294b784 Commentary: Fix typo in for --trace-max-array and --trace-max-width options (#5205) 2024-06-27 21:25:21 -04:00
Bartłomiej Chmiel 864a852bca Add --compiler-include for additional C++ includes (#5139) (#5202) 2024-06-27 18:53:44 -04:00
Wilson Snyder b0992406aa Commentary: Changes update 2024-06-25 19:22:00 -04:00
Bartłomiej Chmiel 9e2c8aefc8 Add --pins-sc-uint-bool to force SystemC uint type (#5192) 2024-06-25 05:27:09 -04:00
Gus Smith 4c584c17e6 Add -std=gnu++20 (#5200) 2024-06-25 05:23:50 -04:00
Wilson Snyder 3315a6e431 Internals: Also cleanup gettes/setters in .cpp. No functional change intended. 2024-06-22 19:50:59 -04:00
github action ba0183131c Apply 'make format' 2024-06-22 22:58:42 +00:00
Ryan Ziegler e1580b9c3d Internals: Reorder class getters/setters (#5197). No functional change intended. 2024-06-22 18:57:54 -04:00
Tim Hutt aadd5ea509 Fix linking with pthreads on CMake (#5194) 2024-06-21 10:34:11 -04:00
Philip Axer 833df6d9ea Fix CMake installation missing verilated.mk (#5187) (#5188) 2024-06-19 08:07:17 -04:00
Wilson Snyder 607c19a67d Fix isPure to be superset of isOutputter.
This may cause some additional SIDEEFFECT warnings that previously were not shown.
2024-06-16 21:43:30 -04:00
Wilson Snyder d5cfe1a379 Fix fusing macro arguments to not ignore whitespace (#5061). 2024-06-15 09:21:21 -04:00
Wilson Snyder 6e557791c8 devel release 2024-06-15 08:49:57 -04:00
Wilson Snyder cd693ce02b Version bump 2024-06-15 08:43:45 -04:00
Wilson Snyder 2641bc98aa Update include/gtkwave from upstream 2024-06-15 08:14:43 -04:00
Wilson Snyder ad2862ce3f Fix DPI import of null C-string (#5179). 2024-06-14 22:50:54 -04:00
Bartłomiej Chmiel 4695967185 Fix signed types emitted in hierarchical Verilation (#5178)
Signed-off-by: Bartłomiej Chmiel <[email protected]>
2024-06-14 08:07:32 -04:00
Wilson Snyder f96e99542c Internals: Fix cppcheck warnings. No functional change. 2024-06-13 21:29:03 -04:00
Wilson Snyder 7de193c2e9 Commentary: Changes update 2024-06-13 18:04:57 -04:00
Arkadiusz Kozdra 442c9bc316 Support parsing and otherwise ignoring inline constraints (#5126) 2024-06-13 08:38:20 -04:00
Arkadiusz Kozdra 3203019408 Fix coroutines without awaits to have a co_return (#4208) (#5175)
After the V3Timing refactoring the V3SchedTiming phase could apparently
move away all awaits from a coroutine without adding a co_return
statement.

Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-06-12 15:13:52 -04:00
Geza Lore bc853e260b Support StructSel in unpacked array assignments (#5176) 2024-06-12 17:07:33 +01:00
github action d6e5703298 Apply 'make format' 2024-06-12 11:44:29 +00:00
Alex Solomatnikov a9e50327fd Fix hierarchical compilation with nested -F (#5114) (#5124) 2024-06-12 07:42:52 -04:00
Wilson Snyder 6f0a36318e Tests: Fix test stability (#5167 update). 2024-06-11 20:55:01 -04:00
Wilson Snyder 221fecad0b Commentary 2024-06-11 19:47:53 -04:00
Wilson Snyder fd1e4d9e45 Add assertion on reusing VerilatedContext (#5167). 2024-06-11 19:38:58 -04:00
Wilson Snyder 920ed5ca3d Internals: Avoid unordered_map/set. No functional change intended. 2024-06-11 18:52:09 -04:00
Arthur Rosa 2537431273 Fix table optimization when applied on real data type (#5172) (#5173) 2024-06-11 13:26:11 -04:00
Bartłomiej Chmiel 13114f2efe Fix SystemC BITS_PER_DIGIT in VL_ASSIGN_SBW (#5170)
The BITS_PER_DIGIT macro value differs between SystemC versions,
thus referencing it directly is required.
2024-06-10 20:45:49 -04:00
Arkadiusz Kozdra e9f59e44a5 Fix non-constant replication in concats (#5171)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-06-10 11:21:43 -04:00
Arkadiusz Kozdra d4c3e35f97 Support $psprintf system function (#4314) (#5169)
`$psprintf` is a non-standard system function present in some other
simulators, and has been rejected for standardization by IEEE because
of being basically the same as `$sformatf`.

To encourage users to fix their codebase, a warning is emitted by
default, but it gets otherwise interpreted as `$sformatf` as early as
during lexing.

Signed-off-by: Arkadiusz Kozdra <[email protected]>

* wording/formatting

Signed-off-by: Arkadiusz Kozdra <[email protected]>

---------

Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-06-10 08:38:26 -04:00
Wilson Snyder 02dd33b60e Commentary: Changes update 2024-06-09 17:10:53 -04:00
Geza Lore 2bc883f3b3 Extend out-of-range select (#5159) (#5164) 2024-06-09 17:05:14 -04:00
Geza Lore 29db25b70e Reset radix in width warnings (#5166) 2024-06-09 14:39:17 +01:00
Geza Lore d6bc0c712a Disable ASLR for more tests. (#5165)
These show spurious errors with some toolchains due to an address sanitizer bug.
2024-06-09 14:32:31 +01:00
github action 962efa3691 Apply 'make format' 2024-06-09 02:45:26 +00:00
Valentin Atepalikhin 4babba16d6 Support 2D dynamic array initialization (#4700) (#5122)
* Support 2D dynamic array initialization (#4700)
- new[] on sub arrays (as per original issue)
- Built-in methods for sub-arrays
- Initialization and literals assignmensts
- Dynamic array as an element for other arrays and queues
2024-06-08 22:44:45 -04:00
Wilson Snyder 5363d89870 Tests: Disable msbuild executable running (#5163) 2024-06-08 19:37:31 -04:00
Wilson Snyder 44acb08ea9 Tests: Commentary - enable msbuild debug 2024-06-08 19:36:50 -04:00
Yutetsu TAKATSUKASA 6584b4d426 Fix assertion failure in V3Gate (#5101) 2024-06-08 08:37:01 -04:00
Paul Bowen-Huggett 9ff06c1664 Fix 'experimental/coroutine' file not found on macOS (#5030) (#5031) (#5151)
libcxx has removed the experimental/coroutine include file in favor of
the C++20-standard coroutine include. If the latter is available we
use it otherwise falling back to the existing experimental version (in
which case we also disable the deprecated-experimental-coroutine warning).

(See also https://reviews.llvm.org/D108697.)
2024-06-08 08:33:50 -04:00
github action 1537c9badc Apply 'make format' 2024-06-08 03:05:01 +00:00
Wilson Snyder 708f875b31 Fix MSVC warnings. No functional change intended. 2024-06-07 22:52:56 -04:00
Kaleb Barrett 0d3cb86a44 Fix VerilatedVpiPutHolder class (#5156) 2024-06-07 22:25:33 -04:00
Wilson Snyder 5c2b9d55b2 Fix MSVC C4805 == type mismatch warning 2024-06-07 22:14:32 -04:00
Arkadiusz Kozdra 1dbf1be3e6 Support inout clocking items (#5160) 2024-06-07 08:30:58 -04:00
Wilson SnyderandDercury 3f886f7c61 Fix select value too wide (#5148) (#5153).
Co-authored-by: Dercury <[email protected]>
2024-06-07 08:27:03 -04:00
Wilson Snyder 7c9fa8647c Fix string to be more standard (#5082) (#5083). 2024-05-31 21:51:12 -04:00
Wilson Snyder dbf68a99e8 Tests: SHow expected values in t_typenames 2024-05-30 22:54:40 -04:00
Andrew NolteandAndrew Nolte 53c2e416f4 Tests: Extend t_typenames (#5083)
Co-authored-by: Andrew Nolte <[email protected]>
2024-05-30 22:32:14 -04:00
github action e834b5be2d Apply 'make format' 2024-05-29 20:56:45 +00:00
Kaleb Barrett bd7689f59a Fix overflow of string on VPI reads (#5145) (#5146) 2024-05-29 16:55:46 -04:00
Kaleb Barrett 3496eb80a5 Improve VerilatedVpiPutHolder storage requirements (#5144) 2024-05-29 08:39:08 -04:00
Ryszard Rozak 839c3bf444 Commentary: Fix docs of verilator_coverage (#5149)
Signed-off-by: Ryszard Rozak <[email protected]>
2024-05-29 08:11:35 -04:00
Geza Lore d4b3583307 Apply DFG regularization to cyclic graphs (#5142)
The Dfg2Ast conversion assumes the 'regularize' pass was run, but we
failed to run it on cyclic sub-graphs. Do so now.

Fixes #5130.
2024-05-26 12:01:30 +01:00
Wilson Snyder ee130cb20d Tests: Skip if no constraint solver 2024-05-23 22:25:14 -04:00
github action 552a146f9c Apply 'make format' 2024-05-22 11:54:22 +00:00
Pawel Jewstafjew 913679f261 Fix output C++ type error on change detect of I/O arrays (#5125) (#5131)
operand order reversed for AstCMethodHard "neq"
interface between C-style arrays and VlUnpacked
overloads added to VlUnpacked::neq(), VlUnpacked::assign()
VlUnpacked::operator=() added

Fixes  #5125
2024-05-22 12:53:28 +01:00
Ethan Sifferman d9078df650 Fix 4-state value support for $readmem (#5070) (#5078) 2024-05-21 17:27:32 -04:00
Wilson Snyder f84592af49 Fix x-valued parameters with --x-assign unique (#5129). 2024-05-21 08:07:57 -04:00
Wilson Snyder 5f85c89425 Commentary: Changes update 2024-05-18 17:48:52 -04:00
Wilson Snyder 94f789b677 Update issue template 2024-05-18 17:41:36 -04:00
Arkadiusz Kozdra 739be2f782 Support constrained randomization with external solvers (#4947) 2024-05-17 10:38:34 -04:00
Krzysztof Bieganski 25b9a16bc7 Fix references to ports in forks (#5123)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-05-17 07:38:36 -04:00
Arkadiusz Kozdra 9a8e68928d Internals: Move getenvStr to verilatedos. (#5118)
* Internals: Move getenvStr to verilatedos. No functional change intended.
* Fix POS34-C. Do not call putenv() with a pointer to an automatic variable as the argument.

Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-05-13 10:23:51 -04:00
Todd Strader 19cccd170e Fix vpiInertialDelay for memories (#5113) 2024-05-10 18:49:43 -04:00
Wilson Snyder d99c8f5d44 Fix redundant AstExtend breaks DFG (#5112) 2024-05-10 18:48:09 -04:00
Geza Lore cf111d2e1f Do not create aliases for forced port signals (#5105)
+ don't remove forced signals in V3Const and Dfg

Fixes #5062
2024-05-10 18:19:51 +01:00
Arkadiusz Kozdra 45eb5b8a5c Fix method calls parsing in constraints (#5110) 2024-05-10 06:37:19 -04:00
Yinan Xu ce5cad17a8 Add increasing of stack size when possible (#5071) (#5104) 2024-05-08 22:40:42 -04:00
Wilson Snyder ed01befc25 Fix tracing interface functions (#5108). 2024-05-08 22:23:53 -04:00
Wilson Snyder 69a2bfee68 Commentary: Changes update 2024-05-08 08:41:56 -04:00
Wilson Snyder 1cc37ad6ce Internals: Add missing VL_DO_DANGLING. No functional change intended. 2024-05-08 08:36:24 -04:00
2a9f29912c Add parameterless assert control system tasks (#5010)
Signed-off-by: Bartłomiej Chmiel <[email protected]>
Co-authored-by: Ryszard Rozak <[email protected]>
Co-authored-by: Wilson Snyder <[email protected]>
Co-authored-by: Arkadiusz Kozdra <[email protected]>
2024-05-08 08:31:34 -04:00
Wilson Snyder 298e0f24d1 Add traceCapable indication to model header (#5053). 2024-05-03 20:18:06 -04:00
Geza Lore 80b08b71aa Support NBAs to arrays inside loops (#5092)
For NBAs that might execute a dynamic number of times in a single
evaluation (specifically: those that assign to array elements inside
loops), we introduce a new run-time VlNBACommitQueue data-structure
(currently a vector), which stores all pending updates and the necessary
info to reconstruct the LHS reference of the AstAssignDly at run-time.

All variables needing a commit queue has their corresponding unique
commit queue.

All NBAs to a variable that requires a commit queue go through the
commit queue. This is necessary to preserve update order in sequential
code, e.g.:
 a[7] <= 10
 for (int i = 1 ; i < 10; ++i) a[i] <= i;
 a[2] <= 10
needs to end with array elements 1..9 being 1, 10, 3, 4, 5, 6, 7, 8, 9.

This enables supporting common forms of NBAs to arrays on the left hand
side of <= in non-suspendable/non-fork code. (Suspendable/fork
implementation is unclear to me so I left it unchanged, see #5084).

Any NBA that does not need a commit queue (i.e.: those that were
supported before), use the same scheme as before, and this patch should
have no effect on the generated code for those NBAs.
2024-05-03 07:45:49 -04:00
Vito Gamberini 64ba569f0d Fix MacOS missing <type_traits> header (#5096) (#5097) 2024-05-02 21:38:36 -04:00
Wilson Snyder 3cb4033c97 Fix width extension of unpacked array select (#5095). 2024-05-02 20:41:39 -04:00
github action 6509bac59e Apply 'make format' 2024-05-02 23:03:39 +00:00
Wilson Snyder ec45a77d93 Fix macro expansion in strings per 1800-2023 (#5094). 2024-05-02 19:02:28 -04:00
Wilson Snyder 8044833c74 Fix $typename on array.min and others (#5049). 2024-05-01 20:07:13 -04:00
Geza Lore 3f89bdcfac Defer conversion of set flag based AssignDlys (#5091)
No functional change. Postpone the conversion of all AstAssignDlys that
use the 'VdlySet' scheme for array LHSs until after the complete
traversal of the netlist. The next patch takes advantage of this by
using some extra information also gathered through the traversal to
change the conversion.

AstAssignDlys inside suspendable or fork are not deferred and are
processed identical to the previous version.

There are some TODOs in this patch that are fixed in the next patch.

Output code perturbed due to variable ordering.

MULTIDRIVEN message ordering perturbed due to processing order change.
2024-05-02 00:24:00 +01:00
Todd Strader c99364b81a Support vpiInertialDelay (#5087) 2024-05-01 18:56:50 -04:00
Geza Lore d841a791e6 Refactor V3Delayed to enable algorithmic extension (#5090)
No functional change.

This patch is just cleanup with some non-functional changes to enable
the next patch. Most importantly createDlyOnSet, which implements NBAs
for arrays, has a new streamlined implementation that does the same
thing. Some output code is perturbed due to statement/local variable
insertion order.

Also renamed Vdlyvfoo to VdlyFoo for easier readability of the generated
code.
2024-05-01 23:06:25 +01:00
Geza Lore b5b937e2f2 Fix missed optimization in V3Delayed (#5089)
Checking the wrong node meant we never actually pushed constant
bit-select indices into the delayed update, as was the intention, but
always generated a temporary instead.
2024-05-01 16:22:56 +01:00
Todd Strader 4a41f69293 Internals: Remove C-style cast in VPI_HANDLE (#5088) 2024-05-01 10:11:44 -04:00
Wilson Snyder 8fd038f88e Add --localize-max-size option and optimization (#5072). 2024-04-30 19:46:54 -04:00
Wilson Snyder 4982f63da2 Update coverage for last commit 2024-04-30 19:34:41 -04:00
Wilson Snyder 71bc60fb91 Add error on zero width select (#5028). 2024-04-30 18:38:37 -04:00
Wilson Snyder 0da7ecf753 Commentary: Changes update 2024-04-30 07:37:06 -04:00
Andrea Calabrese 25ca1af8b3 Internals: Fix missing const (#4899) 2024-04-29 22:57:56 -04:00
Wilson Snyder 5d54fa8e6f Fix missing parameters with comma to be errors (#4979) (#5012). 2024-04-29 22:41:16 -04:00
Christopher Taylor e8c5b9faad Fix missing flex include path variable (#4970) (#4971) 2024-04-29 22:01:09 -04:00
Aleksander Kiryk 8e0301c287 Fix bound queue printing (#5032) 2024-04-29 21:52:06 -04:00
Iztok Jeras 3f625fc359 Tests: Add unsupported streaming LHS tests (#4302) 2024-04-29 21:35:19 -04:00
Geza Lore 72b96d5069 tests: disable ASLR for t_trace_ub_misaligned_address (#5075)
This works around an address-sanitizer bug hit with some GCC versions
2024-04-29 15:38:00 +01:00
Bartłomiej Chmiel d1b45b8d32 Fix attempted to destroy locked thread pool error (#5040) 2024-04-29 07:59:24 -04:00
Wilson Snyder 5601056ed0 Tests: Check for bad event methods 2024-04-28 13:10:25 -04:00
Wilson Snyder 8ed269c77f Make 'disable isn't underneath a begin' into UNSUPPORTED error (#4699). 2024-04-27 21:30:40 -04:00
Geza Lore 27b7e70218 Fix DFG assertion with SystemC (#5076)
Fixes #5050
2024-04-27 13:41:10 +01:00
Geza Lore 97680656d3 Remove unknown '--' form cmake -E (#5073)
After #5063, with cmake 3.22.1, I get test failures, saying:
    cmake -E env: unknown option '--'
The '--' is necessary so removing.
2024-04-27 11:09:59 +01:00
Michael Bikovitsky 4202ec2ab2 Fix CMake builds to export VERILATOR_ROOT (#5063) 2024-04-26 19:29:10 -04:00
Wilson Snyder 4f3a816fb0 Fix false ASSIGNIN on functions with explicit port map (#5069). 2024-04-26 19:26:21 -04:00
Todd Strader 25fd8ef5c0 Add VPI eval needed tracking (#5065) 2024-04-25 09:07:31 -04:00
Gökçe Aydos a9664b8d16 Remove $(info) which cannot be silenced by -MAKEFLAGS '--quiet' (#5059) 2024-04-20 08:48:16 -05:00
Wilson Snyder 26a5729514 Add CITATION.cff (#5057) (#5058). 2024-04-19 20:33:11 -04:00
Arkadiusz Kozdra 5b839699ac Support empty queue as dynarray default value (#5055)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-04-18 11:53:23 -05:00
Wilson Snyder 1315aa31ed Commentary 2024-04-13 08:18:45 -04:00
Wilson Snyder 5d8da6b4ac Fix width extension on delays (#5045). 2024-04-13 08:16:59 -04:00
Wilson Snyder aac2563a07 Commentary: Changes update 2024-04-13 08:03:57 -04:00
Wilson Snyder 8e44487354 Tests: update style 2024-04-13 08:02:25 -04:00
Wilson Snyder 1012c054e6 Fix $system with string argument (#5042). 2024-04-11 17:31:14 -04:00
Paul Wright a8b5738b44 Support __en/__out signals on top level inout ports (#4812) (#4856) 2024-04-11 09:02:58 -04:00
Wilson Snyder 72845167e6 Commenary: Fix README badge paragraph 2024-04-10 17:02:07 -04:00
Krzysztof Bieganski 7ca2d6470a Fix consecutive zero-delays (#5038)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-04-05 16:48:47 -04:00
Wilson Snyder 33e999e01a devel release 2024-04-05 06:23:30 -04:00
683 changed files with 44069 additions and 42105 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
Checks: '*,-hicpp*,-android-cloexec-fopen,-cert-dcl50-cpp,-cert-env33-c,-cert-err34-c,-cert-err58-cpp,-clang-analyzer-core.UndefinedBinaryOperatorResult,-clang-analyzer-security*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-no-malloc,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-special-member-functions,-fuchsia-*,-google-default-arguments,-google-readability-todo,-google-runtime-references,-llvm-header-guard,-llvm-include-order,-misc-string-integer-assignment,-misc-string-literal-with-embedded-nul,-modernize-use-auto,-modernize-use-trailing-return-type,-readability-braces-around-statements,-readability-container-size-empty,-readability-delete-null-pointer,-readability-else-after-return,-readability-implicit-bool-conversion,-readability-named-parameter,-readability-static-accessed-through-instance'
Checks: '*,-hicpp*,-android-cloexec-fopen,-cert-dcl50-cpp,-cert-env33-c,-cert-err34-c,-cert-err58-cpp,-clang-analyzer-core.UndefinedBinaryOperatorResult,-clang-analyzer-security*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-no-malloc,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-special-member-functions,-fuchsia-*,-google-default-arguments,-google-readability-todo,-google-runtime-references,-llvm-header-guard,-llvm-include-order,-misc-string-integer-assignment,-misc-string-literal-with-embedded-nul,-modernize-use-auto,-modernize-use-trailing-return-type,-readability-braces-around-statements,-readability-container-size-empty,-readability-delete-null-pointer,-readability-else-after-return,-readability-implicit-bool-conversion,-readability-named-parameter,-readability-static-accessed-through-instance,-llvmlibc-*,-altera-*'
WarningsAsErrors: ''
HeaderFilterRegex: ''
FormatStyle: none
+2
View File
@@ -18,3 +18,5 @@ What 'verilator --version' are you using? Did you try it with the git master ve
What OS and distribution are you using?
May we assist you in trying to fix this in Verilator yourself?
(Please avoid attaching screenshots that show text - you can convert images to text using e.g. https://ocr.space)
+2
View File
@@ -11,6 +11,8 @@ Thanks for taking the time to report this.
What would you like added/supported?
What 'verilator --version' are you using? Did you try it with the git master version?
Can you attach an example that runs on other simulators? (Must be openly licensed, ideally in test_regress format.)
May we assist you in trying to fix this in Verilator yourself?
+2
View File
@@ -8,3 +8,5 @@ assignees: ''
---
How may we help - what is your question?
(If reporting a bug or requesting a feature please use one of the other issue templates.)
+7
View File
@@ -96,3 +96,10 @@ jobs:
env:
TESTS: coverage-${{ matrix.test }}${{ matrix.num }}
run: ./ci/ci-script.bash
- name: Upload coverage data to Codecov
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: |
find . -name '*.gcno' -exec rm {} \;
./ci/codecov -v upload-process -Z --sha ${{ github.sha }} -f nodist/obj_dir/coverage/app_total.info
+3
View File
@@ -46,3 +46,6 @@ verilator-config-version.cmake
/test_regress/snapshot/
xmverilog.*
xrun.history
# Normal CMake build directory
/build
+27
View File
@@ -0,0 +1,27 @@
# See https://citation-file-format.github.io/
cff-version: 1.2.0
title: Verilator
message: >-
If you use this software, please cite it using the
metadata from this file.
type: software
authors:
- given-names: Wilson
family-names: Snyder
email: [email protected]
affiliation: Veripool
- given-names: Paul
family-names: Wasson
- given-names: Duane
family-names: Galbi
- name: 'et al'
repository-code: 'https://github.com/verilator/verilator'
url: 'https://verilator.org'
abstract: >-
The Verilator package converts Verilog and SystemVerilog hardware
description language (HDL) designs into a fast C++ or SystemC model
that, after compiling, can be executed. Verilator is not a
traditional simulator but a compiler.
license:
- LGPL-3.0-only
- Artistic-2.0
+8 -3
View File
@@ -15,7 +15,7 @@
cmake_minimum_required(VERSION 3.15)
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
project(Verilator
VERSION 5.024
VERSION 5.028
HOMEPAGE_URL https://verilator.org
LANGUAGES CXX
)
@@ -24,12 +24,13 @@ option(DEBUG_AND_RELEASE_AND_COVERAGE
"Builds both the debug and release binaries, overriding CMAKE_BUILD_TYPE. Not supported under MSBuild.")
find_package(Python3 COMPONENTS Interpreter)
set(PYTHON3 Python3::Interpreter)
set(PYTHON3 ${Python3_EXECUTABLE})
set(CMAKE_INSTALL_DATADIR ${CMAKE_INSTALL_PREFIX})
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
include(CheckStructHasMember)
include(ExternalProject)
include(FindThreads)
if (NOT WIN32)
message(WARNING "CMake support on Linux/OSX is experimental.")
@@ -95,10 +96,14 @@ endif()
set(PACKAGE_NAME ${PROJECT_NAME})
set(PACKAGE_VERSION ${PROJECT_VERSION})
set(CXX ${CMAKE_CXX_COMPILER})
set(AR ${CMAKE_AR})
configure_file(include/verilated_config.h.in include/verilated_config.h @ONLY)
configure_file(include/verilated.mk.in include/verilated.mk @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/verilated_config.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/verilated.mk DESTINATION ${CMAKE_INSTALL_PREFIX}/include)
configure_package_config_file(verilator-config.cmake.in verilator-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}
@@ -118,6 +123,7 @@ foreach (program
verilator_ccache_report
verilator_difftree
verilator_profcfunc
verilator_includer
)
install(PROGRAMS bin/${program} TYPE BIN)
endforeach()
@@ -130,7 +136,6 @@ install(DIRECTORY examples TYPE DATA FILES_MATCHING
install(DIRECTORY include TYPE DATA FILES_MATCHING
PATTERN "include/verilated_config.h"
PATTERN "include/verilated.mk"
PATTERN "include/*.[chv]"
PATTERN "include/*.cpp"
PATTERN "include/*.sv"
+155 -1
View File
@@ -8,6 +8,158 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 5.028 2024-08-21
==========================
**Minor:**
* Support state-dependent constraints (#5217). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support cross-module clocking variable access (#5184). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support inline constraints for class randomization methods (#5234). [Krzysztof Boroński]
* Support clocking blocks in virtual interfaces (#5235). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support `$assertcontrol` assertion_type (#5236). [Bartłomiej Chmiel, Antmicro Ltd.]
* Support conditional constraints (#5245). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support`--compiler-include` headers in user-supplied cpp files (#5271). [Bartłomiej Chmiel, Antmicro Ltd.]
* Support `rand_mode` (#5273). [Krzysztof Bieganski, Antmicro Ltd.]
* Support `this.randomize with` (#5282). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support foreach constraints (#5302). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support `parameter type` in hierarchical blocks (#5309) (#5333). [Bartłomiej Chmiel, Antmicro Ltd.]
* Support assertcontrol directive type (#5310). [Bartłomiej Chmiel, Antmicro Ltd.]
* Support inline random variable control (#5317). [Krzysztof Bieganski, Antmicro Ltd.]
* Support streaming operator on arrays and wide data (#5326). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support streams to/from arrays of wide data (#5334). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support `constraint_mode` (#5338). [Krzysztof Bieganski, Antmicro Ltd.]
* Support constraining AstSel (#5344). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support default value on module input (#5358) (#5373). [Drew Ranck]
* Add `--compiler-include` for additional C++ includes (#5139) (#5202). [Bartłomiej Chmiel, Antmicro Ltd.]
* Add `--emit-accessors` (#5182) (#5227). [Ryan Ziegler]
* Add suggestions on misspelled PLI functions.
* Add warning on dist in constraints (#5264). [Arkadiusz Kozdra, Antmicro Ltd.]
* Add more `rand_mode` unsupported errors (#5329). [Krzysztof Bieganski, Antmicro Ltd.]
* Add parsing but otherwise ignore std::randomize (#5354). [Arkadiusz Kozdra, Antmicro Ltd.]
* Add Verilated cc define when `--timing` used (#5383). [Kaleb Barrett]
* Improve emitted code to use a reference for VlSelf (#5254). [Yangyu Chen]
* Fix monitor block sensitivity items (#4400) (#5294). [Udaya Raj Subedi]
* Fix fusing macro arguments to not ignore whitespace (#5061). [Tudor Timi]
* Fix optimized-out sensitivity trees with `--timing` (#5080) (#5349). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix classes/modules of case-similar names (#5109). [Arkadiusz Kozdra]
* Fix mis-removing $value$plusargs calls (#5127) (#5137). [Seth Pellegrino]
* Fix incorrect result of width mismatch (#5186) (#5189). [Yutetsu TAKATSUKASA]
* Fix compiler coroutine check (#5190) (#5300). [Ricardo Barbedo]
* Fix shortened module names when searching for files (#5196) (#5246). [Tim Hutt]
* Fix `--x-assign` to be independent from `+verilator+rand+reset` (#5214). [Andrew Nolte]
* Fix splitting if statements with impure conditions (#5219). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix unknown conversion on queues (#5220). [Alex Solomatnikov]
* Fix top-level unpacked structure resets (#5221).
* Fix concurrency for mailbox and semaphores (#5222). [Liam Braun]
* Fix forks capturing non-input ports in tasks (#5237) (#5343). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix toggle coverage aggregation on same line (#5248). [Krzysztof Obłonczek]
* Fix error on empty generate with -O0 (#5250).
* Fix unconstrained randomization of unpacked structs (#5252). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix inlining of variables driven from forced vars (#5259). [Geza Lore]
* Fix tracing with `--main-top-name -` (#5261). [Ethan Sifferman]
* Fix randomization when used with inheritance (#5268). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix inline constraints creating class random generator (#5280). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix WIDTHEXPAND on left shift of intuitive amount (#5284). [Greg Taylor]
* Fix elaborating foreach loops (#5285). [Arkadiusz Kozdra, Antmicro Ltd.]
* Fix initializing static array in dynamic arrays and queues (#5287). [Baruch Sterin]
* Fix static variable initializers in procedures (#5296). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix randomizing current object with `rand` class instance member (#5292). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix handling of rand fields not referenced in constraints (#5305). [Ryszard Rozak, Antmicro Ltd.]
* Fix Python3 path discovery in make flows to avoid mixing system and user python interpreters (#5307) [Markus Krause]
* Fix make flows to pass PYTHON3 (like PERL) (#5307) (#5308). [Markus Krause]
* Fix assert on wide expression (#5319) (#5324). [Varun Koyyalagunta]
* Fix output clock variable overwriting signal (#5320) (#5347). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix stringify in nested preprocessor macros (#5323). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix $sformat with array arguments (#5330). [Abe Jordan]
* Fix -Wunused-but-set-variable clang warning (#5331). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix purity of functions with AstJumpBlock or AstStmtExpr (#5332). [Ryszard Rozak, Antmicro Ltd.]
* Fix compilation error on unreachable disable fork / wait fork (#5339). [Arkadiusz Kozdra, Antmicro Ltd.]
* Fix missing type coercion in 'inside {array}' (#5340). [Arkadiusz Kozdra, Antmicro Ltd.]
* Fix +: and -: unpacked array slicing when array has nonzero low index (#5345) (#5387). [James Bailey]
* Fix tracing_{on,off} in the presence of non-inlined modules (#5346). [Geza Lore]
* Fix NBAs in suspendables (#5348). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix lint_off on Errors (#5351) (#5363). [Ethan Sifferman]
* Fix cache config file resolution performance (#5369). [Geza Lore]
* Fix capturing fields from superclass in `randomize() with` (#5389). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix virtual interface null checks (#5391). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix ==? and !=? with X values.
* Fix CPU time being zero.
* Fix inline function ref port persistence.
Verilator 5.026 2024-06-15
==========================
**Major:**
* Support constrained randomization with external solvers (#4947). [Arkadiusz Kozdra, Antmicro Ltd.]
**Minor:**
* Support `$psprintf` system function (#4314) (#5169). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support 2D dynamic array initialization (#4700) (#5122). [Valentin Atepalikhin]
* Support __en/__out signals on top level inout ports (#4812) (#4856). [Paul Wright]
* Support empty queue as dynarray default value (#5055). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support vpiInertialDelay (#5087). [Todd Strader]
* Support NBAs to arrays inside loops (#5092). [Geza Lore]
* Support parsing and otherwise ignoring inline constraints (#5126). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support `inout` clocking items (#5160). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support StructSel in unpacked array assignments (#5176). [Geza Lore]
* Add error on zero width select (#5028).
* Add CITATION.cff (#5057) (#5058). [Gijs Burghoorn]
* Add VPI eval needed tracking (#5065). [Todd Strader]
* Add `--localize-max-size` option and optimization (#5072).
* Add parameterless assert control system tasks (#5010). [Bartłomiej Chmiel]
* Add traceCapable indication to model header (#5053). [Vito Gamberini]
* Add increasing of stack size when possible (#5071) (#5104). [Yinan Xu]
* Add assertion on reusing VerilatedContext (#5167).
* Add `--pins-sc-uint-bool` to force SystemC uint type (#5192). [Bartłomiej Chmiel, Antmicro Ltd.]
* Improve DFG regularization in cyclic graphs (#5142). [Geza Lore]
* Improve VerilatedVpiPutHolder storage requirements (#5144). [Kaleb Barrett]
* Fix coroutines without awaits to have a co_return (#4208) (#5175). [Arkadiusz Kozdra, Antmicro Ltd.]
* Fix missing flex include path variable (#4970) (#4971). [Christopher Taylor]
* Fix missing parameters with comma to be errors (#4979) (#5012). [Paul Swirhun]
* Fix 'experimental/coroutine' file not found on MacOS (#5030) (#5031) (#5151). [Paul Bowen-Huggett]
* Fix bound queue printing (#5032). [Aleksander Kiryk, Antmicro Ltd.]
* Fix consecutive zero-delays (#5038). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix attempted to destroy locked thread pool error (#5040). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix `$system` with string argument (#5042).
* Fix width extension on delays (#5043).
* Fix `$typename` on array.min and others (#5049). [Gökçe Aydos]
* Fix `make $(info)` which cannot be silenced (#5059). [Gökçe Aydos]
* Fix CMake builds to export VERILATOR_ROOT (#5063). [Michael Bikovitsky]
* Fix false ASSIGNIN on functions with explicit port map (#5069).
* Fix 4-state value support for $readmem (#5070) (#5078). [Ethan Sifferman]
* Fix DFG assertion with SystemC (#5076). [Geza Lore]
* Fix `$typename` string to be more standard (#5082) (#5083). [Andrew Nolte]
* Fix missed optimization in V3Delayed (#5089). [Geza Lore]
* Fix macro expansion in strings per 1800-2023 (#5094). [Geza Lore]
* Fix width extension of unpacked array select (#5095). [Varun Koyyalagunta]
* Fix MacOS missing <type_traits> header (#5096) (#5097). [Vito Gamberini]
* Fix assertion failure in V3Gate (#5101). [Yutetsu TAKATSUKASA]
* Fix aliases for forced port signals (#5105). [Geza Lore]
* Fix tracing interface functions (#5108). [Alex Solomatnikov]
* Fix method calls parsing in constraints (#5110). [Arkadiusz Kozdra, Antmicro Ltd.]
* Fix vpiInertialDelay for memories (#5113). [Todd Strader]
* Fix hierarchical compilation with nested -F (#5114) (#5124). [Alex Solomatnikov]
* Fix references to ports in forks (#5123). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix output C++ type error on change detect of I/O arrays (#5125) (#5131). [Pawel Jewstafjew]
* Fix x-valued parameters with `--x-assign unique` (#5129). [Ethan Sifferman]
* Fix overflow of string on VPI reads (#5145) (#5146). [Kaleb Barrett]
* Fix VerilatedVpiPutHolder class (#5156). [Kaleb Barrett]
* Fix extending out-of-range select (#5159) (#5164). [Geza Lore]
* Fix radix in width warnings (#5166). [Geza Lore]
* Fix SystemC BITS_PER_DIGIT in VL_ASSIGN_SBW (#5170). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix non-constant replication in concats (#5171). [Arkadiusz Kozdra, Antmicro Ltd.]
* Fix table optimization when applied on real data type (#5172) (#5173). [Arthur Rosa]
* Fix signed types emitted in hierarchical Verilation (#5178). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix DPI import of null C-string (#5179).
* Fix CMake installation missing verilated.mk (#5187) (#5188). [Philip Axer]
* Fix linking with pthreads on CMake (#5194). [Tim Hutt]
* Fix clang-17 coroutines configuration with -std=gnu++20 (#5200). [Gus Smith]
Verilator 5.024 2024-04-05
==========================
@@ -180,6 +332,7 @@ Verilator 5.020 2024-01-01
* Fix dynamic NBA conditions (#4773). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix `V3Fork` stage to run only if `--timing` is set (#4778). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix max multiply width and add runtime assertions if too small. (#4781)
* Fix select value too wide (#5148) (#5153). [Dercury]
Verilator 5.018 2023-10-30
@@ -552,7 +705,7 @@ Verilator 5.006 2023-01-22
**Minor:**
* Support clocking blocks (#3674). [Krzysztof Bieganski, Antmicro Ltd]
* Support packed structs (#3802). [Aleksander Kiryk, Antmicro Ltd]
* Support unpacked structs (#3802). [Aleksander Kiryk, Antmicro Ltd]
* Support Windows-native builds using cmake (#3814). [Kritik Bhimani]
* Support p format for UnpackArray (#3877). [Aleksander Kiryk, Antmicro Ltd]
* Support property calls without parenthesis (#3879) (#3893). [Ryszard Rozak, Antmicro Ltd]
@@ -1443,6 +1596,7 @@ Verilator 4.026 2020-01-11
* Fix expand optimization slowing --lint-only. Closes #2091. [Thomas Watts]
* Fix %{number}s with strings. #2093. [agrobman]
* Fix shebang breaking some shells. Closes #2067. [zdave]
* Fix errors on using string in incorrect format (#5240). [John Demme]
Verilator 4.024 2019-12-08
+2
View File
@@ -91,6 +91,7 @@ datarootdir = @datarootdir@
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
CFG_WITH_SOLVER = @CFG_WITH_SOLVER@
PACKAGE_VERSION = @PACKAGE_VERSION@
#### End of system configuration section. ####
@@ -446,6 +447,7 @@ PY_PROGRAMS = \
nodist/fuzzer/actual_fail \
nodist/fuzzer/generate_dictionary \
nodist/install_test \
nodist/log_changes \
PY_FILES = \
$(PY_PROGRAMS) \
+15 -14
View File
@@ -1,21 +1,23 @@
.. Github doesn't render images unless absolute URL
.. Do not know of a conditional tag, "only: github" nor "github display" works
.. image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|badge1| |badge2| |badge3| |badge4| |badge5| |badge6| |badge7| |badge8|
.. |badge1| image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
:target: https://verilator.org
.. image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
.. |badge2| image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
:target: https://www.gnu.org/licenses/lgpl-3.0
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
.. |badge3| image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
:target: https://opensource.org/licenses/Artistic-2.0
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
.. |badge4| image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
:target: https://repology.org/project/verilator/versions
.. 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
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
.. |badge6| image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
:target: https://www.codacy.com/gh/verilator/verilator
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
.. |badge7| image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
:target: https://codecov.io/gh/verilator/verilator
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
.. |badge8| image:: https://github.com/verilator/verilator/workflows/build/badge.svg
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
@@ -85,12 +87,11 @@ thread is about 100 times faster than interpreted Verilog simulators such
as `Icarus Verilog`_. Another 2-10x speedup might be gained from
multithreading (yielding 200-1000x total over interpreted simulators).
Verilator has typically similar or better performance versus
closed-source Verilog simulators (e.g., Carbon Design Systems Carbonator,
Modelsim/Questa, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and
Pragmatic CVer/CVC). But, Verilator is open-sourced, so you can spend on
computes rather than licenses. Thus, Verilator gives you the best
simulation cycles/dollar.
Verilator has typically similar or better performance versus closed-source
Verilog simulators (e.g., Aldec Riviera-Pro, Cadence Incisive/NC-Verilog,
Mentor ModelSim/Questa, Synopsys VCS, VTOC, and Pragmatic CVer/CVC). But,
Verilator is open-sourced, so you can spend on computes rather than
licenses. Thus, Verilator gives you the best simulation cycles/dollar.
Installation & Documentation
+8 -2
View File
@@ -324,6 +324,7 @@ detailed descriptions of these arguments.
--clk <signal-name> Mark specified signal as clock
--no-clk <signal-name> Prevent marking specified signal as clock
--compiler <compiler-name> Tune for specified C++ compiler
--compiler-include Include additional header in the precompiled one
--converge-limit <loops> Tune convergence settle time
--coverage Enable all coverage
--coverage-line Enable line coverage
@@ -356,6 +357,7 @@ detailed descriptions of these arguments.
--dumpi-tree-json <level> Enable dumping Ast .tree.json files at level
--dumpi-<srcfile> <level> Enable dumping everything in source file at level
-E Preprocess, but do not compile
--emit-accessors Emit getter and setter methods for model top class
--error-limit <value> Abort after this number of errors
--exe Link to create executable
--expand-limit <value> Set expand optimization limit
@@ -375,6 +377,7 @@ detailed descriptions of these arguments.
--get-supported <feature> Get if feature is supported
--help Show this help
--hierarchical Enable hierarchical Verilation
--hierarchical-params-file <name> Internal option that specifies parameters file for hier blocks
-I<dir> Directory to search for includes
--if-depth <value> Tune IFDEPTH warning
+incdir+<dir> Directory to search for includes
@@ -387,6 +390,7 @@ detailed descriptions of these arguments.
--lib-create <name> Create a DPI library
+libext+<ext>+[ext]... Extensions for finding modules
--lint-only Lint, but do not make output
--localize-max-size <value> Tune localize optimization variable size
--make <build-tool> Generate scripts for specified build tool
-MAKEFLAGS <flags> Arguments to pass to make during --build
--main Generate C++ main() file
@@ -406,8 +410,10 @@ detailed descriptions of these arguments.
--output-split-ctrace <statements> Split tracing functions
-P Disable line numbers and blanks with -E
--pins-bv <bits> Specify types for top-level ports
--pins-inout-enables Specify that __en and __out signals be created for inouts
--pins-sc-biguint Specify types for top-level ports
--pins-sc-uint Specify types for top-level ports
--pins-sc-uint-bool Specify types for top-level ports
--pins-uint8 Specify types for top-level ports
--no-pins64 Don't use uint64_t's for 33-64 bit sigs
--pipe-filter <command> Filter all input through a script
@@ -457,8 +463,8 @@ detailed descriptions of these arguments.
--trace-coverage Enable tracing of coverage
--trace-depth <levels> Depth of tracing
--trace-fst Enable FST waveform creation
--trace-max-array <depth> Maximum bit width for tracing
--trace-max-width <width> Maximum array depth for tracing
--trace-max-array <depth> Maximum array depth for tracing
--trace-max-width <width> Maximum bit width for tracing
--trace-params Enable tracing of parameters
--trace-structs Enable tracing structure names
--trace-threads <threads> Enable FST waveform creation on separate threads
+2 -2
View File
@@ -154,9 +154,9 @@ verilator_coverage - Verilator coverage analyzer
verilator_coverage --annotate <obj>
verilator_coverage -write merged.dat -read <datafiles>...
verilator_coverage -write merged.dat <datafiles>...
verilator_coverage -write-info merged.info -read <datafiles>...
verilator_coverage -write-info merged.info <datafiles>...
Verilator_coverage processes Verilated model-generated coverage reports.
+4 -4
View File
@@ -92,8 +92,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
sudo apt-get update ||
sudo apt-get update
# libfl-dev needed for internal coverage's test runs
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq ||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3 ||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3
# Required for test_regress/t/t_dist_attributes.pl
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
sudo apt-get install python3-clang mold ||
@@ -106,10 +106,10 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
elif [ "$CI_OS_NAME" = "osx" ]; then
brew update
# brew cask install gtkwave # fst2vcd hangs at launch, so don't bother
brew install ccache perl jq
brew install ccache perl jq z3
elif [ "$CI_OS_NAME" = "freebsd" ]; then
# fst2vcd fails with "Could not open '<input file>', exiting."
sudo pkg install -y ccache gmake perl5 python3 jq
sudo pkg install -y ccache gmake perl5 python3 jq z3
else
fatal "Unknown os: '$CI_OS_NAME'"
fi
+1 -4
View File
@@ -192,13 +192,10 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
fatal "Unknown test: $TESTS"
;;
esac
# 22.04: ccache -s -v
ccache -s
# Upload coverage data
if [[ $TESTS == coverage-* ]]; then
bash <(cat ci/coverage-upload.sh) -f nodist/obj_dir/coverage/app_total.info
fi
else
##############################################################################
# Unknown build stage
+13
View File
@@ -1,3 +1,15 @@
# DESCRIPTION: Verilator: CI Windows Power Shell - Compile Verilator
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
#
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
Set-PSDebug -Trace 1
if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
git clone --depth 1 https://github.com/lexxmark/winflexbison
cd winflexbison
@@ -8,6 +20,7 @@ if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
cmake --install . --prefix $PWD/../../../.ccache
cd ../..
}
mkdir build
cd build
cmake .. --install-prefix $PWD/../install
+17 -1
View File
@@ -1,3 +1,16 @@
# DESCRIPTION: Verilator: CI Windows Power Shell - Verilate a test
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
#
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
Set-PSDebug -Trace 1
cd install
$Env:VERILATOR_ROOT=$PWD
cd examples/cmake_tracing_c
@@ -5,6 +18,9 @@ mkdir build
cd build
cmake ..
cmake --build . --config Release -j 3
Release/example.exe
# TODO put this back in, see issue# 5163
# Release/example.exe
cd ..
Remove-Item -path build -recurse
Executable
BIN
View File
Binary file not shown.
File diff suppressed because it is too large Load Diff
+1
View File
@@ -43,6 +43,7 @@ RUN apt-get update \
perl \
python3 \
wget \
z3 \
zlib1g \
zlib1g-dev \
&& apt-get clean \
+3 -2
View File
@@ -11,10 +11,11 @@ easily as a docker image, e.g.:
docker run -ti verilator/verilator:latest --version
This will install the container, run the latest Verilator and print
This will pull the container from `docker hub
<https://hub.docker.com/r/verilator/verilator>`_, run the latest Verilator and print
Verilator's version.
Containers are automatically built for all released versions, so you may
Containers are automatically built and pushed to docker hub for all released versions, so you may
easily compare results across versions, e.g.:
::
+66 -27
View File
@@ -5,12 +5,12 @@
# General Public License Version 3 or the Perl Artistic License Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# When releasing, also update header of Changes file, and CmakeLists.txt,
# When releasing, also update header of Changes file, and CMakeLists.txt,
# and commit using "devel release" or "Version bump" message
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.024 2024-04-05],
AC_INIT([Verilator],[5.028 2024-08-21],
[https://verilator.org],
[verilator],[https://verilator.org])
@@ -134,6 +134,28 @@ AC_ARG_ENABLE([longtests],
AC_SUBST(CFG_WITH_LONGTESTS)
AC_MSG_RESULT($CFG_WITH_LONGTESTS)
AC_CHECK_PROG(HAVE_Z3,z3,yes)
AC_CHECK_PROG(HAVE_CVC5,cvc5,yes)
AC_CHECK_PROG(HAVE_CVC4,cvc4,yes)
# Special Substitutions - CFG_WITH_SOLVER
AC_MSG_CHECKING(for SMT solver)
AC_ARG_WITH([solver],
[AS_HELP_STRING([--with-solver='z3 --in'],
[set default SMT solver for constrained randomization])],
[CFG_WITH_SOLVER="${withval}"],
[CFG_WITH_SOLVER=no
if test "x$HAVE_Z3" = "xyes"; then
CFG_WITH_SOLVER="z3 --in"
elif test "x$HAVE_CVC5" = "xyes"; then
CFG_WITH_SOLVER="cvc5 --incremental"
elif test "x$HAVE_CVC4" = "xyes"; then
CFG_WITH_SOLVER="cvc4 --lang=smt2 --incremental"
fi]
)
AC_SUBST(CFG_WITH_SOLVER)
AC_MSG_RESULT($CFG_WITH_SOLVER)
# Compiler flags (ensure they are not empty to avoid configure defaults)
CFLAGS="$CFLAGS "
CPPFLAGS="$CPPFLAGS "
@@ -266,6 +288,43 @@ AC_DEFUN([_MY_CXX_CHECK_OPT],
_MY_CXX_CHECK_IFELSE($2, $1="$$1 $2")
])
AC_DEFUN([_MY_CXX_CHECK_CORO_SET],
[# _MY_CXX_CHECK_CORO_SET(variable,option)
# Check if compiler supports coroutines with specific option. If it does,
# set variable to option, only if not previously set.
# Define HAVE_COROUTINES if supported.
if test "$$1" = ""; then
if test "$2" != ""; then
_my_msg_opt=" with $2"
else
_my_msg_opt=""
fi
AC_MSG_CHECKING([whether coroutines are supported by $CXX$_my_msg_opt])
ACO_SAVE_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS $2"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([
#if defined(__clang__) && !defined(__cpp_impl_coroutine)
#define __cpp_impl_coroutine 1
#endif
#include <coroutine>
],[[std::coroutine_handle<> h;]])],
[_my_result=yes],
[AC_LINK_IFELSE(
[AC_LANG_PROGRAM(
[#include <experimental/coroutine>],
[[std::experimental::coroutine_handle<> h;]])],
[_my_result=yes],
[_my_result=no])])
AC_MSG_RESULT($_my_result)
CXXFLAGS="$ACO_SAVE_CXXFLAGS"
if test "$_my_result" = "yes" ; then
$1="$2"
AC_DEFINE([HAVE_COROUTINES],[1],[Defined if coroutines are supported by $CXX])
fi
fi
])
AC_DEFUN([_MY_LDLIBS_CHECK_FLAG],
[# _MY_LDLIBS_CHECK_FLAG(flag) -- Check if linker supports specific options
# Set $_my_result appropriately
@@ -351,32 +410,12 @@ _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_WEXTRA,-Wthread-safety)
AC_SUBST(CFG_CXXFLAGS_WEXTRA)
# Flags for coroutine support for dynamic scheduling
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines-ts)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines-ts -Wno-deprecated-experimental-coroutine)
_MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES, "")
_MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines-ts)
_MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines)
_MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES,-fcoroutines-ts -Wno-deprecated-experimental-coroutine)
_MY_CXX_CHECK_CORO_SET(CFG_CXXFLAGS_COROUTINES,-std=gnu++20)
AC_SUBST(CFG_CXXFLAGS_COROUTINES)
# HAVE_COROUTINES
# Check if coroutines are supported at all
AC_MSG_CHECKING([whether coroutines are supported by $CXX])
ACO_SAVE_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS $CFG_CXXFLAGS_COROUTINES"
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([
#ifdef __clang__
#define __cpp_impl_coroutine 1
#endif
#include <coroutine>
],[[]])],
[_my_result=yes
AC_DEFINE([HAVE_COROUTINES],[1],[Defined if coroutines are supported by $CXX])],
[AC_LINK_IFELSE(
[AC_LANG_PROGRAM([#include <experimental/coroutine>],[[]])],
[_my_result=yes
AC_DEFINE([HAVE_COROUTINES],[1],[Defined if coroutines are supported by $CXX])],
[_my_result=no])])
AC_MSG_RESULT($_my_result)
CXXFLAGS="$ACO_SAVE_CXXFLAGS"
AC_SUBST(HAVE_COROUTINES)
# Flags for compiling Verilator internals including parser always
+22
View File
@@ -9,20 +9,25 @@ Adrien Le Masle
Ahmed El-Mahmoudy
Aleksander Kiryk
Alex Chadwick
Alex Solomatnikov
Àlex Torregrosa
Aliaksei Chapyzhenka
Ameya Vikram Singh
Andrea Calabrese
Andreas Kuster
Andrei Kostovski
Andrew Miloradovsky
Andrew Nolte
Anthony Donlon
Arkadiusz Kozdra
Arthur Rosa
Aylon Chaim Porat
Bartłomiej Chmiel
Cameron Kirk
Chih-Mao Chen
Chris Bachhuber
Chris Randall
Christopher Taylor
Chuxuan Wang
Chykon
Conor McCullough
@@ -33,6 +38,7 @@ David Ledger
David Metz
David Stanford
David Turner
Dercury
Don Williamson
Drew Ranck
Drew Taussig
@@ -51,6 +57,7 @@ G-A. Kamendje
Garrett Smith
Geza Lore
Gianfranco Costamagna
Gijs Burghoorn
Glen Gibb
Gökçe Aydos
Graham Rushton
@@ -70,6 +77,7 @@ Iru Cai
Ivan Vnučec
Iztok Jeras
Jake Merdich
James Bailey
James Hanlon
James Hutchinson
James Pallister
@@ -112,8 +120,11 @@ Kritik Bhimani
Krzysztof Bieganski
Krzysztof Boronski
Krzysztof Boroński
Krzysztof Obłonczek
Kuba Ober
Larry Doolittle
Liam Braun
Luca Colagrande
Ludwig Rogiers
Lukasz Dalek
Maarten De Braekeleer
@@ -127,6 +138,7 @@ Marshal Qiao
Martin Schmidt
Martin Stadler
Matthew Ballance
Michael Bikovitsky
Michael Killough
Michaël Lefebvre
Michal Czyz
@@ -141,13 +153,16 @@ Nathan Myers
Nolan Poe
Oleh Maksymenko
Patrick Stewart
Paul Bowen-Huggett
Paul Swirhun
Paul Wright
Pawel Jewstafjew
Pawel Sagan
Pengcheng Xu
Peter Debacker
Peter Horvath
Peter Monsson
Philip Axer
Philipp Wagner
Pierre-Henri Horrein
Pieter Kapsenberg
@@ -157,15 +172,18 @@ Quentin Corradi
Rafal Kapuscik
Rasfunk
Raynard Qiao
Ricardo Barbedo
Richard Myers
Risto Pejašinović
Robert Balas
Rupert Swarbrick
Ryan Ziegler
Ryszard Rozak
Samuel Riedel
Sean Cross
Sebastien Van Cauwenberghe
Sergi Granell
Seth Pellegrino
Srinivasan Venkataramanan
Stefan Wallentowitz
Stephen Henry
@@ -185,6 +203,8 @@ Tudor Timi
Tymoteusz Blazejczyk
Udi Finkelstein
Unai Martinez-Corral
Udaya Raj Subedi
Valentin Atepalikhin
Varun Koyyalagunta
Vassilis Papaefstathiou
Veripool API Bot
@@ -194,6 +214,7 @@ William D. Jones
Wilson Snyder
Xi Zhang
Yan Xu
Yangyu Chen
Yinan Xu
Yoda Lee
Yossi Nivin
@@ -205,3 +226,4 @@ Zhanglei Wang
Zhou Shen
Zixi Li
أحمد المحمودي
404allen404
+1 -1
View File
@@ -1,6 +1,6 @@
.. comment: generated by t_lint_multidriven_bad
.. code-block::
%Warning-MULTIDRIVEN: example.v:1:22 Signal has multiple driving blocks with different clocking: 'out2'
%Warning-MULTIDRIVEN: example.v:1:22 Signal has multiple driving blocks with different clocking: 't.mem'
example.v:1:7 ... Location of first driving block
example.v:1:7 ... Location of other driving block
+11
View File
@@ -0,0 +1,11 @@
.. comment: generated by t_lint_pinmissing_bad
.. code-block:: sv
:linenos:
:emphasize-lines: 2
module t;
sub sub(); // <--- Warning
endmodule
module sub
(output port);
endmodule
+4
View File
@@ -0,0 +1,4 @@
.. comment: generated by t_lint_pinmissing_bad
.. code-block::
%Warning-PINMISSING: example.v:1:8 Cell has missing pin: 'port'
+11
View File
@@ -0,0 +1,11 @@
.. comment: generated by t_lint_pkgnodecl_bad
.. code-block:: sv
:linenos:
:emphasize-lines: 2
module t;
initial Pkg::hello(); //<--- Warning
endmodule
package Pkg;
function void hello(); endfunction
endpackage
+4
View File
@@ -0,0 +1,4 @@
.. comment: generated by t_lint_pkgnodecl_bad
.. code-block::
%Error-PKGNODECL: example.v:1:12 Package/class 'Pkg' not found, and needs to be predeclared (IEEE 1800-2023 26.3)
+10
View File
@@ -407,6 +407,16 @@ only a couple of instructions.
For signal callbacks to work the main loop of the program must call
:code:`VerilatedVpi::callValueCbs()`.
Verilator also tracks when the model state has been modified via the VPI with
an :code:`evalNeeded` flag. This flag can be checked with :code:`VerilatedVpi::evalNeeded()`
and it can be cleared with :code:`VerilatedVpi::clearEvalNeeded()`. Used together
it is possible to skip :code:`eval()` calls if no model state has been changed
since the last :code:`eval()`.
Any data written via :code:`vpi_put_value` with :code:`vpiInertialDelay` will
be deferred for later. These delayed values can be flushed to the model with
:code:`VerilatedVpi::doInertialPuts()`.
.. _VPI Example:
+6
View File
@@ -118,6 +118,12 @@ associated programs.
See :ref:`Installation` for more details.
.. option:: VERILATOR_SOLVER
If set, the command to run as a constrained randomization backend, such
as :command:`cvc4 --lang=smt2 --incremental`. If not specified, it will use
the one supplied or found during configure, or :command:`z3 --in` if empty.
.. option:: VERILATOR_VALGRIND
If set, the command to run when using the :vlopt:`--valgrind` option, such as
+46 -3
View File
@@ -222,6 +222,13 @@ Summary:
expressions into sub-expressions to avoid error C1009, and breaking
deep blocks into functions to avoid error C1061.
.. option:: --compiler-include <header-path>
Specifies additional headers to be included in the final PCH header.
It is required to add them to this header, due to compilers'
limitation that allow only one precompiled header per compilation.
Use this instead of ::vlopt:`-CFLAGS` with `-include <header-path>`.
.. option:: --converge-limit <loops>
Rarely needed. Specifies the maximum number of runtime iterations
@@ -476,6 +483,12 @@ Summary:
See also :vlopt:`--dump-defines`, :vlopt:`-P`, and
:vlopt:`--pp-comments` options.
.. option:: --emit-accessors
Emit getter and setter methods for each top-level signal in the
model top class. Signals are still available as public members,
but with the `__Vm_sig_` prefix.
.. option:: --error-limit <value>
After this number of errors are encountered during Verilator run, exit.
@@ -710,6 +723,13 @@ Summary:
:option:`/*verilator&32;hier_block*/` metacomment is ignored. See
:ref:`Hierarchical Verilation`.
.. option:: --hierarchical-params-file <filename>
Internal flag inserted used during :vlopt:`--hierarchical`; specifies
name of hierarchical parameters file for deparametrized modules with
:option:`/*verilator&32;hier_block*/` metacomment. See
:ref:`Hierarchical Verilation`.
.. option:: -I<dir>
See :vlopt:`-y`.
@@ -817,6 +837,11 @@ Summary:
If the design is not to be completely Verilated, see also the
:vlopt:`--bbox-sys` and :vlopt:`--bbox-unsup` options.
.. option:: --localize-max-size <value>
Rarely needed. Set the maximum variable size in bytes for it to be
subject to localizing-to-stack optimization. Defaults to 1024.
.. option:: --make <build-tool>
Generates a script for the specified build tool.
@@ -985,6 +1010,13 @@ Summary:
:option:`/*verilator&32;sc_bv*/` metacomment to select specific ports to
be sc_bv.
.. option:: --pins-inout-enables
Specifies that the __en and __out outputs will always be created for
inouts in the top-level module. The __en variable has a one in a bit
position to indicate the corresponding bit of the __out variable has
a value being driven from within the Verilated model.
.. option:: --pins-sc-uint
Specifies SystemC inputs/outputs greater than 2 bits wide should use
@@ -992,6 +1024,10 @@ Summary:
:vlopt:`--pins-sc-biguint` combination, it results in sc_uint being used
between 2 and 64 and sc_biguint being used between 65 and 512.
.. option:: --pins-sc-uint-bool
Specifies SystemC inputs/outputs one bit wide should use sc_uint<1>.
.. option:: --pins-sc-biguint
Specifies SystemC inputs/outputs greater than 65 bits wide should use
@@ -1732,9 +1768,9 @@ Summary:
.. note::
This option applies only to values explicitly written as X
in modules (not classes) in the Verilog source code. Initial values
of clocks are set to 0 unless `--x-initial-edge` is
This option applies only to values explicitly written as X in modules
(not classes, nor parameters) in the Verilog source code. Initial
values of clocks are set to 0 unless `--x-initial-edge` is
specified. Initial values of all other state holding variables are
controlled with `--x-initial`.
@@ -1967,6 +2003,13 @@ The grammar of configuration commands is as follows:
that the setting is ignored unless the :vlopt:`--hierarchical` option is
specified. See :ref:`Hierarchical Verilation`.
.. option:: hier_params -module "<modulename>"
Specifies that the module contains parameters a :vlopt:`--hierarchical` block. This option
is used internally to specify parameters for deparametrized hier block instances.
This option should not be used directly.
See :ref:`Hierarchical Verilation`.
.. option:: inline -module "<modulename>"
Specifies the module may be inlined into any modules that use this
+2 -2
View File
@@ -37,9 +37,9 @@ verilator_coverage Example Usage
verilator_coverage --annotate <obj>
verilator_coverage -write merged.dat -read <datafiles>...
verilator_coverage -write merged.dat <datafiles>...
verilator_coverage -write-info merged.info -read <datafiles>...
verilator_coverage -write-info merged.info <datafiles>...
verilator_coverage Arguments
+5 -3
View File
@@ -259,9 +259,11 @@ How do I view waveforms (aka dumps or traces)?
""""""""""""""""""""""""""""""""""""""""""""""
Verilator creates standard VCD (Value Change Dump) and FST files. VCD
files are viewable with the open-source GTKWave (recommended), or Dinotrace
(legacy) programs, or any of the many closed-source offerings; FST is
supported only by GTKWave.
files are viewable with the open-source
`GTKWave <http://gtkwave.sourceforge.net/>`_,
`Surfer <https://surfer-project.org/>`_, Dinotrace (legacy), or any of the
many closed-source viewer offerings; FST is supported only by GTKWave and
Surfer.
How do I speed up writing large waveform (trace) files?
+12 -10
View File
@@ -48,12 +48,6 @@ For --cc/--sc, it creates:
- Make include file for compiling (from --make gmake)
* - *{prefix}*\ _classes.mk
- Make include file with class names (from --make gmake)
* - *{prefix}*\ _hier.mk
- Make file for hierarchy blocks (from --make gmake)
* - *{prefix}*\ __hierMkArgs.f
- Arguments for hierarchical Verilation (from --make gmake)
* - *{prefix}*\ __hierCMakeArgs.f
- Arguments for hierarchical Verilation (from --make cmake)
* - *{prefix}*\ .h
- Model header
* - *{prefix}*\ .cpp
@@ -95,16 +89,24 @@ For --cc/--sc, it creates:
* - *{prefix}{each_verilog_module}{__DepSet_hash__n}*\ .cpp
- Additional lower C++ files (hashed to reduce build times)
For --hierarchy mode, it creates:
For --hierarchical mode, it creates:
.. list-table::
* - V\ *{hier_block}*\ /
- Directory to Verilate each hierarchy block (from --hierarchy)
- Directory to Verilate each hierarchical block (from --hierarchical)
* - *{prefix}*\ __hierVer.d
- Make dependencies of the top module (from --hierarchy)
- Make dependencies of the top module (from --hierarchical)
* - *{prefix}*\ _hier.mk
- Make file for hierarchical blocks (from --make gmake)
* - *{prefix}*\ __hierCMakeArgs.f
- Arguments for hierarchical Verilation (from --make cmake)
* - *{prefix}*\ __hierMkArgs.f
- Arguments for hierarchical Verilation (from --make gmake)
* - *{prefix}*\ __hierParameters.v
- Module parameters for hierarchical blocks
* - *{prefix}*\ __hier.dir
- Directory to store .dot, .vpp, .tree of top module (from --hierarchy)
- Directory to store .dot, .vpp, .tree of top module (from --hierarchical)
In specific debug and other modes, it also creates:
+14 -2
View File
@@ -152,6 +152,17 @@ To make use of Verilator FST tracing you will want `GTKwave
required at Verilator build time.
Install Z3
^^^^^^^^^^
In order to use constrained randomization the `Z3 Theorem Prover
<https://github.com/z3prover/z3#readme>`__ must be installed, however this is
not required at Verilator build time. There are other compatible SMT solvers,
like CVC5/CVC4, but they are not guaranteed to work. Since different solvers are
faster for different scenarios, the solver to use at run-time can be specified
by the environment variable :option:`VERILATOR_SOLVER`.
.. _Obtain Sources:
Obtain Sources
@@ -193,8 +204,9 @@ Eventual Installation Options
Before configuring the build, you must decide how you're going to
eventually install Verilator onto your system. Verilator will be compiling
the current value of the environment variables :option:`VERILATOR_ROOT`,
:option:`SYSTEMC_INCLUDE`, and :option:`SYSTEMC_LIBDIR` as defaults into
the executable, so they must be correct before configuring.
:option:`VERILATOR_SOLVER`, :option:`SYSTEMC_INCLUDE`, and
:option:`SYSTEMC_LIBDIR` as defaults into the executable, so they must be
correct before configuring.
These are the installation options:
+2 -2
View File
@@ -304,9 +304,9 @@ GNU Make
========
Verilator defaults to creating GNU Make makefiles for the model. Verilator
will call make automatically when the :vlopt:'--build' option is used.
will call make automatically when the :vlopt:`--build` option is used.
If calling Verilator from a makefile, the :vlopt:'-MMD' option will create
If calling Verilator from a makefile, the :vlopt:`--MMD` option will create
a dependency file, allowing Make to only run Verilator if input Verilog
files change.
+88 -32
View File
@@ -210,22 +210,35 @@ List Of Warnings
.. option:: BLKLOOPINIT
.. TODO better example
This indicates that the initialization of an array needs to use
non-delayed assignments. This is done in the interest of speed; if
delayed assignments were used, the simulator would have to copy large
arrays every cycle. (In smaller loops, loop unrolling allows the
delayed assignment to work, though it's a bit slower than a non-delayed
assignment.) Here's an example
Indicates certain constructs where non-blocking assignments to unpacked
arrays (memories) are not supported inside loops. These typically appear in
initialization/reset code:
.. code-block:: sv
always @(posedge clk)
if (~reset_l)
for (i=0; i<`ARRAY_SIZE; i++)
array[i] = 0; // Non-delayed for verilator
array[i] <= 0; // Non-blocking assignment inside loop
else
array[address] <= data;
While this is supported in typical synthesizeable code (including the
example above), some complicated cases are not supported. Namely:
1. If the above loop is inside a suspendable process or fork statement.
2. If the variable is also the target of a '<=' non-blocking assignment
in a suspendable process or fork statement (in addition to a synthesizable
loop).
3. If the element type of the array is a compound type.
4. In versions before 5.026, any delayed assignment to an array.
It might slightly improve run-time performance if you change the
non-blocking assignment inside the loop into a blocking assignment
(that is: use '=' instead of '<='), if possible.
This message is only seen on large or complicated loops because
Verilator generally unrolls small loops. You may want to try increasing
@@ -406,9 +419,9 @@ List Of Warnings
.. option:: CONSTRAINTIGN
Warns that Verilator does not support :code:`constraint`,
:code:`constraint_mode`, or :code:`rand_mode`, and the construct was are
ignored.
Warns that Verilator does not support certain forms of
:code:`constraint`, :code:`constraint_mode`, or :code:`rand_mode`, and
the construct was are ignored.
Ignoring this warning may make Verilator randomize() simulations differ
from other simulators.
@@ -1204,6 +1217,14 @@ List Of Warnings
:vlopt:`--no-timing` option.
.. option:: NONSTD
Warns when a non-standard language feature is used that has a standard
equivalent, which might behave differently in corner cases. For example
:code:`$psprintf` system function is replaced by its standard equivalent
:code:`$sformatf`.
.. option:: NULLPORT
Warns that a null port was detected in the module definition port
@@ -1243,7 +1264,7 @@ List Of Warnings
.. option:: PINMISSING
.. TODO better example
.. TODO better example (frequent)
Warns that a module has a pin that is not mentioned in an instance. If
a pin is not missing it should still be specified on the instance
@@ -1252,6 +1273,22 @@ List Of Warnings
Ignoring this warning will only suppress the lint check; it will
simulate correctly.
Faulty example:
.. include:: ../../docs/gen/ex_PKGNODECL_faulty.rst
Results in:
.. include:: ../../docs/gen/ex_PKGNODECL_msg.rst
Repaired example:
.. code-block:: sv
:emphasize-lines: 2
sub sub (
.port());
Other tools with similar warnings: Icarus Verilog's portbind, "warning:
Instantiating module ... with dangling input port (...)". Slang's
unconnected-port, "port '...' has no connection".
@@ -1283,7 +1320,7 @@ List Of Warnings
generate
if (A==0) begin
b b_inst1 (.x(1'b0)); //<--- error nonexistent port
b #(.PX(1'b0)) b_inst2 (); //<--- error nonexistent parameter
b #(.PX(1'b0)) b_inst2 (); //<--- error nonexistent parameter
end
endgenerate
endmodule
@@ -1325,11 +1362,22 @@ List Of Warnings
.. option:: PKGNODECL
.. TODO better example
An error that a package/class appears to have been referenced that has
not yet been declared. According to IEEE 1800-2023 26.3, all packages
must be declared before being used.
An error that a package/class appears to have been referenced that has not
yet been declared. According to IEEE 1800-2023 26.3, all packages must
be declared before being used.
Faulty example:
.. include:: ../../docs/gen/ex_PKGNODECL_faulty.rst
Results in:
.. include:: ../../docs/gen/ex_PKGNODECL_msg.rst
Often the package is declared in its own header file. In this case add
an include of that package header file to the referencing file. (And
make sure you have header guards in the package's header file to prevent
multiple declarations of the package.)
.. option:: PREPROCZERO
@@ -1354,7 +1402,7 @@ List Of Warnings
`endif
The portable way to suppress this warning is to use a define value other
than zero to when used in a preprocessor expression.
than zero, when it is to be used in a preprocessor expression.
.. option:: PROCASSWIRE
@@ -1876,7 +1924,8 @@ List Of Warnings
.. option:: UNPACKED
Warns that unpacked structs and unions are not supported.
Warns that unpacked structs and unions are not supported because
:vlopt:`--structs-packed` was used, or by up through version 5.004.
Ignoring this warning will make Verilator treat the structure as packed,
which may make Verilator simulations differ from other simulators. This
@@ -1927,14 +1976,16 @@ List Of Warnings
.. TODO better example
Warns that the specified genvar is never used/consumed.
Warns that the specified genvar is never used/consumed. See similar
:option:`UNUSEDSIGNAL`.
.. option:: UNUSEDPARAM
.. TODO better example
Warns that the specified parameter is never used/consumed.
Warns that the specified parameter is never used/consumed. See similar
:option:`UNUSEDSIGNAL`.
.. option:: UNUSEDSIGNAL
@@ -2057,10 +2108,6 @@ List Of Warnings
.. option:: WAITCONST
.. code-block:: sv
wait(1); // Blocks forever
Warns that a `wait` statement awaits a constant condition, which means it
either blocks forever or never blocks.
@@ -2068,6 +2115,12 @@ List Of Warnings
something that elaborates to zero), does not warn, as it is presumed the
code is making the intent clear.
Faulty example:
.. code-block:: sv
wait(1); // Blocks forever
.. option:: WIDTH
@@ -2104,7 +2157,7 @@ List Of Warnings
.. include:: ../../docs/gen/ex_WIDTHEXPAND_1_faulty.rst
Results in a WIDTHEXPAND warning:
Results in a :option:`WIDTHEXPAND` warning:
.. include:: ../../docs/gen/ex_WIDTHEXPAND_1_msg.rst
@@ -2115,15 +2168,18 @@ List Of Warnings
.. option:: WIDTHTRUNC
A more granular WIDTH warning, for when a value is truncated.
A more granular :option:`WIDTH` warning, for when a value is
truncated. See :option:`WIDTH`.
.. option:: WIDTHEXPAND
A more granular WIDTH warning, for when a value is zero expanded.
A more granular :option:`WIDTH` warning, for when a value is zero
expanded. See :option:`WIDTH`.
.. option:: WIDTHXZEXPAND
A more granular WIDTH warning, for when a value is X/Z expanded.
A more granular :option:`WIDTH` warning, for when a value is X/Z
expanded. See :option:`WIDTH`.
.. option:: WIDTHCONCAT
@@ -2172,7 +2228,7 @@ List Of Warnings
#(parameter int MY_PARAM = 0);
reg [7:0] data;
always @* begin
data = {MY_PARAM{1'b1}}; //<--- WARNING
data = {MY_PARAM{1'b1}}; //<--- Warning
end
endmodule
@@ -2193,7 +2249,7 @@ List Of Warnings
:emphasize-lines: 2
module dut
#(parameter int MY_PARAM = 1); //<--- REPAIRED
#(parameter int MY_PARAM = 1); //<--- REPAIRED
reg [7:0] data;
always @* begin
data = {MY_PARAM{1'b1}};
+89
View File
@@ -1031,6 +1031,91 @@ the evaluation process records a bitmask of variables that might have
changed; if clear, checking those signals for changes may be skipped.
Constrained randomization
-------------------------
Because general constrained randomization is a co-NP-hard problem, not all
cases are implemented in Verilator, and an external specialized SMT solver is
used for any non-obvious ones.
The ``randomize()`` method spawns an SMT solver in a sub-process. Then the
solver gets a setup query, then the definition of variables, then all the
constraints (SMT assertions) about the variables. Since the solver has no
information about the class' PRNG state, if the problem is satisfiable,
the solution space is further constrained by adding extra random constraints,
and querying the values satisfying the problem statement.
The constraint is currently constructed as fixing a simple xor of randomly
chosen bits of the variables being randomized.
The runtime classes used for handling the randomization are defined in
``verilated_random.h`` and ``verilated_random.cpp``.
``VlSubprocess``
~~~~~~~~~~~~~~~~
Subprocess handle, responsible for keeping track of the resources like child
PID, read and write file descriptors, and presenting them as a C++ iostream.
``VlRandomizer``
~~~~~~~~~~~~~~~~
Randomizer class, responsible for keeping track of variables and constraints,
and communicating with the solver subprocess.
The solver gets the constraints in `SMT-LIB2
<https://smtlib.cs.uiowa.edu/>`__ textual format in the following syntax:
::
(set-info :smt-lib-version 2.0)
(set-option :produce-models true)
(set-logic QF_BV)
(declare-fun v () (_ BitVec 16))
(declare-fun w () (_ BitVec 64))
(declare-fun x () (_ BitVec 48))
(declare-fun z () (_ BitVec 24))
(declare-fun t () (_ BitVec 23))
(assert (or (= v #x0003) (= v #x0008)))
(assert (= w #x0000000000000009))
(assert (or (or (= x #x000000000001) (= x #x000000000002)) (or (= x #x000000000004) (= x #x000000000009))))
(assert (bvult ((_ zero_extend 8) z) #x00000015))
(assert (bvugt ((_ zero_extend 8) z) #x0000000d))
(check-sat)
The solver responds with either ``sat`` or ``unsat``. Then the initial solution
is queried with:
::
(get-value (v w x z t ))
The solver then responds with e.g.:
::
((v #x0008)
(w #x0000000000000005)
(x #x000000000002)
(z #x000010)
(t #b00000000000000000000000))
And then a follow-up query (or a series thereof) is asked, and the solver gets
reset, so that it can be reused by subsequent randomization attempts:
::
(assert (= (bvxor (bvxor <...> (bvxor ((_ extract 21 21) z) ((_ extract 39 39) x)) ((_ extract 5 5) w)) <...> ((_ extract 10 10) w)) #b0))
(check-sat)
(get-value)
...
(reset)
Coding Conventions
==================
@@ -2125,6 +2210,10 @@ VERILATOR_ROOT
Standard path to Verilator distribution root; see primary Verilator
documentation.
VERILATOR_SOLVER
SMT solver command for constrained randomization; see primary Verilator
documentation.
VERILATOR_TESTS_SITE
Used with ``--site``, a colon-separated list of directories with tests to
be added to testlist.
+20
View File
@@ -40,6 +40,7 @@ Brej
Briquet
Brownlee
Buckenmaier
Burghoorn
Bybell
CLang
CMake
@@ -131,6 +132,7 @@ Gernot
Gerst
Gielda
Gigerl
Gijs
Gileadi
Giri
Gisselquist
@@ -164,6 +166,7 @@ Homebrew
Hornung
Hossell
Hsu
Huggett
Hutt
Hyperthreading
Ibrahim
@@ -376,6 +379,7 @@ Srini
Srinivasan
Stamness
Stephane
Sterin
Stoddard
Stroebel
Strouble
@@ -458,6 +462,7 @@ Wojciech
Wolfel
Wouters
Wpedantic
Wunused
Wwarn
XSim
Xcelium
@@ -494,6 +499,7 @@ architected
args
arrarys
assertOn
assertcontrol
astgen
async
atClone
@@ -586,6 +592,7 @@ defname
defparam
demangling
dep
deparametrized
der
dereference
desassign
@@ -698,6 +705,7 @@ genvar
genvars
getenv
getline
getter
ggdb
glibc
gmake
@@ -711,6 +719,7 @@ hh
hier
hierCMakeArgs
hierMkArgs
hierParameters
hierVer
hx
hyperthreading
@@ -842,6 +851,8 @@ parallelized
param
parameterization
parameterized
parameterized
parameterless
params
parens
pawel
@@ -885,7 +896,9 @@ profcfunc
profiler
prototyptes
ps
psprintf
pthread
pthreads
ptr
pulldown
pulldowns
@@ -968,6 +981,9 @@ submakes
submodule
submodules
substring
superclass
suspendable
suspendables
sv
svBitVal
svBitVecVal
@@ -978,6 +994,7 @@ svLogicVal
svdpi
svgGetTimeUnit
swrite
synthesizeable
sys
systemc
tenghtt
@@ -996,6 +1013,7 @@ toolchain
topcell
toplevel
toupper
traceCapable
traceEverOn
tran
treei
@@ -1060,6 +1078,7 @@ vluint
vpi
vpiConstType
vpiDefName
vpiInertialDelay
vpiInstance
vpiLeftRange
vpiModule
@@ -1093,3 +1112,4 @@ zdave
Øyvind
Алексеевич
Исаак
+1 -1
View File
@@ -47,12 +47,12 @@ int main(int argc, char** argv) {
// Simulate until $finish
while (!contextp->gotFinish()) {
contextp->timeInc(1);
top->clk = ~top->clk & 0x1;
top->eval();
#if VM_TRACE
if (tfp) tfp->dump(contextp->time());
#endif
contextp->timeInc(1);
}
// Final model cleanup
+13 -2
View File
@@ -130,6 +130,7 @@ void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint3
#define FST_HDR_TIMEZERO_SIZE (8)
#define FST_GZIO_LEN (32768)
#define FST_HDR_FOURPACK_DUO_SIZE (4*1024*1024)
#define FST_ZWRAPPER_HDR_SIZE (1+8+8)
#if defined(__APPLE__) && defined(__MACH__)
#define FST_MACOSX
@@ -347,8 +348,9 @@ return(NULL);
static void *fstMmap2(size_t __len, int __fd, fst_off_t __off)
{
DWORD64 len64 = __len; /* Must be 64-bit for shift below */
HANDLE handle = CreateFileMapping((HANDLE)_get_osfhandle(__fd), NULL,
PAGE_READWRITE, (DWORD)(__len >> 32),
PAGE_READWRITE, (DWORD)(len64 >> 32),
(DWORD)__len, NULL);
if (!handle) { return NULL; }
@@ -4632,9 +4634,14 @@ if(sectype == FST_BL_ZWRAPPER)
}
#endif
fstReaderFseeko(xc, xc->f, 1+8+8, SEEK_SET);
fstReaderFseeko(xc, xc->f, FST_ZWRAPPER_HDR_SIZE, SEEK_SET);
#ifndef __MINGW32__
fflush(xc->f);
#else
/* Windows UCRT runtime library reads one byte ahead in the file
even with buffering disabled and does not synchronise the
file position after fseek. */
_lseek(fileno(xc->f), FST_ZWRAPPER_HDR_SIZE, SEEK_SET);
#endif
zfd = dup(fileno(xc->f));
@@ -6184,7 +6191,9 @@ struct fstReaderContext *xc = (struct fstReaderContext *)ctx;
fst_off_t blkpos = 0, prev_blkpos;
uint64_t beg_tim, end_tim, beg_tim2, end_tim2;
int sectype;
#ifdef FST_DEBUG
unsigned int secnum = 0;
#endif
uint64_t seclen;
uint64_t tsec_uclen = 0, tsec_clen = 0;
uint64_t tsec_nitems;
@@ -6276,7 +6285,9 @@ for(;;)
}
blkpos += seclen;
#ifdef FST_DEBUG
secnum++;
#endif
}
xc->rvat_beg_tim = beg_tim;
+137 -54
View File
@@ -82,6 +82,12 @@
#include "verilated_trace.h"
#ifdef VM_SOLVER_DEFAULT
#define VL_SOLVER_DEFAULT VM_SOLVER_DEFAULT
#else
#define VL_SOLVER_DEFAULT "z3 --in"
#endif
// Max characters in static char string for VL_VALUE_STRING
constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192;
@@ -406,6 +412,8 @@ IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
data &= VL_MASK_I(obits);
return data;
}
IData VL_RAND_RESET_ASSIGN_I(int obits) VL_MT_SAFE { return VL_RANDOM_I() & VL_MASK_I(obits); }
QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
if (Verilated::threadContextp()->randReset() == 0) return 0;
QData data = ~0ULL;
@@ -415,11 +423,19 @@ QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
data &= VL_MASK_Q(obits);
return data;
}
QData VL_RAND_RESET_ASSIGN_Q(int obits) VL_MT_SAFE { return VL_RANDOM_Q() & VL_MASK_Q(obits); }
WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = VL_RAND_RESET_I(32);
outwp[VL_WORDS_I(obits) - 1] = VL_RAND_RESET_I(32) & VL_MASK_E(obits);
return outwp;
}
WDataOutP VL_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp) VL_MT_SAFE {
for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = VL_RAND_RESET_ASSIGN_I(32);
outwp[VL_WORDS_I(obits) - 1] = VL_RAND_RESET_ASSIGN_I(32) & VL_MASK_E(obits);
return outwp;
}
WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
// Not inlined to speed up compilation of slowpath code
return VL_ZERO_W(obits, outwp);
@@ -1171,8 +1187,8 @@ static char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp, const
static void _vl_vsss_setbit(WDataOutP iowp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE {
for (; nbits && lsb < obits; nbits--, lsb++, ld >>= 1) VL_ASSIGNBIT_WI(lsb, iowp, ld & 1);
}
static void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
size_t posstart, size_t posend) VL_MT_SAFE {
void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp, size_t posstart,
size_t posend) VL_MT_SAFE {
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
VL_ZERO_W(obits, owp);
int lsb = 0;
@@ -1749,7 +1765,10 @@ IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
char filenamez[VL_VALUE_STRING_MAX_CHARS + 1];
_vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp);
const int code = std::system(filenamez); // Yes, std::system() is threadsafe
return VL_SYSTEM_IN(filenamez);
}
IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE {
const int code = std::system(lhs.c_str()); // Yes, std::system() is threadsafe
return code >> 8; // Want exit status
}
@@ -2037,22 +2056,26 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
if (VL_UNLIKELY(!m_fp)) return false;
valuer = "";
// Prep for reading
bool indata = false;
bool ignore_to_eol = false;
bool ignore_to_cmt = false;
bool reading_addr = false;
int lastc = ' ';
bool inData = false;
bool ignoreToEol = false;
bool ignoreToComment = false;
bool readingAddress = false;
int lastCh = ' ';
// Read the data
// We process a character at a time, as then we don't need to deal
// with changing buffer sizes dynamically, etc.
while (true) {
int c = std::fgetc(m_fp);
if (VL_UNLIKELY(c == EOF)) break;
const bool chIs4StateBin
= c == '0' || c == '1' || c == 'x' || c == 'X' || c == 'z' || c == 'Z';
const bool chIs2StateHex = std::isxdigit(c);
const bool chIs4StateHex = std::isxdigit(c) || chIs4StateBin;
// printf("%d: Got '%c' Addr%lx IN%d IgE%d IgC%d\n",
// m_linenum, c, m_addr, indata, ignore_to_eol, ignore_to_cmt);
// m_linenum, c, m_addr, inData, ignoreToEol, ignoreToComment);
// See if previous data value has completed, and if so return
if (c == '_') continue; // Ignore _ e.g. inside a number
if (indata && !std::isxdigit(c) && c != 'x' && c != 'X') {
if (inData && !chIs4StateHex) {
// printf("Got data @%lx = %s\n", m_addr, valuer.c_str());
ungetc(c, m_fp);
addrr = m_addr;
@@ -2062,50 +2085,46 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
// Parse line
if (c == '\n') {
++m_linenum;
ignore_to_eol = false;
reading_addr = false;
ignoreToEol = false;
readingAddress = false;
} else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') {
reading_addr = false;
readingAddress = false;
}
// Skip // comments and detect /* comments
else if (ignore_to_cmt && lastc == '*' && c == '/') {
ignore_to_cmt = false;
reading_addr = false;
} else if (!ignore_to_eol && !ignore_to_cmt) {
if (lastc == '/' && c == '*') {
ignore_to_cmt = true;
} else if (lastc == '/' && c == '/') {
ignore_to_eol = true;
else if (ignoreToComment && lastCh == '*' && c == '/') {
ignoreToComment = false;
readingAddress = false;
} else if (!ignoreToEol && !ignoreToComment) {
if (lastCh == '/' && c == '*') {
ignoreToComment = true;
} else if (lastCh == '/' && c == '/') {
ignoreToEol = true;
} else if (c == '/') { // Part of /* or //
} else if (c == '#') {
ignore_to_eol = true;
ignoreToEol = true;
} else if (c == '@') {
reading_addr = true;
readingAddress = true;
m_anyAddr = true;
m_addr = 0;
}
// Check for hex or binary digits as file format requests
else if (std::isxdigit(c) || (!reading_addr && (c == 'x' || c == 'X'))) {
} else if (readingAddress && chIs2StateHex) {
c = std::tolower(c);
const int value
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
if (reading_addr) {
// Decode @ addresses
m_addr = (m_addr << 4) + value;
} else {
indata = true;
valuer += static_cast<char>(c);
// printf(" Value width=%d @%x = %c\n", width, m_addr, c);
if (VL_UNLIKELY(value > 1 && !m_hex)) {
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
"$readmemb (binary) file contains hex characters");
}
const int addressValue = (c >= 'a') ? (c - 'a' + 10) : (c - '0');
m_addr = (m_addr << 4) + addressValue;
} else if (readingAddress && chIs4StateHex) {
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
"$readmem address contains 4-state characters");
} else if (chIs4StateHex) {
inData = true;
valuer += static_cast<char>(c);
if (VL_UNLIKELY(!m_hex && !chIs4StateBin)) {
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
"$readmemb (binary) file contains hex characters");
}
} else {
VL_FATAL_MT(m_filename.c_str(), m_linenum, "", "$readmem file syntax error");
}
}
lastc = c;
lastCh = c;
}
if (VL_UNLIKELY(m_end != ~0ULL && m_addr <= m_end && !m_anyAddr)) {
@@ -2114,7 +2133,7 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
}
addrr = m_addr;
return indata; // EOF
return inData; // EOF
}
void VlReadMem::setData(void* valuep, const std::string& rhs) {
const QData shift = m_hex ? 4ULL : 1ULL;
@@ -2122,8 +2141,9 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
// Shift value in
for (const auto& i : rhs) {
const char c = std::tolower(i);
const int value
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
const int value = (c == 'x' || c == 'z') ? VL_RAND_RESET_I(m_hex ? 4 : 1)
: (c >= 'a') ? (c - 'a' + 10)
: (c - '0');
if (m_bits <= 8) {
CData* const datap = reinterpret_cast<CData*>(valuep);
if (!innum) *datap = 0;
@@ -2457,6 +2477,7 @@ VerilatedContext::VerilatedContext()
m_ns.m_coverageFilename = "coverage.dat";
m_ns.m_profExecFilename = "profile_exec.dat";
m_ns.m_profVltFilename = "profile.vlt";
m_ns.m_solverProgram = VlOs::getenvStr("VERILATOR_SOLVER", VL_SOLVER_DEFAULT);
m_fdps.resize(31);
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
m_fdFreeMct.resize(30);
@@ -2483,9 +2504,47 @@ VerilatedContext::Serialized::Serialized() {
m_timeprecision = picosecond; // Initial value until overridden by _Vconfigure
}
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_s.m_assertOn = flag;
// Set all assert and directive types when true, clear otherwise.
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
}
bool VerilatedContext::assertOnGet(VerilatedAssertType_t type,
VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
// Check if selected directive type bit in the assertOn is enabled for assertion type.
// Note: it is assumed that this is checked only for one type at the time.
// Flag unspecified assertion types as disabled.
if (type == 0) return false;
// Get index of 3-bit group guarding assertion type status.
// Since the assertOnGet is generated __always__ for a single assert type, we assume that only
// a single bit will be set. Thus, ceil log2 will work fine.
VL_DEBUG_IFDEF(assert((type & (type - 1)) == 0););
const IData typeMaskPosition = VL_CLOG2_I(type);
// Check if directive type bit is enabled in corresponding assertion type bits.
return m_s.m_assertOn & (directive << (typeMaskPosition * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH));
}
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
// For each assertion type, set directive bits.
// Iterate through all positions of assertion type bits. If bit for this assertion type is set,
// set directive type bits mask at this group index.
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
if (VL_BITISSET_I(types, i))
m_s.m_assertOn |= directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH);
}
}
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
// Iterate through all positions of assertion type bits. If bit for this assertion type is set,
// clear directive type bits mask at this group index.
for (int i = 0; i < std::numeric_limits<VerilatedAssertType_t>::digits; ++i) {
if (VL_BITISSET_I(types, i))
m_s.m_assertOn &= ~(directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH));
}
}
void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
@@ -2567,6 +2626,14 @@ std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_profVltFilename;
}
void VerilatedContext::solverProgram(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_ns.m_solverProgram = flag;
}
std::string VerilatedContext::solverProgram() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_solverProgram;
}
void VerilatedContext::quiet(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_s.m_quiet = flag;
@@ -2645,8 +2712,16 @@ void VerilatedContext::addModel(VerilatedModel* modelp) {
m_ns.m_cpuTimeStart.start();
m_ns.m_wallTimeStart.start();
}
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
// We look for time passing, as opposed to post-eval(), as embedded
// models might get added inside initial blocks.
if (VL_UNLIKELY(time()))
VL_FATAL_MT(
"", 0, "",
"Adding model when time is non-zero. ... Suggest check time(), or for restarting"
" model use a new VerilatedContext");
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
m_threadsInModels += modelp->threads();
if (VL_UNLIKELY(modelp->threads() > m_threads)) {
std::ostringstream msg;
@@ -2882,7 +2957,7 @@ void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
VL_PRINTF("- Verilator: %s at %s; walltime %0.3f s; speed %s/s\n", endwhy.c_str(),
simtime.c_str(), walltime, simtimePerf.c_str());
const double modelMB = VlOs::memUsageBytes() / 1024.0 / 1024.0;
VL_PRINTF("- Verilator: cpu %0.3f s on %d threads; alloced %0.0f MB\n", cputime,
VL_PRINTF("- Verilator: cpu %0.3f s on %u threads; alloced %0.0f MB\n", cputime,
threadsInModels(), modelMB);
}
@@ -2933,7 +3008,6 @@ void VerilatedContext::trace(VerilatedTraceBaseC* tfp, int levels, int options)
VL_FATAL_MT("", 0, "",
"Testbench C call to 'VerilatedContext::trace()' must not be called"
" after 'VerilatedTrace*::open()'\n");
return;
}
{
// Legacy usage may call {modela}->trace(...) then {modelb}->trace(...)
@@ -3126,12 +3200,21 @@ void Verilated::stackCheck(QData needSize) VL_MT_UNSAFE {
if (haveSize == RLIM_INFINITY) haveSize = 0;
}
// VL_PRINTF_MT("-Info: stackCheck(%" PRIu64 ") have %" PRIu64 "\n", needSize, haveSize);
// Check for 1.5x need, but suggest 2x so small model increase won't cause warning
// if the user follows the suggestions
if (VL_UNLIKELY(haveSize && needSize && haveSize < (needSize + needSize / 2))) {
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
" which may be too small; suggest 'ulimit -s %" PRIu64 "' or larger\n",
haveSize / 1024, (needSize * 2) / 1024);
// Check and request for 1.5x need. This is automated so the user doesn't need to do anything.
QData requestSize = needSize + needSize / 2;
if (VL_UNLIKELY(haveSize && needSize && haveSize < requestSize)) {
// Try to increase the stack limit to the requested size
rlim.rlim_cur = requestSize;
if (
#ifdef _VL_TEST_RLIMIT_FAIL
true ||
#endif
setrlimit(RLIMIT_STACK, &rlim)) {
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
" which may be too small; failed to request more"
" using 'ulimit -s %" PRIu64 "'\n",
haveSize / 1024, requestSize);
}
}
#else
(void)needSize; // Unused argument
+92 -40
View File
@@ -55,6 +55,7 @@
#include <cstring>
#include <deque>
#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <set>
@@ -153,6 +154,27 @@ enum VerilatedVarFlags {
VLVF_DPI_CLAY = (1 << 10) // DPI compatible C standard layout
};
// IEEE 1800-2023 Table 20-6
enum class VerilatedAssertType : uint8_t {
ASSERT_TYPE_CONCURRENT = (1 << 0),
ASSERT_TYPE_SIMPLE_IMMEDIATE = (1 << 1),
ASSERT_TYPE_OBSERVED_DEFERRED_IMMEDIATE = (1 << 2),
ASSERT_TYPE_FINAL_DEFERRED_IMMEDIATE = (1 << 3),
ASSERT_TYPE_EXPECT = (1 << 4),
ASSERT_TYPE_UNIQUE = (1 << 5),
ASSERT_TYPE_UNIQUE0 = (1 << 6),
ASSERT_TYPE_PRIORITY = (1 << 7),
};
// IEEE 1800-2023 Table 20-7
enum class VerilatedAssertDirectiveType : uint8_t {
DIRECTIVE_TYPE_ASSERT = (1 << 0),
DIRECTIVE_TYPE_COVER = (1 << 1),
DIRECTIVE_TYPE_ASSUME = (1 << 2),
};
using VerilatedAssertType_t = std::underlying_type<VerilatedAssertType>::type;
using VerilatedAssertDirectiveType_t = std::underlying_type<VerilatedAssertDirectiveType>::type;
//=============================================================================
// Utility functions
@@ -347,6 +369,19 @@ public:
class VerilatedContext VL_NOT_FINAL {
friend class VerilatedContextImp;
private:
// MEMBERS
// Numer of assertion directive type members. Then each of them will represented as 1-bit in a
// mask.
static constexpr size_t ASSERT_DIRECTIVE_TYPE_MASK_WIDTH = 3;
// Specifies how many groups of directive type bit groups there are based on a number of
// assertion types.
// Note: we add one bit to store information whether Verilator's internal
// directive types are enabled, for example `violation if`s.
static constexpr size_t ASSERT_ON_WIDTH
= ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits<VerilatedAssertType_t>::digits
+ 1;
protected:
// TYPES
using traceBaseModelCb_t
@@ -360,7 +395,11 @@ protected:
// No std::strings or pointers or will serialize badly!
// Fast path
uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
bool m_assertOn = true; // Assertions are enabled
std::atomic<uint32_t> m_assertOn{
std::numeric_limits<uint32_t>::max()}; // Enabled assertions,
// for each VerilatedAssertType we store
// 3-bits, one for each directive type. Last
// bit guards internal directive types.
bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated
bool m_fatalOnError = true; // Fatal on $stop/non-fatal error
bool m_fatalOnVpiError = true; // Fatal on vpi error/unsupported
@@ -396,6 +435,7 @@ protected:
std::string m_coverageFilename; // +coverage+file filename
std::string m_profExecFilename; // +prof+exec+file filename
std::string m_profVltFilename; // +prof+vlt filename
std::string m_solverProgram; // SMT solver program
VlOs::DeltaCpuTime m_cpuTimeStart{false}; // CPU time, starts when create first model
VlOs::DeltaWallTime m_wallTimeStart{false}; // Wall time, starts when create first model
std::vector<traceBaseModelCb_t> m_traceBaseModelCbs; // Callbacks to traceRegisterModel
@@ -447,14 +487,23 @@ public:
// METHODS - User called
/// Enable assertions
void assertOn(bool flag) VL_MT_SAFE;
/// Return if assertions enabled
bool assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
/// Enable calculation of unused signals (for traces)
void calcUnusedSigs(bool flag) VL_MT_SAFE;
bool assertOn() const VL_MT_SAFE;
/// Enable all assertion types
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 calculating of unused signals (for traces)
bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
/// Enable calculation of unused signals (for traces)
void calcUnusedSigs(bool flag) VL_MT_SAFE;
/// Record command-line arguments, for retrieval by $test$plusargs/$value$plusargs,
/// and for parsing +verilator+ run-time arguments.
/// This should be called before the first model is created.
@@ -469,52 +518,51 @@ public:
/// Return VerilatedCovContext, allocate if needed
/// Note if get unresolved reference then likely forgot to link verilated_cov.cpp
VerilatedCovContext* coveragep() VL_MT_SAFE;
/// Set debug level
/// Return debug level
static inline int debug() VL_MT_SAFE; /// Set debug level
/// Debug is currently global, but for forward compatibility have a per-context method
static inline void debug(int val) VL_MT_SAFE;
/// Return debug level
static inline int debug() VL_MT_SAFE;
/// Return current number of errors/assertions
int errorCount() const VL_MT_SAFE { return m_s.m_errorCount; }
/// Set current number of errors/assertions
void errorCount(int val) VL_MT_SAFE;
/// Increment current number of errors/assertions
void errorCountInc() VL_MT_SAFE;
/// Return current number of errors/assertions
int errorCount() const VL_MT_SAFE { return m_s.m_errorCount; }
/// Set number of errors/assertions before stop
void errorLimit(int val) VL_MT_SAFE;
/// Return number of errors/assertions before stop
int errorLimit() const VL_MT_SAFE { return m_s.m_errorLimit; }
/// Set to throw fatal error on $stop/non-fatal error
void fatalOnError(bool flag) VL_MT_SAFE;
/// Set number of errors/assertions before stop
void errorLimit(int val) VL_MT_SAFE;
/// Return if to throw fatal error on $stop/non-fatal
bool fatalOnError() const VL_MT_SAFE { return m_s.m_fatalOnError; }
/// Set to throw fatal error on VPI errors
void fatalOnVpiError(bool flag) VL_MT_SAFE;
/// Set to throw fatal error on $stop/non-fatal error
void fatalOnError(bool flag) VL_MT_SAFE;
/// Return if to throw fatal error on VPI errors
bool fatalOnVpiError() const VL_MT_SAFE { return m_s.m_fatalOnVpiError; }
/// Set if got a $stop or non-fatal error
void gotError(bool flag) VL_MT_SAFE;
/// Set to throw fatal error on VPI errors
void fatalOnVpiError(bool flag) VL_MT_SAFE;
/// Return if got a $stop or non-fatal error
bool gotError() const VL_MT_SAFE { return m_s.m_gotError; }
/// Set if got a $finish or $stop/error
void gotFinish(bool flag) VL_MT_SAFE;
/// Set if got a $stop or non-fatal error
void gotError(bool flag) VL_MT_SAFE;
/// Return if got a $finish or $stop/error
bool gotFinish() const VL_MT_SAFE { return m_s.m_gotFinish; }
/// Enable quiet (also prevents need for OS calls to get CPU time)
void quiet(bool flag) VL_MT_SAFE;
/// Set if got a $finish or $stop/error
void gotFinish(bool flag) VL_MT_SAFE;
/// Return if quiet enabled
bool quiet() const VL_MT_SAFE { return m_s.m_quiet; }
/// Enable quiet (also prevents need for OS calls to get CPU time)
void quiet(bool flag) VL_MT_SAFE;
/// Return randReset value
int randReset() VL_MT_SAFE { return m_s.m_randReset; }
/// Select initial value of otherwise uninitialized signals.
/// 0 = Set to zeros
/// 1 = Set all bits to one
/// 2 = Randomize all bits
void randReset(int val) VL_MT_SAFE;
/// Return randReset value
int randReset() VL_MT_SAFE { return m_s.m_randReset; }
/// Return default random seed
void randSeed(int val) VL_MT_SAFE;
/// Set default random seed, 0 = seed it automatically
int randSeed() const VL_MT_SAFE { return m_s.m_randSeed; }
/// Return default random seed
void randSeed(int val) VL_MT_SAFE;
/// Return statistic: CPU time delta from model created until now
double statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex);
@@ -606,23 +654,27 @@ public:
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
// Internal: coverage
void coverageFilename(const std::string& flag) VL_MT_SAFE;
std::string coverageFilename() const VL_MT_SAFE;
void coverageFilename(const std::string& flag) VL_MT_SAFE;
// Internal: $dumpfile
void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
std::string dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
// Internal: --prof-exec related settings
void profExecStart(uint64_t flag) VL_MT_SAFE;
uint64_t profExecStart() const VL_MT_SAFE { return m_ns.m_profExecStart; }
void profExecWindow(uint64_t flag) VL_MT_SAFE;
void profExecStart(uint64_t flag) VL_MT_SAFE;
uint32_t profExecWindow() const VL_MT_SAFE { return m_ns.m_profExecWindow; }
void profExecFilename(const std::string& flag) VL_MT_SAFE;
void profExecWindow(uint64_t flag) VL_MT_SAFE;
std::string profExecFilename() const VL_MT_SAFE;
void profVltFilename(const std::string& flag) VL_MT_SAFE;
void profExecFilename(const std::string& flag) VL_MT_SAFE;
std::string profVltFilename() const VL_MT_SAFE;
void profVltFilename(const std::string& flag) VL_MT_SAFE;
// Internal: SMT solver program
std::string solverProgram() const VL_MT_SAFE;
void solverProgram(const std::string& flag) VL_MT_SAFE;
// Internal: Find scope
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
@@ -799,18 +851,18 @@ public:
}
#ifndef VL_NO_LEGACY
/// Call VerilatedContext::assertOn using current thread's VerilatedContext
static void assertOn(bool flag) VL_MT_SAFE { Verilated::threadContextp()->assertOn(flag); }
/// Return VerilatedContext::assertOn() using current thread's VerilatedContext
static bool assertOn() VL_MT_SAFE { return Verilated::threadContextp()->assertOn(); }
/// Call VerilatedContext::calcUnusedSigs using current thread's VerilatedContext
static void calcUnusedSigs(bool flag) VL_MT_SAFE {
Verilated::threadContextp()->calcUnusedSigs(flag);
}
/// Call VerilatedContext::assertOn using current thread's VerilatedContext
static void assertOn(bool flag) VL_MT_SAFE { Verilated::threadContextp()->assertOn(flag); }
/// Return VerilatedContext::calcUnusedSigs using current thread's VerilatedContext
static bool calcUnusedSigs() VL_MT_SAFE {
return Verilated::threadContextp()->calcUnusedSigs();
}
/// Call VerilatedContext::calcUnusedSigs using current thread's VerilatedContext
static void calcUnusedSigs(bool flag) VL_MT_SAFE {
Verilated::threadContextp()->calcUnusedSigs(flag);
}
/// Call VerilatedContext::commandArgs using current thread's VerilatedContext
static void commandArgs(int argc, const char** argv) VL_MT_SAFE {
Verilated::threadContextp()->commandArgs(argc, argv);
@@ -953,8 +1005,8 @@ public:
// Internal: Set the mtaskId, called when an mtask starts
// Per thread, so no need to be in VerilatedContext
static void mtaskId(uint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; }
static uint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; }
static void mtaskId(uint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; }
static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; }
static void endOfEvalReqdDec() VL_MT_SAFE { --t_s.t_endOfEvalReqd; }
+1 -1
View File
@@ -80,6 +80,7 @@ VK_CPPFLAGS_ALWAYS += \
-I$(VERILATOR_ROOT)/include/vltstd \
-DVM_COVERAGE=$(VM_COVERAGE) \
-DVM_SC=$(VM_SC) \
-DVM_TIMING=$(VM_TIMING) \
-DVM_TRACE=$(VM_TRACE) \
-DVM_TRACE_FST=$(VM_TRACE_FST) \
-DVM_TRACE_VCD=$(VM_TRACE_VCD) \
@@ -225,7 +226,6 @@ endif
# 1. Extract object files from .a
# 2. Create a new archive from extracted .o and given .o
%.a: | %.verilator_deplist.tmp
$(info Archive $(AR) -rcs $@ $^)
$(foreach L, $(filter-out %.a,$^), $(shell echo $L >>[email protected]_deplist.tmp))
@if test $(words $(filter %.a,$^)) -eq 0; then \
$(RM) -f $@; \
+1 -1
View File
@@ -92,9 +92,9 @@ public:
std::string defaultFilename() VL_MT_SAFE;
/// Make all data per_instance, overriding point's per_instance
void forcePerInstance(bool flag) VL_MT_SAFE;
void write() VL_MT_SAFE { write(defaultFilename()); }
/// Write all coverage data to a file
void write(const std::string& filename) VL_MT_SAFE;
void write() VL_MT_SAFE { write(defaultFilename()); }
/// Clear coverage points (and call delete on all items)
void clear() VL_MT_SAFE;
/// Clear items not matching the provided string
+348 -33
View File
@@ -106,6 +106,14 @@ extern IData VL_RAND_RESET_I(int obits) VL_MT_SAFE;
extern QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE;
/// Random reset a signal of given width (init time only)
extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE;
/// Random reset a signal of given width (assign time only)
extern IData VL_RAND_RESET_ASSIGN_I(int obits) VL_MT_SAFE;
/// Random reset a signal of given width (assign time only)
extern QData VL_RAND_RESET_ASSIGN_Q(int obits) VL_MT_SAFE;
/// Random reset a signal of given width (assign time only)
extern WDataOutP VL_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp) VL_MT_SAFE;
/// Zero reset a signal (slow - else use VL_ZERO_W)
extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE;
@@ -115,6 +123,9 @@ extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP const lwp, WDataInP const rwp,
bool is_modulus) VL_MT_SAFE;
extern void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
size_t posstart, size_t posend) VL_MT_SAFE;
extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE;
extern void VL_FFLUSH_I(IData fdi) VL_MT_SAFE;
@@ -152,6 +163,7 @@ extern std::string VL_STACKTRACE_N() VL_MT_SAFE;
extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp) VL_MT_SAFE;
extern IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE;
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
extern IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE;
extern IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE;
extern const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE; // PLIish
@@ -215,6 +227,10 @@ static inline QData VL_CVT_Q_D(double lhs) VL_PURE {
return u.q;
}
// clang-format on
// Return string from DPI char*
static inline std::string VL_CVT_N_CSTR(const char* lhsp) VL_PURE {
return lhsp ? std::string{lhsp} : ""s;
}
// Return double from lhs (numeric) unsigned
double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
@@ -238,7 +254,7 @@ static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_MT_SAFE {
VL_SET_WQ(lwp, lhs);
return VL_ISTOR_D_W(lbits, lwp);
}
// Return QData from double (numeric)
// Return IData truncated from double (numeric)
static inline IData VL_RTOI_I_D(double lhs) VL_PURE { return static_cast<int32_t>(VL_TRUNC(lhs)); }
// Sign extend such that if MSB set, we get ffff_ffff, else 0s
@@ -504,19 +520,18 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
{ (svar).write(rd); }
#define VL_ASSIGN_SBQ(obits, svar, rd) \
{ (svar).write(rd); }
#define VL_SC_BITS_PER_DIGIT 30 // This comes from sc_nbdefs.h BITS_PER_DIGIT
#define VL_ASSIGN_SBW(obits, svar, rwp) \
{ \
sc_dt::sc_biguint<(obits)> _butemp; \
int32_t lsb = 0; \
uint32_t* chunkp = _butemp.get_raw(); \
while (lsb + VL_SC_BITS_PER_DIGIT < (obits)) { \
while (lsb + BITS_PER_DIGIT < (obits)) { \
static_assert(std::is_same<IData, EData>::value, "IData and EData mismatch"); \
const uint32_t data = VL_SEL_IWII(lsb + VL_SC_BITS_PER_DIGIT + 1, (rwp).data(), lsb, \
VL_SC_BITS_PER_DIGIT); \
*chunkp = data & VL_MASK_E(VL_SC_BITS_PER_DIGIT); \
const uint32_t data \
= VL_SEL_IWII(lsb + BITS_PER_DIGIT + 1, (rwp).data(), lsb, BITS_PER_DIGIT); \
*chunkp = data & VL_MASK_E(BITS_PER_DIGIT); \
++chunkp; \
lsb += VL_SC_BITS_PER_DIGIT; \
lsb += BITS_PER_DIGIT; \
} \
if (lsb < (obits)) { \
const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits)-lsb); \
@@ -1570,49 +1585,184 @@ static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const
return owp;
}
template <typename T>
static inline void VL_ASSIGN_DYN_Q(VlQueue<T>& q, int elem_size, int lbits, QData from) {
const int size = (lbits + elem_size - 1) / elem_size;
q.renew(size);
const QData mask = VL_MASK_Q(elem_size);
for (int i = 0; i < size; ++i) q.at(i) = (T)((from >> (i * elem_size)) & mask);
static inline IData VL_PACK_II(int obits, int lbits, const VlQueue<CData>& q) {
IData ret = 0;
for (size_t i = 0; i < q.size(); ++i) ret |= static_cast<IData>(q.at(i)) << (i * lbits);
return ret;
}
template <typename T, std::size_t T_Depth>
static inline void VL_ASSIGN_UNPACK_Q(VlUnpacked<T, T_Depth>& q, size_t elem_size, QData from) {
const QData mask = VL_MASK_Q(elem_size);
static inline IData VL_PACK_II(int obits, int lbits, const VlQueue<SData>& q) {
IData ret = 0;
for (size_t i = 0; i < q.size(); ++i) ret |= static_cast<IData>(q.at(i)) << (i * lbits);
return ret;
}
static inline IData VL_PACK_II(int obits, int lbits, const VlQueue<IData>& q) {
IData ret = 0;
for (size_t i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * lbits);
return ret;
}
template <std::size_t T_Depth>
static inline IData VL_PACK_II(int obits, int lbits, const VlUnpacked<CData, T_Depth>& q) {
IData ret = 0;
for (size_t i = 0; i < T_Depth; ++i)
q[i] = (T)((from >> ((T_Depth - 1 - i) * elem_size)) & mask);
ret |= static_cast<IData>(q[T_Depth - 1 - i]) << (i * lbits);
return ret;
}
template <typename T>
static inline IData VL_DYN_TO_I(const VlQueue<T>& q, int elem_size) {
template <std::size_t T_Depth>
static inline IData VL_PACK_II(int obits, int lbits, const VlUnpacked<SData, T_Depth>& q) {
IData ret = 0;
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
for (size_t i = 0; i < T_Depth; ++i)
ret |= static_cast<IData>(q[T_Depth - 1 - i]) << (i * lbits);
return ret;
}
template <typename T>
static inline QData VL_DYN_TO_Q(const VlQueue<T>& q, int elem_size) {
QData ret = 0;
for (int i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * elem_size);
return ret;
}
template <typename T, std::size_t T_Depth>
static inline IData VL_UNPACK_TO_I(const VlUnpacked<T, T_Depth>& q, size_t elem_size) {
template <std::size_t T_Depth>
static inline IData VL_PACK_II(int obits, int lbits, const VlUnpacked<IData, T_Depth>& q) {
IData ret = 0;
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * elem_size);
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * lbits);
return ret;
}
template <typename T, std::size_t T_Depth>
static inline QData VL_UNPACK_TO_Q(const VlUnpacked<T, T_Depth>& q, size_t elem_size) {
static inline QData VL_PACK_QI(int obits, int lbits, const VlQueue<CData>& q) {
QData ret = 0;
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * elem_size);
for (size_t i = 0; i < q.size(); ++i) ret |= static_cast<QData>(q.at(i)) << (i * lbits);
return ret;
}
static inline QData VL_PACK_QI(int obits, int lbits, const VlQueue<SData>& q) {
QData ret = 0;
for (size_t i = 0; i < q.size(); ++i) ret |= static_cast<QData>(q.at(i)) << (i * lbits);
return ret;
}
static inline QData VL_PACK_QI(int obits, int lbits, const VlQueue<IData>& q) {
QData ret = 0;
for (size_t i = 0; i < q.size(); ++i) ret |= static_cast<QData>(q.at(i)) << (i * lbits);
return ret;
}
template <std::size_t T_Depth>
static inline QData VL_PACK_QI(int obits, int lbits, const VlUnpacked<CData, T_Depth>& q) {
QData ret = 0;
for (size_t i = 0; i < T_Depth; ++i)
ret |= static_cast<QData>(q[T_Depth - 1 - i]) << (i * lbits);
return ret;
}
template <std::size_t T_Depth>
static inline QData VL_PACK_QI(int obits, int lbits, const VlUnpacked<SData, T_Depth>& q) {
QData ret = 0;
for (size_t i = 0; i < T_Depth; ++i)
ret |= static_cast<QData>(q[T_Depth - 1 - i]) << (i * lbits);
return ret;
}
template <std::size_t T_Depth>
static inline QData VL_PACK_QI(int obits, int lbits, const VlUnpacked<IData, T_Depth>& q) {
QData ret = 0;
for (size_t i = 0; i < T_Depth; ++i)
ret |= static_cast<QData>(q[T_Depth - 1 - i]) << (i * lbits);
return ret;
}
static inline QData VL_PACK_QQ(int obits, int lbits, const VlQueue<QData>& q) {
QData ret = 0;
for (size_t i = 0; i < q.size(); ++i) ret |= q.at(i) << (i * lbits);
return ret;
}
template <std::size_t T_Depth>
static inline QData VL_PACK_QQ(int obits, int lbits, const VlUnpacked<QData, T_Depth>& q) {
QData ret = 0;
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * lbits);
return ret;
}
static inline WDataOutP VL_PACK_WI(int obits, int lbits, WDataOutP owp, const VlQueue<CData>& q) {
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
for (size_t i = 0; i < q.size(); ++i)
_vl_insert_WI(owp, q.at(i), i * lbits + lbits - 1, i * lbits);
return owp;
}
static inline WDataOutP VL_PACK_WI(int obits, int lbits, WDataOutP owp, const VlQueue<SData>& q) {
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
for (size_t i = 0; i < q.size(); ++i)
_vl_insert_WI(owp, q.at(i), i * lbits + lbits - 1, i * lbits);
return owp;
}
static inline WDataOutP VL_PACK_WI(int obits, int lbits, WDataOutP owp, const VlQueue<IData>& q) {
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
for (size_t i = 0; i < q.size(); ++i)
_vl_insert_WI(owp, q.at(i), i * lbits + lbits - 1, i * lbits);
return owp;
}
template <std::size_t T_Depth>
static inline WDataOutP VL_PACK_WI(int obits, int lbits, WDataOutP owp,
const VlUnpacked<CData, T_Depth>& q) {
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
for (size_t i = 0; i < T_Depth; ++i)
_vl_insert_WI(owp, q[T_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
return owp;
}
template <std::size_t T_Depth>
static inline WDataOutP VL_PACK_WI(int obits, int lbits, WDataOutP owp,
const VlUnpacked<SData, T_Depth>& q) {
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
for (size_t i = 0; i < T_Depth; ++i)
_vl_insert_WI(owp, q[T_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
return owp;
}
template <std::size_t T_Depth>
static inline WDataOutP VL_PACK_WI(int obits, int lbits, WDataOutP owp,
const VlUnpacked<IData, T_Depth>& q) {
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
for (size_t i = 0; i < T_Depth; ++i)
_vl_insert_WI(owp, q[T_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
return owp;
}
static inline WDataOutP VL_PACK_WQ(int obits, int lbits, WDataOutP owp, const VlQueue<QData>& q) {
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
for (size_t i = 0; i < q.size(); ++i)
_vl_insert_WQ(owp, q.at(i), i * lbits + lbits - 1, i * lbits);
return owp;
}
template <std::size_t T_Depth>
static inline WDataOutP VL_PACK_WQ(int obits, int lbits, WDataOutP owp,
const VlUnpacked<QData, T_Depth>& q) {
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
for (size_t i = 0; i < T_Depth; ++i)
_vl_insert_WQ(owp, q[T_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
return owp;
}
template <std::size_t N>
static inline WDataOutP VL_PACK_WW(int obits, int lbits, WDataOutP owp,
const VlQueue<VlWide<N>>& q) {
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
for (size_t i = 0; i < q.size(); ++i)
_vl_insert_WW(owp, q.at(i), i * lbits + lbits - 1, i * lbits);
return owp;
}
template <std::size_t T_Depth, std::size_t N>
static inline WDataOutP VL_PACK_WW(int obits, int lbits, WDataOutP owp,
const VlUnpacked<VlWide<N>, T_Depth>& q) {
VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
for (size_t i = 0; i < T_Depth; ++i)
_vl_insert_WW(owp, q[T_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
return owp;
}
// Because concats are common and wide, it's valuable to always have a clean output.
// Thus we specify inputs must be clean, so we don't need to clean the output.
// Note the bit shifts are always constants, so the adds in these constify out.
@@ -2052,6 +2202,171 @@ static inline WDataOutP VL_SEL_WWII(int obits, int lbits, WDataOutP owp, WDataIn
//======================================================================
// Expressions needing insert/select
static inline void VL_UNPACK_II(int lbits, int rbits, VlQueue<CData>& q, IData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_II(int lbits, int rbits, VlQueue<SData>& q, IData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_II(int lbits, int rbits, VlQueue<IData>& q, IData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlQueue<CData>& q, QData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlQueue<SData>& q, QData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlQueue<IData>& q, QData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_QQ(int lbits, int rbits, VlQueue<QData>& q, QData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const QData mask = VL_MASK_Q(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_IW(int lbits, int rbits, VlQueue<CData>& q, WDataInP rwp) {
const int size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
}
static inline void VL_UNPACK_IW(int lbits, int rbits, VlQueue<SData>& q, WDataInP rwp) {
const int size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
}
static inline void VL_UNPACK_IW(int lbits, int rbits, VlQueue<IData>& q, WDataInP rwp) {
const int size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
}
static inline void VL_UNPACK_QW(int lbits, int rbits, VlQueue<QData>& q, WDataInP rwp) {
const int size = (rbits + lbits - 1) / lbits;
q.renew(size);
const QData mask = VL_MASK_Q(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = VL_SEL_QWII(rbits, rwp, i * lbits, lbits) & mask;
}
template <std::size_t N>
static inline void VL_UNPACK_WW(int lbits, int rbits, VlQueue<VlWide<N>>& q, WDataInP rwp) {
const int size = (rbits + lbits - 1) / lbits;
q.renew(size);
for (size_t i = 0; i < size; ++i) VL_SEL_WWII(lbits, rbits, q.at(i), rwp, i * lbits, lbits);
}
template <std::size_t T_Depth>
static inline void VL_UNPACK_II(int lbits, int rbits, VlUnpacked<CData, T_Depth>& q, IData from) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t T_Depth>
static inline void VL_UNPACK_II(int lbits, int rbits, VlUnpacked<SData, T_Depth>& q, IData from) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t T_Depth>
static inline void VL_UNPACK_II(int lbits, int rbits, VlUnpacked<IData, T_Depth>& q, IData from) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t T_Depth>
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlUnpacked<CData, T_Depth>& q, QData from) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t T_Depth>
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlUnpacked<SData, T_Depth>& q, QData from) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t T_Depth>
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlUnpacked<IData, T_Depth>& q, QData from) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t T_Depth>
static inline void VL_UNPACK_QQ(int lbits, int rbits, VlUnpacked<QData, T_Depth>& q, QData from) {
const QData mask = VL_MASK_Q(lbits);
for (size_t i = 0; i < T_Depth; ++i) q[i] = (from >> ((T_Depth - 1 - i) * lbits)) & mask;
}
template <std::size_t T_Depth>
static inline void VL_UNPACK_IW(int lbits, int rbits, VlUnpacked<CData, T_Depth>& q,
WDataInP rwp) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < T_Depth; ++i)
q[i] = VL_SEL_IWII(rbits, rwp, (T_Depth - 1 - i) * lbits, lbits) & mask;
}
template <std::size_t T_Depth>
static inline void VL_UNPACK_IW(int lbits, int rbits, VlUnpacked<SData, T_Depth>& q,
WDataInP rwp) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < T_Depth; ++i)
q[i] = VL_SEL_IWII(rbits, rwp, (T_Depth - 1 - i) * lbits, lbits) & mask;
}
template <std::size_t T_Depth>
static inline void VL_UNPACK_IW(int lbits, int rbits, VlUnpacked<IData, T_Depth>& q,
WDataInP rwp) {
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < T_Depth; ++i)
q[i] = VL_SEL_IWII(rbits, rwp, (T_Depth - 1 - i) * lbits, lbits) & mask;
}
template <std::size_t T_Depth>
static inline void VL_UNPACK_QW(int lbits, int rbits, VlUnpacked<QData, T_Depth>& q,
WDataInP rwp) {
const QData mask = VL_MASK_Q(lbits);
for (size_t i = 0; i < T_Depth; ++i)
q[i] = VL_SEL_QWII(rbits, rwp, (T_Depth - 1 - i) * lbits, lbits) & mask;
}
template <std::size_t T_Depth, std::size_t N>
static inline void VL_UNPACK_WW(int lbits, int rbits, VlUnpacked<VlWide<N>, T_Depth>& q,
WDataInP rwp) {
for (size_t i = 0; i < T_Depth; ++i)
VL_SEL_WWII(lbits, rbits, q[i], rwp, (T_Depth - 1 - i) * lbits, lbits);
}
// Return QData from double (numeric)
// EMIT_RULE: VL_RTOIROUND_Q_D: oclean=dirty; lclean==clean/real
static inline QData VL_RTOIROUND_Q_D(double lhs) VL_PURE {
+430
View File
@@ -0,0 +1,430 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2024 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU Lesser
// General Public License Version 3 or the Perl Artistic License Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//=========================================================================
///
/// \file
/// \brief Verilated randomization implementation code
///
/// This file must be compiled and linked against all Verilated objects
/// that use randomization features.
///
/// See the internals documentation docs/internals.rst for details.
///
//=========================================================================
#include "verilated_random.h"
#include <iostream>
#include <sstream>
#include <streambuf>
#define _VL_SOLVER_HASH_LEN 1
#define _VL_SOLVER_HASH_LEN_TOTAL 4
// clang-format off
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
# define _VL_SOLVER_PIPE // Allow pipe SMT solving. Needs fork()
#endif
#ifdef _VL_SOLVER_PIPE
# include <sys/wait.h>
# include <fcntl.h>
#endif
#if defined(_WIN32) || defined(__MINGW32__)
# include <io.h> // open, read, write, close
#endif
// clang-format on
class Process final : private std::streambuf, public std::iostream {
static constexpr int BUFFER_SIZE = 4096;
const char* const* m_cmd = nullptr; // fork() process argv
#ifdef _VL_SOLVER_PIPE
pid_t m_pid = 0; // fork() process id
#else
int m_pid = 0; // fork() process id - always zero as disabled
#endif
bool m_pidExited = true; // If subprocess has exited and can be opened
int m_pidStatus = 0; // fork() process exit status, valid if m_pidExited
int m_writeFd = -1; // File descriptor TO subprocess
int m_readFd = -1; // File descriptor FROM subprocess
char m_readBuf[BUFFER_SIZE];
char m_writeBuf[BUFFER_SIZE];
public:
typedef std::streambuf::traits_type traits_type;
protected:
int overflow(int c = traits_type::eof()) override {
char c2 = static_cast<char>(c);
if (pbase() == pptr()) return 0;
size_t size = pptr() - pbase();
ssize_t n = ::write(m_writeFd, pbase(), size);
if (n == -1) perror("write");
if (n <= 0) {
wait_report();
return traits_type::eof();
}
if (n == size)
setp(m_writeBuf, m_writeBuf + sizeof(m_writeBuf));
else
setp(m_writeBuf + n, m_writeBuf + sizeof(m_writeBuf));
if (c != traits_type::eof()) sputc(c2);
return 0;
}
int underflow() override {
sync();
ssize_t n = ::read(m_readFd, m_readBuf, sizeof(m_readBuf));
if (n == -1) perror("read");
if (n <= 0) {
wait_report();
return traits_type::eof();
}
setg(m_readBuf, m_readBuf, m_readBuf + n);
return traits_type::to_int_type(m_readBuf[0]);
}
int sync() override {
overflow();
return 0;
}
public:
explicit Process(const char* const* const cmd = nullptr)
: std::streambuf{}
, std::iostream{this}
, m_cmd{cmd} {
open(cmd);
}
void wait_report() {
if (m_pidExited) return;
#ifdef _VL_SOLVER_PIPE
if (waitpid(m_pid, &m_pidStatus, 0) != m_pid) return;
if (m_pidStatus) {
std::stringstream msg;
msg << "Subprocess command `" << m_cmd[0];
for (const char* const* arg = m_cmd + 1; *arg; arg++) msg << ' ' << *arg;
msg << "' failed: ";
if (WIFSIGNALED(m_pidStatus))
msg << strsignal(WTERMSIG(m_pidStatus))
<< (WCOREDUMP(m_pidStatus) ? " (core dumped)" : "");
else if (WIFEXITED(m_pidStatus))
msg << "exit status " << WEXITSTATUS(m_pidStatus);
const std::string str = msg.str();
VL_WARN_MT("", 0, "Process", str.c_str());
}
#endif
m_pidExited = true;
m_pid = 0;
closeFds();
}
void closeFds() {
if (m_writeFd != -1) {
close(m_writeFd);
m_writeFd = -1;
}
if (m_readFd != -1) {
close(m_readFd);
m_readFd = -1;
}
}
bool open(const char* const* const cmd) {
setp(std::begin(m_writeBuf), std::end(m_writeBuf));
setg(m_readBuf, m_readBuf, m_readBuf);
#ifdef _VL_SOLVER_PIPE
if (!cmd || !cmd[0]) return false;
m_cmd = cmd;
int fd_stdin[2]; // Can't use std::array
int fd_stdout[2]; // Can't use std::array
constexpr int P_RD = 0;
constexpr int P_WR = 1;
if (pipe(fd_stdin) != 0) {
perror("Process::open: pipe");
return false;
}
if (pipe(fd_stdout) != 0) {
perror("Process::open: pipe");
close(fd_stdin[P_RD]);
close(fd_stdin[P_WR]);
return false;
}
if (fd_stdin[P_RD] <= 2 || fd_stdin[P_WR] <= 2 || fd_stdout[P_RD] <= 2
|| fd_stdout[P_WR] <= 2) {
// We'd have to rearrange all of the FD usages in this case.
// Too unlikely; verilator isn't a daemon.
fprintf(stderr, "stdin/stdout closed before pipe opened\n");
close(fd_stdin[P_RD]);
close(fd_stdin[P_WR]);
close(fd_stdout[P_RD]);
close(fd_stdout[P_WR]);
return false;
}
const pid_t pid = fork();
if (pid < 0) {
perror("Process::open: fork");
close(fd_stdin[P_RD]);
close(fd_stdin[P_WR]);
close(fd_stdout[P_RD]);
close(fd_stdout[P_WR]);
return false;
}
if (pid == 0) {
// Child
close(fd_stdin[P_WR]);
dup2(fd_stdin[P_RD], STDIN_FILENO);
close(fd_stdout[P_RD]);
dup2(fd_stdout[P_WR], STDOUT_FILENO);
execvp(cmd[0], const_cast<char* const*>(cmd));
std::stringstream msg;
msg << "Process::open: execvp(" << cmd[0] << ")";
const std::string str = msg.str();
perror(str.c_str());
_exit(127);
}
// Parent
m_pid = pid;
m_pidExited = false;
m_pidStatus = 0;
m_readFd = fd_stdout[P_RD];
m_writeFd = fd_stdin[P_WR];
close(fd_stdin[P_RD]);
close(fd_stdout[P_WR]);
return true;
#else
return false;
#endif
}
};
static Process& getSolver() {
static Process s_solver;
static bool s_done = false;
if (s_done) return s_solver;
s_done = true;
static std::vector<const char*> s_argv;
static std::string s_program = Verilated::threadContextp()->solverProgram();
s_argv.emplace_back(&s_program[0]);
for (char* arg = &s_program[0]; *arg; arg++) {
if (*arg == ' ') {
*arg = '\0';
s_argv.emplace_back(arg + 1);
}
}
s_argv.emplace_back(nullptr);
const char* const* const cmd = &s_argv[0];
s_solver.open(cmd);
s_solver << "(set-logic QF_BV)\n";
s_solver << "(check-sat)\n";
s_solver << "(reset)\n";
std::string s;
getline(s_solver, s);
if (s == "sat") return s_solver;
std::stringstream msg;
msg << "Unable to communicate with SAT solver, please check its installation or specify a "
"different one in VERILATOR_SOLVER environment variable.\n";
msg << " ... Tried: $";
for (const char* const* arg = cmd; *arg; arg++) msg << ' ' << *arg;
msg << '\n';
const std::string str = msg.str();
VL_WARN_MT("", 0, "randomize", str.c_str());
while (getline(s_solver, s)) {}
return s_solver;
}
//======================================================================
// VlRandomizer:: Methods
void VlRandomVar::emit(std::ostream& s) const { s << m_name; }
void VlRandomConst::emit(std::ostream& s) const {
s << "#b";
for (int i = 0; i < m_width; i++) s << (VL_BITISSET_Q(m_val, m_width - i - 1) ? '1' : '0');
}
void VlRandomBinOp::emit(std::ostream& s) const {
s << '(' << m_op << ' ';
m_lhs->emit(s);
s << ' ';
m_rhs->emit(s);
s << ')';
}
void VlRandomExtract::emit(std::ostream& s) const {
s << "((_ extract " << m_idx << ' ' << m_idx << ") ";
m_expr->emit(s);
s << ')';
}
bool VlRandomVar::set(std::string&& val) const {
VlWide<VL_WQ_WORDS_E> qowp;
VL_SET_WQ(qowp, 0ULL);
WDataOutP owp = qowp;
int obits = width();
if (obits > VL_QUADSIZE) owp = reinterpret_cast<WDataOutP>(datap());
int i;
for (i = 0; val[i] && val[i] != '#'; i++) {}
if (val[i++] != '#') return false;
switch (val[i++]) {
case 'b': _vl_vsss_based(owp, obits, 1, &val[i], 0, val.size() - i); break;
case 'o': _vl_vsss_based(owp, obits, 3, &val[i], 0, val.size() - i); break;
case 'h': // FALLTHRU
case 'x': _vl_vsss_based(owp, obits, 4, &val[i], 0, val.size() - i); break;
default:
VL_WARN_MT(__FILE__, __LINE__, "randomize",
"Internal: Unable to parse solver's randomized number");
return false;
}
if (obits <= VL_BYTESIZE) {
CData* const p = static_cast<CData*>(datap());
*p = VL_CLEAN_II(obits, obits, owp[0]);
} else if (obits <= VL_SHORTSIZE) {
SData* const p = static_cast<SData*>(datap());
*p = VL_CLEAN_II(obits, obits, owp[0]);
} else if (obits <= VL_IDATASIZE) {
IData* const p = static_cast<IData*>(datap());
*p = VL_CLEAN_II(obits, obits, owp[0]);
} else if (obits <= VL_QUADSIZE) {
QData* const p = static_cast<QData*>(datap());
*p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp));
} else {
_vl_clean_inplace_w(obits, owp);
}
return true;
}
std::shared_ptr<const VlRandomExpr> VlRandomizer::randomConstraint(VlRNG& rngr, int bits) {
unsigned long long hash = VL_RANDOM_RNG_I(rngr) & ((1 << bits) - 1);
std::shared_ptr<const VlRandomExpr> concat = nullptr;
std::vector<std::shared_ptr<const VlRandomExpr>> varbits;
for (const auto& var : m_vars) {
for (int i = 0; i < var.second->width(); i++)
varbits.emplace_back(std::make_shared<const VlRandomExtract>(var.second, i));
}
for (int i = 0; i < bits; i++) {
std::shared_ptr<const VlRandomExpr> bit = nullptr;
for (unsigned j = 0; j * 2 < varbits.size(); j++) {
unsigned idx = j + VL_RANDOM_RNG_I(rngr) % (varbits.size() - j);
auto sel = varbits[idx];
std::swap(varbits[idx], varbits[j]);
bit = bit == nullptr ? sel : std::make_shared<const VlRandomBinOp>("bvxor", bit, sel);
}
concat = concat == nullptr ? bit
: std::make_shared<const VlRandomBinOp>("concat", concat, bit);
}
return std::make_shared<const VlRandomBinOp>(
"=", concat, std::make_shared<const VlRandomConst>(hash, bits));
}
bool VlRandomizer::next(VlRNG& rngr) {
if (m_vars.empty()) return true;
std::iostream& f = getSolver();
if (!f) return false;
f << "(set-option :produce-models true)\n";
f << "(set-logic QF_BV)\n";
for (const auto& var : m_vars) {
f << "(declare-fun " << var.second->name() << " () (_ BitVec " << var.second->width()
<< "))\n";
}
for (const std::string& constraint : m_constraints) { f << "(assert " << constraint << ")\n"; }
f << "(check-sat)\n";
bool sat = parseSolution(f);
if (!sat) {
f << "(reset)\n";
return false;
}
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; i++) {
f << "(assert ";
randomConstraint(rngr, _VL_SOLVER_HASH_LEN)->emit(f);
f << ")\n";
f << "\n(check-sat)\n";
sat = parseSolution(f);
}
f << "(reset)\n";
return true;
}
bool VlRandomizer::parseSolution(std::iostream& f) {
std::string sat;
do { std::getline(f, sat); } while (sat == "");
if (sat == "unsat") return false;
if (sat != "sat") {
std::stringstream msg;
msg << "Internal: Solver error: " << sat;
const std::string str = msg.str();
VL_WARN_MT(__FILE__, __LINE__, "randomize", str.c_str());
return false;
}
f << "(get-value (";
for (const auto& var : m_vars) f << var.second->name() << ' ';
f << "))\n";
// Quasi-parse S-expression of the form ((x #xVALUE) (y #bVALUE) (z #xVALUE))
char c;
f >> c;
if (c != '(') {
VL_WARN_MT(__FILE__, __LINE__, "randomize",
"Internal: Unable to parse solver's response: invalid S-expression");
return false;
}
while (true) {
f >> c;
if (c == ')') break;
if (c != '(') {
VL_WARN_MT(__FILE__, __LINE__, "randomize",
"Internal: Unable to parse solver's response: invalid S-expression");
return false;
}
std::string name, value;
f >> name;
std::getline(f, value, ')');
auto it = m_vars.find(name);
if (it == m_vars.end()) continue;
const VlRandomVar& varr = *it->second;
if (m_randmode && !varr.randModeIdxNone()) {
if (!(m_randmode->at(varr.randModeIdx()))) continue;
}
varr.set(std::move(value));
}
return true;
}
void VlRandomizer::hard(std::string&& constraint) {
m_constraints.emplace_back(std::move(constraint));
}
void VlRandomizer::clear() { m_constraints.clear(); }
#ifdef VL_DEBUG
void VlRandomizer::dump() const {
for (const auto& var : m_vars) {
VL_PRINTF("Variable (%d): %s\n", var.second->width(), var.second->name());
}
for (const std::string& c : m_constraints) VL_PRINTF("Constraint: %s\n", c.c_str());
}
#endif
+128
View File
@@ -0,0 +1,128 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2024 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU Lesser
// General Public License Version 3 or the Perl Artistic License Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilated randomization header
///
/// This file is included automatically by Verilator in some of the C++ files
/// it generates if randomization features are used.
///
/// This file is not part of the Verilated public-facing API.
/// It is only for internal use.
///
/// See the internals documentation docs/internals.rst for details.
///
//*************************************************************************
#ifndef VERILATOR_VERILATED_RANDOM_H_
#define VERILATOR_VERILATED_RANDOM_H_
#include "verilated.h"
//=============================================================================
// VlRandomExpr and subclasses represent expressions for the constraint solver.
class VlRandomExpr VL_NOT_FINAL {
public:
virtual void emit(std::ostream& s) const = 0;
};
class VlRandomVar final : public VlRandomExpr {
const char* const m_name; // Variable name
void* const m_datap; // Reference to variable data
const int m_width; // Variable width in bits
const std::uint32_t m_randModeIdx; // rand_mode index
public:
VlRandomVar(const char* name, int width, void* datap, std::uint32_t randModeIdx)
: m_name{name}
, m_datap{datap}
, m_width{width}
, m_randModeIdx{randModeIdx} {}
const char* name() const { return m_name; }
int width() const { return m_width; }
void* datap() const { return m_datap; }
std::uint32_t randModeIdx() const { return m_randModeIdx; }
bool randModeIdxNone() const { return randModeIdx() == std::numeric_limits<unsigned>::max(); }
bool set(std::string&&) const;
void emit(std::ostream& s) const override;
};
class VlRandomConst final : public VlRandomExpr {
const QData m_val; // Constant value
const int m_width; // Constant width in bits
public:
VlRandomConst(QData val, int width)
: m_val{val}
, m_width{width} {
assert(width <= sizeof(m_val) * 8);
}
void emit(std::ostream& s) const override;
};
class VlRandomExtract final : public VlRandomExpr {
const std::shared_ptr<const VlRandomExpr> m_expr; // Sub-expression
const unsigned m_idx; // Extracted index
public:
VlRandomExtract(std::shared_ptr<const VlRandomExpr> expr, unsigned idx)
: m_expr{expr}
, m_idx{idx} {}
void emit(std::ostream& s) const override;
};
class VlRandomBinOp final : public VlRandomExpr {
const char* const m_op; // Binary operation identifier
const std::shared_ptr<const VlRandomExpr> m_lhs, m_rhs; // Sub-expressions
public:
VlRandomBinOp(const char* op, std::shared_ptr<const VlRandomExpr> lhs,
std::shared_ptr<const VlRandomExpr> rhs)
: m_op{op}
, m_lhs{lhs}
, m_rhs{rhs} {}
void emit(std::ostream& s) const override;
};
//=============================================================================
// VlRandomizer is the object holding constraints and variable references.
class VlRandomizer final {
// MEMBERS
std::vector<std::string> m_constraints; // Solver-dependent constraints
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent
// variables
const VlQueue<CData>* m_randmode; // rand_mode state;
// PRIVATE METHODS
std::shared_ptr<const VlRandomExpr> randomConstraint(VlRNG& rngr, int bits);
bool parseSolution(std::iostream& file);
public:
// METHODS
// Finds the next solution satisfying the constraints
bool next(VlRNG& rngr);
template <typename T>
void write_var(T& var, int width, const char* name,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
auto it = m_vars.find(name);
if (it != m_vars.end()) return;
m_vars[name] = std::make_shared<const VlRandomVar>(name, width, &var, randmodeIdx);
}
void hard(std::string&& constraint);
void clear();
void set_randmode(const VlQueue<CData>& randmode) { m_randmode = &randmode; }
#ifdef VL_DEBUG
void dump() const;
#endif
};
#endif // Guard
+1 -2
View File
@@ -95,8 +95,7 @@ void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)))) {
const std::string fn = filename();
const std::string msg
= std::
string{"Can't deserialize; file has wrong header signature, or file not found: "}
= "Can't deserialize; file has wrong header signature, or file not found: "s
+ filename();
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
// Die before we close() as close would infinite loop
+13 -4
View File
@@ -40,7 +40,7 @@ package std;
task put(T message);
`ifdef VERILATOR_TIMING
if (m_bound != 0)
while (m_bound != 0 && m_queue.size() >= m_bound)
wait (m_queue.size() < m_bound);
m_queue.push_back(message);
`endif
@@ -56,7 +56,9 @@ package std;
task get(ref T message);
`ifdef VERILATOR_TIMING
wait (m_queue.size() > 0);
while (m_queue.size() == 0) begin
wait (m_queue.size() > 0);
end
message = m_queue.pop_front();
`endif
endtask
@@ -71,7 +73,9 @@ package std;
task peek(ref T message);
`ifdef VERILATOR_TIMING
wait (m_queue.size() > 0);
while (m_queue.size() == 0) begin
wait (m_queue.size() > 0);
end
message = m_queue[0];
`endif
endtask
@@ -98,7 +102,9 @@ package std;
task get(int keyCount = 1);
`ifdef VERILATOR_TIMING
wait (m_keyCount >= keyCount);
while (m_keyCount < keyCount) begin
wait (m_keyCount >= keyCount);
end
m_keyCount -= keyCount;
`endif
endtask
@@ -189,4 +195,7 @@ package std;
$urandom(s.atoi()); // Set the seed using a string
endfunction
endclass
function int randomize();
randomize = 0;
endfunction
endpackage
+9 -2
View File
@@ -67,9 +67,16 @@ void VlDelayScheduler::resume() {
}
if (!m_zeroDelayed.empty()) {
for (auto&& handle : m_zeroDelayed) handle.resume();
m_zeroDelayed.clear();
// First, we need to move the coroutines out of the queue, as a resumed coroutine can
// suspend on #0 again, adding itself to the queue, which can result in reallocating the
// queue mid-iteration.
// We swap with the m_zeroDlyResumed field to keep the allocated buffer.
m_zeroDlyResumed.swap(m_zeroDelayed);
for (auto&& handle : m_zeroDlyResumed) handle.resume();
m_zeroDlyResumed.clear();
resumed = true;
// We are now in the Active region, so any coroutines added to m_zeroDelayed in the
// meantime will have to wait until the next Inactive region.
}
if (!resumed) {
+18 -11
View File
@@ -32,14 +32,18 @@
// clang-format off
// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
#if defined _LIBCPP_VERSION // libc++
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
# if defined(__has_include) && !__has_include(<coroutine>) && __has_include(<experimental/coroutine>)
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
# endif
# include <experimental/coroutine>
namespace std {
using namespace experimental; // Bring std::experimental into the std namespace
}
# else
# include <coroutine>
# endif
# include <experimental/coroutine>
namespace std {
using namespace experimental; // Bring std::experimental into the std namespace
}
#else
# if defined __clang__ && defined __GLIBCXX__ && !defined __cpp_impl_coroutine
# define __cpp_impl_coroutine 1 // Clang doesn't define this, but it's needed for libstdc++
@@ -165,6 +169,9 @@ class VlDelayScheduler final {
VerilatedContext& m_context;
VlDelayedCoroutineQueue m_queue; // Coroutines to be restored at a certain simulation time
std::vector<VlCoroutineHandle> m_zeroDelayed; // Coroutines waiting for #0
std::vector<VlCoroutineHandle> m_zeroDlyResumed; // Coroutines that waited for #0 and are
// to be resumed. Kept as a field to avoid
// reallocation.
public:
// CONSTRUCTORS
@@ -193,9 +200,9 @@ public:
VlProcessRef process; // Data of the suspended process, null if not needed
VlDelayedCoroutineQueue& queue;
std::vector<VlCoroutineHandle>& queueZeroDelay;
uint64_t delay;
VlDelayPhase phase;
VlFileLineDebug fileline;
const uint64_t delay;
const VlDelayPhase phase;
const VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
@@ -208,7 +215,7 @@ public:
void await_resume() const {}
};
VlDelayPhase phase = (delay == 0) ? VlDelayPhase::INACTIVE : VlDelayPhase::ACTIVE;
const VlDelayPhase phase = (delay == 0) ? VlDelayPhase::INACTIVE : VlDelayPhase::ACTIVE;
#ifdef VL_DEBUG
if (phase == VlDelayPhase::INACTIVE) {
VL_WARN_MT(filename, lineno, VL_UNKNOWN,
+4 -3
View File
@@ -30,8 +30,9 @@
#include <condition_variable>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <type_traits>
#include <map>
#include <set>
#include <vector>
#include <deque>
@@ -294,7 +295,7 @@ private:
uint64_t m_timeLastDump = 0; // Last time we did a dump
bool m_didSomeDump = false; // Did at least one dump (i.e.: m_timeLastDump is valid)
VerilatedContext* m_contextp = nullptr; // The context used by the traced models
std::unordered_set<const VerilatedModel*> m_models; // The collection of models being traced
std::set<const VerilatedModel*> m_models; // The collection of models being traced
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex);
+4 -3
View File
@@ -633,12 +633,13 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
// Validate
if (!newModel) { // LCOV_EXCL_START
VL_FATAL_MT(__FILE__, __LINE__, "",
"The same model has already been added to this trace file");
VL_FATAL_MT(
__FILE__, __LINE__, "",
"The same model has already been added to this trace file or VerilatedContext");
}
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"A trace file instance can only handle models from the same context");
"A trace file instance can only handle models from the same VerilatedContext");
}
if (VL_UNCOVERABLE(m_didSomeDump)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
+240 -36
View File
@@ -36,7 +36,6 @@
#include <memory>
#include <set>
#include <string>
#include <unordered_set>
#include <utility>
//=========================================================================
@@ -415,8 +414,20 @@ public:
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
template <std::size_t T_Words>
struct VlWide;
// Type trait to check if a type is VlWide
template <typename>
struct VlIsVlWide : public std::false_type {};
template <std::size_t T_Words>
struct VlIsVlWide<VlWide<T_Words>> : public std::true_type {};
template <std::size_t T_Words>
struct VlWide final {
static constexpr size_t Words = T_Words;
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
EData m_storage[T_Words]; // Contents of the packed array
@@ -501,28 +512,29 @@ public:
return *this;
}
static VlQueue cons(const T_Value& lhs) {
// Construct new object from _V_alue and/or _C_ontainer child objects
static VlQueue consV(const T_Value& lhs) {
VlQueue out;
out.push_back(lhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const T_Value& rhs) {
static VlQueue consVV(const T_Value& lhs, const T_Value& rhs) {
VlQueue out;
out.push_back(rhs);
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const T_Value& rhs) {
static VlQueue consCV(const VlQueue& lhs, const T_Value& rhs) {
VlQueue out = lhs;
out.push_front(rhs);
return out;
}
static VlQueue cons(const T_Value& lhs, const VlQueue& rhs) {
static VlQueue consVC(const T_Value& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
out.push_back(lhs);
return out;
}
static VlQueue cons(const VlQueue& lhs, const VlQueue& rhs) {
static VlQueue consCC(const VlQueue& lhs, const VlQueue& rhs) {
VlQueue out = rhs;
for (const auto& i : lhs.m_deque) out.push_back(i);
return out;
@@ -740,7 +752,7 @@ public:
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue::cons(i);
if (with_func(index, i)) return VlQueue::consV(i);
++index;
}
return VlQueue{};
@@ -749,7 +761,7 @@ public:
VlQueue<IData> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
if (with_func(index, i)) return VlQueue<IData>::consV(index);
++index;
}
return VlQueue<IData>{};
@@ -758,7 +770,7 @@ public:
VlQueue find_last(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto& item : vlstd::reverse_view(m_deque)) {
if (with_func(index, item)) return VlQueue::cons(item);
if (with_func(index, item)) return VlQueue::consV(item);
--index;
}
return VlQueue{};
@@ -767,7 +779,7 @@ public:
VlQueue<IData> find_last_index(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto& item : vlstd::reverse_view(m_deque)) {
if (with_func(index, item)) return VlQueue<IData>::cons(index);
if (with_func(index, item)) return VlQueue<IData>::consV(index);
--index;
}
return VlQueue<IData>{};
@@ -777,7 +789,7 @@ public:
VlQueue min() const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::min_element(m_deque.cbegin(), m_deque.cend());
return VlQueue::cons(*it);
return VlQueue::consV(*it);
}
template <typename Func>
VlQueue min(Func with_func) const {
@@ -786,12 +798,12 @@ public:
[&with_func](const IData& a, const IData& b) {
return with_func(0, a) < with_func(0, b);
});
return VlQueue::cons(*it);
return VlQueue::consV(*it);
}
VlQueue max() const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::max_element(m_deque.cbegin(), m_deque.cend());
return VlQueue::cons(*it);
return VlQueue::consV(*it);
}
template <typename Func>
VlQueue max(Func with_func) const {
@@ -800,7 +812,7 @@ public:
[&with_func](const IData& a, const IData& b) {
return with_func(0, a) < with_func(0, b);
});
return VlQueue::cons(*it);
return VlQueue::consV(*it);
}
T_Value r_sum() const {
@@ -891,8 +903,8 @@ public:
}
};
template <class T_Value>
std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
template <class T_Value, size_t T_MaxSize>
std::string VL_TO_STRING(const VlQueue<T_Value, T_MaxSize>& obj) {
return obj.to_string();
}
@@ -1081,7 +1093,7 @@ public:
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
return VlQueue<T_Value>::consV(it->second);
}
template <typename Func>
VlQueue<T_Key> find_first_index(Func with_func) const {
@@ -1090,7 +1102,7 @@ public:
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
return VlQueue<T_Key>::consV(it->first);
}
template <typename Func>
VlQueue<T_Value> find_last(Func with_func) const {
@@ -1098,7 +1110,7 @@ public:
m_map.crbegin(), m_map.crend(),
[=](const std::pair<T_Key, T_Value>& i) { return with_func(i.first, i.second); });
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
return VlQueue<T_Value>::consV(it->second);
}
template <typename Func>
VlQueue<T_Key> find_last_index(Func with_func) const {
@@ -1106,7 +1118,7 @@ public:
m_map.crbegin(), m_map.crend(),
[=](const std::pair<T_Key, T_Value>& i) { return with_func(i.first, i.second); });
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
return VlQueue<T_Key>::consV(it->first);
}
// Reduction operators
@@ -1117,7 +1129,7 @@ public:
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
return VlQueue<T_Value>::consV(it->second);
}
template <typename Func>
VlQueue<T_Value> min(Func with_func) const {
@@ -1127,7 +1139,7 @@ public:
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return with_func(a.first, a.second) < with_func(b.first, b.second);
});
return VlQueue<T_Value>::cons(it->second);
return VlQueue<T_Value>::consV(it->second);
}
VlQueue<T_Value> max() const {
if (m_map.empty()) return VlQueue<T_Value>();
@@ -1136,7 +1148,7 @@ public:
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
return VlQueue<T_Value>::cons(it->second);
return VlQueue<T_Value>::consV(it->second);
}
template <typename Func>
VlQueue<T_Value> max(Func with_func) const {
@@ -1146,7 +1158,7 @@ public:
[&with_func](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return with_func(a.first, a.second) < with_func(b.first, b.second);
});
return VlQueue<T_Value>::cons(it->second);
return VlQueue<T_Value>::consV(it->second);
}
T_Value r_sum() const {
@@ -1309,7 +1321,11 @@ public:
// Similar to 'neq' above, *this = that used for change detection
void assign(const VlUnpacked<T_Value, T_Depth>& that) { *this = that; }
bool operator==(const VlUnpacked<T_Value, T_Depth>& that) const { return !neq(that); }
bool operator!=(const VlUnpacked<T_Value, T_Depth>& that) { return neq(that); }
bool operator!=(const VlUnpacked<T_Value, T_Depth>& that) const { return neq(that); }
// interface to C style arrays (used in ports), see issue #5125
bool neq(const T_Value that[T_Depth]) const { return neq(*this, that); }
void assign(const T_Value that[T_Depth]) { std::copy_n(that, T_Depth, m_storage); }
void operator=(const T_Value that[T_Depth]) { assign(that); }
// inside (set membership operator)
bool inside(const T_Value& value) const {
@@ -1386,7 +1402,7 @@ public:
VlQueue<T_Key> unique_index(Func with_func) const {
VlQueue<T_Key> out;
IData index = 0;
std::unordered_set<T_Value> saw;
std::set<T_Value> saw;
for (const auto& i : m_storage) {
const auto i_mapped = with_func(index, i);
auto it = saw.find(i_mapped);
@@ -1423,7 +1439,7 @@ public:
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_storage) {
if (with_func(index, i)) return VlQueue<T_Value>::cons(i);
if (with_func(index, i)) return VlQueue<T_Value>::consV(i);
++index;
}
return VlQueue<T_Value>{};
@@ -1432,7 +1448,7 @@ public:
VlQueue<T_Key> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_storage) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
if (with_func(index, i)) return VlQueue<IData>::consV(index);
++index;
}
return VlQueue<T_Key>{};
@@ -1440,14 +1456,14 @@ public:
template <typename Func>
VlQueue<T_Value> find_last(Func with_func) const {
for (int i = T_Depth - 1; i >= 0; i--) {
if (with_func(i, m_storage[i])) return VlQueue<T_Value>::cons(m_storage[i]);
if (with_func(i, m_storage[i])) return VlQueue<T_Value>::consV(m_storage[i]);
}
return VlQueue<T_Value>{};
}
template <typename Func>
VlQueue<T_Key> find_last_index(Func with_func) const {
for (int i = T_Depth - 1; i >= 0; i--) {
if (with_func(i, m_storage[i])) return VlQueue<IData>::cons(i);
if (with_func(i, m_storage[i])) return VlQueue<IData>::consV(i);
}
return VlQueue<T_Key>{};
}
@@ -1455,7 +1471,7 @@ public:
// Reduction operators
VlQueue<T_Value> min() const {
const auto it = std::min_element(std::begin(m_storage), std::end(m_storage));
return VlQueue<T_Value>::cons(*it);
return VlQueue<T_Value>::consV(*it);
}
template <typename Func>
VlQueue<T_Value> min(Func with_func) const {
@@ -1463,11 +1479,11 @@ public:
[&with_func](const IData& a, const IData& b) {
return with_func(0, a) < with_func(0, b);
});
return VlQueue<T_Value>::cons(*it);
return VlQueue<T_Value>::consV(*it);
}
VlQueue<T_Value> max() const {
const auto it = std::max_element(std::begin(m_storage), std::end(m_storage));
return VlQueue<T_Value>::cons(*it);
return VlQueue<T_Value>::consV(*it);
}
template <typename Func>
VlQueue<T_Value> max(Func with_func) const {
@@ -1475,7 +1491,7 @@ public:
[&with_func](const IData& a, const IData& b) {
return with_func(0, a) < with_func(0, b);
});
return VlQueue<T_Value>::cons(*it);
return VlQueue<T_Value>::consV(*it);
}
// Dumping. Verilog: str = $sformatf("%p", assoc)
@@ -1499,6 +1515,15 @@ private:
return false;
}
template <typename T_Val, std::size_t T_Dep>
static bool neq(const VlUnpacked<T_Val, T_Dep>& a, const T_Val b[T_Dep]) {
for (size_t i = 0; i < T_Dep; ++i) {
// Recursive 'neq', in case T_Val is also a VlUnpacked<_, _>
if (neq(a.m_storage[i], b[i])) return true;
}
return false;
}
template <typename T_Other> //
static bool neq(const T_Other& a, const T_Other& b) {
// Base case (T_Other is not VlUnpacked<_, _>), fall back on !=
@@ -1511,6 +1536,181 @@ std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
return obj.to_string();
}
//===================================================================
// Helper to apply the given indices to a target expression
template <size_t Curr, size_t Rank, typename T_Target>
struct VlApplyIndices final {
VL_ATTR_ALWINLINE
static auto& apply(T_Target& target, const size_t* indicesp) {
return VlApplyIndices<Curr + 1, Rank, decltype(target[indicesp[Curr]])>::apply(
target[indicesp[Curr]], indicesp);
}
};
template <size_t Rank, typename T_Target>
struct VlApplyIndices<Rank, Rank, T_Target> final {
VL_ATTR_ALWINLINE
static T_Target& apply(T_Target& target, const size_t*) { return target; }
};
//===================================================================
// Commit queue for NBAs - currently only for unpacked arrays
//
// This data-structure is used to handle non-blocking assignments
// that might execute a variable number of times in a single
// evaluation. It has 2 operations:
// - 'enqueue' will add an update to the queue
// - 'commit' will apply all enqueued updates to the target variable,
// in the order they were enqueued. This ensures the last NBA
// takes effect as it is expected.
// There are 2 specializations of this class below:
// - A version when a partial element update is not required,
// e.g, to handle:
// logic [31:0] array[N];
// for (int i = 0 ; i < N ; ++i) array[i] <= x;
// Here 'enqueue' takes the RHS ('x'), and the array indices ('i')
// as arguments.
// - A different version when a partial element update is required,
// e.g. for:
// logic [31:0] array[N];
// for (int i = 0 ; i < N ; ++i) array[i][3:1] <= y;
// Here 'enqueue' takes one additional argument, which is a bitmask
// derived from the bit selects (_[3:1]), which masks the bits that
// need to be updated, and additionally the RHS is widened to a full
// element size, with the bits inserted into the masked region.
template <typename T_Target, // Type of the variable this commit queue updates
bool Partial, // Whether partial element updates are necessary
// The following we could figure out from 'T_Target using type traits, but passing
// explicitly to avoid template expansion, as Verilator already knows them
typename T_Element, // Non-array leaf element type of T_Target array
std::size_t T_Rank // Rank of T_Target (i.e.: how many dimensions it has)
>
class VlNBACommitQueue;
// Specialization for whole element updates only
template <typename T_Target, typename T_Element, std::size_t T_Rank>
class VlNBACommitQueue<T_Target, /* Partial: */ false, T_Element, T_Rank> final {
// TYPES
struct Entry final {
T_Element value;
size_t indices[T_Rank];
};
// STATE
std::vector<Entry> m_pending; // Pending updates, in program order
public:
// CONSTRUCTOR
VlNBACommitQueue() = default;
VL_UNCOPYABLE(VlNBACommitQueue);
// METHODS
template <typename... Args>
void enqueue(const T_Element& value, Args... indices) {
m_pending.emplace_back(Entry{value, {indices...}});
}
// Note: T_Commit might be different from T_Target. Specifically, when the signal is a
// top-level IO port, T_Commit will be a native C array, while T_Target, will be a VlUnpacked
template <typename T_Commit>
void commit(T_Commit& target) {
if (m_pending.empty()) return;
for (const Entry& entry : m_pending) {
VlApplyIndices<0, T_Rank, T_Commit>::apply(target, entry.indices) = entry.value;
}
m_pending.clear();
}
};
// With partial element updates
template <typename T_Target, typename T_Element, std::size_t T_Rank>
class VlNBACommitQueue<T_Target, /* Partial: */ true, T_Element, T_Rank> final {
// TYPES
struct Entry final {
T_Element value;
T_Element mask;
size_t indices[T_Rank];
};
// STATE
std::vector<Entry> m_pending; // Pending updates, in program order
// STATIC METHODS
// Binary & | ~ for elements to use for masking in partial updates. Sorry for the templates.
template <typename T>
VL_ATTR_ALWINLINE static typename std::enable_if<!VlIsVlWide<T>::value, T>::type
bAnd(const T& a, const T& b) {
return a & b;
}
template <typename T>
VL_ATTR_ALWINLINE static typename std::enable_if<VlIsVlWide<T>::value, T>::type
bAnd(const T& a, const T& b) {
T result;
for (size_t i = 0; i < T::Words; ++i) {
result.m_storage[i] = a.m_storage[i] & b.m_storage[i];
}
return result;
}
template <typename T>
VL_ATTR_ALWINLINE static typename std::enable_if<!VlIsVlWide<T>::value, T>::type
bOr(const T& a, const T& b) {
return a | b;
}
template <typename T>
VL_ATTR_ALWINLINE static typename std::enable_if<VlIsVlWide<T>::value, T>::type //
bOr(const T& a, const T& b) {
T result;
for (size_t i = 0; i < T::Words; ++i) {
result.m_storage[i] = a.m_storage[i] | b.m_storage[i];
}
return result;
}
template <typename T>
VL_ATTR_ALWINLINE static typename std::enable_if<!VlIsVlWide<T>::value, T>::type
bNot(const T& a) {
return ~a;
}
template <typename T>
VL_ATTR_ALWINLINE static typename std::enable_if<VlIsVlWide<T>::value, T>::type
bNot(const T& a) {
T result;
for (size_t i = 0; i < T::Words; ++i) result.m_storage[i] = ~a.m_storage[i];
return result;
}
public:
// CONSTRUCTOR
VlNBACommitQueue() = default;
VL_UNCOPYABLE(VlNBACommitQueue);
// METHODS
template <typename... Args>
void enqueue(const T_Element& value, const T_Element& mask, Args... indices) {
m_pending.emplace_back(Entry{value, mask, {indices...}});
}
// Note: T_Commit might be different from T_Target. Specifically, when the signal is a
// top-level IO port, T_Commit will be a native C array, while T_Target, will be a VlUnpacked
template <typename T_Commit>
void commit(T_Commit& target) {
if (m_pending.empty()) return;
for (const Entry& entry : m_pending) { //
auto& ref = VlApplyIndices<0, T_Rank, T_Commit>::apply(target, entry.indices);
// Maybe inefficient, but it works for now ...
const auto oldValue = ref;
ref = bOr(bAnd(entry.value, entry.mask), bAnd(oldValue, bNot(entry.mask)));
}
m_pending.clear();
}
};
//===================================================================
// Object that VlDeleter is capable of deleting
@@ -1587,12 +1787,16 @@ public:
};
//===================================================================
// Represents the null pointer. Used for setting VlClassRef to null instead of
// via nullptr_t, to prevent the implicit conversion of 0 to nullptr.
// Represents the null pointer. Used for:
// * setting VlClassRef to null instead of via nullptr_t, to prevent the implicit conversion of 0
// to nullptr,
// * comparing interface pointers to null.
struct VlNull final {
operator bool() const { return false; }
bool operator==(void* ptr) const { return !ptr; }
};
inline bool operator==(void* ptr, VlNull) { return !ptr; }
//===================================================================
// Verilog class reference container
+222 -81
View File
@@ -31,11 +31,14 @@
#include "verilated.h"
#include "verilated_imp.h"
#include "vltstd/vpi_user.h"
#include <cstdarg>
#include <cstdio>
#include <list>
#include <map>
#include <set>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -630,6 +633,146 @@ public:
void invalidate() { m_id = 0; }
};
class VerilatedVpiPutHolder final {
VerilatedVpioVar m_var;
s_vpi_value m_value;
union Storage {
char init = 0; // to ensure trivial constructor
std::string str;
std::vector<s_vpi_vecval> vec;
~Storage() noexcept {/* handled by VerilatedVpiPutHolder */};
} m_storage{};
public:
VerilatedVpiPutHolder(const VerilatedVpioVar* vop, p_vpi_value valuep)
: m_var{vop} {
m_value.format = valuep->format;
switch (valuep->format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: {
new (&m_storage.str) std::string{valuep->value.str};
m_value.value.str = const_cast<char*>(m_storage.str.c_str());
break;
}
case vpiScalarVal: {
m_value.value.scalar = valuep->value.scalar;
break;
}
case vpiIntVal: {
m_value.value.integer = valuep->value.integer;
break;
}
case vpiRealVal: {
m_value.value.real = valuep->value.real;
break;
}
case vpiVectorVal: {
size_t words = 0;
switch (vop->varp()->vltype()) {
case VLVT_UINT8:
case VLVT_UINT16:
case VLVT_UINT32: {
words = 1;
break;
}
case VLVT_UINT64: {
words = 2;
break;
}
case VLVT_WDATA: {
words = VL_WORDS_I(vop->varp()->packed().elements());
break;
}
default: break;
}
new (&m_storage.vec)
std::vector<s_vpi_vecval>{valuep->value.vector, &valuep->value.vector[words]};
m_value.value.vector = m_storage.vec.data();
break;
}
}
}
VerilatedVpiPutHolder(VerilatedVpiPutHolder const& o)
: m_var{o.m_var}
, m_value{o.m_value} {
switch (m_value.format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: {
new (&m_storage.str) std::string{o.m_storage.str};
break;
}
case vpiVectorVal: {
new (&m_storage.vec) std::vector<s_vpi_vecval>{o.m_storage.vec};
break;
}
}
}
VerilatedVpiPutHolder(VerilatedVpiPutHolder&& o) noexcept
: m_var{std::move(o.m_var)}
, m_value{std::move(o.m_value)} {
switch (m_value.format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: {
new (&m_storage.str) std::string{std::move(o.m_storage.str)};
break;
}
case vpiVectorVal: {
new (&m_storage.vec) std::vector<s_vpi_vecval>{std::move(o.m_storage.vec)};
break;
}
}
}
~VerilatedVpiPutHolder() noexcept {
switch (m_value.format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: m_storage.str.~basic_string(); break;
case vpiVectorVal: m_storage.vec.~vector(); break;
}
}
VerilatedVpioVar* varp() { return &m_var; }
p_vpi_value valuep() { return &m_value; }
static bool canInertialDelay(p_vpi_value valuep) {
switch (valuep->format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: {
if (VL_UNLIKELY(!valuep->value.str)) return false;
break;
}
case vpiScalarVal: // FALLTHRU
case vpiIntVal: // FALLTHRU
case vpiRealVal: break;
case vpiVectorVal: {
if (VL_UNLIKELY(!valuep->value.vector)) return false;
break;
}
default: {
return false;
}
}
return true;
}
};
struct VerilatedVpiTimedCbsCmp final {
// Ordering sets keyed by time, then callback unique id
bool operator()(const std::pair<QData, uint64_t>& a,
@@ -652,9 +795,11 @@ class VerilatedVpiImp final {
std::array<VpioCbList, CB_ENUM_MAX_VALUE> m_cbCurrentLists;
VpioFutureCbs m_futureCbs; // Time based callbacks for future timestamps
VpioFutureCbs m_nextCbs; // cbNextSimTime callbacks
std::list<VerilatedVpiPutHolder> m_inertialPuts; // Pending vpi puts due to vpiInertialDelay
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
uint64_t m_nextCallbackId = 1; // Id to identify callback
bool m_evalNeeded = false; // Model has had signals updated via vpi_put_value()
static VerilatedVpiImp& s() { // Singleton
static VerilatedVpiImp s_s;
@@ -776,7 +921,7 @@ public:
assertOneCheck();
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
bool called = false;
std::unordered_set<VerilatedVpioVar*> update; // set of objects to update after callbacks
std::set<VerilatedVpioVar*> update; // set of objects to update after callbacks
if (cbObjList.empty()) return called;
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
for (auto it = cbObjList.begin(); true;) {
@@ -813,6 +958,17 @@ public:
}
static void dumpCbs() VL_MT_UNSAFE_ONE;
static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info
static bool evalNeeded() { return s().m_evalNeeded; }
static void evalNeeded(bool evalNeeded) { s().m_evalNeeded = evalNeeded; }
static void inertialDelay(const VerilatedVpioVar* vop, p_vpi_value valuep) {
s().m_inertialPuts.emplace_back(vop, valuep);
}
static void doInertialPuts() {
for (auto& it : s().m_inertialPuts) {
vpi_put_value(it.varp()->castVpiHandle(), it.valuep(), nullptr, vpiNoDelay);
}
s().m_inertialPuts.clear();
}
};
//======================================================================
@@ -915,6 +1071,11 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
return 1;
}
void VerilatedVpi::clearEvalNeeded() VL_MT_UNSAFE_ONE { VerilatedVpiImp::evalNeeded(false); }
bool VerilatedVpi::evalNeeded() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::evalNeeded(); }
void VerilatedVpi::doInertialPuts() VL_MT_UNSAFE_ONE { VerilatedVpiImp::doInertialPuts(); }
//======================================================================
// VerilatedVpiImp implementation
@@ -2194,13 +2355,28 @@ bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const c
return status;
}
static void vl_strprintf(std::string& buffer, char const* fmt, ...) {
va_list args, args_copy;
va_start(args, fmt);
buffer.clear();
// Make copy of args since we may need to call VL_VSNPRINTF more than once
va_copy(args_copy, args);
// Try VL_VSNPRINTF in existing buffer
const int result
= VL_VSNPRINTF(const_cast<char*>(buffer.data()), buffer.capacity(), fmt, args_copy);
va_end(args_copy);
const int required = result + 1; // Returned size doesn't include NUL terminator
// If there wasn't enough space, reallocate and try again
if (buffer.capacity() < required) {
buffer.reserve(required * 2);
VL_VSNPRINTF(const_cast<char*>(buffer.data()), buffer.capacity(), fmt, args);
}
va_end(args);
}
void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
const char* fullname) {
if (!vl_check_format(varp, valuep, fullname, true)) return;
// Maximum required size is for binary string, one byte per bit plus null termination
static thread_local char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1];
// cppcheck-suppress variableScope
static const thread_local int t_outStrSz = sizeof(t_outStr) - 1;
// string data type is dynamic and may vary in size during simulation
static thread_local std::string t_outDynamicStr;
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
@@ -2244,43 +2420,21 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
return;
}
} else if (valuep->format == vpiBinStrVal) {
valuep->value.str = t_outStr;
int bits = varp->packed().elements();
t_outDynamicStr.resize(bits);
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (bits > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_VALUE_STRING_MAX_WORDS, bits);
bits = t_outStrSz;
}
for (i = 0; i < bits; ++i) {
for (size_t i = 0; i < bits; ++i) {
const char val = (datap[i >> 3] >> (i & 7)) & 1;
t_outStr[bits - i - 1] = val ? '1' : '0';
t_outDynamicStr[bits - i - 1] = val ? '1' : '0';
}
t_outStr[i] = '\0';
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiOctStrVal) {
valuep->value.str = t_outStr;
int chars = (varp->packed().elements() + 2) / 3;
t_outDynamicStr.resize(chars);
const int bytes = VL_BYTES_I(varp->packed().elements());
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (chars > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_VALUE_STRING_MAX_WORDS, chars);
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
for (size_t i = 0; i < chars; ++i) {
const div_t idx = div(i * 3, 8);
int val = datap[idx.quot];
if ((idx.quot + 1) < bytes) {
@@ -2299,46 +2453,31 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
val &= (1 << rem) - 1;
}
}
t_outStr[chars - i - 1] = '0' + (val & 7);
t_outDynamicStr[chars - i - 1] = '0' + (val & 7);
}
t_outStr[i] = '\0';
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiDecStrVal) {
valuep->value.str = t_outStr;
// outStrSz does not include nullptr termination so add one
if (varp->vltype() == VLVT_UINT8) {
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%hhu",
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
return;
vl_strprintf(t_outDynamicStr, "%hhu",
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
} else if (varp->vltype() == VLVT_UINT16) {
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%hu",
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
return;
vl_strprintf(t_outDynamicStr, "%hu",
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
} else if (varp->vltype() == VLVT_UINT32) {
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%u",
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
return;
vl_strprintf(t_outDynamicStr, "%u",
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
} else if (varp->vltype() == VLVT_UINT64) {
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%llu",
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
return;
vl_strprintf(t_outDynamicStr, "%llu",
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
}
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiHexStrVal) {
valuep->value.str = t_outStr;
int chars = (varp->packed().elements() + 3) >> 2;
t_outDynamicStr.resize(chars);
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (chars > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_VALUE_STRING_MAX_WORDS, chars);
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
for (size_t i = 0; i < chars; ++i) {
char val = (datap[i >> 1] >> ((i & 1) << 2)) & 15;
if (i == (chars - 1)) {
// most significant char, mask off nonexistent bits when vector
@@ -2349,9 +2488,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
val &= (1 << rem) - 1;
}
}
t_outStr[chars - i - 1] = "0123456789abcdef"[static_cast<int>(val)];
t_outDynamicStr[chars - i - 1] = "0123456789abcdef"[static_cast<int>(val)];
}
t_outStr[i] = '\0';
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiStringVal) {
if (varp->vltype() == VLVT_STRING) {
@@ -2364,26 +2503,15 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
return;
}
} else {
valuep->value.str = t_outStr;
int bytes = VL_BYTES_I(varp->packed().elements());
t_outDynamicStr.resize(bytes);
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (bytes > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than "
"required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format),
fullname, t_outStrSz, VL_VALUE_STRING_MAX_WORDS, bytes);
bytes = t_outStrSz;
}
for (i = 0; i < bytes; ++i) {
for (size_t i = 0; i < bytes; ++i) {
const char val = datap[bytes - i - 1];
// other simulators replace [leading?] zero chars with spaces, replicate here.
t_outStr[i] = val ? val : ' ';
t_outDynamicStr[i] = val ? val : ' ';
}
t_outStr[i] = '\0';
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
}
} else if (valuep->format == vpiIntVal) {
@@ -2432,7 +2560,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
}
vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/,
PLI_INT32 /*flags*/) {
PLI_INT32 flags) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, valuep););
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
@@ -2440,11 +2568,24 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer");
return nullptr;
}
PLI_INT32 delay_mode = flags & 0xfff;
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
if (delay_mode == vpiInertialDelay) {
if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) {
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Unsupported p_vpi_value as requested for '%s' with vpiInertialDelay",
__func__, vop->fullname());
return nullptr;
}
VerilatedVpiImp::inertialDelay(vop, valuep);
return object;
}
VL_DEBUG_IF_PLI(
VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(),
valuep->format, valuep->value.integer);
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap()););
VerilatedVpiImp::evalNeeded(true);
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
VL_VPI_WARNING_(__FILE__, __LINE__,
+7
View File
@@ -54,6 +54,13 @@ public:
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
/// Debug dump of callbacks
static void dumpCbs() VL_MT_UNSAFE_ONE;
/// Checks VPI dirty state (i.e. whether vpi_put_value() has
/// been called since the last clearEvalNeeded())
static bool evalNeeded() VL_MT_UNSAFE_ONE;
/// Clears VPI dirty state (see evalNeeded())
static void clearEvalNeeded() VL_MT_UNSAFE_ONE;
/// Perform inertially delayed puts
static void doInertialPuts() VL_MT_UNSAFE_ONE;
// Self test, for internal use only
static void selfTest() VL_MT_UNSAFE_ONE;
+5
View File
@@ -614,8 +614,13 @@ static inline double VL_ROUND(double n) {
//=========================================================================
// Time and performance
#include <string>
namespace VlOs {
/// Get environment variable
extern std::string getenvStr(const std::string& envvar,
const std::string& defaultValue) VL_MT_SAFE;
extern uint64_t memUsageBytes() VL_MT_SAFE; ///< Return memory usage in bytes, or 0 if unknown
// Internal: Record CPU time, starting point on construction, and current delta from that
+30 -4
View File
@@ -51,10 +51,10 @@ double DeltaCpuTime::gettime() VL_MT_SAFE {
#else
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
timespec ts;
if (0 != clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)) // MT-Safe
if (0 == clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)) // MT-Safe // LCOV_EXCL_BR_LINE
return ts.tv_sec + ts.tv_nsec * 1e-9;
#endif
return 0.0;
return 0.0; // LCOV_EXCL_LINE
}
double DeltaWallTime::gettime() VL_MT_SAFE {
#if defined(_WIN32) || defined(__MINGW32__)
@@ -66,9 +66,9 @@ double DeltaWallTime::gettime() VL_MT_SAFE {
#else
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
timespec ts;
if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) // MT-Safe
if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) // MT-Safe // LCOV_EXCL_BR_LINE
return ts.tv_sec + ts.tv_nsec * 1e-9;
return 0.0;
return 0.0; // LCOV_EXCL_LINE
#endif
}
@@ -99,5 +99,31 @@ uint64_t memUsageBytes() VL_MT_SAFE {
#endif
}
//=========================================================================
// VlOs::getenvStr implementation
std::string getenvStr(const std::string& envvar, const std::string& defaultValue) VL_MT_SAFE {
std::string ret;
#if defined(_MSC_VER)
// Note: MinGW does not offer _dupenv_s
const char* envvalue = nullptr;
_dupenv_s((char**)&envvalue, nullptr, envvar.c_str());
if (envvalue != nullptr) {
const std::string result{envvalue};
free((void*)envvalue);
ret = result;
} else {
ret = defaultValue;
}
#else
if (const char* const envvalue = getenv(envvar.c_str())) {
ret = envvalue;
} else {
ret = defaultValue;
}
#endif
return ret;
}
//=========================================================================
} //namespace VlOs
+24 -5
View File
@@ -180,9 +180,18 @@ def test():
if Args.stage_enabled[18]:
ci_fold_start("upload")
print("Stage 18: Upload")
cmd = "bash <(cat ci/coverage-upload.sh) -f " + cc_dir + "/app_total.info"
print("print: Not running: export CODECOV_TOKEN=<hidden>")
print("print: Not running: " + cmd)
# curl -Os https://cli.codecov.io/latest/linux/codecov ; sudo chmod +x codecov
# --disable-search does not seem to work
# -d with false directory does not seem to work
# So, remove gcno files before calling codecov
upload_dir = "nodist/obj_dir/upload"
os.makedirs(upload_dir, exist_ok=True)
cmd = ("ci/codecov -v upload-process -Z" + " -f " + cc_dir +
"/app_total.info )")
print("print: Not running:")
print(" export CODECOV_TOKEN=<hidden>")
print(" find . -name '*.gcno' -exec rm {} \\;")
print(" " + cmd)
ci_fold_end()
if Args.stage_enabled[19]:
@@ -253,12 +262,21 @@ def cleanup_abs_paths_info(cc_dir, infile, outfile):
lines = []
with open(infile, "r", encoding="utf8") as fh:
for line in fh:
if re.search(r'^SF:', line):
if re.search(r'^SF:', line) and not re.search(r'^SF:/usr/', line):
line = re.sub(os.environ['VERILATOR_ROOT'] + '/',
'',
line,
count=1)
line = re.sub(cc_dir + '/', '', line, count=1)
line = re.sub(r'^SF:.*?/include/',
'SF:include/',
line,
count=1)
line = re.sub(r'^SF:.*?/src/', 'SF:src/', line, count=1)
line = re.sub(r'^SF:.*?/test_regress/',
'SF:test_regress/',
line,
count=1)
line = re.sub(r'obj_dbg/verilog.y$', 'verilog.y', line)
# print("Remaining SF: "+line)
lines.append(line)
@@ -364,12 +382,13 @@ parser.add_argument('--scenarios',
help='pass test scenarios onto driver.pl test harness')
parser.add_argument(
'--stages',
'--stage',
action='store',
help='runs a specific stage or range of stages (see the script)')
parser.add_argument(
'--tests',
'--test',
action='append',
dest='tests',
default=[],
help=
'Instead of normal regressions, run the specified test(s), may be used multiple times'
+374 -49
View File
@@ -18,20 +18,21 @@
$ ./fastcov.py --exclude /usr/include test/ --lcov -o report.info
$ genhtml -o code_coverage report.info
"""
import re
import os
import sys
import glob
import json
import time
import fnmatch
import logging
import argparse
import threading
import subprocess
import multiprocessing
from pathlib import Path
FASTCOV_VERSION = (1,7)
FASTCOV_VERSION = (1,15)
MINIMUM_PYTHON = (3,5)
MINIMUM_GCOV = (9,0,0)
@@ -40,6 +41,10 @@ START_TIME = time.monotonic()
GCOVS_TOTAL = 0
GCOVS_SKIPPED = 0
# Gcov Coverage File Extensions
GCOV_GCNO_EXT = ".gcno" # gcno = "[gc]ov [no]te"
GCOV_GCDA_EXT = ".gcda" # gcda = "[gc]ov [da]ta"
# For when things go wrong...
# Start error codes at 3 because 1-2 are special
# See https://stackoverflow.com/a/1535733/2516916
@@ -49,6 +54,8 @@ EXIT_CODES = {
"python_version": 4,
"unsupported_coverage_format": 5,
"excl_not_found": 6,
"bad_chunk_file": 7,
"missing_json_key": 8,
}
# Disable all logging in case developers are using this as a module
@@ -60,6 +67,132 @@ class FastcovFormatter(logging.Formatter):
log_message = super(FastcovFormatter, self).format(record)
return "[{:.3f}s] {}".format(stopwatch(), log_message)
class DiffParseError(Exception):
pass
class DiffParser(object):
def _refinePaths(self, diff_metadata, diff_base_dir):
diff_metadata.pop('/dev/null', None)
diff_metadata.pop('', None)
for key, value in diff_metadata.copy().items():
diff_metadata.pop(key)
#sources without added lines will be excluded
if value:
newpath = os.path.join(diff_base_dir, key) if diff_base_dir else os.path.abspath(key)
diff_metadata[newpath] = value
def _parseTargetFile(self, line_with_target_file):
#f.e. '+++ b/README.md1' or '+++ b/README.md1 timestamp'
target_source = line_with_target_file[4:].partition('\t')[0].strip()
target_source = target_source[2:] if target_source.startswith('b/') else target_source
return target_source
def _parseHunkBoundaries(self, line_with_hunk_boundaries, line_index):
#f.e. '@@ -121,4 +122,4 @@ Time to process all gcda and parse all gcov:'
# Here ['-121,4', '+122,4']
lines_info = line_with_hunk_boundaries[3:].partition("@@")[0].strip().split(' ')
if len(lines_info) != 2:
raise DiffParseError("Found invalid hunk. Line #{}. {}".format(line_index, line_with_hunk_boundaries))
# Here ['122','4']
target_lines_info = lines_info[1].strip('+').partition(',')
target_line_current = int(target_lines_info[0])
target_lines_count = int(target_lines_info[2]) if target_lines_info[2] else 1
# Here ['121','4']
source_lines_info = lines_info[0].strip('-').partition(',')
source_line_current = int(source_lines_info[0])
source_lines_count = int(source_lines_info[2]) if source_lines_info[2] else 1
return target_line_current, target_lines_count, source_line_current, source_lines_count
def parseDiffFile(self, diff_file, diff_base_dir, fallback_encodings=[]):
diff_metadata = {}
target_source = None
target_hunk = set()
target_line_current = 0
target_line_end = 0
source_line_current = 0
source_line_end = 0
found_hunk = False
for i, line in enumerate(getSourceLines(diff_file, fallback_encodings), 1):
line = line.rstrip()
if not found_hunk:
if line.startswith('+++ '):
# refresh file
target_source = self._parseTargetFile(line)
elif line.startswith('@@ '):
# refresh hunk
target_line_current, target_lines_count, source_line_current, source_lines_count = self._parseHunkBoundaries(line, i)
target_line_end = target_line_current + target_lines_count
source_line_end = source_line_current + source_lines_count
target_hunk = set()
found_hunk = True
continue
if target_line_current > target_line_end or source_line_current > source_line_end:
raise DiffParseError("Hunk longer than expected. Line #{}. {}".format(i, line))
if line.startswith('+'):
#line related to target
target_hunk.add(target_line_current)
target_line_current = target_line_current + 1
elif line.startswith(' ') or line == '':
# line related to both
target_line_current = target_line_current + 1
source_line_current = source_line_current + 1
elif line.startswith('-'):
# line related to source
source_line_current = source_line_current + 1
elif not line.startswith('\\'): # No newline at end of file
# line with newline marker is not included into any boundaries
raise DiffParseError("Found unrecognized hunk line type. Line #{}. {}".format(i, line))
if target_line_current == target_line_end and source_line_current == source_line_end:
# Checked all lines, save data
if target_source in diff_metadata:
diff_metadata[target_source] = target_hunk.union(diff_metadata[target_source])
else:
diff_metadata[target_source] = target_hunk
target_hunk = set()
found_hunk = False
if target_line_current != target_line_end or source_line_current != source_line_end:
raise DiffParseError("Unexpected end of file. Expected hunk with {} target lines, {} source lines".format(
target_line_end - target_line_current, source_line_end - source_line_current))
self._refinePaths(diff_metadata, diff_base_dir)
return diff_metadata
def filterByDiff(self, diff_file, dir_base_dir, fastcov_json, fallback_encodings=[]):
diff_metadata = self.parseDiffFile(diff_file, dir_base_dir, fallback_encodings)
logging.debug("Include only next files: {}".format(diff_metadata.keys()))
excluded_files_count = 0
excluded_lines_count = 0
for source in list(fastcov_json["sources"].keys()):
diff_lines = diff_metadata.get(source, None)
if not diff_lines:
excluded_files_count = excluded_files_count + 1
logging.debug("Exclude {} according to diff file".format(source))
fastcov_json["sources"].pop(source)
continue
for test_name, report_data in fastcov_json["sources"][source].copy().items():
#No info about functions boundaries, removing all
for function in list(report_data["functions"].keys()):
report_data["functions"].pop(function, None)
for line in list(report_data["lines"].keys()):
if line not in diff_lines:
excluded_lines_count = excluded_lines_count + 1
report_data["lines"].pop(line)
for branch_line in list(report_data["branches"].keys()):
if branch_line not in diff_lines:
report_data["branches"].pop(branch_line)
if len(report_data["lines"]) == 0:
fastcov_json["sources"][source].pop(test_name)
if len(fastcov_json["sources"][source]) == 0:
excluded_files_count = excluded_files_count + 1
logging.debug('Exclude {} file as it has no lines due to diff filter'.format(source))
fastcov_json["sources"].pop(source)
logging.info("Excluded {} files and {} lines according to diff file".format(excluded_files_count, excluded_lines_count))
return fastcov_json
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
@@ -69,6 +202,10 @@ def setExitCode(key):
global EXIT_CODE
EXIT_CODE = EXIT_CODES[key]
def setExitCodeRaw(code):
global EXIT_CODE
EXIT_CODE = code
def incrementCounters(total, skipped):
global GCOVS_TOTAL
global GCOVS_SKIPPED
@@ -98,10 +235,48 @@ def getGcovVersion(gcov):
p.wait()
return parseVersionFromLine(output.split("\n")[0])
def tryParseNumber(s):
try:
return int(s)
except ValueError:
# Log a warning if not hyphen
if s != "-":
logging.warning("Unsupported numerical value '%s', using 0", s)
# Default to 0 if we can't parse the number (e.g. "-", "NaN", etc.)
return 0
def removeFiles(files):
for file in files:
os.remove(file)
def processPrefix(path, prefix, prefix_strip):
p = Path(path)
if p.exists() or not p.is_absolute():
return path
if prefix_strip > 0:
segments = p.parts
if len(segments) < prefix_strip + 1:
logging.warning("Couldn't strip %i path levels from %s.", prefix_strip, path)
return path
segments = segments[prefix_strip+1:]
p = Path(segments[0])
segments = segments[1:]
for s in segments:
p = p.joinpath(s)
if len(prefix) > 0:
if p.is_absolute():
p = Path(prefix).joinpath(p.relative_to('/'))
else:
p = Path(prefix).joinpath(p)
return str(p)
def getFilteredCoverageFiles(coverage_files, exclude):
def excludeGcda(gcda):
for ex in exclude:
@@ -111,11 +286,16 @@ def getFilteredCoverageFiles(coverage_files, exclude):
return True
return list(filter(excludeGcda, coverage_files))
def globCoverageFiles(cwd, coverage_type):
return glob.glob(os.path.join(os.path.abspath(cwd), "**/*" + coverage_type), recursive=True)
def findCoverageFiles(cwd, coverage_files, use_gcno):
coverage_type = "user provided"
if not coverage_files:
coverage_type = "gcno" if use_gcno else "gcda"
coverage_files = glob.glob(os.path.join(os.path.abspath(cwd), "**/*." + coverage_type), recursive=True)
# gcov strips off extension of whatever you pass it and searches [extensionless name] + .gcno/.gcda
# We should pass either gcno or gcda, but not both - if you pass both it will be processed twice
coverage_type = GCOV_GCNO_EXT if use_gcno else GCOV_GCDA_EXT
coverage_files = globCoverageFiles(cwd, coverage_type)
logging.info("Found {} coverage files ({})".format(len(coverage_files), coverage_type))
logging.debug("Coverage files found:\n %s", "\n ".join(coverage_files))
@@ -125,15 +305,32 @@ def gcovWorker(data_q, metrics_q, args, chunk, gcov_filter_options):
base_report = {"sources": {}}
gcovs_total = 0
gcovs_skipped = 0
error_exit = False
gcov_args = "-it"
gcov_bin = args.gcov
gcov_args = ["--json-format", "--stdout"]
if args.branchcoverage or args.xbranchcoverage:
gcov_args += "b"
gcov_args.append("--branch-probabilities")
p = subprocess.Popen([args.gcov, gcov_args] + chunk, cwd=args.cdirectory, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
for line in iter(p.stdout.readline, b''):
intermediate_json = json.loads(line.decode(sys.stdout.encoding))
intermediate_json_files = processGcovs(args.cdirectory, intermediate_json["files"], gcov_filter_options)
encoding = sys.stdout.encoding if sys.stdout.encoding else 'UTF-8'
workdir = args.cdirectory if args.cdirectory else "."
p = subprocess.Popen([gcov_bin] + gcov_args + chunk, cwd=workdir, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
for i, line in enumerate(iter(p.stdout.readline, b'')):
try:
intermediate_json = json.loads(line.decode(encoding))
except json.decoder.JSONDecodeError as e:
logging.error("Could not process chunk file '{}' ({}/{})".format(chunk[i], i+1, len(chunk)))
logging.error(str(e))
setExitCode("bad_chunk_file")
continue
if "current_working_directory" not in intermediate_json:
logging.error("Missing 'current_working_directory' for data file: {}".format(intermediate_json))
setExitCode("missing_json_key")
continue
intermediate_json_files = processGcovs(args.cdirectory, intermediate_json["files"], intermediate_json["current_working_directory"], gcov_filter_options)
for f in intermediate_json_files:
distillSource(f, base_report["sources"], args.test_name, args.xbranchcoverage)
gcovs_total += len(intermediate_json["files"])
@@ -143,6 +340,8 @@ def gcovWorker(data_q, metrics_q, args, chunk, gcov_filter_options):
data_q.put(base_report)
metrics_q.put((gcovs_total, gcovs_skipped))
sys.exit(EXIT_CODE)
def processGcdas(args, coverage_files, gcov_filter_options):
chunk_size = max(args.minimum_chunk, int(len(coverage_files) / args.jobs) + 1)
@@ -163,6 +362,8 @@ def processGcdas(args, coverage_files, gcov_filter_options):
for p in processes:
p.join()
if p.exitcode != 0:
setExitCodeRaw(p.exitcode)
base_fastcov = fastcov_jsons.pop()
for fj in fastcov_jsons:
@@ -184,6 +385,12 @@ def shouldFilterSource(source, gcov_filter_options):
logging.debug("Filtering coverage for '%s' due to option '--exclude %s'", source, ex)
return True
# Check exclude filter
for ex_glob in gcov_filter_options["exclude_glob"]:
if fnmatch.fnmatch(source, ex_glob):
logging.debug("Filtering coverage for '%s' due to option '--exclude-glob %s'", source, ex_glob)
return True
# Check include filter
if gcov_filter_options["include"]:
included = False
@@ -205,9 +412,12 @@ def filterFastcov(fastcov_json, args):
if shouldFilterSource(source, gcov_filter_options):
del fastcov_json["sources"][source]
def processGcov(cwd, gcov, files, gcov_filter_options):
def processGcov(cwd, gcov, source_base_dir, files, gcov_filter_options):
# Uses cwd if set, else source_base_dir from gcov json. If both are empty, uses "."
base_dir = cwd if cwd else source_base_dir
base_dir = base_dir if base_dir else "."
# Add absolute path
gcov["file_abs"] = os.path.abspath(os.path.join(cwd, gcov["file"]))
gcov["file_abs"] = os.path.abspath(os.path.join(base_dir, gcov["file"]))
if shouldFilterSource(gcov["file_abs"], gcov_filter_options):
return
@@ -215,10 +425,10 @@ def processGcov(cwd, gcov, files, gcov_filter_options):
files.append(gcov)
logging.debug("Accepted coverage for '%s'", gcov["file_abs"])
def processGcovs(cwd, gcov_files, gcov_filter_options):
def processGcovs(cwd, gcov_files, source_base_dir, gcov_filter_options):
files = []
for gcov in gcov_files:
processGcov(cwd, gcov, files, gcov_filter_options)
processGcov(cwd, gcov, source_base_dir, files, gcov_filter_options)
return files
def dumpBranchCoverageToLcovInfo(f, branches):
@@ -288,15 +498,44 @@ def getSourceLines(source, fallback_encodings=[]):
with open(source, errors="ignore") as f:
return f.readlines()
def exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_branches_sw, fallback_encodings):
def containsMarker(markers, strBody):
for marker in markers:
if marker in strBody:
return True
return False
# Returns whether source coverage changed or not
def exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_branches_sw, exclude_line_marker, fallback_encodings, gcov_prefix, gcov_prefix_strip):
source_to_open = processPrefix(source, gcov_prefix, gcov_prefix_strip)
# Before doing any work, check if this file even needs to be processed
if not exclude_branches_sw and not include_branches_sw:
# Ignore unencodable characters
with open(source_to_open, errors="ignore") as f:
if not containsMarker(exclude_line_marker + ["LCOV_EXCL"], f.read()):
return False
# If we've made it this far we have to check every line
start_line = 0
end_line = 0
# Start enumeration at line 1 because the first line of the file is line 1 not 0
for i, line in enumerate(getSourceLines(source, fallback_encodings), 1):
for i, line in enumerate(getSourceLines(source_to_open, fallback_encodings), 1):
# Cycle through test names (likely only 1)
for test_name in fastcov_sources[source]:
fastcov_data = fastcov_sources[source][test_name]
# Check if branch coverage should be deleted based on CLI options
if (exclude_branches_sw or include_branches_sw) and (i in fastcov_data["branches"]):
del_exclude_br = exclude_branches_sw and any(line.lstrip().startswith(e) for e in exclude_branches_sw)
del_include_br = include_branches_sw and all(not line.lstrip().startswith(e) for e in include_branches_sw)
if del_exclude_br or del_include_br:
del fastcov_data["branches"][i]
# Skip to next line as soon as possible
if not containsMarker(exclude_line_marker + ["LCOV_EXCL"], line):
continue
# Build line to function dict so can quickly delete by line number
line_to_func = {}
for f in fastcov_data["functions"].keys():
@@ -305,16 +544,7 @@ def exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_bran
line_to_func[l] = set()
line_to_func[l].add(f)
if i in fastcov_data["branches"]:
del_exclude_br = exclude_branches_sw and any(line.lstrip().startswith(e) for e in exclude_branches_sw)
del_include_br = include_branches_sw and all(not line.lstrip().startswith(e) for e in include_branches_sw)
if del_exclude_br or del_include_br:
del fastcov_data["branches"][i]
if "LCOV_EXCL" not in line:
continue
if "LCOV_EXCL_LINE" in line:
if any(marker in line for marker in exclude_line_marker):
for key in ["lines", "branches"]:
if i in fastcov_data[key]:
del fastcov_data[key][i]
@@ -347,26 +577,56 @@ def exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_bran
if i in fastcov_data["branches"]:
del fastcov_data["branches"][i]
def exclMarkerWorker(fastcov_sources, chunk, exclude_branches_sw, include_branches_sw, fallback_encodings):
# Source coverage changed
return True
def exclMarkerWorker(data_q, fastcov_sources, chunk, exclude_branches_sw, include_branches_sw, exclude_line_marker, fallback_encodings, gcov_prefix, gcov_prefix_strip):
changed_sources = []
for source in chunk:
try:
exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_branches_sw, fallback_encodings)
if exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_branches_sw, exclude_line_marker, fallback_encodings, gcov_prefix, gcov_prefix_strip):
changed_sources.append((source, fastcov_sources[source]))
except FileNotFoundError:
logging.error("Could not find '%s' to scan for exclusion markers...", source)
setExitCode("excl_not_found") # Set exit code because of error
def scanExclusionMarkers(fastcov_json, jobs, exclude_branches_sw, include_branches_sw, min_chunk_size, fallback_encodings):
# Write out changed sources back to main fastcov file
data_q.put(changed_sources)
# Exit current process with appropriate code
sys.exit(EXIT_CODE)
def processExclusionMarkers(fastcov_json, jobs, exclude_branches_sw, include_branches_sw, exclude_line_marker, min_chunk_size, fallback_encodings, gcov_prefix, gcov_prefix_strip):
chunk_size = max(min_chunk_size, int(len(fastcov_json["sources"]) / jobs) + 1)
threads = []
processes = []
data_q = multiprocessing.Queue()
for chunk in chunks(list(fastcov_json["sources"].keys()), chunk_size):
t = threading.Thread(target=exclMarkerWorker, args=(fastcov_json["sources"], chunk, exclude_branches_sw, include_branches_sw, fallback_encodings))
threads.append(t)
t.start()
p = multiprocessing.Process(target=exclMarkerWorker, args=(data_q, fastcov_json["sources"], chunk, exclude_branches_sw, include_branches_sw, exclude_line_marker, fallback_encodings, gcov_prefix, gcov_prefix_strip))
processes.append(p)
p.start()
logging.info("Spawned {} threads each scanning at most {} source files".format(len(threads), chunk_size))
for t in threads:
t.join()
logging.info("Spawned {} exclusion marker scanning processes, each processing at most {} source files".format(len(processes), chunk_size))
changed_sources = []
for p in processes:
changed_sources += data_q.get()
for p in processes:
p.join()
if p.exitcode != 0:
setExitCodeRaw(p.exitcode)
for changed_source in changed_sources:
fastcov_json["sources"][changed_source[0]] = changed_source[1]
def validateSources(fastcov_json, gcov_prefix, gcov_prefix_strip):
logging.info("Checking if all sources exist")
for source in fastcov_json["sources"].keys():
source = processPrefix(source, gcov_prefix, gcov_prefix_strip)
if not os.path.exists(source):
logging.error("Cannot find '{}'".format(source))
def distillFunction(function_raw, functions):
function_name = function_raw["name"]
@@ -413,6 +673,13 @@ def filterExceptionalBranches(branches):
def distillLine(line_raw, lines, branches, include_exceptional_branches):
line_number = int(line_raw["line_number"])
count = int(line_raw["count"])
if count < 0:
if "function_name" in line_raw:
logging.warning("Ignoring negative count found in '%s'.", line_raw["function_name"])
else:
logging.warning("Ignoring negative count.")
count = 0
if line_number not in lines:
lines[line_number] = count
else:
@@ -458,6 +725,7 @@ def getGcovFilterOptions(args):
"sources": set([os.path.abspath(s) for s in args.sources]), #Make paths absolute, use set for fast lookups
"include": args.includepost,
"exclude": args.excludepost,
"exclude_glob":args.excludepost_glob
}
def addDicts(dict1, dict2):
@@ -529,30 +797,30 @@ def parseInfo(path):
current_test_name = line[3:].strip()
elif line.startswith("SF:"):
current_sf = line[3:].strip()
fastcov_json["sources"][current_sf] = {
fastcov_json["sources"].setdefault(current_sf, {
current_test_name: {
"functions": {},
"branches": {},
"lines": {},
}
}
})
current_data = fastcov_json["sources"][current_sf][current_test_name]
elif line.startswith("FN:"):
line_num, function_name = line[3:].strip().split(",")
current_data["functions"][function_name] = {}
current_data["functions"][function_name]["start_line"] = int(line_num)
current_data["functions"][function_name]["start_line"] = tryParseNumber(line_num)
elif line.startswith("FNDA:"):
count, function_name = line[5:].strip().split(",")
current_data["functions"][function_name]["execution_count"] = int(count)
current_data["functions"][function_name]["execution_count"] = tryParseNumber(count)
elif line.startswith("DA:"):
line_num, count = line[3:].strip().split(",")
current_data["lines"][line_num] = int(count)
current_data["lines"][line_num] = tryParseNumber(count)
elif line.startswith("BRDA:"):
branch_tokens = line[5:].strip().split(",")
line_num, count = branch_tokens[0], branch_tokens[-1]
if line_num not in current_data["branches"]:
current_data["branches"][line_num] = []
current_data["branches"][line_num].append(int(count))
current_data["branches"][line_num].append(tryParseNumber(count))
return fastcov_json
@@ -610,9 +878,9 @@ def getGcovCoverage(args):
coverage_files = getFilteredCoverageFiles(coverage_files, args.excludepre)
logging.info("Found {} coverage files after filtering".format(len(coverage_files)))
# We "zero" the "counters" by deleting all gcda files
# We "zero" the "counters" by simply deleting all gcda files
if args.zerocounters:
removeFiles(coverage_files)
removeFiles(globCoverageFiles(args.directory, GCOV_GCDA_EXT))
logging.info("Removed {} .gcda files".format(len(coverage_files)))
sys.exit()
@@ -626,6 +894,37 @@ def getGcovCoverage(args):
return fastcov_json
def formatCoveredItems(covered, total):
coverage = (covered * 100.0) / total if total > 0 else 100.0
coverage = round(coverage, 2)
return "{:.2f}%, {}/{}".format(coverage, covered, total)
def dumpStatistic(fastcov_json):
total_lines = 0
covered_lines = 0
total_functions = 0
covered_functions = 0
total_files = len(fastcov_json["sources"])
covered_files = 0
for source_name, source in fastcov_json["sources"].items():
is_file_covered = False
for test_name, test in source.items():
total_lines += len(test["lines"])
for execution_count in test["lines"].values():
covered_lines += 1 if execution_count > 0 else 0
is_file_covered = is_file_covered or execution_count > 0
total_functions += len(test["functions"])
for function in test["functions"].values():
covered_functions += 1 if function['execution_count'] > 0 else 0
is_file_covered = is_file_covered or function['execution_count'] > 0
if is_file_covered:
covered_files = covered_files + 1
logging.info("Files Coverage: {}".format(formatCoveredItems(covered_files, total_files)))
logging.info("Functions Coverage: {}".format(formatCoveredItems(covered_functions, total_functions)))
logging.info("Lines Coverage: {}".format(formatCoveredItems(covered_lines, total_lines)))
def dumpFile(fastcov_json, args):
if args.lcov:
dumpToLcovInfo(fastcov_json, args.output)
@@ -634,6 +933,9 @@ def dumpFile(fastcov_json, args):
dumpToJson(fastcov_json, args.output)
logging.info("Created fastcov json file '{}'".format(args.output))
if args.dump_statistic:
dumpStatistic(fastcov_json)
def tupleToDotted(tup):
return ".".join(map(str, tup))
@@ -655,15 +957,19 @@ def parseArgs():
# Filtering Options
parser.add_argument('-s', '--source-files', dest='sources', nargs="+", metavar='', default=[], help='Filter: Specify exactly which source files should be included in the final report. Paths must be either absolute or relative to current directory.')
parser.add_argument('-e', '--exclude', dest='excludepost', nargs="+", metavar='', default=[], help='Filter: Exclude source files from final report if they contain one of the provided substrings (i.e. /usr/include test/, etc.)')
parser.add_argument('-eg', '--exclude-glob', dest='excludepost_glob', nargs="+", metavar='', default=[], help='Filter: Exclude source files by glob pattern from final report if they contain one of the provided substrings (i.e. /usr/include test/, etc.)')
parser.add_argument('-i', '--include', dest='includepost', nargs="+", metavar='', default=[], help='Filter: Only include source files in final report that contain one of the provided substrings (i.e. src/ etc.)')
parser.add_argument('-f', '--gcda-files', dest='coverage_files', nargs="+", metavar='', default=[], help='Filter: Specify exactly which gcda or gcno files should be processed. Note that specifying gcno causes both gcno and gcda to be processed.')
parser.add_argument('-E', '--exclude-gcda', dest='excludepre', nargs="+", metavar='', default=[], help='Filter: Exclude gcda or gcno files from being processed via simple find matching (not regex)')
parser.add_argument('-u', '--diff-filter', dest='diff_file', default='', help='Unified diff file with changes which will be included into final report')
parser.add_argument('-ub', '--diff-base-dir', dest='diff_base_dir', default='', help='Base directory for sources in unified diff file, usually repository dir')
parser.add_argument('-ce', '--custom-exclusion-marker', dest='exclude_line_marker', nargs="+", metavar='', default=["LCOV_EXCL_LINE"], help='Filter: Add filter for lines that will be excluded from coverage (same behavior as "LCOV_EXCL_LINE")')
parser.add_argument('-g', '--gcov', dest='gcov', default='gcov', help='Which gcov binary to use')
parser.add_argument('-d', '--search-directory', dest='directory', default=".", help='Base directory to recursively search for gcda files (default: .)')
parser.add_argument('-c', '--compiler-directory', dest='cdirectory', default=".", help='Base directory compiler was invoked from (default: .) \
This needs to be set if invoking fastcov from somewhere other than the base compiler directory.')
parser.add_argument('-c', '--compiler-directory', dest='cdirectory', default="", help='Base directory compiler was invoked from (default: . or read from gcov) \
This needs to be set if invoking fastcov from somewhere other than the base compiler directory. No need to set it if gcc version > 9.1')
parser.add_argument('-j', '--jobs', dest='jobs', type=int, default=multiprocessing.cpu_count(), help='Number of parallel gcov to spawn (default: {}).'.format(multiprocessing.cpu_count()))
parser.add_argument('-m', '--minimum-chunk-size', dest='minimum_chunk', type=int, default=5, help='Minimum number of files a thread should process (default: 5). \
@@ -672,17 +978,23 @@ def parseArgs():
parser.add_argument('-F', '--fallback-encodings', dest='fallback_encodings', nargs="+", metavar='', default=[], help='List of encodings to try if opening a source file with the default fails (i.e. latin1, etc.). This option is not usually needed.')
parser.add_argument('-l', '--lcov', dest='lcov', action="store_true", help='Output in lcov info format instead of fastcov json')
parser.add_argument('-o', '--output', dest='output', default="coverage.json", help='Name of output file (default: coverage.json)')
parser.add_argument('-o', '--output', dest='output', default="", help='Name of output file (default: coverage.json or coverage.info, depends on --lcov option)')
parser.add_argument('-q', '--quiet', dest='quiet', action="store_true", help='Suppress output to stdout')
parser.add_argument('-t', '--test-name', dest='test_name', default="", help='Specify a test name for the coverage. Equivalent to lcov\'s `-t`.')
parser.add_argument('-C', '--add-tracefile', dest='combine', nargs="+", help='Combine multiple coverage files into one. If this flag is specified, fastcov will do a combine operation instead invoking gcov. Equivalent to lcov\'s `-a`.')
parser.add_argument('-V', '--verbose', dest="verbose", action="store_true", help="Print more detailed information about what fastcov is doing")
parser.add_argument('-w', '--validate-sources', dest="validate_sources", action="store_true", help="Check if every source file exists")
parser.add_argument('-p', '--dump-statistic', dest="dump_statistic", action="store_true", help="Dump total statistic at the end")
parser.add_argument('-v', '--version', action="version", version='%(prog)s {version}'.format(version=__version__), help="Show program's version number and exit")
args = parser.parse_args()
parser.add_argument('-gps', '--gcov_prefix_strip', dest="gcov_prefix_strip", action="store", default=0, type=int, help="The number of initial directory names to strip off the absolute paths in the object file.")
parser.add_argument('-gp', '--gcov_prefix', dest="gcov_prefix", action="store", default="", help="The prefix to add to the paths in the object file.")
args = parser.parse_args()
if not args.output:
args.output = 'coverage.info' if args.lcov else 'coverage.json'
return args
def checkPythonVersion(version):
@@ -717,6 +1029,12 @@ def main():
# Setup logging
setupLogging(args.quiet, args.verbose)
if args.gcov_prefix_strip > 0:
os.environ["GCOV_PREFIX_STRIP"] = str(args.gcov_prefix_strip)
if len(args.gcov_prefix) > 0:
os.environ["GCOV_PREFIX"] = args.gcov_prefix
# Get report from appropriate source
if args.combine:
fastcov_json = getCombineCoverage(args)
@@ -727,9 +1045,16 @@ def main():
# Scan for exclusion markers
if not skip_exclusion_markers:
scanExclusionMarkers(fastcov_json, args.jobs, args.exclude_branches_sw, args.include_branches_sw, args.minimum_chunk, args.fallback_encodings)
processExclusionMarkers(fastcov_json, args.jobs, args.exclude_branches_sw, args.include_branches_sw, args.exclude_line_marker, args.minimum_chunk, args.fallback_encodings, args.gcov_prefix, args.gcov_prefix_strip)
logging.info("Scanned {} source files for exclusion markers".format(len(fastcov_json["sources"])))
if args.diff_file:
logging.info("Filtering according to {} file".format(args.diff_file))
DiffParser().filterByDiff(args.diff_file, args.diff_base_dir, fastcov_json, args.fallback_encodings)
if args.validate_sources:
validateSources(fastcov_json, args.gcov_prefix, args.gcov_prefix_strip)
# Dump to desired file format
dumpFile(fastcov_json, args)
+120
View File
@@ -0,0 +1,120 @@
#!/usr/bin/env python3
# pylint: disable=C0114,C0116,C0209,R0912,R0915
######################################################################
import argparse
import os
import re
#from pprint import pprint,pformat
#######################################################################
def message_section(msg):
"""Return sorting-section number for given commit message"""
if re.match(r'^Support', msg):
return 10
if re.match(r'^Add', msg):
return 20
if re.match(r'^Improve', msg):
return 30
if re.match(r'^Fix', msg):
return 40
if re.match(r'^(Internals|CI|Tests)', msg):
return -1
return 0
def process():
cmd = "git log"
msgs = {}
with os.popen(cmd) as fh:
author = ""
lineno = 0
for line in fh:
lineno += 1
line = line.rstrip()
if re.match(r'^Date', line):
continue
if re.match(r'^commit', line):
continue
if re.search(r'Commentary: Changes update', line):
break
am = re.match(r'^Author: (.*) <(.*)>', line)
dm = re.match(r'^ +(.*)', line)
if am:
email = am.group(2)
author = am.group(1)
if re.search(r'antmicro', email):
author += ", Antmicro Ltd."
if re.search(r'github action', author):
author = ""
continue
elif author != "" and dm:
msg = dm.group(1)
if not re.search(r'\.$', msg):
msg += '.'
msg += ' [' + author + ']'
mid = re.search(r'\(#([0-9][0-9][0-9][0-9]+)', line)
if mid:
bug_id = mid.group(1)
else:
bug_id = " %d" % lineno
section = message_section(msg)
if section >= 0:
key = "%06s_%06s_%06d" % (section, bug_id, lineno)
msgs[key] = '* ' + msg
# print("i [%s] %s" % (key, msg))
author = ""
if not msgs:
print("No Changes need to be inserted.")
return
print()
print("Insertion-sort the following lines into 'Changes' file:")
print()
dedup = {}
for key in sorted(msgs.keys()):
if msgs[key] not in dedup:
dedup[msgs[key]] = True
print(msgs[key])
print()
print("You may now want to clean up spelling, and commit:")
print(" (cd docs ; make spelling)")
print(" git ci -am 'Commentary: Changes update'")
print()
#######################################################################
parser = argparse.ArgumentParser(
allow_abbrev=False,
prog="log_changes",
description="Create example entries for 'Changes' from parsing 'git log'",
epilog=
"""Copyright 2019-2024 by Wilson Snyder. This program is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License
Version 2.0.
SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0""")
parser.add_argument('--debug', action='store_true', help='enable debug')
Args = parser.parse_args()
process()
######################################################################
# Local Variables:
# compile-command: "cd .. ; nodist/log_changes"
# End:
+5
View File
@@ -142,6 +142,7 @@ set(HEADERS
V3Randomize.h
V3Reloop.h
V3Rtti.h
V3Sampled.h
V3Sched.h
V3Scope.h
V3Scoreboard.h
@@ -288,6 +289,7 @@ set(COMMON_SOURCES
V3ProtectLib.cpp
V3Randomize.cpp
V3Reloop.cpp
V3Sampled.cpp
V3Sched.cpp
V3SchedAcyclic.cpp
V3SchedPartition.cpp
@@ -480,6 +482,7 @@ set_target_properties(${verilator} PROPERTIES
MSVC_RUNTIME_LIBRARY MultiThreaded$<IF:$<CONFIG:Release>,,DebugDLL>
#JOB_POOL_LINK one_job # Linking takes lots of resources
INTERPROCEDURAL_OPTIMIZATION_RELEASE $<IF:MINGW,FALSE,TRUE>
INCLUDE_DIRECTORIES ${FLEX_INCLUDE_DIR}
)
add_dependencies(${verilator}
@@ -487,6 +490,8 @@ add_dependencies(${verilator}
V3PreLex_yy_cpp${CMAKE_BUILD_TYPE}
)
target_link_libraries(${verilator} PRIVATE Threads::Threads)
# verilated_cov_key.h is only regenerated in a single-configuration environment.
# This limitation can be lifted when `add_dependencies` will support generator
# expressions. See https://gitlab.kitware.com/cmake/cmake/issues/19467
+9
View File
@@ -56,6 +56,7 @@ pkgdatadir = @pkgdatadir@
# Compile options
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
CFG_WITH_SOLVER = @CFG_WITH_SOLVER@
CPPFLAGS += @CPPFLAGS@
CFLAGS += @CFLAGS@
CXXFLAGS += @CXXFLAGS@
@@ -125,6 +126,13 @@ ifeq ($(CFG_WITH_DEFENV),yes)
else
CPPFLAGS += -DDEFENV_VERILATOR_ROOT=\"$(VERILATOR_ROOT)\"
endif
ifneq ($(CFG_WITH_SOLVER),no)
CPPFLAGS += -DDEFENV_VERILATOR_SOLVER='"$(CFG_WITH_SOLVER)"'
else
ifneq ($(VERILATOR_SOLVER),)
CPPFLAGS += -DDEFENV_VERILATOR_SOLVER='"$(VERILATOR_SOLVER)"'
endif
endif
endif
HEADERS = $(wildcard V*.h v*.h)
@@ -276,6 +284,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3ProtectLib.o \
V3Randomize.o \
V3Reloop.o \
V3Sampled.o \
V3Sched.o \
V3SchedAcyclic.o \
V3SchedPartition.o \
+2 -2
View File
@@ -68,7 +68,7 @@ class ActiveTopVisitor final : public VNVisitor {
UINFO(4, " ACTIVE " << nodep << endl);
// Remove duplicate clocks and such; sensesp() may change!
V3Const::constifyExpensiveEdit(nodep);
AstSenTree* const sensesp = nodep->sensesp();
AstSenTree* sensesp = nodep->sensesp();
UASSERT_OBJ(sensesp, nodep, "nullptr");
if (sensesp->sensesp() && sensesp->sensesp()->isNever()) {
// Never executing. Kill it.
@@ -106,7 +106,7 @@ class ActiveTopVisitor final : public VNVisitor {
// There may be other references to same sense tree,
// we'll be removing all references when we get to them,
// but don't dangle our pointer yet!
pushDeletep(sensesp);
VL_DO_DANGLING(pushDeletep(sensesp), sensesp);
}
nodep->sensesp(wantp);
}
+206 -51
View File
@@ -26,13 +26,10 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Assert class functions
class AssertVisitor final : public VNVisitor {
// TYPES
enum assertType_e : uint8_t {
ASSERT_TYPE_INTRINSIC, // AstNodeAssertIntrinsinc
ASSERT_TYPE_SVA, // SVA, PSL
ASSERT_TYPE_CASE, // unique/unique0/priority case related checks
ASSERT_TYPE_IF // unique/unique0/priority if related checks
};
// CONSTANTS
static constexpr uint8_t ALL_ASSERT_TYPES
= std::numeric_limits<std::underlying_type<VAssertType::en>::type>::max();
// NODE STATE/TYPES
// Cleared on netlist
// AstNode::user() -> bool. True if processed
@@ -54,24 +51,82 @@ class AssertVisitor final : public VNVisitor {
bool m_inSampled = false; // True inside a sampled expression
// METHODS
static bool assertTypeOn(assertType_e assertType) {
if (assertType == ASSERT_TYPE_INTRINSIC) return true;
if (v3Global.opt.assertOn()) return true;
if (assertType == ASSERT_TYPE_CASE && v3Global.opt.assertCaseOn()) return true;
return false;
static AstNodeExpr* assertOnCond(FileLine* fl, VAssertType type,
VAssertDirectiveType directiveType) {
switch (directiveType) {
case VAssertDirectiveType::INTRINSIC: return new AstConst{fl, AstConst::BitTrue{}};
case VAssertDirectiveType::VIOLATION_CASE: {
if (v3Global.opt.assertCaseOn()) {
return new AstCExpr{fl, "vlSymsp->_vm_contextp__->assertOn()", 1};
}
// If assertions are off, have constant propagation rip them out later
// This allows syntax errors and such to be detected normally.
return new AstConst{fl, AstConst::BitFalse{}};
}
case VAssertDirectiveType::ASSERT:
case VAssertDirectiveType::COVER:
case VAssertDirectiveType::ASSUME: {
if (v3Global.opt.assertOn()) {
return new AstCExpr{fl,
"vlSymsp->_vm_contextp__->assertOnGet("s + std::to_string(type)
+ ", "s + std::to_string(directiveType) + ")"s,
1};
}
return new AstConst{fl, AstConst::BitFalse{}};
}
case VAssertDirectiveType::INTERNAL:
case VAssertDirectiveType::VIOLATION_IF:
case VAssertDirectiveType::RESTRICT: {
if (v3Global.opt.assertOn()) {
return new AstCExpr{fl, "vlSymsp->_vm_contextp__->assertOn()", 1};
}
return new AstConst{fl, AstConst::BitFalse{}};
}
}
VL_UNREACHABLE;
}
string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message,
VDisplayType severity) {
if (severity == VDisplayType::DT_ERROR || severity == VDisplayType::DT_FATAL) {
return (string{"[%0t] " + prefix + ": "} + nodep->fileline()->filebasename() + ":"
return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
+ cvtToStr(nodep->fileline()->lineno()) + ": Assertion failed in %m"
+ ((message != "") ? ": " : "") + message + "\n");
} else {
return (string{"[%0t] " + prefix + ": "} + nodep->fileline()->filebasename() + ":"
return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
+ cvtToStr(nodep->fileline()->lineno()) + ": %m"
+ ((message != "") ? ": " : "") + message + "\n");
}
}
static bool resolveAssertType(AstAssertCtl* nodep) {
if (!nodep->assertTypesp()) {
nodep->ctlAssertTypes(VAssertType{ALL_ASSERT_TYPES});
return true;
}
if (const AstConst* const assertTypesp = VN_CAST(nodep->assertTypesp(), Const)) {
nodep->ctlAssertTypes(VAssertType{assertTypesp->toSInt()});
return true;
}
return false;
}
static bool resolveControlType(AstAssertCtl* nodep) {
if (const AstConst* const constp = VN_CAST(nodep->controlTypep(), Const)) {
nodep->ctlType(constp->toSInt());
return true;
}
return false;
}
static bool resolveDirectiveType(AstAssertCtl* nodep) {
if (!nodep->directiveTypesp()) {
nodep->ctlDirectiveTypes(VAssertDirectiveType::ASSERT | VAssertDirectiveType::ASSUME
| VAssertDirectiveType::COVER);
return true;
}
if (const AstConst* const directiveTypesp = VN_CAST(nodep->directiveTypesp(), Const)) {
nodep->ctlDirectiveTypes(VAssertDirectiveType{directiveTypesp->toSInt()});
return true;
}
return false;
}
void replaceDisplay(AstDisplay* nodep, const string& prefix) {
nodep->fmtp()->text(
assertDisplayMessage(nodep, prefix, nodep->fmtp()->text(), nodep->displayType()));
@@ -112,43 +167,37 @@ class AssertVisitor final : public VNVisitor {
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
return varrefp;
}
AstNode* newIfAssertOn(AstNode* nodep, assertType_e assertType) {
static AstNodeStmt* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
VAssertType type = VAssertType::INTERNAL) {
// Add a internal if to check assertions are on.
// Don't make this a AND term, as it's unlikely to need to test this.
FileLine* const fl = nodep->fileline();
FileLine* const fl = bodyp->fileline();
// If assertions are off, have constant propagation rip them out later
// This allows syntax errors and such to be detected normally.
AstNodeExpr* const condp
= assertType == ASSERT_TYPE_INTRINSIC
? static_cast<AstNodeExpr*>(new AstConst{fl, AstConst::BitTrue{}})
: assertTypeOn(assertType)
? static_cast<AstNodeExpr*>(
new AstCExpr{fl, "vlSymsp->_vm_contextp__->assertOn()", 1})
: static_cast<AstNodeExpr*>(new AstConst{fl, AstConst::BitFalse{}});
AstNodeIf* const newp = new AstIf{fl, condp, nodep};
AstNodeExpr* const condp = assertOnCond(fl, type, directiveType);
AstNodeIf* const newp = new AstIf{fl, condp, bodyp};
newp->isBoundsCheck(true); // To avoid LATCH warning
newp->user1(true); // Don't assert/cover this if
return newp;
}
AstNode* newFireAssertUnchecked(AstNode* nodep, const string& message,
AstNodeExpr* exprsp = nullptr) {
AstNodeStmt* newFireAssertUnchecked(AstNodeStmt* nodep, const string& message,
AstNodeExpr* exprsp = nullptr) {
// Like newFireAssert() but omits the asserts-on check
AstDisplay* const dispp
= new AstDisplay{nodep->fileline(), VDisplayType::DT_ERROR, message, nullptr, nullptr};
dispp->fmtp()->timeunit(m_modp->timeunit());
AstNode* const bodysp = dispp;
AstNodeStmt* const bodysp = dispp;
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
if (exprsp) dispp->fmtp()->exprsp()->addNext(exprsp);
if (v3Global.opt.stopFail()) bodysp->addNext(new AstStop{nodep->fileline(), true});
return bodysp;
}
AstNode* newFireAssert(AstNode* nodep, assertType_e assertType, const string& message,
AstNodeExpr* exprsp = nullptr) {
AstNode* bodysp = newFireAssertUnchecked(nodep, message, exprsp);
bodysp = newIfAssertOn(bodysp, assertType);
AstNodeStmt* newFireAssert(AstNodeStmt* nodep, VAssertDirectiveType directiveType,
VAssertType assertType, const string& message,
AstNodeExpr* exprsp = nullptr) {
AstNodeStmt* bodysp = newFireAssertUnchecked(nodep, message, exprsp);
bodysp = newIfAssertOn(bodysp, directiveType, assertType);
return bodysp;
}
@@ -191,6 +240,7 @@ class AssertVisitor final : public VNVisitor {
}
if (bodysp && passsp) bodysp = bodysp->addNext(passsp);
if (bodysp) bodysp = newIfAssertOn(bodysp, nodep->directive(), nodep->type());
ifp = new AstIf{nodep->fileline(), propp, bodysp};
ifp->isBoundsCheck(true); // To avoid LATCH warning
bodysp = ifp;
@@ -200,17 +250,13 @@ class AssertVisitor final : public VNVisitor {
} else {
++m_statAsNotImm;
}
const assertType_e assertType
= VN_IS(nodep, AssertIntrinsic) ? ASSERT_TYPE_INTRINSIC : ASSERT_TYPE_SVA;
if (passsp) passsp = newIfAssertOn(passsp, assertType);
if (failsp) failsp = newIfAssertOn(failsp, assertType);
if (!passsp && !failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
ifp = new AstIf{nodep->fileline(), propp, passsp, failsp};
ifp->isBoundsCheck(true); // To avoid LATCH warning
// It's more LIKELY that we'll take the nullptr if clause
// than the sim-killing else clause:
ifp->branchPred(VBranchPred::BP_LIKELY);
bodysp = newIfAssertOn(ifp, assertType);
bodysp = newIfAssertOn(ifp, nodep->directive(), nodep->type());
} else {
nodep->v3fatalSrc("Unknown node type");
}
@@ -281,13 +327,17 @@ class AssertVisitor final : public VNVisitor {
= ((allow_none || hasDefaultElse)
? static_cast<AstNodeExpr*>(new AstOneHot0{nodep->fileline(), propp})
: static_cast<AstNodeExpr*>(new AstOneHot{nodep->fileline(), propp}));
AstIf* const checkifp = new AstIf{
nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
newFireAssert(nodep, ASSERT_TYPE_IF, "'unique if' statement violated"), newifp};
const VAssertType assertType
= nodep->uniquePragma() ? VAssertType::UNIQUE : VAssertType::UNIQUE0;
AstIf* const checkifp
= new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
newFireAssert(nodep, VAssertDirectiveType::VIOLATION_IF, assertType,
"'unique if' statement violated"),
newifp};
checkifp->isBoundsCheck(true); // To avoid LATCH warning
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
nodep->replaceWith(checkifp);
pushDeletep(nodep);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else {
iterateChildren(nodep);
}
@@ -303,6 +353,16 @@ class AssertVisitor final : public VNVisitor {
if (itemp->isDefault()) has_default = true;
}
const AstNodeDType* exprDtypep = nodep->exprp()->dtypep()->skipRefp();
VAssertType assertType = VAssertType::INTERNAL;
if (nodep->priorityPragma()) {
assertType = VAssertType::PRIORITY;
} else if (nodep->uniquePragma()) {
assertType = VAssertType::UNIQUE;
} else if (nodep->unique0Pragma()) {
assertType = VAssertType::UNIQUE0;
}
string valFmt;
if (exprDtypep->isIntegralOrPacked())
valFmt = " for '" + cvtToStr(exprDtypep->widthMin()) + "'h%X'";
@@ -312,7 +372,7 @@ class AssertVisitor final : public VNVisitor {
if (!has_default) {
nodep->addItemsp(new AstCaseItem{
nodep->fileline(), nullptr /*DEFAULT*/,
newFireAssert(nodep, ASSERT_TYPE_CASE,
newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
nodep->pragmaString() + ", but non-match found" + valFmt,
valFmt.empty() ? nullptr
: nodep->exprp()->cloneTreePure(false))});
@@ -360,19 +420,22 @@ class AssertVisitor final : public VNVisitor {
// else $error("multiple match");
// end
AstNodeExpr* const ohot = new AstOneHot{nodep->fileline(), propp};
AstConst* const zero = new AstConst{
nodep->fileline(), AstConst::WidthedValue{}, propp->width(), 0};
AstIf* const ohotIfp
= new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot}};
AstIf* const zeroIfp
= new AstIf{nodep->fileline(),
new AstLogNot{nodep->fileline(), propp->cloneTreePure(false)}};
AstIf* const zeroIfp = new AstIf{
nodep->fileline(),
new AstEq{nodep->fileline(), propp->cloneTreePure(false), zero}};
AstNodeExpr* const exprp = nodep->exprp();
const string pragmaStr = nodep->pragmaString();
if (!allow_none)
zeroIfp->addThensp(newFireAssert(
nodep, ASSERT_TYPE_CASE, pragmaStr + ", but none matched" + valFmt,
valFmt.empty() ? nullptr : exprp->cloneTreePure(false)));
zeroIfp->addThensp(
newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
pragmaStr + ", but none matched" + valFmt,
valFmt.empty() ? nullptr : exprp->cloneTreePure(false)));
zeroIfp->addElsesp(
newFireAssert(nodep, ASSERT_TYPE_CASE,
newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
pragmaStr + ", but multiple matches found" + valFmt,
valFmt.empty() ? nullptr : exprp->cloneTreePure(false)));
ohotIfp->addThensp(zeroIfp);
@@ -440,6 +503,7 @@ class AssertVisitor final : public VNVisitor {
AstSampled* const newp = newSampledExpr(nodep);
relinkHandle.relink(newp);
newp->user1(1);
v3Global.setHasSampled();
}
}
}
@@ -467,6 +531,22 @@ class AssertVisitor final : public VNVisitor {
} else if (nodep->displayType() == VDisplayType::DT_MONITOR) {
nodep->displayType(VDisplayType::DT_DISPLAY);
const auto fl = nodep->fileline();
AstNode* monExprsp = nodep->fmtp()->exprsp();
AstSenItem* monSenItemsp = nullptr;
while (monExprsp) {
if (AstNodeVarRef* varrefp = VN_CAST(monExprsp, NodeVarRef)) {
AstSenItem* const senItemp
= new AstSenItem(fl, VEdgeType::ET_CHANGED,
new AstVarRef{fl, varrefp->varp(), VAccess::READ});
if (!monSenItemsp) {
monSenItemsp = senItemp;
} else {
monSenItemsp->addNext(senItemp);
}
}
monExprsp = monExprsp->nextp();
}
AstSenTree* const monSenTree = new AstSenTree{fl, monSenItemsp};
const auto monNum = ++m_monitorNum;
// Where $monitor was we do "__VmonitorNum = N;"
const auto newsetp = new AstAssign{fl, newMonitorNumVarRefp(nodep, VAccess::WRITE),
@@ -482,7 +562,7 @@ class AssertVisitor final : public VNVisitor {
stmtsp};
ifp->isBoundsCheck(true); // To avoid LATCH warning
ifp->branchPred(VBranchPred::BP_UNLIKELY);
AstNode* const newp = new AstAlways{fl, VAlwaysKwd::ALWAYS, nullptr, ifp};
AstNode* const newp = new AstAlways{fl, VAlwaysKwd::ALWAYS, monSenTree, ifp};
m_modp->addStmtsp(newp);
} else if (nodep->displayType() == VDisplayType::DT_STROBE) {
nodep->displayType(VDisplayType::DT_DISPLAY);
@@ -518,6 +598,81 @@ class AssertVisitor final : public VNVisitor {
iterateChildren(nodep);
newPslAssertion(nodep, nodep->failsp());
}
void visit(AstAssertCtl* nodep) override {
if (VN_IS(m_modp, Class) || VN_IS(m_modp, Iface)) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assertcontrols in classes or interfaces");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
iterateChildren(nodep);
if (!resolveAssertType(nodep)) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: non-constant assert assertion-type expression");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
if (nodep->ctlAssertTypes() != ALL_ASSERT_TYPES
&& nodep->ctlAssertTypes().containsAny(VAssertType::EXPECT | VAssertType::UNIQUE
| VAssertType::UNIQUE0
| VAssertType::PRIORITY)) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assert control assertion_type");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
if (!resolveControlType(nodep)) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: non-const assert control type expression");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
if (!resolveDirectiveType(nodep)) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: non-const assert directive type expression");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
FileLine* const fl = nodep->fileline();
switch (nodep->ctlType()) {
case VAssertCtlType::ON:
UINFO(9, "Generating assertctl for a module: " << m_modp << endl);
nodep->replaceWith(new AstCExpr{
fl,
"vlSymsp->_vm_contextp__->assertOnSet("s + std::to_string(nodep->ctlAssertTypes())
+ ", "s + std::to_string(nodep->ctlDirectiveTypes()) + ");\n"s,
1});
break;
case VAssertCtlType::OFF:
case VAssertCtlType::KILL: {
UINFO(9, "Generating assertctl for a module: " << m_modp << endl);
nodep->replaceWith(new AstCExpr{fl,
"vlSymsp->_vm_contextp__->assertOnClear("s
+ std::to_string(nodep->ctlAssertTypes()) + " ,"s
+ std::to_string(nodep->ctlDirectiveTypes())
+ ");\n"s,
1});
break;
}
case VAssertCtlType::LOCK:
case VAssertCtlType::UNLOCK:
case VAssertCtlType::PASS_ON:
case VAssertCtlType::PASS_OFF:
case VAssertCtlType::FAIL_ON:
case VAssertCtlType::FAIL_OFF:
case VAssertCtlType::NONVACUOUS_ON:
case VAssertCtlType::VACUOUS_OFF: {
nodep->unlinkFrBack();
nodep->v3warn(E_UNSUPPORTED, "Unsupported assertcontrol control_type");
break;
}
default: {
nodep->unlinkFrBack();
nodep->v3warn(EC_ERROR, "Bad assertcontrol control_type (IEEE 1800-2023 Table 20-5)");
}
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstAssertIntrinsic* nodep) override {
iterateChildren(nodep);
newPslAssertion(nodep, nodep->failsp());
+98 -17
View File
@@ -57,6 +57,7 @@ private:
bool m_inAssign = false; // True if in an AssignNode
bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS
bool m_inSynchDrive = false; // True if in synchronous drive
std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup
// METHODS
@@ -156,8 +157,17 @@ private:
m_clockingp = nodep;
UINFO(8, " CLOCKING" << nodep << endl);
iterateChildren(nodep);
if (nodep->eventp()) nodep->addNextHere(nodep->eventp()->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstClockingItem* const nodep) override {
// Get a ref to the sampled/driven variable
AstVar* const varp = nodep->varp();
if (!varp) {
// Unused item
pushDeletep(nodep->unlinkFrBack());
return;
}
FileLine* const flp = nodep->fileline();
V3Const::constifyEdit(nodep->skewp());
if (!VN_IS(nodep->skewp(), Const)) {
@@ -168,23 +178,51 @@ private:
AstConst* const skewp = VN_AS(nodep->skewp(), Const);
if (skewp->num().isNegative()) skewp->v3error("Skew cannot be negative");
AstNodeExpr* const exprp = nodep->exprp();
// Get a ref to the sampled/driven variable
AstVar* const varp = nodep->varp()->unlinkFrBack();
m_clockingp->addVarsp(varp);
varp->name(m_clockingp->name() + "__DOT__" + varp->name());
m_clockingp->addNextHere(varp->unlinkFrBack());
varp->user1p(nodep);
if (nodep->direction() == VDirection::OUTPUT) {
AstVarRef* const skewedRefp = new AstVarRef{flp, varp, VAccess::READ};
skewedRefp->user1(true);
AstAssign* const assignp = new AstAssign{flp, exprp->cloneTreePure(false), skewedRefp};
exprp->foreach([](AstNodeVarRef* varrefp) {
// Prevent confusing BLKANDNBLK warnings on clockvars due to generated assignments
varrefp->fileline()->warnOff(V3ErrorCode::BLKANDNBLK, true);
});
AstVarRef* const skewedReadRefp = new AstVarRef{flp, varp, VAccess::READ};
skewedReadRefp->user1(true);
// Initialize the clockvar
AstVarRef* const skewedWriteRefp = new AstVarRef{flp, varp, VAccess::WRITE};
skewedWriteRefp->user1(true);
AstInitialStatic* const initClockvarp = new AstInitialStatic{
flp, new AstAssign{flp, skewedWriteRefp, exprp->cloneTreePure(false)}};
m_modp->addStmtsp(initClockvarp);
// A var to keep the previous value of the clockvar
AstVar* const prevVarp = new AstVar{
flp, VVarType::MODULETEMP, "__Vclocking_prev__" + varp->name(), exprp->dtypep()};
prevVarp->lifetime(VLifetime::STATIC);
AstInitialStatic* const initPrevClockvarp = new AstInitialStatic{
flp, new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
skewedReadRefp->cloneTreePure(false)}};
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
= new AstAssign{flp, exprp->cloneTreePure(false), skewedReadRefp};
AstIf* const ifp
= new AstIf{flp,
new AstNeq{flp, new AstVarRef{flp, prevVarp, VAccess::READ},
skewedReadRefp->cloneTreePure(false)},
assignp};
ifp->addThensp(new AstAssign{flp, new AstVarRef{flp, prevVarp, VAccess::WRITE},
skewedReadRefp->cloneTree(false)});
if (skewp->isZero()) {
// Drive the var in Re-NBA (IEEE 1800-2023 14.16)
m_clockingp->addNextHere(new AstAlwaysReactive{
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, assignp});
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, ifp});
} else if (skewp->fileline()->timingOn()) {
// Create a fork so that this AlwaysObserved can be retriggered before the
// assignment happens. Also then it can be combo, avoiding the need for creating
// new triggers.
AstFork* const forkp = new AstFork{flp, "", assignp};
AstFork* const forkp = new AstFork{flp, "", ifp};
forkp->joinType(VJoinType::JOIN_NONE);
// Use Observed for this to make sure we do not miss the event
m_clockingp->addNextHere(new AstAlwaysObserved{
@@ -221,9 +259,9 @@ private:
AstSampleQueueDType* const queueDtp
= new AstSampleQueueDType{flp, exprp->dtypep()};
m_netlistp->typeTablep()->addTypesp(queueDtp);
AstVar* const queueVarp = new AstVar{
flp, VVarType::MODULETEMP,
"__Vqueue__" + m_clockingp->name() + "__DOT__" + varp->name(), queueDtp};
AstVar* const queueVarp
= new AstVar{flp, VVarType::MODULETEMP, "__Vqueue__" + varp->name(), queueDtp};
queueVarp->lifetime(VLifetime::STATIC);
m_clockingp->addNextHere(queueVarp);
// Create a process like this:
// always queue.push(<sampled var>);
@@ -235,7 +273,7 @@ private:
m_clockingp->addNextHere(
new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr, pushp->makeStmt()});
// Create a process like this:
// always @<clocking event> queue.pop(<skew>, /*out*/<skewed var>});
// always @<clocking event> queue.pop(<skew>, /*out*/<skewed var>);
AstCMethodHard* const popp = new AstCMethodHard{
flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, "pop",
new AstTime{nodep->fileline(), m_modp->timeunit()}};
@@ -250,7 +288,7 @@ private:
} else {
nodep->v3fatal("Invalid direction");
}
pushDeletep(nodep->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstDelay* nodep) override {
// Only cycle delays are relevant in this stage; also only process once
@@ -259,6 +297,7 @@ private:
nodep->v3error("Only cycle delays can be used in synchronous drives"
" (IEEE 1800-2023 14.16)");
}
iterateChildren(nodep);
return;
}
if (m_inAssign && !m_inSynchDrive) {
@@ -290,6 +329,8 @@ private:
const std::string delayName = m_cycleDlyNames.get(nodep);
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::AUTOMATIC);
cntVarp->funcLocal(true);
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, false, true};
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
beginp->addStmtsp(new AstWhile{
@@ -309,9 +350,26 @@ private:
}
}
void visit(AstNodeVarRef* nodep) override {
if (AstClockingItem* const itemp = VN_CAST(nodep->varp()->user1p(), ClockingItem)) {
if (nodep->access().isReadOrRW() && !nodep->user1()
&& itemp->direction() == VDirection::OUTPUT) {
UINFO(8, " -varref: " << nodep << endl);
UINFO(8, " -varref-var-back: " << nodep->varp()->backp() << endl);
UINFO(8, " -varref-var-user1: " << nodep->varp()->user1p() << endl);
if (AstClockingItem* const itemp = VN_CAST(
nodep->varp()->user1p() ? nodep->varp()->user1p() : nodep->varp()->firstAbovep(),
ClockingItem)) {
if (nodep->user1()) return;
// ensure linking still works, this has to be done only once
if (AstVarXRef* xrefp = VN_CAST(nodep, VarXRef)) {
UINFO(8, " -clockvarxref-in: " << xrefp << endl);
string dotted = xrefp->dotted();
const size_t dotPos = dotted.rfind('.');
dotted.erase(dotPos, string::npos);
xrefp->dotted(dotted);
UINFO(8, " -clockvarxref-out: " << xrefp << endl);
m_xrefsp.emplace_back(xrefp);
}
if (nodep->access().isReadOrRW() && itemp->direction() == VDirection::OUTPUT) {
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2023 14.3)");
}
if (nodep->access().isWriteOrRW()) {
@@ -321,7 +379,28 @@ private:
"to clockvars (IEEE 1800-2023 14.16)");
}
if (m_inAssign) m_inSynchDrive = true;
} else if (!nodep->user1() && itemp->direction() == VDirection::INPUT) {
} else if (itemp->direction() == VDirection::INPUT) {
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
}
}
nodep->user1(true);
}
}
void visit(AstMemberSel* nodep) override {
if (AstClockingItem* const itemp = VN_CAST(
nodep->varp()->user1p() ? nodep->varp()->user1p() : nodep->varp()->firstAbovep(),
ClockingItem)) {
if (nodep->access().isReadOrRW() && itemp->direction() == VDirection::OUTPUT) {
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2023 14.3)");
}
if (nodep->access().isWriteOrRW()) {
if (itemp->direction() == VDirection::OUTPUT) {
if (!m_inAssignDlyLhs) {
nodep->v3error("Only non-blocking assignments can write "
"to clockvars (IEEE 1800-2023 14.16)");
}
if (m_inAssign) m_inSynchDrive = true;
} else if (itemp->direction() == VDirection::INPUT) {
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
}
}
@@ -488,6 +567,8 @@ public:
clearAssertInfo();
// Process
iterate(nodep);
// Fix up varref names
for (AstVarXRef* xrefp : m_xrefsp) xrefp->name(xrefp->varp()->name());
}
~AssertPreVisitor() override = default;
};
+1 -2
View File
@@ -801,7 +801,6 @@ AstNode* AstNode::cloneTreeIter(bool needPure) {
<< this->warnMore()
<< "... Suggest use a temporary variable in place of this expression");
// this->v3fatalSrc("cloneTreePure debug backtrace"); // Comment in to debug where caused
// it
}
AstNode* const newp = this->clone();
if (this->m_op1p) newp->op1p(this->m_op1p->cloneTreeIterList(needPure));
@@ -1586,7 +1585,7 @@ VCastable AstNode::computeCastable(const AstNodeDType* toDtp, const AstNodeDType
const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp);
UINFO(9, " castable=" << castable << " for " << toDtp << endl);
UINFO(9, " =?= " << fromDtp << endl);
UINFO(9, " const= " << fromConstp << endl);
if (fromConstp) UINFO(9, " const= " << fromConstp << endl);
return castable;
}
+209 -37
View File
@@ -253,17 +253,17 @@ public:
, m_final{false}
, m_initial{false} {}
class Extends {};
VBaseOverride(Extends)
explicit VBaseOverride(Extends)
: m_extends{true}
, m_final{false}
, m_initial{false} {}
class Final {};
VBaseOverride(Final)
explicit VBaseOverride(Final)
: m_extends{false}
, m_final{true}
, m_initial{false} {}
class Initial {};
VBaseOverride(Initial)
explicit VBaseOverride(Initial)
: m_extends{false}
, m_final{false}
, m_initial{true} {}
@@ -330,6 +330,7 @@ public:
ILLEGAL,
COVERAGE_BLOCK_OFF,
HIER_BLOCK,
HIER_PARAMS,
INLINE_MODULE,
NO_INLINE_MODULE,
NO_INLINE_TASK,
@@ -559,6 +560,7 @@ public:
DYNAMIC_TRIGGER_SCHEDULER,
FORK_SYNC,
PROCESS_REFERENCE,
RANDOM_GENERATOR,
// Unsigned and two state; fundamental types
UINT32,
UINT64,
@@ -592,6 +594,7 @@ public:
"VlDynamicTriggerScheduler",
"VlFork",
"VlProcessRef",
"VlRandomizer",
"IData",
"QData",
"LOGIC_IMPLICIT",
@@ -599,20 +602,13 @@ public:
return names[m_e];
}
const char* dpiType() const {
static const char* const names[] = {"%E-unk", "svBit",
"char", "void*",
"char", "int",
"%E-integer", "svLogic",
"long long", "double",
"short", "%E-time",
"const char*", "%E-untyped",
"dpiScope", "const char*",
"%E-mtaskstate", "%E-triggervec",
"%E-dly-sched", "%E-trig-sched",
"%E-dyn-sched", "%E-fork",
"%E-proc-ref", "IData",
"QData", "%E-logic-implct",
" MAX"};
static const char* const names[]
= {"%E-unk", "svBit", "char", "void*", "char",
"int", "%E-integer", "svLogic", "long long", "double",
"short", "%E-time", "const char*", "%E-untyped", "dpiScope",
"const char*", "%E-mtaskstate", "%E-triggervec", "%E-dly-sched", "%E-trig-sched",
"%E-dyn-sched", "%E-fork", "%E-proc-ref", "%E-rand-gen", "IData",
"QData", "%E-logic-implct", " MAX"};
return names[m_e];
}
static void selfTest() {
@@ -652,6 +648,7 @@ public:
case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque
case FORK_SYNC: return 0; // opaque
case PROCESS_REFERENCE: return 0; // opaque
case RANDOM_GENERATOR: return 0; // opaque
case UINT32: return 32;
case UINT64: return 64;
default: return 0;
@@ -691,7 +688,8 @@ public:
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|| m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER
|| m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC
|| m_e == PROCESS_REFERENCE || m_e == DOUBLE || m_e == UNTYPED);
|| m_e == PROCESS_REFERENCE || m_e == RANDOM_GENERATOR || m_e == DOUBLE
|| m_e == UNTYPED);
}
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
bool isEvent() const { return m_e == EVENT; }
@@ -790,6 +788,7 @@ public:
bool isAny() const { return m_e != NONE; }
// Looks like inout - "ish" because not identical to being an INOUT
bool isInoutish() const { return m_e == INOUT; }
bool isInput() const { return m_e == INPUT; }
bool isNonOutput() const {
return m_e == INPUT || m_e == INOUT || m_e == REF || m_e == CONSTREF;
}
@@ -903,7 +902,7 @@ public:
TRIWIRE,
TRI0,
TRI1,
PORT, // Temp type used in parser only
PORT, // Used in parser and V3Fork to recognize ports
BLOCKTEMP,
MODULETEMP,
STMTTEMP,
@@ -1102,6 +1101,156 @@ constexpr bool operator==(VAlwaysKwd::en lhs, const VAlwaysKwd& rhs) { return lh
// ######################################################################
class VAssertCtlType final {
public:
// IEEE 1800-2023 Table 20-5
enum en : uint8_t {
_TO_BE_EVALUATED = 0,
LOCK = 1,
UNLOCK = 2,
ON = 3,
OFF = 4,
KILL = 5,
PASS_ON = 6,
PASS_OFF = 7,
FAIL_ON = 8,
FAIL_OFF = 9,
NONVACUOUS_ON = 10,
VACUOUS_OFF = 11
};
enum en m_e;
VAssertCtlType()
: m_e{_TO_BE_EVALUATED} {}
// cppcheck-suppress noExplicitConstructor
constexpr VAssertCtlType(en _e)
: m_e{_e} {}
explicit VAssertCtlType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
// IEEE 1800-2023 20.11
static const char* const names[] = {"",
"",
"",
"$asserton",
"$assertoff",
"$assertkill",
"$assertpasson",
"$assertpassoff",
"$assertfailon",
"$assertfailoff",
"$assertnonvacuouson",
"$assertvacuousoff"};
return names[m_e];
}
};
constexpr bool operator==(const VAssertCtlType& lhs, const VAssertCtlType& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VAssertCtlType& lhs, VAssertCtlType::en rhs) {
return lhs.m_e == rhs;
}
constexpr bool operator==(VAssertCtlType::en lhs, const VAssertCtlType& rhs) {
return lhs == rhs.m_e;
}
// ######################################################################
class VAssertDirectiveType final {
public:
// IEEE 1800-2023 Table 20-7
enum en : uint8_t {
INTERNAL = 0, // Non IEEE type, for directives to be evaluated from expression.
ASSERT = (1 << 0),
COVER = (1 << 1),
ASSUME = (1 << 2),
VIOLATION_CASE = (1 << 3), // Non IEEE type, for case constructs
// with unique, unique0 or priority pragmas.
VIOLATION_IF = (1 << 4), // Non IEEE type, for if constructs
// with unique, unique0 or priority pragmas.
INTRINSIC = (1 << 5), // Non IEEE type, for intrinsic assertions.
RESTRICT = (1 << 6), // Non IEEE type, for ignored restrict assertions.
};
enum en m_e;
VAssertDirectiveType()
: m_e{ASSERT} {}
// cppcheck-suppress noExplicitConstructor
constexpr VAssertDirectiveType(en _e)
: m_e{_e} {}
explicit VAssertDirectiveType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
const char* ascii() const {
static const char* const names[]
= {"INTERNAL", "ASSERT", "COVER", "ASSUME",
"VIOLATION_CASE", "VIOLATION_IF", "INTRINSIC", "RESTRICT"};
return names[m_e];
}
constexpr operator en() const { return m_e; }
};
constexpr bool operator==(const VAssertDirectiveType& lhs, const VAssertDirectiveType& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VAssertDirectiveType& lhs, VAssertDirectiveType::en rhs) {
return lhs.m_e == rhs;
}
constexpr bool operator==(VAssertDirectiveType::en lhs, const VAssertDirectiveType& rhs) {
return lhs == rhs.m_e;
}
constexpr VAssertDirectiveType::en operator|(VAssertDirectiveType::en lhs,
VAssertDirectiveType::en rhs) {
return VAssertDirectiveType::en(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}
// ######################################################################
class VAssertType final {
public:
// IEEE 1800-2023 Table 20-6
enum en : uint8_t {
INTERNAL = 0, // Non IEEE type, for assertions that should not be controlled.
CONCURRENT = (1 << 0),
SIMPLE_IMMEDIATE = (1 << 1),
OBSERVED_DEFERRED_IMMEDIATE = (1 << 2),
FINAL_DEFERRED_IMMEDIATE = (1 << 3),
EXPECT = (1 << 4),
UNIQUE = (1 << 5),
UNIQUE0 = (1 << 6),
PRIORITY = (1 << 7),
};
enum en m_e;
VAssertType()
: m_e{INTERNAL} {}
// cppcheck-suppress noExplicitConstructor
constexpr VAssertType(en _e)
: m_e{_e} {}
explicit VAssertType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
bool containsAny(VAssertType other) const { return m_e & other.m_e; }
const char* ascii() const {
static const char* const names[] = {"INTERNAL",
"CONCURRENT",
"SIMPLE_IMMEDIATE",
"OBSERVED_DEFERRED_IMMEDIATE",
"FINAL_DEFERRED_IMMEDIATE",
"EXPECT",
"UNIQUE",
"UNIQUE0",
"PRIORITY"};
return names[m_e];
}
constexpr operator en() const { return m_e; }
};
constexpr bool operator==(const VAssertType& lhs, const VAssertType& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VAssertType& lhs, VAssertType::en rhs) { return lhs.m_e == rhs; }
constexpr bool operator==(VAssertType::en lhs, const VAssertType& rhs) { return lhs == rhs.m_e; }
constexpr VAssertType::en operator|(VAssertType::en lhs, VAssertType::en rhs) {
return VAssertType::en(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
}
// ######################################################################
class VCaseType final {
public:
enum en : uint8_t { CT_CASE, CT_CASEX, CT_CASEZ, CT_CASEINSIDE };
@@ -1441,6 +1590,7 @@ public:
"ENUM_IMPLICIT", "DYNAMIC_CLASS", "INCOMPATIBLE"};
return names[m_e];
}
bool isAssignable() const { return m_e != UNSUPPORTED && m_e != INCOMPATIBLE; }
VCastable()
: m_e{UNSUPPORTED} {}
// cppcheck-suppress noExplicitConstructor
@@ -1532,6 +1682,36 @@ public:
bool operator==(const VSelfPointerText& other) const { return *m_strp == *other.m_strp; }
};
// ######################################################################
// Defines what kind of randomization is done on a variable
class VRandAttr final {
public:
enum en : uint8_t {
NONE, // Not randomizable
RAND, // Has a rand modifier
RAND_CYCLIC, // Has a randc modifier
RAND_INLINE // Not rand/randc, but used in inline random variable control
};
enum en m_e;
const char* ascii() const {
static const char* const names[] = {"NONE", "RAND", "RANDC", "RAND-INLINE"};
return names[m_e];
}
VRandAttr()
: m_e{NONE} {}
// cppcheck-suppress noExplicitConstructor
constexpr VRandAttr(en _e)
: m_e{_e} {}
constexpr operator en() const { return m_e; }
bool isRandomizable() const { return m_e != NONE; }
bool isRand() const { return m_e == RAND || m_e == RAND_CYCLIC; }
bool isRandC() const { return m_e == RAND_CYCLIC; }
};
inline std::ostream& operator<<(std::ostream& os, const VRandAttr& rhs) {
return os << rhs.ascii();
}
//######################################################################
// AstNUser - Generic base class for AST User nodes.
// - Also used to allow parameter passing up/down iterate calls
@@ -2024,8 +2204,8 @@ public:
void fileline(FileLine* fl) { m_fileline = fl; }
inline bool width1() const;
inline int widthInstrs() const;
void didWidth(bool flag) { m_flags.didWidth = flag; }
bool didWidth() const { return m_flags.didWidth; }
void didWidth(bool flag) { m_flags.didWidth = flag; }
bool didWidthAndSet() {
if (didWidth()) return true;
didWidth(true);
@@ -2355,7 +2535,7 @@ protected:
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);
private:
protected:
void iterateListBackwardsConst(VNVisitorConst& v);
// For internal use only.
@@ -2473,14 +2653,6 @@ private:
template <typename T_Arg, bool Default, typename Callable>
inline static bool predicateImpl(ConstCorrectAstNode<T_Arg>* nodep, const Callable& p);
template <typename T_Callable>
struct Arg0NoPointerNoCV final {
using Traits = FunctionTraits<T_Callable>;
using T_Arg0 = typename Traits::template arg<0>::type;
using T_Arg0NoPtr = typename std::remove_pointer<T_Arg0>::type;
using type = typename std::remove_cv<T_Arg0NoPtr>::type;
};
public:
// Given a callable 'f' that takes a single argument of some AstNode subtype 'T_Node', traverse
// the tree rooted at this node, and call 'f' in pre-order on each node that is of type
@@ -2491,7 +2663,7 @@ public:
// caches in modern CPUs, while it is basically unpredictable for VNVisitor.
template <typename Callable>
void foreach(Callable&& f) {
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
static_assert(vlstd::is_invocable<Callable, T_Node*>::value
&& std::is_base_of<AstNode, T_Node>::value,
"Callable 'f' must have a signature compatible with 'void(T_Node*)', "
@@ -2502,7 +2674,7 @@ public:
// Same as above, but for 'const' nodes
template <typename Callable>
void foreach(Callable&& f) const {
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
static_assert(vlstd::is_invocable<Callable, const T_Node*>::value
&& std::is_base_of<AstNode, T_Node>::value,
"Callable 'f' must have a signature compatible with 'void(const T_Node*)', "
@@ -2513,7 +2685,7 @@ public:
// Same as 'foreach' but also traverses 'this->nextp()' transitively
template <typename Callable>
void foreachAndNext(Callable&& f) {
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
static_assert(vlstd::is_invocable<Callable, T_Node*>::value
&& std::is_base_of<AstNode, T_Node>::value,
"Callable 'f' must have a signature compatible with 'void(T_Node*)', "
@@ -2524,7 +2696,7 @@ public:
// Same as above, but for 'const' nodes
template <typename Callable>
void foreachAndNext(Callable&& f) const {
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
static_assert(vlstd::is_invocable<Callable, const T_Node*>::value
&& std::is_base_of<AstNode, T_Node>::value,
"Callable 'f' must have a signature compatible with 'void(const T_Node*)', "
@@ -2539,7 +2711,7 @@ public:
// be determined.
template <typename Callable>
bool exists(Callable&& p) {
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
static_assert(vlstd::is_invocable_r<bool, Callable, T_Node*>::value
&& std::is_base_of<AstNode, T_Node>::value,
"Predicate 'p' must have a signature compatible with 'bool(T_Node*)', "
@@ -2550,7 +2722,7 @@ public:
// Same as above, but for 'const' nodes
template <typename Callable>
bool exists(Callable&& p) const {
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
static_assert(vlstd::is_invocable_r<bool, Callable, const T_Node*>::value
&& std::is_base_of<AstNode, T_Node>::value,
"Predicate 'p' must have a signature compatible with 'bool(const T_Node*)', "
@@ -2564,7 +2736,7 @@ public:
// in some arbitrary order and is terminated as soon as the result can be determined.
template <typename Callable>
bool forall(Callable&& p) {
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
static_assert(vlstd::is_invocable_r<bool, Callable, T_Node*>::value
&& std::is_base_of<AstNode, T_Node>::value,
"Predicate 'p' must have a signature compatible with 'bool(T_Node*)', "
@@ -2575,7 +2747,7 @@ public:
// Same as above, but for 'const' nodes
template <typename Callable>
bool forall(Callable&& p) const {
using T_Node = typename Arg0NoPointerNoCV<Callable>::type;
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 0>::type;
static_assert(vlstd::is_invocable_r<bool, Callable, const T_Node*>::value
&& std::is_base_of<AstNode, T_Node>::value,
"Predicate 'p' must have a signature compatible with 'bool(const T_Node*)', "
+4
View File
@@ -160,6 +160,10 @@ AstCExpr::AstCExpr(FileLine* fl, const string& textStmt, int setwidth, bool clea
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
: ASTGEN_SUPER_VarRef(fl, varp, access) {}
AstVarRef::AstVarRef(FileLine* fl, AstNodeModule* pkgp, AstVar* varp, const VAccess& access)
: AstVarRef{fl, varp, access} {
classOrPackagep(pkgp);
}
// This form only allowed post-link (see above)
AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access)
: ASTGEN_SUPER_VarRef(fl, varscp->varp(), access) {
+53 -17
View File
@@ -89,8 +89,8 @@ public:
virtual AstNodeDType* subDTypep() const VL_MT_SAFE { return nullptr; }
virtual bool isFourstate() const;
// Ideally an IEEE $typename
virtual string prettyDTypeName() const { return prettyTypeName(); }
string prettyDTypeNameQ() const { return "'" + prettyDTypeName() + "'"; }
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
string prettyDTypeNameQ() const { return "'" + prettyDTypeName(false) + "'"; }
//
// Changing the width may confuse the data type resolution, so must clear
// TypeTable cache after use.
@@ -215,9 +215,11 @@ public:
int uniqueNum() const { return m_uniqueNum; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string prettyDTypeName(bool) const override;
bool isCompound() const override { return !packed(); }
// For basicp() we reuse the size to indicate a "fake" basic type of same size
AstBasicDType* basicp() const override {
if (!m_packed) return nullptr;
return (isFourstate()
? VN_AS(findLogicRangeDType(VNumRange{width() - 1, 0}, width(), numeric()),
BasicDType)
@@ -313,7 +315,7 @@ public:
return asamep->subDTypep()
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
}
string prettyDTypeName() const override;
string prettyDTypeName(bool full) const override;
void dumpSmall(std::ostream& str) const override;
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* getChild2DTypep() const override { return keyChildDTypep(); }
@@ -387,7 +389,7 @@ public:
return type() == samep->type() && same(samep);
}
string name() const override VL_MT_STABLE { return m.m_keyword.ascii(); }
string prettyDTypeName() const override;
string prettyDTypeName(bool full) const override;
const char* broken() const override {
BROKEN_RTN(dtypep() != this);
return nullptr;
@@ -428,6 +430,9 @@ public:
bool isDynamicTriggerScheduler() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::DYNAMIC_TRIGGER_SCHEDULER;
}
bool isRandomGenerator() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::RANDOM_GENERATOR;
}
bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); }
bool isString() const VL_MT_SAFE { return keyword().isString(); }
bool isZeroInit() const { return keyword().isZeroInit(); }
@@ -501,7 +506,7 @@ public:
}
bool similarDType(const AstNodeDType* samep) const override { return same(samep); }
string name() const override VL_MT_STABLE { return m_name; }
string prettyDTypeName() const override { return m_name; }
string prettyDTypeName(bool) const override { return m_name; }
// METHODS
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
@@ -548,6 +553,7 @@ public:
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
string name() const override VL_MT_STABLE;
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
@@ -716,7 +722,7 @@ public:
return asamep->subDTypep()
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
}
string prettyDTypeName() const override;
string prettyDTypeName(bool full) const override;
void dumpSmall(std::ostream& str) const override;
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* subDTypep() const override VL_MT_STABLE {
@@ -806,6 +812,7 @@ public:
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
// METHODS
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); }
@@ -814,9 +821,9 @@ public:
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
int itemCount() const {
size_t itemCount() const {
size_t count = 0;
for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) count++;
for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) ++count;
return count;
}
bool isCompound() const override { return false; }
@@ -869,13 +876,13 @@ public:
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
int widthAlignBytes() const override { return 1; }
int widthTotalBytes() const override { return 1; }
int widthAlignBytes() const override { return 0; }
int widthTotalBytes() const override { return 0; }
bool isVirtual() const { return m_virtual; }
void isVirtual(bool flag) {
m_virtual = flag;
if (flag) v3Global.setHasVirtIfaces();
}
bool isVirtual() const { return m_virtual; }
FileLine* modportFileline() const { return m_modportFileline; }
string cellName() const { return m_cellName; }
void cellName(const string& name) { m_cellName = name; }
@@ -950,8 +957,8 @@ public:
int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
// METHODS
void name(const string& name) override { m_name = name; }
void tag(const string& text) override { m_tag = text; }
string tag() const override { return m_tag; }
void tag(const string& text) override { m_tag = text; }
int lsb() const { return m_lsb; }
void lsb(int lsb) { m_lsb = lsb; }
bool isCompound() const override {
@@ -959,6 +966,30 @@ public:
return false;
}
};
class AstNBACommitQueueDType final : public AstNodeDType {
// @astgen ptr := m_subDTypep : AstNodeDType // Type of the corresponding variable
const bool m_partial; // Partial element update required
public:
AstNBACommitQueueDType(FileLine* fl, AstNodeDType* subDTypep, bool partial)
: ASTGEN_SUPER_NBACommitQueueDType(fl)
, m_partial{partial}
, m_subDTypep{subDTypep} {
dtypep(this);
}
ASTGEN_MEMBERS_AstNBACommitQueueDType;
AstNodeDType* subDTypep() const override { return m_subDTypep; }
bool partial() const { return m_partial; }
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
AstBasicDType* basicp() const override { return nullptr; }
AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
int widthAlignBytes() const override { return 1; }
int widthTotalBytes() const override { return 24; }
bool isCompound() const override { return true; }
};
class AstParamTypeDType final : public AstNodeDType {
// Parents: MODULE
// A parameter type statement; much like a var or typedef
@@ -1066,7 +1097,7 @@ public:
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
}
void dumpSmall(std::ostream& str) const override;
string prettyDTypeName() const override;
string prettyDTypeName(bool full) const override;
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* subDTypep() const override VL_MT_STABLE {
return m_refDTypep ? m_refDTypep : childDTypep();
@@ -1129,8 +1160,8 @@ public:
void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
string prettyDTypeName() const override {
return subDTypep() ? prettyName(subDTypep()->name()) : prettyName();
string prettyDTypeName(bool full) const override {
return subDTypep() ? prettyName(subDTypep()->prettyDTypeName(full)) : prettyName();
}
AstBasicDType* basicp() const override VL_MT_STABLE {
return subDTypep() ? subDTypep()->basicp() : nullptr;
@@ -1262,6 +1293,11 @@ public:
refDTypep(nullptr);
dtypep(nullptr); // V3Width will resolve
}
AstUnsizedArrayDType(FileLine* fl, AstNodeDType* dtp)
: ASTGEN_SUPER_UnsizedArrayDType(fl) {
refDTypep(dtp);
dtypep(nullptr); // V3Width will resolve
}
ASTGEN_MEMBERS_AstUnsizedArrayDType;
const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep()) || (!m_refDTypep && childDTypep())));
@@ -1357,7 +1393,7 @@ public:
inline AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep);
inline AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep);
ASTGEN_MEMBERS_AstPackArrayDType;
string prettyDTypeName() const override;
string prettyDTypeName(bool full) const override;
bool isCompound() const override { return false; }
};
class AstUnpackArrayDType final : public AstNodeArrayDType {
@@ -1384,7 +1420,7 @@ public:
widthFromSub(subDTypep());
}
ASTGEN_MEMBERS_AstUnpackArrayDType;
string prettyDTypeName() const override;
string prettyDTypeName(bool full) const override;
bool same(const AstNode* samep) const override {
const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType);
return m_isCompound == sp->m_isCompound;
+187 -81
View File
@@ -56,6 +56,7 @@ public:
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
// For documentation on emitC format see EmitCFunc::emitOpName
virtual string emitC() = 0;
virtual string emitSMT() const { V3ERROR_NA_RETURN(""); };
virtual string emitSimpleOperator() { return ""; } // "" means not ok to use
virtual bool emitCheckMaxWords() { return false; } // Check VL_MULS_MAX_WORDS
virtual bool cleanOut() const = 0; // True if output has extra upper bits zero
@@ -212,8 +213,8 @@ public:
bool isOutputter() override { return !isPure(); }
AstCFunc* funcp() const { return m_funcp; }
void funcp(AstCFunc* funcp) { m_funcp = funcp; }
void argTypes(const string& str) { m_argTypes = str; }
string argTypes() const { return m_argTypes; }
void argTypes(const string& str) { m_argTypes = str; }
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
string emitC() final override { V3ERROR_NA_RETURN(""); }
@@ -419,6 +420,7 @@ public:
const V3Number& ths) override;
string emitVerilog() override { return "%k(%l %f? %r %k: %t)"; }
string emitC() override { return "VL_COND_%nq%lq%rq%tq(%nw, %P, %li, %ri, %ti)"; }
string emitSMT() const override { return "(ite %l %r %t)"; }
bool cleanOut() const override { return false; } // clean if e1 & e2 clean
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return false; }
@@ -808,19 +810,38 @@ class AstConsDynArray final : public AstNodeExpr {
// Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
// @astgen op1 := lhsp : Optional[AstNode]
// @astgen op2 := rhsp : Optional[AstNode]
const bool m_lhsIsValue = false; // LHS constructs value inside the queue, not concat
const bool m_rhsIsValue = false; // RHS constructs value inside the queue, not concat
public:
explicit AstConsDynArray(FileLine* fl, AstNode* lhsp = nullptr, AstNode* rhsp = nullptr)
: ASTGEN_SUPER_ConsDynArray(fl) {
explicit AstConsDynArray(FileLine* fl)
: ASTGEN_SUPER_ConsDynArray(fl) {}
explicit AstConsDynArray(FileLine* fl, bool lhsIsValue, AstNode* lhsp)
: ASTGEN_SUPER_ConsDynArray(fl)
, m_lhsIsValue(lhsIsValue) {
this->lhsp(lhsp);
}
explicit AstConsDynArray(FileLine* fl, bool lhsIsValue, AstNode* lhsp, bool rhsIsValue,
AstNode* rhsp)
: ASTGEN_SUPER_ConsDynArray(fl)
, m_lhsIsValue(lhsIsValue)
, m_rhsIsValue(rhsIsValue) {
this->lhsp(lhsp);
this->rhsp(rhsp);
}
ASTGEN_MEMBERS_AstConsDynArray;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string emitVerilog() override { return "'{%l, %r}"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
int instrCount() const override { return widthInstrs(); }
bool same(const AstNode* /*samep*/) const override { return true; }
bool same(const AstNode* samep) const override {
const AstConsDynArray* const sp = VN_DBG_AS(samep, ConsDynArray);
return m_lhsIsValue == sp->m_lhsIsValue && m_rhsIsValue == sp->m_rhsIsValue;
}
bool lhsIsValue() const { return m_lhsIsValue; }
bool rhsIsValue() const { return m_rhsIsValue; }
};
class AstConsPackMember final : public AstNodeExpr {
// Construct a packed array single emement [member1: value1]
@@ -871,19 +892,38 @@ class AstConsQueue final : public AstNodeExpr {
// Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
// @astgen op1 := lhsp : Optional[AstNode]
// @astgen op2 := rhsp : Optional[AstNode]
const bool m_lhsIsValue = false; // LHS constructs value inside the queue, not concat
const bool m_rhsIsValue = false; // RHS constructs value inside the queue, not concat
public:
explicit AstConsQueue(FileLine* fl, AstNode* lhsp = nullptr, AstNode* rhsp = nullptr)
: ASTGEN_SUPER_ConsQueue(fl) {
explicit AstConsQueue(FileLine* fl)
: ASTGEN_SUPER_ConsQueue(fl) {}
explicit AstConsQueue(FileLine* fl, bool lhsIsValue, AstNode* lhsp)
: ASTGEN_SUPER_ConsQueue(fl)
, m_lhsIsValue(lhsIsValue) {
this->lhsp(lhsp);
}
explicit AstConsQueue(FileLine* fl, bool lhsIsValue, AstNode* lhsp, bool rhsIsValue,
AstNode* rhsp)
: ASTGEN_SUPER_ConsQueue(fl)
, m_lhsIsValue(lhsIsValue)
, m_rhsIsValue(rhsIsValue) {
this->lhsp(lhsp);
this->rhsp(rhsp);
}
ASTGEN_MEMBERS_AstConsQueue;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string emitVerilog() override { return "'{%l, %r}"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
int instrCount() const override { return widthInstrs(); }
bool same(const AstNode* /*samep*/) const override { return true; }
bool same(const AstNode* samep) const override {
const AstConsQueue* const sp = VN_DBG_AS(samep, ConsQueue);
return m_lhsIsValue == sp->m_lhsIsValue && m_rhsIsValue == sp->m_rhsIsValue;
}
bool lhsIsValue() const { return m_lhsIsValue; }
bool rhsIsValue() const { return m_rhsIsValue; }
};
class AstConsWildcard final : public AstNodeExpr {
// Construct a wildcard assoc array and return object, '{}
@@ -1062,83 +1102,89 @@ public:
};
class AstConstraintRef final : public AstNodeExpr {
// A reference to a constraint identifier
// Not saving pointer to constraint yet, as constraint_mode is an unsupported construct
// @astgen op4 := scopeNamep : Optional[AstScopeName]
// @astgen op1 := fromp : Optional[AstNodeExpr]
//
// @astgen ptr := m_constrp : AstConstraint // The constraint pointed to
// @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package of constraint
string m_name; // Name of constraint
public:
AstConstraintRef(FileLine* fl, const string& name)
: ASTGEN_SUPER_ConstraintRef(fl)
, m_name{name} {
AstConstraintRef(FileLine* fl, AstNodeExpr* fromp, AstConstraint* constrp)
: ASTGEN_SUPER_ConstraintRef(fl) {
this->fromp(fromp);
this->constrp(constrp);
dtypep(findConstraintRefDType());
}
ASTGEN_MEMBERS_AstConstraintRef;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
void name(const string& name) override { m_name = name; }
AstNodeModule* classOrPackagep() const { return m_classOrPackagep; }
string name() const override VL_MT_STABLE;
AstConstraint* constrp() const VL_MT_STABLE { return m_constrp; }
void constrp(AstConstraint* nodep) { m_constrp = nodep; }
AstNodeModule* classOrPackagep() const VL_MT_STABLE { return m_classOrPackagep; }
void classOrPackagep(AstNodeModule* nodep) { m_classOrPackagep = nodep; }
string emitVerilog() final override { V3ERROR_NA_RETURN(""); }
string emitC() final override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const final override { V3ERROR_NA_RETURN(true); }
};
class AstCvtDynArrayToPacked final : public AstNodeExpr {
class AstCvtArrayToPacked final : public AstNodeExpr {
// Cast from dynamic queue data type to packed array
// @astgen op1 := fromp : AstNodeExpr
public:
AstCvtDynArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
: ASTGEN_SUPER_CvtDynArrayToPacked(fl) {
AstCvtArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
: ASTGEN_SUPER_CvtArrayToPacked(fl) {
this->fromp(fromp);
dtypeFrom(dtp);
}
ASTGEN_MEMBERS_AstCvtDynArrayToPacked;
ASTGEN_MEMBERS_AstCvtArrayToPacked;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { return "VL_PACK_%nq%rq(%nw, %rw, %P, %li)"; }
bool cleanOut() const override { return true; }
};
class AstCvtPackedToArray final : public AstNodeExpr {
// Cast from packed array to dynamic/unpacked queue data type
// @astgen op1 := fromp : AstNodeExpr
public:
AstCvtPackedToArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
: ASTGEN_SUPER_CvtPackedToArray(fl) {
this->fromp(fromp);
dtypeFrom(dtp);
}
ASTGEN_MEMBERS_AstCvtPackedToArray;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
};
class AstCvtPackedToDynArray final : public AstNodeExpr {
// Cast from packed array to dynamic queue data type
// @astgen op1 := fromp : AstNodeExpr
class AstDist final : public AstNodeExpr {
// @astgen op1 := exprp : AstNodeExpr
// @astgen op2 := itemsp : List[AstDistItem]
public:
AstCvtPackedToDynArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
: ASTGEN_SUPER_CvtPackedToDynArray(fl) {
this->fromp(fromp);
dtypeFrom(dtp);
AstDist(FileLine* fl, AstNodeExpr* exprp, AstDistItem* itemsp)
: ASTGEN_SUPER_Inside(fl) {
this->exprp(exprp);
this->addItemsp(itemsp);
dtypeSetBit();
}
ASTGEN_MEMBERS_AstCvtPackedToDynArray;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
ASTGEN_MEMBERS_AstDist;
string emitVerilog() override { return "%l dist { %r }"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool cleanOut() const override { return false; } // NA
};
class AstCvtPackedToUnpackArray final : public AstNodeExpr {
// Cast from packed array to dynamic queue data type
// @astgen op1 := fromp : AstNodeExpr
class AstDistItem final : public AstNodeExpr {
// Constraint distribution item
// @astgen op1 := rangep : AstNodeExpr
// @astgen op2 := weightp : AstNodeExpr
bool m_isWhole = false; // True for weight ':/', false for ':='
public:
AstCvtPackedToUnpackArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
: ASTGEN_SUPER_CvtPackedToUnpackArray(fl) {
this->fromp(fromp);
dtypeFrom(dtp);
AstDistItem(FileLine* fl, AstNodeExpr* rangep, AstNodeExpr* weightp)
: ASTGEN_SUPER_DistItem(fl) {
this->rangep(rangep);
this->weightp(weightp);
}
ASTGEN_MEMBERS_AstCvtPackedToUnpackArray;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
ASTGEN_MEMBERS_AstDistItem;
string emitVerilog() override { return "%l "s + (m_isWhole ? ":/" : ":=") + " %r"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
};
class AstCvtUnpackArrayToPacked final : public AstNodeExpr {
// Cast from dynamic queue data type to packed array
// @astgen op1 := fromp : AstNodeExpr
public:
AstCvtUnpackArrayToPacked(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
: ASTGEN_SUPER_CvtUnpackArrayToPacked(fl) {
this->fromp(fromp);
dtypeFrom(dtp);
}
ASTGEN_MEMBERS_AstCvtUnpackArrayToPacked;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool cleanOut() const override { return false; } // NA
void isWhole(bool flag) { m_isWhole = flag; }
bool isWhole() const { return m_isWhole; }
};
class AstDot final : public AstNodeExpr {
// A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef
@@ -1530,6 +1576,9 @@ class AstLambdaArgRef final : public AstNodeExpr {
// Lambda argument usage
// These are not AstVarRefs because we need to be able to delete/clone lambdas during
// optimizations and AstVar's are painful to remove.
ASTGEN_MEMBERS_AstLambdaArgRef;
private:
string m_name; // Name of variable
bool m_index; // Index, not value
@@ -1538,7 +1587,6 @@ public:
: ASTGEN_SUPER_LambdaArgRef(fl)
, m_name{name}
, m_index(index) {}
ASTGEN_MEMBERS_AstLambdaArgRef;
bool same(const AstNode* /*samep*/) const override { return true; }
string emitVerilog() override { return name(); }
string emitC() override { V3ERROR_NA_RETURN(""); }
@@ -1562,12 +1610,7 @@ public:
this->fromp(fromp);
dtypep(nullptr); // V3Width will resolve
}
AstMemberSel(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
: ASTGEN_SUPER_MemberSel(fl)
, m_name{dtp->name()} {
this->fromp(fromp);
dtypep(dtp);
}
AstMemberSel(FileLine* fl, AstNodeExpr* fromp, AstVar* varp);
ASTGEN_MEMBERS_AstMemberSel;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
@@ -1743,13 +1786,27 @@ public:
: (m_urandom ? "%f$urandom()" : "%f$random()");
}
string emitC() override {
return m_reset ? "VL_RAND_RESET_%nq(%nw, %P)"
: seedp()
// cppcheck-has-bug-suppress knownConditionTrueFalse
? (urandom() ? "VL_URANDOM_SEEDED_%nq%lq(%li)" //
: "VL_RANDOM_SEEDED_%nq%lq(%li)")
: (isWide() ? "VL_RANDOM_%nq(%nw, %P)" //
: "VL_RANDOM_%nq()");
if (m_reset) {
if (v3Global.opt.xAssign() == "unique") {
return "VL_RAND_RESET_ASSIGN_%nq(%nw, %P)";
} else {
// This follows xInitial randomization
return "VL_RAND_RESET_%nq(%nw, %P)";
}
}
if (seedp()) {
if (urandom()) {
return "VL_URANDOM_SEEDED_%nq%lq(%li)";
} else {
return "VL_RANDOM_SEEDED_%nq%lq(%li)";
}
}
if (isWide()) {
return "VL_RANDOM_%nq(%nw, %P)";
} else {
return "VL_RANDOM_%nq()";
}
}
bool cleanOut() const override { return false; }
bool isGateOptimizable() const override { return false; }
@@ -1846,11 +1903,11 @@ public:
return (name().find("%m") != string::npos || name().find("%M") != string::npos);
}
bool hidden() const { return m_hidden; }
void hasFormat(bool flag) { m_hasFormat = flag; }
bool hasFormat() const { return m_hasFormat; }
void hasFormat(bool flag) { m_hasFormat = flag; }
char missingArgChar() const { return m_missingArgChar; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
@@ -2134,6 +2191,10 @@ public:
: ASTGEN_SUPER_ThisRef(fl) {
childDTypep(dtypep);
}
AstThisRef(FileLine* fl, AstClassRefDType* dtypep)
: ASTGEN_SUPER_ThisRef(fl) {
this->dtypep(dtypep);
}
ASTGEN_MEMBERS_AstThisRef;
string emitC() override { return "this"; }
string emitVerilog() override { return "this"; }
@@ -2172,8 +2233,8 @@ public:
bool cleanOut() const override { return true; }
int instrCount() const override { return widthInstrs(); }
bool same(const AstNode* /*samep*/) const override { return true; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
};
class AstUCFunc final : public AstNodeExpr {
// User's $c function
@@ -2259,7 +2320,7 @@ class AstWith final : public AstNodeExpr {
// @astgen op3 := exprp : List[AstNode]
public:
AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
AstNodeExpr* exprp)
AstNode* exprp)
: ASTGEN_SUPER_With(fl) {
this->indexArgRefp(indexArgRefp);
this->valueArgRefp(valueArgRefp);
@@ -2282,13 +2343,13 @@ class AstWithParse final : public AstNodeExpr {
// Replaced with AstWith
// Parents: expr|stmt
// Children: funcref, expr
// @astgen op1 := funcrefp : AstNode
// @astgen op2 := exprp : Optional[AstNodeExpr]
// @astgen op1 := funcrefp : AstNodeExpr
// @astgen op2 := exprsp : List[AstNode]
public:
AstWithParse(FileLine* fl, AstNode* funcrefp, AstNodeExpr* exprp)
AstWithParse(FileLine* fl, AstNodeExpr* funcrefp, AstNode* exprsp)
: ASTGEN_SUPER_WithParse(fl) {
this->funcrefp(funcrefp);
this->exprp(exprp);
this->addExprsp(exprsp);
}
ASTGEN_MEMBERS_AstWithParse;
bool same(const AstNode* /*samep*/) const override { return true; }
@@ -2401,6 +2462,7 @@ public:
out.opConcat(lhs, rhs);
}
string emitC() override { return "VL_CONCAT_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(concat %l %r)"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -2447,6 +2509,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f/ %r)"; }
string emitC() override { return "VL_DIV_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvudiv %l %r)"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -2493,6 +2556,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f/ %r)"; }
string emitC() override { return "VL_DIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvsdiv %l %r)"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -2519,6 +2583,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f==? %r)"; }
string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(= %l %r)"; }
string emitSimpleOperator() override { return "=="; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2641,6 +2706,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f> %r)"; }
string emitC() override { return "VL_GT_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvugt %l %r)"; }
string emitSimpleOperator() override { return ">"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2711,6 +2777,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f> %r)"; }
string emitC() override { return "VL_GTS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvsgt %l %r)"; }
string emitSimpleOperator() override { return ""; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2734,6 +2801,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f>= %r)"; }
string emitC() override { return "VL_GTE_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvuge %l %r)"; }
string emitSimpleOperator() override { return ">="; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2804,6 +2872,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f>= %r)"; }
string emitC() override { return "VL_GTES_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvsge %l %r)"; }
string emitSimpleOperator() override { return ""; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2827,6 +2896,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f&& %r)"; }
string emitC() override { return "VL_LOGAND_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(and %l %r)"; }
string emitSimpleOperator() override { return "&&"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2850,6 +2920,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f-> %r)"; }
string emitC() override { return "VL_LOGIF_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(=> %l %r)"; }
string emitSimpleOperator() override { return "->"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2873,6 +2944,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f|| %r)"; }
string emitC() override { return "VL_LOGOR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(or %l %r)"; }
string emitSimpleOperator() override { return "||"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2896,6 +2968,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f< %r)"; }
string emitC() override { return "VL_LT_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvult %l %r)"; }
string emitSimpleOperator() override { return "<"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2966,6 +3039,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f< %r)"; }
string emitC() override { return "VL_LTS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvslt %l %r)"; }
string emitSimpleOperator() override { return ""; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2989,6 +3063,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f<= %r)"; }
string emitC() override { return "VL_LTE_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvule %l %r)"; }
string emitSimpleOperator() override { return "<="; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -3059,6 +3134,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f<= %r)"; }
string emitC() override { return "VL_LTES_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvsle %l %r)"; }
string emitSimpleOperator() override { return ""; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -3082,6 +3158,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f%% %r)"; }
string emitC() override { return "VL_MODDIV_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvurem %l %r)"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -3104,6 +3181,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f%% %r)"; }
string emitC() override { return "VL_MODDIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvsmod %l %r)"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -3277,6 +3355,9 @@ public:
}
string emitVerilog() override { return "%f{%r{%k%l}}"; }
string emitC() override { return "VL_REPLICATE_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override {
return "((_ repeat " + cvtToStr(width() / lhsp()->width()) + ") %l)";
}
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -3325,6 +3406,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f<< %r)"; }
string emitC() override { return "VL_SHIFTL_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvshl %l %r)"; }
string emitSimpleOperator() override {
return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : "<<";
}
@@ -3373,6 +3455,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f>> %r)"; }
string emitC() override { return "VL_SHIFTR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvlshr %l %r)"; }
string emitSimpleOperator() override {
return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : ">>";
}
@@ -3425,6 +3508,7 @@ public:
out.opShiftRS(lhs, rhs, lhsp()->widthMinV());
}
string emitVerilog() override { return "%k(%l %f>>> %r)"; }
string emitSMT() const override { return "(bvashr %l %r)"; }
string emitC() override { return "VL_SHIFTRS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSimpleOperator() override { return ""; }
bool cleanOut() const override { return false; }
@@ -3476,6 +3560,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f- %r)"; }
string emitC() override { return "VL_SUB_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvsub %l %r)"; }
string emitSimpleOperator() override { return "-"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
@@ -3552,6 +3637,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f== %r)"; }
string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(= %l %r)"; }
string emitSimpleOperator() override { return "=="; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -3668,6 +3754,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f<-> %r)"; }
string emitC() override { return "VL_LOGEQ_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvxnor %l %r)"; }
string emitSimpleOperator() override { return "<->"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -3692,6 +3779,7 @@ public:
string emitVerilog() override { return "%k(%l %f!= %r)"; }
string emitC() override { return "VL_NEQ_%lq(%lW, %P, %li, %ri)"; }
string emitSimpleOperator() override { return "!="; }
string emitSMT() const override { return "(not (= %l %r))"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -3809,6 +3897,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f+ %r)"; }
string emitC() override { return "VL_ADD_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvadd %l %r)"; }
string emitSimpleOperator() override { return "+"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
@@ -3855,6 +3944,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f& %r)"; }
string emitC() override { return "VL_AND_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvand %l %r)"; }
string emitSimpleOperator() override { return "&"; }
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
bool cleanLhs() const override { return false; }
@@ -3878,6 +3968,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f* %r)"; }
string emitC() override { return "VL_MUL_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvmul %l %r)"; }
string emitSimpleOperator() override { return "*"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
@@ -3950,6 +4041,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f| %r)"; }
string emitC() override { return "VL_OR_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvor %l %r)"; }
string emitSimpleOperator() override { return "|"; }
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
bool cleanLhs() const override { return false; }
@@ -3973,6 +4065,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f^ %r)"; }
string emitC() override { return "VL_XOR_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvxor %l %r)"; }
string emitSimpleOperator() override { return "^"; }
bool cleanOut() const override { return false; } // Lclean && Rclean
bool cleanLhs() const override { return false; }
@@ -4164,7 +4257,7 @@ public:
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
bool sizeMattersLhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
int instrCount() const override { return widthInstrs() * 2; }
};
@@ -4185,7 +4278,7 @@ public:
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
bool cleanRhs() const override { return false; }
bool sizeMattersLhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
int instrCount() const override { return widthInstrs() * 2; }
};
@@ -4406,8 +4499,8 @@ public:
bool same(const AstNode* /*samep*/) const override { return true; }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
};
class AstTimeD final : public AstNodeTermop {
VTimescale m_timeunit; // Parent module time unit
@@ -4427,8 +4520,8 @@ public:
bool same(const AstNode* /*samep*/) const override { return true; }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
};
// === AstNodeTriop ===
@@ -4586,6 +4679,7 @@ public:
: isWide() ? "VL_SEL_%nq%lq%rq%tq(%nw,%lw, %P, %li, %ri, %ti)"
: "VL_SEL_%nq%lq%rq%tq(%lw, %P, %li, %ri, %ti)";
}
string emitSMT() const override { return "((_ extract %t %r) %l)"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -4895,6 +4989,9 @@ public:
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
string emitVerilog() override { return "%l"; }
string emitC() override { return "VL_EXTEND_%nq%lq(%nw,%lw, %P, %li)"; }
string emitSMT() const override {
return "((_ zero_extend " + cvtToStr(width() - lhsp()->width()) + ") %l)";
}
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override {
@@ -4918,6 +5015,9 @@ public:
}
string emitVerilog() override { return "%l"; }
string emitC() override { return "VL_EXTENDS_%nq%lq(%nw,%lw, %P, %li)"; }
string emitSMT() const override {
return "((_ sign_extend " + cvtToStr(width() - lhsp()->width()) + ") %l)";
}
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override {
@@ -5050,6 +5150,7 @@ public:
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opLogNot(lhs); }
string emitVerilog() override { return "%f(! %l)"; }
string emitC() override { return "VL_LOGNOT_%nq%lq(%nw,%lw, %P, %li)"; }
string emitSMT() const override { return "(not %l)"; }
string emitSimpleOperator() override { return "!"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -5080,6 +5181,7 @@ public:
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNegate(lhs); }
string emitVerilog() override { return "%f(- %l)"; }
string emitC() override { return "VL_NEGATE_%lq(%lW, %P, %li)"; }
string emitSMT() const override { return "(bvneg %l)"; }
string emitSimpleOperator() override { return "-"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
@@ -5112,6 +5214,7 @@ public:
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNot(lhs); }
string emitVerilog() override { return "%f(~ %l)"; }
string emitC() override { return "VL_NOT_%lq(%lW, %P, %li)"; }
string emitSMT() const override { return "(bvnot %l)"; }
string emitSimpleOperator() override { return "~"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
@@ -5313,8 +5416,8 @@ public:
bool sizeMattersLhs() const override { return false; }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
};
class AstToLowerN final : public AstNodeUniop {
// string.tolower()
@@ -5574,6 +5677,8 @@ public:
// This form only allowed post-link because output/wire compression may
// lead to deletion of AstVar's
inline AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access);
inline AstVarRef(FileLine* fl, AstNodeModule* classOrPackagep, AstVar* varp,
const VAccess& access);
// This form only allowed post-link (see above)
inline AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access);
ASTGEN_MEMBERS_AstVarRef;
@@ -5603,6 +5708,7 @@ public:
inline AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access);
ASTGEN_MEMBERS_AstVarXRef;
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
void name(const std::string& name) override { m_name = name; } // * = Var name
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string dotted() const { return m_dotted; }
+167 -92
View File
@@ -138,48 +138,48 @@ public:
void dpiOpenParentInc() { ++m_dpiOpenParent; }
void dpiOpenParentClear() { m_dpiOpenParent = 0; }
uint64_t dpiOpenParent() const { return m_dpiOpenParent; }
void taskPublic(bool flag) { m_taskPublic = flag; }
bool taskPublic() const { return m_taskPublic; }
void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; }
void taskPublic(bool flag) { m_taskPublic = flag; }
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
void classMethod(bool flag) { m_classMethod = flag; }
void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; }
bool classMethod() const { return m_classMethod; }
void isExternProto(bool flag) { m_externProto = flag; }
void classMethod(bool flag) { m_classMethod = flag; }
bool isExternProto() const { return m_externProto; }
void isExternDef(bool flag) { m_externDef = flag; }
void isExternProto(bool flag) { m_externProto = flag; }
bool isExternDef() const { return m_externDef; }
void prototype(bool flag) { m_prototype = flag; }
void isExternDef(bool flag) { m_externDef = flag; }
bool prototype() const { return m_prototype; }
void dpiExport(bool flag) { m_dpiExport = flag; }
void prototype(bool flag) { m_prototype = flag; }
bool dpiExport() const { return m_dpiExport; }
void dpiImport(bool flag) { m_dpiImport = flag; }
void dpiExport(bool flag) { m_dpiExport = flag; }
bool dpiImport() const { return m_dpiImport; }
void dpiContext(bool flag) { m_dpiContext = flag; }
void dpiImport(bool flag) { m_dpiImport = flag; }
bool dpiContext() const { return m_dpiContext; }
void dpiOpenChild(bool flag) { m_dpiOpenChild = flag; }
void dpiContext(bool flag) { m_dpiContext = flag; }
bool dpiOpenChild() const { return m_dpiOpenChild; }
void dpiTask(bool flag) { m_dpiTask = flag; }
void dpiOpenChild(bool flag) { m_dpiOpenChild = flag; }
bool dpiTask() const { return m_dpiTask; }
void isConstructor(bool flag) { m_isConstructor = flag; }
void dpiTask(bool flag) { m_dpiTask = flag; }
bool isConstructor() const { return m_isConstructor; }
void isConstructor(bool flag) { m_isConstructor = flag; }
bool isHideLocal() const { return m_isHideLocal; }
void isHideLocal(bool flag) { m_isHideLocal = flag; }
bool isHideProtected() const { return m_isHideProtected; }
void isHideProtected(bool flag) { m_isHideProtected = flag; }
void dpiPure(bool flag) { m_dpiPure = flag; }
bool dpiPure() const { return m_dpiPure; }
void pureVirtual(bool flag) { m_pureVirtual = flag; }
void dpiPure(bool flag) { m_dpiPure = flag; }
bool pureVirtual() const { return m_pureVirtual; }
void recursive(bool flag) { m_recursive = flag; }
void pureVirtual(bool flag) { m_pureVirtual = flag; }
bool recursive() const { return m_recursive; }
void isStatic(bool flag) { m_static = flag; }
void recursive(bool flag) { m_recursive = flag; }
bool isStatic() const { return m_static; }
void underGenerate(bool flag) { m_underGenerate = flag; }
void isStatic(bool flag) { m_static = flag; }
bool underGenerate() const { return m_underGenerate; }
void isVirtual(bool flag) { m_virtual = flag; }
void underGenerate(bool flag) { m_underGenerate = flag; }
bool isVirtual() const { return m_virtual; }
void setNeedProcess() { m_needProcess = true; }
void isVirtual(bool flag) { m_virtual = flag; }
bool needProcess() const { return m_needProcess; }
void setNeedProcess() { m_needProcess = true; }
void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; }
VBaseOverride baseOverride() const { return m_baseOverride; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
@@ -238,6 +238,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
bool m_hasGParam : 1; // Has global parameter (for link)
bool m_hasParameterList : 1; // Has #() for parameter declaration
bool m_hierBlock : 1; // Hierarchical Block marked by HIER_BLOCK pragma
bool m_hierParams : 1; // Block containing params for parametrized hier blocks
bool m_internal : 1; // Internally created
bool m_recursive : 1; // Recursive module
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
@@ -253,6 +254,7 @@ protected:
, m_hasGParam{false}
, m_hasParameterList{false}
, m_hierBlock{false}
, m_hierParams{false}
, m_internal{false}
, m_recursive{false}
, m_recursiveClone{false} {}
@@ -274,30 +276,32 @@ public:
void level(int level) { m_level = level; }
int level() const VL_MT_SAFE { return m_level; }
bool isTop() const VL_MT_SAFE { return level() == 1; }
void modPublic(bool flag) { m_modPublic = flag; }
bool modPublic() const { return m_modPublic; }
void modTrace(bool flag) { m_modTrace = flag; }
void modPublic(bool flag) { m_modPublic = flag; }
bool modTrace() const { return m_modTrace; }
void dead(bool flag) { m_dead = flag; }
void modTrace(bool flag) { m_modTrace = flag; }
bool dead() const { return m_dead; }
void hasGParam(bool flag) { m_hasGParam = flag; }
void dead(bool flag) { m_dead = flag; }
bool hasGParam() const { return m_hasGParam; }
void hasParameterList(bool flag) { m_hasParameterList = flag; }
void hasGParam(bool flag) { m_hasGParam = flag; }
bool hasParameterList() const { return m_hasParameterList; }
void hierBlock(bool flag) { m_hierBlock = flag; }
void hasParameterList(bool flag) { m_hasParameterList = flag; }
bool hierBlock() const { return m_hierBlock; }
void internal(bool flag) { m_internal = flag; }
void hierBlock(bool flag) { m_hierBlock = flag; }
bool hierParams() const { return m_hierParams; }
void hierParams(bool flag) { m_hierParams = flag; }
bool internal() const { return m_internal; }
void recursive(bool flag) { m_recursive = flag; }
void internal(bool flag) { m_internal = flag; }
bool recursive() const { return m_recursive; }
void recursive(bool flag) { m_recursive = flag; }
void recursiveClone(bool flag) { m_recursiveClone = flag; }
bool recursiveClone() const { return m_recursiveClone; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VTimescale timeunit() const { return m_timeunit; }
void unconnectedDrive(const VOptionBool flag) { m_unconnectedDrive = flag; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VOptionBool unconnectedDrive() const { return m_unconnectedDrive; }
void unconnectedDrive(const VOptionBool flag) { m_unconnectedDrive = flag; }
};
class AstNodeProcedure VL_NOT_FINAL : public AstNode {
// IEEE procedure: initial, final, always
@@ -398,13 +402,16 @@ class AstNodeCoverOrAssert VL_NOT_FINAL : public AstNodeStmt {
// op3 used by some sub-types only
// @astgen op4 := passsp: List[AstNode] // Statements when propp is passing/truthly
string m_name; // Name to report
const bool m_immediate; // Immediate assertion/cover
const VAssertType m_type; // Assertion/cover type
const VAssertDirectiveType m_directive; // Assertion directive type
public:
AstNodeCoverOrAssert(VNType t, FileLine* fl, AstNode* propp, AstNode* passsp, bool immediate,
const string& name = "")
AstNodeCoverOrAssert(VNType t, FileLine* fl, AstNode* propp, AstNode* passsp, VAssertType type,
VAssertDirectiveType directive, const string& name = "")
: AstNodeStmt{t, fl}
, m_name{name}
, m_immediate{immediate} {
, m_type{type}
, m_directive{directive} {
this->propp(propp);
this->addPasssp(passsp);
}
@@ -414,7 +421,14 @@ public:
void name(const string& name) override { m_name = name; }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
bool immediate() const { return m_immediate; }
VAssertType type() const { return m_type; }
VAssertDirectiveType directive() const { return m_directive; }
bool immediate() const {
return this->type().containsAny(VAssertType::SIMPLE_IMMEDIATE
| VAssertType::OBSERVED_DEFERRED_IMMEDIATE
| VAssertType::FINAL_DEFERRED_IMMEDIATE)
|| this->type() == VAssertType::INTERNAL;
}
};
class AstNodeFor VL_NOT_FINAL : public AstNodeStmt {
// @astgen op1 := initsp : List[AstNode]
@@ -926,8 +940,7 @@ class AstClocking final : public AstNode {
// Children: SENITEM, CLOCKING ITEMs, VARs
// @astgen op1 := sensesp : AstSenItem
// @astgen op2 := itemsp : List[AstClockingItem]
// @astgen op3 := varsp : List[AstVar]
// @astgen op4 := eventp : Optional[AstVar]
// @astgen op3 := eventp : Optional[AstVar]
std::string m_name; // Clocking block name
const bool m_isDefault = false; // True if default clocking
const bool m_isGlobal = false; // True if global clocking
@@ -956,6 +969,7 @@ class AstClockingItem final : public AstNode {
// @astgen op2 := exprp : Optional[AstNodeExpr]
// @astgen op3 := assignp : Optional[AstAssign]
// @astgen op4 := varp : Optional[AstVar]
// @astgen ptr := m_outputp : Optional[AstClockingItem]
VDirection m_direction;
public:
@@ -971,6 +985,9 @@ public:
}
ASTGEN_MEMBERS_AstClockingItem;
VDirection direction() const { return m_direction; }
AstClockingItem* outputp() const { return m_outputp; }
void outputp(AstClockingItem* outputp) { m_outputp = outputp; }
bool maybePointedTo() const override { return true; }
};
class AstConstPool final : public AstNode {
// Container for const static data
@@ -1018,6 +1035,7 @@ public:
string name() const override VL_MT_STABLE { return m_name; } // * = Scope name
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool maybePointedTo() const override { return true; }
bool same(const AstNode* /*samep*/) const override { return true; }
void isStatic(bool flag) { m_isStatic = flag; }
bool isStatic() const { return m_isStatic; }
@@ -1055,24 +1073,6 @@ public:
bool same(const AstNode*) const override { return true; }
string path() const { return m_path; }
};
class AstDistItem final : public AstNode {
// Constraint distribution item
// @astgen op1 := rangep : AstNodeExpr
// @astgen op2 := weightp : AstNodeExpr
bool m_isWhole = false; // True for weight ':/', false for ':='
public:
AstDistItem(FileLine* fl, AstNodeExpr* rangep, AstNodeExpr* weightp)
: ASTGEN_SUPER_DistItem(fl) {
this->rangep(rangep);
this->weightp(weightp);
}
ASTGEN_MEMBERS_AstDistItem;
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool same(const AstNode* /*samep*/) const override { return true; }
void isWhole(bool flag) { m_isWhole = flag; }
bool isWhole() const { return m_isWhole; }
};
class AstDpiExport final : public AstNode {
// We could put an AstNodeFTaskRef instead of the verilog function name,
// however we're not *calling* it, so that seems somehow wrong.
@@ -1102,7 +1102,7 @@ public:
BROKEN_RTN(!fmtp());
return nullptr;
}
string verilogKwd() const override { return string{"$"} + string{displayType().ascii()}; }
string verilogKwd() const override { return "$"s + string{displayType().ascii()}; }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; } // SPECIAL: $display has 'visual' ordering
@@ -1767,6 +1767,7 @@ class AstVar final : public AstNode {
VDirection m_declDirection; // Declared direction input/output etc
VLifetime m_lifetime; // Lifetime
VVarAttrClocker m_attrClocker;
VRandAttr m_rand; // Randomizability of this variable (rand, randc, etc)
int m_pinNum = 0; // For XML, if non-zero the connection pin number
bool m_ansi : 1; // Params or pins declared in the module header, rather than the body
bool m_declTyped : 1; // Declared as type (for dedup check)
@@ -1790,8 +1791,6 @@ class AstVar final : public AstNode {
bool m_attrSFormat : 1; // User sformat attribute
bool m_attrSplitVar : 1; // declared with split_var metacomment
bool m_fileDescr : 1; // File descriptor
bool m_isRand : 1; // Random variable
bool m_isRandC : 1; // Random cyclic variable (isRand also set)
bool m_isConst : 1; // Table contains constant data
bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier
@@ -1809,6 +1808,7 @@ class AstVar final : public AstNode {
bool m_trace : 1; // Trace this variable
bool m_isLatched : 1; // Not assigned in all control paths of combo always
bool m_isForceable : 1; // May be forced/released externally from user C code
bool m_isForcedByCode : 1; // May be forced/released from AstAssignForce/AstRelease
bool m_isWrittenByDpi : 1; // This variable can be written by a DPI Export
bool m_isWrittenBySuspendable : 1; // This variable can be written by a suspendable process
@@ -1835,8 +1835,6 @@ class AstVar final : public AstNode {
m_attrSFormat = false;
m_attrSplitVar = false;
m_fileDescr = false;
m_isRand = false;
m_isRandC = false;
m_isConst = false;
m_isContinuously = false;
m_hasStrengthAssignment = false;
@@ -1854,6 +1852,7 @@ class AstVar final : public AstNode {
m_trace = false;
m_isLatched = false;
m_isForceable = false;
m_isForcedByCode = false;
m_isWrittenByDpi = false;
m_isWrittenBySuspendable = false;
m_attrClocker = VVarAttrClocker::CLOCKER_UNKNOWN;
@@ -1960,6 +1959,7 @@ public:
void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; }
void attrSFormat(bool flag) { m_attrSFormat = flag; }
void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
void rand(const VRandAttr flag) { m_rand = flag; }
void usedClock(bool flag) { m_usedClock = flag; }
void usedParam(bool flag) { m_usedParam = flag; }
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
@@ -1976,11 +1976,6 @@ public:
void sc(bool flag) { m_sc = flag; }
void scSensitive(bool flag) { m_scSensitive = flag; }
void primaryIO(bool flag) { m_primaryIO = flag; }
void isRand(bool flag) { m_isRand = flag; }
void isRandC(bool flag) {
m_isRandC = flag;
if (flag) isRand(true);
}
void isConst(bool flag) { m_isConst = flag; }
void isContinuously(bool flag) { m_isContinuously = flag; }
void isStatic(bool flag) { m_isStatic = flag; }
@@ -2009,6 +2004,8 @@ public:
void isLatched(bool flag) { m_isLatched = flag; }
bool isForceable() const { return m_isForceable; }
void setForceable() { m_isForceable = true; }
void setForcedByCode() { m_isForcedByCode = true; }
bool isForced() const { return m_isForceable || m_isForcedByCode; }
bool isWrittenByDpi() const { return m_isWrittenByDpi; }
void setWrittenByDpi() { m_isWrittenByDpi = true; }
bool isWrittenBySuspendable() const { return m_isWrittenBySuspendable; }
@@ -2022,6 +2019,7 @@ public:
bool isContinuously() const { return m_isContinuously; }
bool isDeclTyped() const { return m_declTyped; }
bool isInoutish() const { return m_direction.isInoutish(); }
bool isInput() const { return m_direction.isInput(); }
bool isNonOutput() const { return m_direction.isNonOutput(); }
bool isReadOnly() const VL_MT_SAFE { return m_direction.isReadOnly(); }
bool isConstRef() const VL_MT_SAFE { return m_direction.isConstRef(); }
@@ -2059,6 +2057,7 @@ public:
bool isScQuad() const;
bool isScBv() const;
bool isScUint() const;
bool isScUintBool() const;
bool isScBigUint() const;
bool isScSensitive() const { return m_scSensitive; }
bool isSigPublic() const;
@@ -2066,8 +2065,8 @@ public:
bool isSigUserRdPublic() const { return m_sigUserRdPublic; }
bool isSigUserRWPublic() const { return m_sigUserRWPublic; }
bool isTrace() const { return m_trace; }
bool isRand() const { return m_isRand; }
bool isRandC() const { return m_isRandC; }
bool isRand() const { return m_rand.isRand(); }
bool isRandC() const { return m_rand.isRandC(); }
bool isConst() const VL_MT_SAFE { return m_isConst; }
bool isStatic() const VL_MT_SAFE { return m_isStatic; }
bool isLatched() const { return m_isLatched; }
@@ -2084,6 +2083,7 @@ public:
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
AstIface* sensIfacep() const { return m_sensIfacep; }
VVarAttrClocker attrClocker() const { return m_attrClocker; }
VRandAttr rand() const { return m_rand; }
string verilogKwd() const override;
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; }
@@ -2183,8 +2183,8 @@ public:
ASTGEN_MEMBERS_AstBegin;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
void generate(bool flag) { m_generate = flag; }
bool generate() const { return m_generate; }
void generate(bool flag) { m_generate = flag; }
void setNeedProcess() { m_needProcess = true; }
bool needProcess() const { return m_needProcess; }
bool implied() const { return m_implied; }
@@ -2338,6 +2338,42 @@ public:
// Return the lowest class extended from, or this class
AstClass* baseMostClassp();
static bool isCacheableChild(const AstNode* nodep);
// Iterates top level members of the class, taking into account inheritance (starting from the
// root superclass). Note: after V3Scope, several children are moved under an AstScope and will
// not be found by this.
template <typename Callable>
void foreachMember(const Callable& f) {
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 1>::type;
static_assert(
vlstd::is_invocable<Callable, AstClass*, T_Node*>::value
&& std::is_base_of<AstNode, T_Node>::value,
"Callable 'f' must have a signature compatible with 'void(AstClass*, T_Node*)', "
"with 'T_Node' being a subtype of 'AstNode'");
if (AstClassExtends* const extendsp = this->extendsp()) {
extendsp->classp()->foreachMember(f);
}
for (AstNode* stmtp = stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstNode::privateTypeTest<T_Node>(stmtp)) f(this, static_cast<T_Node*>(stmtp));
}
}
// Same as above, but stops after first match
template <typename Callable>
bool existsMember(const Callable& p) const {
using T_Node = typename FunctionArgNoPointerNoCV<Callable, 1>::type;
static_assert(vlstd::is_invocable_r<bool, Callable, const AstClass*, const T_Node*>::value
&& std::is_base_of<AstNode, T_Node>::value,
"Predicate 'p' must have a signature compatible with 'bool(const AstClass*, "
"const T_Node*)', with 'T_Node' being a subtype of 'AstNode'");
if (AstClassExtends* const extendsp = this->extendsp()) {
if (extendsp->classp()->existsMember(p)) return true;
}
for (AstNode* stmtp = stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstNode::privateTypeTest<T_Node>(stmtp)) {
if (p(this, static_cast<T_Node*>(stmtp))) return true;
}
}
return false;
}
};
class AstClassPackage final : public AstNodeModule {
// The static information portion of a class (treated similarly to a package)
@@ -2434,13 +2470,11 @@ public:
ASTGEN_MEMBERS_AstAlwaysObserved;
};
class AstAlwaysPost final : public AstNodeProcedure {
// Like always but post assignments for memory assignment IFs
// @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list iff clocked
// Like always but 'post' scheduled, e.g. for array NBA commits
public:
AstAlwaysPost(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp)
: ASTGEN_SUPER_AlwaysPost(fl, stmtsp) {
this->sensesp(sensesp);
}
explicit AstAlwaysPost(FileLine* fl)
: ASTGEN_SUPER_AlwaysPost(fl, nullptr) {}
ASTGEN_MEMBERS_AstAlwaysPost;
};
class AstAlwaysPostponed final : public AstNodeProcedure {
@@ -2580,6 +2614,37 @@ public:
bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
};
class AstAssertCtl final : public AstNodeStmt {
// @astgen op1 := controlTypep : AstNodeExpr
// @astgen op2 := assertTypesp : Optional[AstNodeExpr]
// @astgen op3 := directiveTypesp : Optional[AstNodeExpr]
// Type of assertcontrol task; either known from parser or from evaluated
// controlTypep expression.
VAssertCtlType m_ctlType; // $assert keyword type (control_type)
VAssertType m_assertTypes; // Types of assertions affected
VAssertDirectiveType m_directiveTypes; // Types of directives affected
public:
AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr* levelp = nullptr,
AstNodeExpr* itemsp = nullptr);
AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertTypesp = nullptr,
AstNodeExpr* directiveTypep = nullptr, AstNodeExpr* levelp = nullptr,
AstNodeExpr* itemsp = nullptr);
ASTGEN_MEMBERS_AstAssertCtl;
string verilogKwd() const override { return m_ctlType.ascii(); }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; }
bool isOutputter() override { return true; }
VAssertCtlType ctlType() const { return m_ctlType; }
void ctlType(int32_t type) { m_ctlType = VAssertCtlType{type}; }
VAssertType ctlAssertTypes() const { return m_assertTypes; }
void ctlAssertTypes(VAssertType types) { m_assertTypes = types; }
VAssertDirectiveType ctlDirectiveTypes() const { return m_directiveTypes; }
void ctlDirectiveTypes(VAssertDirectiveType types) { m_directiveTypes = types; }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
};
class AstBreak final : public AstNodeStmt {
public:
explicit AstBreak(FileLine* fl)
@@ -2657,10 +2722,10 @@ public:
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool same(const AstNode* /*samep*/) const override { return true; }
void isDisableSoft(bool flag) { m_isDisableSoft = flag; }
bool isDisableSoft() const { return m_isDisableSoft; }
void isSoft(bool flag) { m_isSoft = flag; }
void isDisableSoft(bool flag) { m_isDisableSoft = flag; }
bool isSoft() const { return m_isSoft; }
void isSoft(bool flag) { m_isSoft = flag; }
};
class AstConstraintUnique final : public AstNodeStmt {
// Constraint unique statement
@@ -2716,8 +2781,8 @@ public:
void dumpJson(std::ostream& str) const override;
int instrCount() const override { return 1 + 2 * INSTR_COUNT_LD; }
bool maybePointedTo() const override { return true; }
void binNum(int flag) { m_binNum = flag; }
int binNum() const { return m_binNum; }
void binNum(int flag) { m_binNum = flag; }
int offset() const { return m_offset; }
const string& comment() const { return m_text; } // text to insert in code
const string& linescov() const { return m_linescov; }
@@ -2755,7 +2820,7 @@ public:
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isOutputter() override { return true; }
// but isPure() true
bool isPure() override { return false; }
AstCoverDecl* declp() const { return m_declp; } // Where defined
};
class AstCoverToggle final : public AstNodeStmt {
@@ -2852,8 +2917,8 @@ public:
return nullptr;
}
string verilogKwd() const override {
return (filep() ? string{"$f"} + string{displayType().ascii()}
: string{"$"} + string{displayType().ascii()});
return (filep() ? "$f"s + string{displayType().ascii()}
: "$"s + string{displayType().ascii()});
}
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
@@ -2990,6 +3055,7 @@ class AstJumpBlock final : public AstNodeStmt {
//
// @astgen ptr := m_labelp : AstJumpLabel // [After V3Jump] Pointer to declaration
int m_labelNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment
VIsCached m_purity; // Pure state
public:
// After construction must call ->labelp to associate with appropriate label
AstJumpBlock(FileLine* fl, AstNode* stmtsp)
@@ -3005,6 +3071,10 @@ public:
void labelNum(int flag) { m_labelNum = flag; }
AstJumpLabel* labelp() const { return m_labelp; }
void labelp(AstJumpLabel* labelp) { m_labelp = labelp; }
bool isPure() override;
private:
bool getPurityRecurse() const;
};
class AstJumpGo final : public AstNodeStmt {
// Jump point; branch down to a JumpLabel
@@ -3196,6 +3266,7 @@ public:
this->exprp(exprp);
}
ASTGEN_MEMBERS_AstStmtExpr;
bool isPure() override { return exprp()->isPure(); }
};
class AstStop final : public AstNodeStmt {
public:
@@ -3348,7 +3419,7 @@ public:
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isOutputter() override { return true; }
// but isPure() true
bool isPure() override { return false; }
AstTraceDecl* declp() const { return m_declp; }
VTraceType traceType() const { return m_traceType; }
uint32_t baseCode() const { return m_baseCode; }
@@ -3430,8 +3501,8 @@ public:
// Stop statement searchback here
void addNextStmt(AstNode* newp, AstNode* belowp) override;
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
void unrollFull(const VOptionBool flag) { m_unrollFull = flag; }
VOptionBool unrollFull() const { return m_unrollFull; }
void unrollFull(const VOptionBool flag) { m_unrollFull = flag; }
};
// === AstNodeAssign ===
@@ -3588,11 +3659,12 @@ public:
// === AstNodeCoverOrAssert ===
class AstAssert final : public AstNodeCoverOrAssert {
// @astgen op3 := failsp: List[AstNode] // Statements when propp is failing/falsey
public:
ASTGEN_MEMBERS_AstAssert;
AstAssert(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp, bool immediate,
const string& name = "")
: ASTGEN_SUPER_Assert(fl, propp, passsp, immediate, name) {
AstAssert(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp, VAssertType type,
VAssertDirectiveType directive, const string& name = "")
: ASTGEN_SUPER_Assert(fl, propp, passsp, type, directive, name) {
this->addFailsp(failsp);
}
};
@@ -3602,8 +3674,10 @@ class AstAssertIntrinsic final : public AstNodeCoverOrAssert {
public:
ASTGEN_MEMBERS_AstAssertIntrinsic;
AstAssertIntrinsic(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp,
bool immediate, const string& name = "")
: ASTGEN_SUPER_AssertIntrinsic(fl, propp, passsp, immediate, name) {
const string& name = "")
// Intrinsic asserts are always enabled thus 'type' field is set to INTERNAL.
: ASTGEN_SUPER_AssertIntrinsic(fl, propp, passsp, VAssertType::INTERNAL,
VAssertDirectiveType::INTRINSIC, name) {
this->addFailsp(failsp);
}
};
@@ -3611,16 +3685,17 @@ class AstCover final : public AstNodeCoverOrAssert {
// @astgen op3 := coverincsp: List[AstNode] // Coverage node
public:
ASTGEN_MEMBERS_AstCover;
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, bool immediate,
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type,
const string& name = "")
: ASTGEN_SUPER_Cover(fl, propp, stmtsp, immediate, name) {}
virtual bool immediate() const { return false; }
: ASTGEN_SUPER_Cover(fl, propp, stmtsp, type, VAssertDirectiveType::COVER, name) {}
};
class AstRestrict final : public AstNodeCoverOrAssert {
public:
ASTGEN_MEMBERS_AstRestrict;
AstRestrict(FileLine* fl, AstNode* propp)
: ASTGEN_SUPER_Restrict(fl, propp, nullptr, false, "") {}
// Intrinsic asserts are always ignored thus 'type' field is set to INTERNAL.
: ASTGEN_SUPER_Restrict(fl, propp, nullptr, VAssertType::INTERNAL,
VAssertDirectiveType::RESTRICT) {}
};
// === AstNodeFor ===
@@ -3761,8 +3836,8 @@ public:
: ASTGEN_SUPER_TextBlock(fl, textp, tracking)
, m_commas(commas) {}
ASTGEN_MEMBERS_AstTextBlock;
void commas(bool flag) { m_commas = flag; }
bool commas() const { return m_commas; }
void commas(bool flag) { m_commas = flag; }
void addText(FileLine* fl, const string& textp, bool tracking = false) {
addNodesp(new AstText{fl, textp, tracking});
}
+142 -27
View File
@@ -322,6 +322,28 @@ AstNodeExpr* AstInsideRange::newAndFromInside(AstNodeExpr* exprp, AstNodeExpr* l
return new AstLogAnd{fileline(), ap, bp};
}
void AstConsDynArray::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (lhsIsValue()) str << " [LVAL]";
if (rhsIsValue()) str << " [RVAL]";
}
void AstConsDynArray::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, lhsIsValue);
dumpJsonBoolFunc(str, rhsIsValue);
dumpJsonGen(str);
}
void AstConsQueue::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
if (lhsIsValue()) str << " [LVAL]";
if (rhsIsValue()) str << " [RVAL]";
}
void AstConsQueue::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, lhsIsValue);
dumpJsonBoolFunc(str, rhsIsValue);
dumpJsonGen(str);
}
AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
bool success = false;
if (literal[0] == '"') {
@@ -352,6 +374,8 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
return nullptr;
}
string AstConstraintRef::name() const { return constrp()->name(); }
AstNetlist::AstNetlist()
: ASTGEN_SUPER_Netlist(new FileLine{FileLine::builtInFilename()})
, m_typeTablep{new AstTypeTable{fileline()}}
@@ -380,6 +404,9 @@ bool AstVar::isScBv() const {
bool AstVar::isScUint() const {
return ((isSc() && v3Global.opt.pinsScUint() && width() >= 2 && width() <= 64) && !isScBv());
}
bool AstVar::isScUintBool() const {
return (isSc() && v3Global.opt.pinsScUintBool() && width() == 1);
}
bool AstVar::isScBigUint() const {
return ((isSc() && v3Global.opt.pinsScBigUint() && width() >= 65 && width() <= 512)
&& !isScBv());
@@ -669,14 +696,13 @@ string AstVar::dpiTmpVarType(const string& varName) const {
string AstVar::scType() const {
if (isScBigUint()) {
return (string{"sc_dt::sc_biguint<"} + cvtToStr(widthMin())
return ("sc_dt::sc_biguint<"s + cvtToStr(widthMin())
+ "> "); // Keep the space so don't get >>
} else if (isScUint()) {
return (string{"sc_dt::sc_uint<"} + cvtToStr(widthMin())
} else if (isScUint() || isScUintBool()) {
return ("sc_dt::sc_uint<"s + cvtToStr(widthMin())
+ "> "); // Keep the space so don't get >>
} else if (isScBv()) {
return (string{"sc_dt::sc_bv<"} + cvtToStr(widthMin())
+ "> "); // Keep the space so don't get >>
return ("sc_dt::sc_bv<"s + cvtToStr(widthMin()) + "> "); // Keep the space so don't get >>
} else if (widthMin() == 1) {
return "bool";
} else if (widthMin() <= VL_IDATASIZE) {
@@ -788,6 +814,22 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
info.m_type = "VlUnpacked<" + sub.m_type;
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
info.m_type += ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, NBACommitQueueDType)) {
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
compound = true;
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound, false);
AstNodeDType* eDTypep = adtypep->subDTypep();
unsigned rank = 0;
while (AstUnpackArrayDType* const uaDTypep = VN_CAST(eDTypep, UnpackArrayDType)) {
eDTypep = uaDTypep->subDTypep()->skipRefp();
++rank;
}
info.m_type = "VlNBACommitQueue<";
info.m_type += sub.m_type;
info.m_type += adtypep->partial() ? ", true" : ", false";
info.m_type += ", " + eDTypep->cTypeRecurse(compound, false).m_type;
info.m_type += ", " + std::to_string(rank);
info.m_type += ">";
} else if (packed && (VN_IS(dtypep, PackArrayDType))) {
const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType);
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(false, true);
@@ -827,6 +869,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
info.m_type = "VlForkSync";
} else if (bdtypep->isProcessRef()) {
info.m_type = "VlProcessRef";
} else if (bdtypep->isRandomGenerator()) {
info.m_type = "VlRandomizer";
} else if (bdtypep->isEvent()) {
info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent";
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
@@ -885,9 +929,10 @@ std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) {
}
dtypep = adtypep->subDTypep();
continue;
} else if (const AstQueueDType* const qdtypep = VN_CAST(dtypep, QueueDType)) {
} else if (VN_IS(dtypep, QueueDType) || VN_IS(dtypep, DynArrayDType)
|| VN_IS(dtypep, AssocArrayDType) || VN_IS(dtypep, WildcardArrayDType)) {
unpacked++;
dtypep = qdtypep->subDTypep();
dtypep = dtypep->subDTypep();
continue;
} else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
if (includeBasic && (adtypep->isRanged() || adtypep->isString())) packed++;
@@ -959,6 +1004,16 @@ const char* AstJumpBlock::broken() const {
BROKEN_RTN(!labelp()->brokeExistsBelow());
return nullptr;
}
bool AstJumpBlock::isPure() {
if (!m_purity.isCached()) m_purity.set(getPurityRecurse());
return m_purity.get();
}
bool AstJumpBlock::getPurityRecurse() const {
for (AstNode* stmtp = this->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (!stmtp->isPure()) return false;
}
return true;
}
string AstScope::nameDotless() const {
string result = shortName();
@@ -1442,7 +1497,29 @@ void AstAlways::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "keyword", keyword().ascii());
dumpJsonGen(str);
}
AstAssertCtl::AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr*, AstNodeExpr*)
: ASTGEN_SUPER_AssertCtl(fl)
, m_ctlType{ctlType} {
controlTypep(new AstConst{fl, ctlType});
}
AstAssertCtl::AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertTypesp,
AstNodeExpr* directiveTypep, AstNodeExpr*, AstNodeExpr*)
: ASTGEN_SUPER_AssertCtl(fl)
, m_ctlType{VAssertCtlType::_TO_BE_EVALUATED}
, m_assertTypes{VAssertType::INTERNAL}
, m_directiveTypes{VAssertDirectiveType::INTERNAL} {
this->controlTypep(controlTypep);
this->assertTypesp(assertTypesp);
this->directiveTypesp(directiveTypep);
}
void AstAssertCtl::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " [" << ctlType().ascii() << "]";
}
void AstAssertCtl::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "ctlType", ctlType().ascii());
dumpJsonGen(str);
}
void AstAttrOf::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " [" << attrType().ascii() << "]";
@@ -1463,7 +1540,7 @@ void AstBasicDType::dumpJson(std::ostream& str) const {
}
dumpJsonGen(str);
}
string AstBasicDType::prettyDTypeName() const {
string AstBasicDType::prettyDTypeName(bool) const {
std::ostringstream os;
os << keyword().ascii();
if (isRanged() && !rangep() && keyword().width() <= 1) {
@@ -1588,13 +1665,14 @@ void AstClassRefDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "class:" << name();
}
string AstClassRefDType::prettyDTypeName(bool) const { return "class{}"s + prettyName(); }
string AstClassRefDType::name() const { return classp() ? classp()->name() : "<unlinked>"; }
void AstNodeCoverOrAssert::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (immediate()) str << " [IMMEDIATE]";
str << " ["s + this->type().ascii() + "]";
}
void AstNodeCoverOrAssert::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, immediate);
dumpJsonStr(str, "type", "["s + this->type().ascii() + "]");
dumpJsonGen(str);
}
void AstClocking::dump(std::ostream& str) const {
@@ -1624,6 +1702,22 @@ void AstEnumDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "enum";
}
string AstEnumDType::prettyDTypeName(bool full) const {
string result = "enum{";
if (full) { // else shorten for error messages
for (AstEnumItem* itemp = itemsp(); itemp; itemp = VN_AS(itemp->nextp(), EnumItem)) {
result += itemp->prettyName() + "=";
if (AstConst* constp = VN_CAST(itemp->valuep(), Const)) {
result += constp->num().ascii(true, true);
} else {
result += "?";
}
result += ";";
}
}
result += "}" + prettyName();
return result;
}
void AstEnumItemRef::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
str << " -> ";
@@ -1765,6 +1859,13 @@ AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
return VN_CAST(subdtp->skipRefp(), NodeUOrStructDType); // Maybe nullptr
}
AstMemberSel::AstMemberSel(FileLine* fl, AstNodeExpr* fromp, AstVar* varp)
: ASTGEN_SUPER_MemberSel(fl)
, m_name{varp->name()} {
this->fromp(fromp);
this->varp(varp);
dtypep(varp->dtypep());
}
bool AstMemberSel::same(const AstNode* samep) const {
const AstMemberSel* const sp = VN_DBG_AS(samep, MemberSel);
return sp != nullptr && access() == sp->access() && fromp()->isSame(sp->fromp())
@@ -1938,6 +2039,18 @@ void AstNodeUOrStructDType::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, isFourstate);
dumpJsonGen(str);
}
string AstNodeUOrStructDType::prettyDTypeName(bool full) const {
string result = verilogKwd() + "{";
if (full) { // else shorten for errors
for (AstMemberDType* itemp = membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
result += itemp->subDTypep()->prettyDTypeName(full);
result += " " + itemp->prettyName() + ";";
}
}
result += "}" + prettyName();
return result;
}
void AstNodeDType::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (generic()) str << " [GENERIC]";
@@ -1977,13 +2090,13 @@ void AstNodeArrayDType::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "declRange", cvtToStr(declRange()));
dumpJsonGen(str);
}
string AstPackArrayDType::prettyDTypeName() const {
string AstPackArrayDType::prettyDTypeName(bool full) const {
std::ostringstream os;
if (const auto subp = subDTypep()) os << subp->prettyDTypeName();
if (const auto subp = subDTypep()) os << subp->prettyDTypeName(full);
os << declRange();
return os.str();
}
string AstUnpackArrayDType::prettyDTypeName() const {
string AstUnpackArrayDType::prettyDTypeName(bool full) const {
std::ostringstream os;
string ranges = cvtToStr(declRange());
// Unfortunately we need a single $ for the first unpacked, and all
@@ -1993,7 +2106,7 @@ string AstUnpackArrayDType::prettyDTypeName() const {
ranges += cvtToStr(adtypep->declRange());
subp = adtypep->subDTypep()->skipRefp();
}
os << subp->prettyDTypeName() << "$" << ranges;
os << subp->prettyDTypeName(full) << "$" << ranges;
return os.str();
}
std::vector<AstUnpackArrayDType*> AstUnpackArrayDType::unpackDimensions() {
@@ -2137,20 +2250,22 @@ void AstAssocArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[assoc-" << nodeAddr(keyDTypep()) << "]";
}
string AstAssocArrayDType::prettyDTypeName() const {
return subDTypep()->prettyDTypeName() + "[" + keyDTypep()->prettyDTypeName() + "]";
string AstAssocArrayDType::prettyDTypeName(bool full) const {
return subDTypep()->prettyDTypeName(full) + "$[" + keyDTypep()->prettyDTypeName(full) + "]";
}
void AstDynArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[]";
str << "$[]";
}
string AstDynArrayDType::prettyDTypeName(bool full) const {
return subDTypep()->prettyDTypeName(full) + "$[]";
}
string AstDynArrayDType::prettyDTypeName() const { return subDTypep()->prettyDTypeName() + "[]"; }
void AstQueueDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[queue]";
}
string AstQueueDType::prettyDTypeName() const {
string str = subDTypep()->prettyDTypeName() + "[$";
string AstQueueDType::prettyDTypeName(bool full) const {
string str = subDTypep()->prettyDTypeName(full) + "$[$";
if (boundConst()) str += ":" + cvtToStr(boundConst());
return str + "]";
}
@@ -2282,11 +2397,7 @@ void AstVar::dump(std::ostream& str) const {
if (isInternal()) str << " [INTERNAL]";
if (isLatched()) str << " [LATCHED]";
if (isUsedLoopIdx()) str << " [LOOP]";
if (isRandC()) {
str << " [RANDC]";
} else if (isRand()) {
str << " [RAND]";
}
if (rand().isRandomizable()) str << rand();
if (noReset()) str << " [!RST]";
if (attrIsolateAssign()) str << " [aISO]";
if (attrFileDescr()) str << " [aFD]";
@@ -2683,6 +2794,7 @@ void AstCMethodHard::setPurity() {
{"commit", false},
{"delay", false},
{"done", false},
{"enqueue", false},
{"erase", false},
{"evaluate", false},
{"evaluation", false},
@@ -2695,6 +2807,7 @@ void AstCMethodHard::setPurity() {
{"find_last_index", true},
{"fire", false},
{"first", false},
{"hard", false},
{"init", false},
{"insert", false},
{"inside", true},
@@ -2724,6 +2837,7 @@ void AstCMethodHard::setPurity() {
{"reverse", false},
{"rsort", false},
{"set", false},
{"set_randmode", false},
{"shuffle", false},
{"size", true},
{"slice", true},
@@ -2734,7 +2848,8 @@ void AstCMethodHard::setPurity() {
{"trigger", false},
{"unique", true},
{"unique_index", true},
{"word", true}};
{"word", true},
{"write_var", false}};
auto isPureIt = isPureMethod.find(name());
UASSERT_OBJ(isPureIt != isPureMethod.end(), this, "Unknown purity of method " + name());
+151 -1
View File
@@ -134,6 +134,9 @@ class BeginVisitor final : public VNVisitor {
dotNames(nodep, "__FORK__");
nodep->name("");
}
void visit(AstForeach* nodep) override {
VL_DO_DANGLING(V3Begin::convertToWhile(nodep), nodep);
}
void visit(AstNodeAssign* nodep) override {
// Keep begin under assignment (in nodep->timingControlp())
VL_RESTORER(m_keepBegins);
@@ -274,7 +277,7 @@ class BeginVisitor final : public VNVisitor {
// To keep correct visual order, must add before other Text's
AstText* const afterp = nodep->scopeAttrp();
if (afterp) afterp->unlinkFrBackWithNext();
nodep->addScopeAttrp(new AstText{nodep->fileline(), string{"__DOT__"} + scname});
nodep->addScopeAttrp(new AstText{nodep->fileline(), "__DOT__"s + scname});
if (afterp) nodep->addScopeAttrp(afterp);
}
iterateChildren(nodep);
@@ -327,6 +330,7 @@ private:
// VISITORS
void visit(AstNodeFTaskRef* nodep) override {
UASSERT_OBJ(nodep->taskp(), nodep, "unlinked");
if (nodep->taskp()->user1()) { // It was converted
UINFO(9, " relinkFTask " << nodep << endl);
nodep->name(nodep->taskp()->name());
@@ -368,3 +372,149 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
} // Destruct before checking
V3Global::dumpCheckGlobalTree("begin", 0, dumpTreeEitherLevel() >= 3);
}
static AstNode* createForeachLoop(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
AstNodeExpr* leftp, AstNodeExpr* rightp, VNType nodeType) {
FileLine* const fl = varp->fileline();
AstNodeExpr* varRefp = new AstVarRef{fl, varp, VAccess::READ};
AstNodeExpr* condp;
bool inc = true;
switch (nodeType) {
case VNType::atLteS: condp = new AstLteS{fl, varRefp, rightp}; break;
case VNType::atLt: condp = new AstLt{fl, varRefp, rightp}; break;
case VNType::atGteS:
condp = new AstGteS{fl, varRefp, rightp};
inc = false;
break;
default: UASSERT_OBJ(0, varp, "Missing comparison handling"); break;
}
AstNodeExpr* incp;
if (inc)
incp = new AstAdd{fl, varRefp->cloneTree(false), new AstConst{fl, 1}};
else
incp = new AstSub{fl, varRefp->cloneTree(false), new AstConst{fl, 1}};
AstWhile* const whilep = new AstWhile{
fl, condp, bodysp, new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, incp}};
AstNode* const stmtsp = varp; // New statements for outer loop
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, leftp});
stmtsp->addNext(whilep);
return stmtsp;
}
static AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
const VNumRange& declRange) {
FileLine* const fl = varp->fileline();
V3Number left{nodep, 32}, right{nodep, 32};
left.isSigned(true);
right.isSigned(true);
left.setLongS(declRange.left());
right.setLongS(declRange.right());
AstNodeExpr* const leftp = new AstConst{fl, left};
AstNodeExpr* const rightp = new AstConst{fl, right};
return createForeachLoop(nodep, bodysp, varp, leftp, rightp,
declRange.left() <= declRange.right() ? VNType::atLteS
: VNType::atGteS);
}
AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
// if (debug()) dumpTree(cout, "- foreach-old: ");
const AstSelLoopVars* const loopsp = VN_CAST(nodep->arrayp(), SelLoopVars);
UASSERT_OBJ(loopsp, nodep, "No loop variables under foreach");
AstNodeExpr* const fromp = loopsp->fromp();
UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type");
AstNodeDType* fromDtp = fromp->dtypep()->skipRefp();
// Split into for loop
// We record where the body needs to eventually go with bodyPointp
AstNode* bodyPointp = new AstBegin{nodep->fileline(), "[EditWrapper]", nullptr};
AstNode* newp = nullptr;
AstNode* lastp = nodep;
// Major dimension first
for (AstNode *argsp = loopsp->elementsp(), *next_argsp; argsp; argsp = next_argsp) {
next_argsp = argsp->nextp();
const bool empty = VN_IS(argsp, Empty);
AstVar* const varp = VN_CAST(argsp, Var);
UASSERT_OBJ(varp || empty, argsp, "Missing foreach loop variable");
if (varp) varp->unlinkFrBack()->usedLoopIdx(true);
UASSERT_OBJ(fromDtp, argsp, "more loop vars than dimensions");
fromDtp = fromDtp->skipRefp();
FileLine* const fl = argsp->fileline();
if (varp) {
AstNode* loopp = nullptr;
VNRelinker handle;
lastp->unlinkFrBack(&handle);
if (const AstNodeArrayDType* const adtypep = VN_CAST(fromDtp, NodeArrayDType)) {
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
} else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) {
if (adtypep->isString()) {
AstConst* const leftp = new AstConst{fl, 0};
AstNodeExpr* const rightp = new AstLenN{fl, fromp->cloneTreePure(false)};
loopp
= createForeachLoop(nodep, bodyPointp, varp, leftp, rightp, VNType::atLt);
} else {
UASSERT_OBJ(adtypep->isRanged(), varp, "foreach on basic " << adtypep);
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
}
} else if (VN_IS(fromDtp, DynArrayDType) || VN_IS(fromDtp, QueueDType)) {
AstConst* const leftp = new AstConst{fl, 0};
AstNodeExpr* const rightp
= new AstCMethodHard{fl, fromp->cloneTreePure(false), "size"};
rightp->dtypeSetSigned32();
rightp->protect(false);
loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, rightp, VNType::atLt);
} else if (VN_IS(fromDtp, AssocArrayDType)) {
// Make this: var KEY_TYPE index;
// bit index__Vfirst;
// index__Vfirst = 0;
// if (0 != array.first(index))
// do body while (index__Vfirst || 0 != array.next(index))
AstVar* const first_varp = new AstVar{
fl, VVarType::BLOCKTEMP, varp->name() + "__Vfirst", VFlagBitPacked{}, 1};
first_varp->usedLoopIdx(true);
first_varp->lifetime(VLifetime::AUTOMATIC);
AstNodeExpr* const firstp
= new AstCMethodHard{fl, fromp->cloneTreePure(false), "first",
new AstVarRef{fl, varp, VAccess::READWRITE}};
firstp->dtypeSetSigned32();
AstNodeExpr* const nextp
= new AstCMethodHard{fl, fromp->cloneTreePure(false), "next",
new AstVarRef{fl, varp, VAccess::READWRITE}};
nextp->dtypeSetSigned32();
AstNode* const first_clearp
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
new AstConst{fl, AstConst::BitFalse{}}};
auto* const orp = new AstLogOr{fl, new AstVarRef{fl, first_varp, VAccess::READ},
new AstNeq{fl, new AstConst{fl, 0}, nextp}};
AstNode* const whilep = new AstWhile{fl, orp, first_clearp};
first_clearp->addNext(bodyPointp);
AstNode* const ifbodyp
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
new AstConst{fl, AstConst::BitTrue{}}};
ifbodyp->addNext(whilep);
loopp = varp;
loopp->addNext(first_varp);
loopp->addNext(
new AstIf{fl, new AstNeq{fl, new AstConst{fl, 0}, firstp}, ifbodyp});
}
UASSERT_OBJ(loopp, argsp, "unable to foreach " << fromDtp);
// New loop goes UNDER previous loop
handle.relink(loopp);
lastp = bodyPointp;
if (!newp) newp = loopp;
}
// Prep for next
fromDtp = fromDtp->subDTypep();
}
// The parser validates we don't have "foreach (array[,,,])"
AstNode* const bodyp = nodep->stmtsp();
UASSERT_OBJ(newp, nodep, "foreach has no non-empty loop variable");
if (bodyp) {
bodyPointp->replaceWith(bodyp->unlinkFrBackWithNext());
} else {
bodyPointp->unlinkFrBack();
}
VL_DO_DANGLING(bodyPointp->deleteTree(), bodyPointp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
// if (debug()) newp->dumpTreeAndNext(cout, "- foreach-new: ");
return newp;
}
+3
View File
@@ -23,12 +23,15 @@
#include "V3ThreadSafety.h"
class AstNetlist;
class AstNode;
class AstForeach;
//============================================================================
class V3Begin final {
public:
static void debeginAll(AstNetlist* nodep) VL_MT_DISABLED;
static AstNode* convertToWhile(AstForeach* nodep) VL_MT_DISABLED;
};
#endif // Guard
+9 -50
View File
@@ -68,14 +68,10 @@ public:
class ClockVisitor final : public VNVisitor {
// NODE STATE
// ???
const VNUser1InUse m_user1InUse;
// STATE
AstCFunc* m_evalp = nullptr; // The '_eval' function
AstScope* m_scopep = nullptr; // Current scope
AstSenTree* m_lastSenp = nullptr; // Last sensitivity match, so we can detect duplicates.
AstIf* m_lastIfp = nullptr; // Last sensitivity if active to add more under
bool m_inSampled = false; // True inside a sampled expression
// METHODS
@@ -88,25 +84,6 @@ class ClockVisitor final : public VNVisitor {
}
return senEqnp;
}
AstVarScope* createSampledVar(AstVarScope* vscp) {
if (vscp->user1p()) return VN_AS(vscp->user1p(), VarScope);
const AstVar* const varp = vscp->varp();
const string newvarname
= string{"__Vsampled__"} + vscp->scopep()->nameDotless() + "__" + varp->name();
FileLine* const flp = vscp->fileline();
AstVar* const newvarp = new AstVar{flp, VVarType::MODULETEMP, newvarname, varp->dtypep()};
newvarp->noReset(true); // Reset by below assign
m_scopep->modp()->addStmtsp(newvarp);
AstVarScope* const newvscp = new AstVarScope{flp, m_scopep, newvarp};
vscp->user1p(newvscp);
m_scopep->addVarsp(newvscp);
// At the top of _eval, assign them
AstAssign* const finalp = new AstAssign{flp, new AstVarRef{flp, newvscp, VAccess::WRITE},
new AstVarRef{flp, vscp, VAccess::READ}};
m_evalp->addInitsp(finalp);
UINFO(4, "New Sampled: " << newvscp << endl);
return newvscp;
}
AstIf* makeActiveIf(AstSenTree* sensesp) {
AstNodeExpr* const senEqnp = createSenseEquation(sensesp->sensesp());
UASSERT_OBJ(senEqnp, sensesp, "No sense equation, shouldn't be in sequent activation.");
@@ -180,33 +157,15 @@ class ClockVisitor final : public VNVisitor {
clearLastSen();
}
//========== Create sampled values
void visit(AstScope* nodep) override {
VL_RESTORER(m_scopep);
{
m_scopep = nodep;
iterateChildren(nodep);
}
}
void visit(AstSampled* nodep) override {
VL_RESTORER(m_inSampled);
{
m_inSampled = true;
iterateChildren(nodep);
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
}
void visit(AstVarRef* nodep) override {
iterateChildren(nodep);
if (m_inSampled && !nodep->user1SetOnce()) {
UASSERT_OBJ(nodep->access().isReadOnly(), nodep, "Should have failed in V3Access");
AstVarScope* const varscp = nodep->varScopep();
AstVarScope* const lastscp = createSampledVar(varscp);
AstNode* const newp = new AstVarRef{nodep->fileline(), lastscp, VAccess::READ};
newp->user1SetOnce(); // Don't sample this one
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
//========== Move sampled assignments
void visit(AstVarScope* nodep) override {
AstVar* varp = nodep->varp();
if (varp->valuep() && varp->name().substr(0, strlen("__Vsampled")) == "__Vsampled") {
m_evalp->addInitsp(new AstAssign{
nodep->fileline(), new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE},
VN_AS(varp->valuep()->unlinkFrBack(), NodeExpr)});
varp->direction(VDirection::NONE); // Restore defaults
varp->primaryIO(false);
}
}
+3 -1
View File
@@ -113,7 +113,9 @@ static void makeToStringMiddle(AstClass* nodep) {
std::string comma;
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
if (const auto* const varp = VN_CAST(itemp, Var)) {
if (!varp->isParam() && !varp->isInternal()) {
if (!varp->isParam() && !varp->isInternal()
&& !(varp->dtypeSkipRefp()->basicp()
&& varp->dtypeSkipRefp()->basicp()->isRandomGenerator())) {
string stmt = "out += \"";
stmt += comma;
comma = ", ";
+29 -29
View File
@@ -20,6 +20,7 @@
#include "V3String.h"
#include <memory>
#include <set>
#include <unordered_map>
@@ -34,11 +35,12 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// function that takes a reference of this type to join multiple entities into one.
template <typename T>
class V3ConfigWildcardResolver final {
using Map = std::map<const std::string, T>;
mutable V3Mutex m_mutex; // protects members
Map m_mapWildcard VL_GUARDED_BY(m_mutex); // Wildcard strings to entities
Map m_mapResolved VL_GUARDED_BY(m_mutex); // Resolved strings to converged entities
// Pattern strings (wildcard, or simple name) to entities
std::map<const std::string, T> m_mapPatterns VL_GUARDED_BY(m_mutex);
// Resolved strings to converged entities - nullptr, iff none of the patterns applies
std::map<const std::string, std::unique_ptr<T>> m_mapResolved VL_GUARDED_BY(m_mutex);
public:
V3ConfigWildcardResolver() = default;
~V3ConfigWildcardResolver() = default;
@@ -48,41 +50,36 @@ public:
VL_EXCLUDES(other.m_mutex) {
V3LockGuard lock{m_mutex};
V3LockGuard otherLock{other.m_mutex};
for (const auto& itr : other.m_mapResolved) m_mapResolved[itr.first].update(itr.second);
for (const auto& itr : other.m_mapWildcard) m_mapWildcard[itr.first].update(itr.second);
// Clear the resolved cache, as 'other' might add new patterns that need to be applied as
// well.
m_mapResolved.clear();
for (const auto& itr : other.m_mapPatterns) m_mapPatterns[itr.first].update(itr.second);
}
// Access and create a (wildcard) entity
// Access and create a pattern entry
T& at(const string& name) VL_MT_SAFE_EXCLUDES(m_mutex) {
V3LockGuard lock{m_mutex};
// Don't store into wildcards if the name is not a wildcard string
return m_mapWildcard[name];
// We might be adding a new entry under this, so clear the cache.
m_mapResolved.clear();
return m_mapPatterns[name];
}
// Access an entity and resolve wildcards that match it
// Access an entity and resolve patterns that match it
T* resolve(const string& name) VL_MT_SAFE_EXCLUDES(m_mutex) {
V3LockGuard lock{m_mutex};
// Lookup if it was resolved before, typically not
auto it = m_mapResolved.find(name);
if (VL_UNLIKELY(it != m_mapResolved.end())) return &it->second;
T* newp = nullptr;
// Cannot be resolved, create if matched
// Update this entity with all matches in the wildcards
for (const auto& wildent : m_mapWildcard) {
if (VString::wildmatch(name, wildent.first)) {
if (!newp) {
newp = &m_mapResolved[name]; // Emplace and get pointer
const auto pair = m_mapResolved.emplace(name, nullptr);
std::unique_ptr<T>& entryr = pair.first->second;
// Resolve entry when first requested, cache the result
if (pair.second) {
// Update the entity with all matches in the patterns
for (const auto& patEnt : m_mapPatterns) {
if (VString::wildmatch(name, patEnt.first)) {
if (!entryr) entryr.reset(new T{});
entryr->update(patEnt.second);
}
newp->update(wildent.second);
}
}
return newp;
}
// Flush on update
void flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
V3LockGuard lock{m_mutex};
m_mapResolved.clear();
return entryr.get();
}
};
@@ -203,6 +200,9 @@ public:
modp->addStmtsp(nodep);
}
for (const auto& itr : m_modPragmas) {
// Catch hier param modules to mark their attributes before they are
// flagged dead in LinkDot.
if (itr == VPragmaType::HIER_PARAMS) modp->hierParams(true);
AstNode* const nodep = new AstPragma{modp->fileline(), itr};
modp->addStmtsp(nodep);
}
@@ -336,6 +336,7 @@ public:
}
}
bool waive(V3ErrorCode code, const string& match) {
if (code.hardError()) return false;
for (const auto& itr : m_waivers) {
if ((code.isUnder(itr.first) || (itr.first == V3ErrorCode::I_LINT))
&& VString::wildmatch(match, itr.second)) {
@@ -508,7 +509,6 @@ void V3Config::addIgnore(V3ErrorCode code, bool on, const string& filename, int
} else {
V3ConfigResolver::s().files().at(filename).addIgnore(code, min, on);
if (max) V3ConfigResolver::s().files().at(filename).addIgnore(code, max, !on);
V3ConfigResolver::s().files().flush();
}
}
+30 -43
View File
@@ -125,13 +125,13 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
void updateBitRange(const AstShiftR* shiftp) {
m_lsb += VN_AS(shiftp->rhsp(), Const)->toUInt();
}
int wordIdx() const { return m_wordIdx; }
void wordIdx(int i) { m_wordIdx = i; }
bool polarity() const { return m_polarity; }
void polarity(bool p) { m_polarity = p; }
AstVarRef* refp() const { return m_refp; }
const AstConst* constp() const { return m_constp; }
int wordIdx() const { return m_wordIdx; }
bool polarity() const { return m_polarity; }
bool missingWordSel() const {
// When V3Expand is skipped, WordSel is not inserted.
return m_refp->isWide() && m_wordIdx == -1;
@@ -853,7 +853,8 @@ public:
// of the masking node e.g. by the "AND with all ones" rule. If the result width happens
// to be 1, we still need to ensure the AstAnd is not dropped, so use a wider mask in this
// special case.
const int maskWidth = resultWidth == 1 ? VL_IDATASIZE : resultWidth;
const int maskWidth
= std::max(resultp->width(), resultWidth == 1 ? VL_IDATASIZE : resultWidth);
// Apply final polarity flip
if (needsFlip) {
@@ -1556,7 +1557,9 @@ class ConstVisitor final : public VNVisitor {
}
return false;
}
bool operandsSameSize(AstNode* lhsp, AstNode* rhsp) { return lhsp->width() == rhsp->width(); }
static bool operandsSameWidth(const AstNode* lhsp, const AstNode* rhsp) {
return lhsp->width() == rhsp->width();
}
//----------------------------------------
// Constant Replacement functions.
@@ -2168,26 +2171,17 @@ class ConstVisitor final : public VNVisitor {
AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack();
AstNodeDType* const srcDTypep = srcp->dtypep();
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
if (nodep->lhsp()->widthMin() > 64) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
"array to a variable of size greater than 64");
}
srcp = new AstCvtDynArrayToPacked{srcp->fileline(), srcp, srcDTypep};
srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, nodep->dtypep()};
} else if (VN_IS(srcDTypep, UnpackArrayDType)) {
if (nodep->lhsp()->widthMin() > 64) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
"array to a variable of size greater than 64");
}
srcp = new AstCvtUnpackArrayToPacked{srcp->fileline(), srcp,
nodep->lhsp()->dtypep()};
srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, srcDTypep};
// Handling the case where lhs is wider than rhs by inserting zeros. StreamL does
// not require this, since the left streaming operator implicitly handles this.
const uint32_t packedBits = nodep->lhsp()->widthMin();
const uint32_t unpackBits
const int packedBits = nodep->lhsp()->widthMin();
const int unpackBits
= srcDTypep->arrayUnpackedElements() * srcDTypep->subDTypep()->widthMin();
const uint32_t offset = packedBits > unpackBits ? packedBits - unpackBits : 0;
srcp = new AstShiftL{srcp->fileline(), srcp,
new AstConst{srcp->fileline(), offset}, 64};
new AstConst{srcp->fileline(), offset}, packedBits};
}
nodep->rhsp(srcp);
VL_DO_DANGLING(pushDeletep(streamp), streamp);
@@ -2209,19 +2203,18 @@ class ConstVisitor final : public VNVisitor {
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
}
if (VN_IS(dstp->dtypep(), UnpackArrayDType)) {
streamp
= new AstCvtPackedToUnpackArray{nodep->fileline(), streamp, dstp->dtypep()};
streamp = new AstCvtPackedToArray{nodep->fileline(), streamp, dstp->dtypep()};
} else {
UASSERT(sWidth >= dWidth, "sWidth >= dWidth should have caused an error earlier");
if (dWidth == 0) {
streamp
= new AstCvtPackedToDynArray{nodep->fileline(), streamp, dstp->dtypep()};
streamp = new AstCvtPackedToArray{nodep->fileline(), streamp, dstp->dtypep()};
} else if (sWidth >= dWidth) {
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
}
}
nodep->lhsp(dstp);
nodep->rhsp(streamp);
nodep->dtypep(dstp->dtypep());
return true;
} else if (m_doV && VN_IS(nodep->lhsp(), StreamR)) {
// The right stream operator on lhs of assignment statement does
@@ -2242,17 +2235,18 @@ class ConstVisitor final : public VNVisitor {
srcp
= new AstSel{streamp->fileline(), srcp, sWidth - dstBitWidth, dstBitWidth};
}
srcp = new AstCvtPackedToUnpackArray{nodep->fileline(), srcp, dstp->dtypep()};
srcp = new AstCvtPackedToArray{nodep->fileline(), srcp, dstp->dtypep()};
} else {
UASSERT(sWidth >= dWidth, "sWidth >= dWidth should have caused an error earlier");
if (dWidth == 0) {
srcp = new AstCvtPackedToDynArray{nodep->fileline(), srcp, dstp->dtypep()};
srcp = new AstCvtPackedToArray{nodep->fileline(), srcp, dstp->dtypep()};
} else if (sWidth >= dWidth) {
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
}
}
nodep->lhsp(dstp);
nodep->rhsp(srcp);
nodep->dtypep(dstp->dtypep());
VL_DO_DANGLING(pushDeletep(streamp), streamp);
// Further reduce, any of the nodes may have more reductions.
return true;
@@ -2261,21 +2255,10 @@ class ConstVisitor final : public VNVisitor {
AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL);
AstNodeExpr* const srcp = streamp->lhsp();
const AstNodeDType* const srcDTypep = srcp->dtypep();
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
if (lhsDtypep->widthMin() > 64) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of dynamic "
"array to a variable of size greater than 64");
}
srcp->unlinkFrBack();
streamp->lhsp(new AstCvtDynArrayToPacked{srcp->fileline(), srcp, lhsDtypep});
streamp->dtypeFrom(lhsDtypep);
} else if (VN_IS(srcDTypep, UnpackArrayDType)) {
if (lhsDtypep->widthMin() > 64) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assignment of stream of unpacked "
"array to a variable of size greater than 64");
}
srcp->unlinkFrBack();
streamp->lhsp(new AstCvtUnpackArrayToPacked{srcp->fileline(), srcp, lhsDtypep});
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)
|| VN_IS(srcDTypep, UnpackArrayDType)) {
streamp->lhsp(new AstCvtArrayToPacked{srcp->fileline(), srcp->unlinkFrBack(),
nodep->dtypep()});
streamp->dtypeFrom(lhsDtypep);
}
} else if (m_doV && replaceAssignMultiSel(nodep)) {
@@ -2847,6 +2830,8 @@ class ConstVisitor final : public VNVisitor {
void visit(AstSenItem* nodep) override {
iterateChildren(nodep);
if (m_doNConst
&& !v3Global.opt.timing().isSetTrue() // If --timing, V3Sched would turn this into an
// infinite loop. See #5080
&& (VN_IS(nodep->sensp(), Const) || VN_IS(nodep->sensp(), EnumItemRef)
|| (nodep->varrefp() && nodep->varrefp()->varp()->isParam()))) {
// Constants in sensitivity lists may be removed (we'll simplify later)
@@ -3071,8 +3056,9 @@ class ConstVisitor final : public VNVisitor {
&& varrefp // Don't do messes with BITREFs/ARRAYREFs
&& !varrefp->varp()->hasStrengthAssignment() // Strengths are resolved in V3Tristate
&& !varrefp->varp()->valuep() // Not already constified
&& !varrefp->varScopep()) { // Not scoped (or each scope may have different initial
// value)
&& !varrefp->varScopep() // Not scoped (or each scope may have different initial val.)
&& !varrefp->varp()->isForced() // Not forced (not really a constant)
) {
// ASSIGNW (VARREF, const) -> INITIAL ( ASSIGN (VARREF, const) )
UINFO(4, "constAssignW " << nodep << endl);
// Make a initial assignment
@@ -3542,7 +3528,7 @@ class ConstVisitor final : public VNVisitor {
TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
TREEOP ("AstDiv {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
TREEOP ("AstDivS {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
TREEOP ("AstMul {operandIsPowTwo($lhsp), operandsSameSize($lhsp,,$rhsp)}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
TREEOP ("AstMul {operandIsPowTwo($lhsp), operandsSameWidth($lhsp,,$rhsp)}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
TREEOP ("AstDiv {$lhsp, operandIsPowTwo($rhsp)}", "replaceDivShift(nodep)"); // a/2^n -> a>>n
TREEOP ("AstModDiv{$lhsp, operandIsPowTwo($rhsp)}", "replaceModAnd(nodep)"); // a % 2^n -> a&(2^n-1)
TREEOP ("AstPow {operandIsTwo($lhsp), !$rhsp.isZero}", "replacePowShift(nodep)"); // 2**a == 1<<a
@@ -3736,7 +3722,8 @@ class ConstVisitor final : public VNVisitor {
TREEOPC("AstXor {matchBitOpTree(nodep)}", "DONE");
// Note can't simplify a extend{extends}, extends{extend}, as the sign
// bits end up in the wrong places
TREEOPV("AstExtend {$lhsp.castExtend}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), Extend)->lhsp())");
TREEOPV("AstExtend{operandsSameWidth(nodep,,$lhsp)}", "replaceWLhs(nodep)");
TREEOPV("AstExtend{$lhsp.castExtend}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), Extend)->lhsp())");
TREEOPV("AstExtendS{$lhsp.castExtendS}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), ExtendS)->lhsp())");
TREEOPV("AstReplicate{$srcp, $countp.isOne, $srcp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
TREEOPV("AstReplicateN{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
+1 -1
View File
@@ -311,7 +311,7 @@ class DeadVisitor final : public VNVisitor {
// still get deleted.
}
void visit(AstNode* nodep) override {
if (nodep->isOutputter()) m_sideEffect = true;
if (!m_sideEffect && !nodep->isPure()) m_sideEffect = true;
iterateChildren(nodep);
checkAll(nodep);
}
+673 -435
View File
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -193,7 +193,7 @@ class DescopeVisitor final : public VNVisitor {
// to come up with some return value
// newfuncp->addStmtsp(new AstDisplay(newfuncp->fileline(),
// VDisplayType::DT_WARNING,
// string{"%%Error: "}+name+"() called with bad
// "%%Error: "s+name+"() called with bad
// scope", nullptr));
// newfuncp->addStmtsp(new AstStop(newfuncp->fileline()));
if (debug() >= 9) newfuncp->dumpTree("- newfunc: ");
@@ -223,8 +223,7 @@ class DescopeVisitor final : public VNVisitor {
}
void visit(AstVarScope* nodep) override {
// Delete the varscope when we're finished
nodep->unlinkFrBack();
pushDeletep(nodep);
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstNodeVarRef* nodep) override {
iterateChildren(nodep);
+8 -9
View File
@@ -189,19 +189,19 @@ void DfgGraph::dumpDot(std::ostream& os, const string& label) const {
os << "}\n";
}
void DfgGraph::dumpDotFile(const string& fileName, const string& label) const {
void DfgGraph::dumpDotFile(const string& filename, const string& label) const {
// This generates a file used by graphviz, https://www.graphviz.org
// "hardcoded" parameters:
const std::unique_ptr<std::ofstream> os{V3File::new_ofstream(fileName)};
if (os->fail()) v3fatal("Cannot write to file: " << fileName);
const std::unique_ptr<std::ofstream> os{V3File::new_ofstream(filename)};
if (os->fail()) v3fatal("Cannot write to file: " << filename);
dumpDot(*os.get(), label);
os->close();
}
void DfgGraph::dumpDotFilePrefixed(const string& label) const {
string fileName = name();
if (!label.empty()) fileName += "-" + label;
dumpDotFile(v3Global.debugFilename(fileName) + ".dot", label);
string filename = name();
if (!label.empty()) filename += "-" + label;
dumpDotFile(v3Global.debugFilename(filename) + ".dot", label);
}
// Dump upstream logic cone starting from given vertex
@@ -360,8 +360,8 @@ bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
const auto key = (this < &that) ? EqualsCache::key_type{this, &that} //
: EqualsCache::key_type{&that, this};
// Note: the recursive invocation can cause a re-hash of the cache which invalidates iterators
uint8_t result = cache[key];
// Note: the recursive invocation can cause a re-hash but that will not invalidate references
uint8_t& result = cache[key];
if (!result) {
result = 2; // Assume equals
auto thisPair = this->sourceEdges();
@@ -380,7 +380,6 @@ bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
break;
}
}
cache[key] = result;
}
return result >> 1;
}
+4 -4
View File
@@ -705,15 +705,15 @@ public:
// Dump graph in Graphviz format into the given stream 'os'. 'label' is added to the name of
// the graph which is included in the output.
void dumpDot(std::ostream& os, const string& label = "") const VL_MT_DISABLED;
// Dump graph in Graphviz format into a new file with the given 'fileName'. 'label' is added to
// Dump graph in Graphviz format into a new file with the given 'filename'. 'label' is added to
// the name of the graph which is included in the output.
void dumpDotFile(const string& fileName, const string& label = "") const VL_MT_DISABLED;
void dumpDotFile(const string& filename, const string& label = "") const VL_MT_DISABLED;
// Dump graph in Graphviz format into a new automatically numbered debug file. 'label' is
// added to the name of the graph, which is included in the file name and the output.
void dumpDotFilePrefixed(const string& label = "") const VL_MT_DISABLED;
// Dump upstream (source) logic cone starting from given vertex into a file with the given
// 'fileName'. 'name' is the name of the graph, which is included in the output.
void dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx,
// 'filename'. 'name' is the name of the graph, which is included in the output.
void dumpDotUpstreamCone(const string& filename, const DfgVertex& vtx,
const string& name = "") const VL_MT_DISABLED;
// Dump all individual logic cones driving external variables in Graphviz format into separate
// new automatically numbered debug files. 'label' is added to the name of the graph, which is
+1 -1
View File
@@ -443,7 +443,7 @@ class AstToDfgVisitor final : public VNVisitor {
// Mark variables with external references
if (nodep->isIO() // Ports
|| nodep->user2() // Target of a hierarchical reference
|| nodep->isForceable() // Forceable
|| nodep->isForced() // Forced
) {
getNet(nodep)->setHasExtRefs();
}
+5 -1
View File
@@ -64,6 +64,7 @@ public:
struct Hash final {
size_t operator()(const KeySel& key) const {
// cppcheck-suppress unreadVariable // cppcheck bug
V3Hash hash{vertexHash(key.m_fromp)};
hash += key.m_lsb;
hash += key.m_width;
@@ -83,6 +84,7 @@ class KeyUnary final {
const DfgVertex* const m_source0p;
public:
// cppcheck-suppress noExplicitConstructor
KeyUnary(DfgVertex* source0p)
: m_source0p{source0p} {}
@@ -110,6 +112,7 @@ public:
struct Hash final {
size_t operator()(const KeyBinary& key) const {
// cppcheck-suppress unreadVariable // cppcheck bug
V3Hash hash{vertexHash(key.m_source0p)};
hash += vertexHash(key.m_source1p);
return hash.value();
@@ -137,6 +140,7 @@ public:
struct Hash final {
size_t operator()(const KeyTernary& key) const {
// cppcheck-suppress unreadVariable // cppcheck bug
V3Hash hash{vertexHash(key.m_source0p)};
hash += vertexHash(key.m_source1p);
hash += vertexHash(key.m_source2p);
@@ -348,7 +352,7 @@ class V3DfgCache final {
inline CacheType<Vertex>& cacheForType();
public:
V3DfgCache(DfgGraph& dfg)
explicit V3DfgCache(DfgGraph& dfg)
: m_dfg{dfg} {}
// Find a vertex of type 'Vertex', with the given operands, or create a new one and add it.
+2 -1
View File
@@ -295,7 +295,8 @@ void V3DfgOptimizer::optimize(AstNetlist* netlistp, const string& label) {
// For each cyclic component
for (auto& component : cyclicComponents) {
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
// TODO: Apply optimizations safe for cyclic graphs
// Converting back to Ast assumes the 'regularize' pass was run, so we must run it
V3DfgPasses::regularize(*component, ctx.m_regularizeContext);
// Add back under the main DFG (we will convert everything back in one go)
dfg->addGraph(*component);
}
+4 -13
View File
@@ -160,22 +160,13 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) {
DfgVertex* const driverp = varp->source(0);
// We must keep the original driver in certain cases, when swapping them would
// not be functionally or technically (implementation reasons) equivalent
// not be functionally or technically (implementation reasons) equivalent:
// 1. If driven from a SystemC variable (assignment is non-trivial)
if (DfgVertexVar* const driverVarp = driverp->cast<DfgVarPacked>()) {
const AstVar* const astVarp = driverVarp->varp();
// If driven from a SystemC variable
if (astVarp->isSc()) continue;
// If the variable is forceable
if (astVarp->isForceable()) continue;
if (driverVarp->varp()->isSc()) continue;
}
varp->forEachSinkEdge([=](DfgEdge& edge) {
// If sink is a SystemC variable, don't inline that sink
if (DfgVertexVar* const sinkVarp = edge.sinkp()->cast<DfgVarPacked>()) {
if (sinkVarp->varp()->isSc()) return;
}
edge.relinkSource(driverp);
});
varp->forEachSinkEdge([=](DfgEdge& edge) { edge.relinkSource(driverp); });
}
}
}
+23 -6
View File
@@ -27,6 +27,7 @@
VL_DEFINE_DEBUG_FUNCTIONS;
std::string V3DfgRegularizeContext::tmpNamePrefix(DfgGraph& dfg) {
// cppcheck-suppress unreadVariable // cppcheck bug
V3Hash hash{dfg.modulep()->name()};
hash += m_label;
std::string name = hash.toString();
@@ -45,11 +46,15 @@ class DfgRegularize final {
// Return canonical variable that can be used to hold the value of this vertex
DfgVarPacked* getCanonicalVariable(DfgVertex& vtx) {
// First gather all existing variables fully written by this vertex
// First gather all existing variables fully written by this vertex. Ignore SystemC
// variables, those cannot act as canonical variables, as they cannot participate in
// expressions or be assigned rvalues.
std::vector<DfgVarPacked*> varVtxps;
vtx.forEachSink([&](DfgVertex& sink) {
if (DfgVarPacked* const varVtxp = sink.cast<DfgVarPacked>()) {
if (varVtxp->isDrivenFullyByDfg()) varVtxps.push_back(varVtxp);
if (varVtxp->isDrivenFullyByDfg() && !varVtxp->varp()->isSc()) {
varVtxps.push_back(varVtxp);
}
}
});
@@ -95,10 +100,22 @@ class DfgRegularize final {
// Ensure intermediate values used multiple times are written to variables
for (DfgVertex& vtx : m_dfg.opVertices()) {
// Operations without multiple sinks need no variables
if (!vtx.hasMultipleSinks()) continue;
// Array selects need no variables, they are just memory references
if (vtx.is<DfgArraySel>()) continue;
const bool needsIntermediateVariable = [&]() {
// Anything that drives an SC variable needs an intermediate,
// as we can only assign simple variables to SC variables at runtime.
const bool hasScSink = vtx.findSink<DfgVertexVar>([](const DfgVertexVar& var) { //
return var.varp()->isSc();
});
if (hasScSink) return true;
// Operations without multiple sinks need no variables
if (!vtx.hasMultipleSinks()) return false;
// Array selects need no variables, they are just memory references
if (vtx.is<DfgArraySel>()) return false;
// Otherwise needs an intermediate variable
return true;
}();
if (!needsIntermediateVariable) continue;
// This is an op which has multiple sinks. Ensure it is assigned to a variable.
DfgVarPacked* const varp = getCanonicalVariable(vtx);
+1 -1
View File
@@ -239,7 +239,7 @@ public:
ASTGEN_MEMBERS_DfgVarPacked;
bool isDrivenFullyByDfg() const {
return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForceable();
return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForced();
}
void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) {
+47 -2
View File
@@ -35,6 +35,42 @@ EmitCParentModule::EmitCParentModule() {
setAll(v3Global.rootp()->constPoolp()->modp());
}
//######################################################################
// EmitCBase implementation
string EmitCBase::prefixNameProtect(const AstNode* nodep) VL_MT_STABLE {
const string prefix = v3Global.opt.modPrefix() + "_" + VIdProtect::protect(nodep->name());
// If all-uppercase prefix conflicts with a previous usage of the
// prefix with different capitalization, rename to avoid conflict.
// This is to support OSes where filename compares are non-case significant.
// STATIC:
static V3Mutex s_mutex;
const V3LockGuard lock{s_mutex}; // Otherwise map access is unsafe
static std::map<std::string, std::string> s_memoized;
static std::map<std::string, std::string> s_ucToPrefix;
// Memoize results
const auto mit = s_memoized.find(prefix);
if (mit != s_memoized.end()) return mit->second;
//
// Check capitalization
const string prefixUpper = VString::upcase(prefix);
string result;
{
const auto it = s_ucToPrefix.find(prefixUpper);
if (it == s_ucToPrefix.end()) {
s_ucToPrefix.emplace(prefixUpper, prefix);
result = prefix;
} else if (it->second == prefix) {
result = prefix; // Same capitialization as last time
} else {
VHashSha256 hash{prefix};
result = prefix + "__Vphsh" + hash.digestSymbol();
}
}
s_memoized.emplace(prefix, result);
return result;
}
//######################################################################
// EmitCBaseVisitor implementation
@@ -211,11 +247,11 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) {
if (nodep->isWide()) puts("," + cvtToStr(nodep->widthWords()));
puts(");\n");
} else {
// strings and other fundamental c types
// Strings and other fundamental C types
if (nodep->isFuncLocal() && nodep->isString()) {
const string name = nodep->name();
const string suffix = V3Task::dpiTemporaryVarSuffix();
// string temporary variable for DPI-C needs to be static because c_str() will be
// String temporary variable for DPI-C needs to be static because c_str() will be
// passed to C code and the lifetime of the variable must be long enough. See also
// Issue 2622.
const bool beStatic = name.size() >= suffix.size()
@@ -227,6 +263,15 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) {
}
}
void EmitCBaseVisitorConst::emitVarAccessors(const AstVar* nodep) {
assert(nodep->name().rfind("__Vm_sig_") == 0 && nodep->isIO());
const string privateName = nodep->name();
const string publicName = nodep->name().substr(strlen("__Vm_sig_"));
puts("decltype("s + privateName + ") "s + publicName + "() {return "s + privateName + ";}\n");
puts("void "s + publicName + "(decltype(" + privateName + ") v) {"s + privateName + "=v;}\n");
}
void EmitCBaseVisitorConst::emitModCUse(const AstNodeModule* modp, VUseType useType) {
bool nl = false;
forModCUse(modp, useType, [&](string entry) {
+3 -3
View File
@@ -66,9 +66,8 @@ public:
static string topClassName() VL_MT_SAFE { // Return name of top wrapper module
return v3Global.opt.prefix();
}
static string prefixNameProtect(const AstNode* nodep) VL_MT_STABLE { // C++ name with prefix
return v3Global.opt.modPrefix() + "_" + VIdProtect::protect(nodep->name());
}
// Return C++ class name for a module/class object
static string prefixNameProtect(const AstNode* nodep) VL_MT_STABLE;
static bool isAnonOk(const AstVar* varp) {
AstNodeDType* const dtp = varp->dtypep()->skipRefp();
return v3Global.opt.compLimitMembers() != 0 // Enabled
@@ -118,6 +117,7 @@ public:
void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope);
void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false);
void emitVarDecl(const AstVar* nodep, bool asRef = false);
void emitVarAccessors(const AstVar* nodep);
template <typename F>
static void forModCUse(const AstNodeModule* modp, VUseType useType, F action) {
for (AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
+1 -2
View File
@@ -48,8 +48,7 @@ protected:
VL_RESTORER(m_inUnpacked);
VL_RESTORER(m_unpackedWord);
m_inUnpacked = true;
if (const AstAssocArrayDType* const dtypep
= VN_CAST(nodep->dtypep()->skipRefp(), AssocArrayDType)) {
if (VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType)) {
// Note the double {{ initializer. The first { starts the initializer of the
// VlUnpacked, and the second starts the initializer of m_storage within the
// VlUnpacked.
+1
View File
@@ -84,6 +84,7 @@ class EmitCConstPool final : public EmitCConstInit {
putns(varp, "extern const ");
putns(varp, varp->dtypep()->cType(nameProtect, false, false));
putns(varp, " = ");
UASSERT_OBJ(varp, varp->valuep(), "Var without value");
iterateConst(varp->valuep());
putns(varp, ";\n");
// Keep track of stats
+12 -6
View File
@@ -274,9 +274,9 @@ void EmitCFunc::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const
double dchars = mantissabits / 3.321928094887362 + 1.0;
if (fmtLetter == 'd') dchars++; // space for sign
const int nchars = int(dchars);
pfmt = string{"%"} + cvtToStr(nchars) + fmtLetter;
pfmt = "%"s + cvtToStr(nchars) + fmtLetter;
} else {
pfmt = string{"%"} + vfmt + fmtLetter;
pfmt = "%"s + vfmt + fmtLetter;
}
m_emitDispState.pushFormat(pfmt);
if (!ignore) {
@@ -445,8 +445,12 @@ void EmitCFunc::emitDereference(AstNode* nodep, const string& pointer) {
putns(nodep, pointer.substr(2, pointer.length() - 3));
puts(".");
} else {
putns(nodep, pointer);
puts("->");
if (pointer == "vlSelf" && m_usevlSelfRef) {
puts("vlSelfRef.");
} else {
putns(nodep, pointer);
puts("->");
}
}
}
@@ -691,7 +695,7 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
UASSERT_OBJ(adtypep->hi() >= adtypep->lo(), varp,
"Should have swapped msb & lsb earlier.");
const string ivar = string{"__Vi"} + cvtToStr(depth);
const string ivar = "__Vi"s + cvtToStr(depth);
const string pre = ("for (int " + ivar + " = " + cvtToStr(0) + "; " + ivar + " < "
+ cvtToStr(adtypep->elementsConst()) + "; ++" + ivar + ") {\n");
const string below = emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(),
@@ -722,6 +726,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
return "";
} else if (basicp && basicp->isDynamicTriggerScheduler()) {
return "";
} else if (basicp && basicp->isRandomGenerator()) {
return "";
} else if (basicp) {
const bool zeroit
= (varp->attrFileDescr() // Zero so we don't do file IO if never $fopen
@@ -762,7 +768,7 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
}
return out;
}
} else {
} else { // LCOV_EXCL_BR_LINE
v3fatalSrc("Unknown node type in reset generator: " << varp->prettyTypeName());
}
return "";
+46 -60
View File
@@ -148,6 +148,7 @@ class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
protected:
EmitCLazyDecls m_lazyDecls; // Visitor for emitting lazy declarations
bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
bool m_usevlSelfRef = false; // Use vlSelfRef reference instead of vlSelf pointer
const AstNodeModule* m_modp = nullptr; // Current module being emitted
const AstCFunc* m_cfuncp = nullptr; // Current function being emitted
bool m_instantiatesOwnProcess = false;
@@ -192,10 +193,10 @@ public:
}
void emitScIQW(AstVar* nodep) {
UASSERT_OBJ(nodep->isSc(), nodep, "emitting SystemC operator on non-SC variable");
puts(nodep->isScBigUint() ? "SB"
: nodep->isScUint() ? "SU"
: nodep->isScBv() ? "SW"
: (nodep->isScQuad() ? "SQ" : "SI"));
puts(nodep->isScBigUint() ? "SB"
: (nodep->isScUint() || nodep->isScUintBool()) ? "SU"
: nodep->isScBv() ? "SW"
: (nodep->isScQuad() ? "SQ" : "SI"));
}
void emitDatap(AstNode* nodep) {
// When passing to a function with va_args the compiler doesn't
@@ -343,6 +344,21 @@ public:
}
}
if (m_useSelfForThis) {
m_usevlSelfRef = true;
/*
* Using reference to the vlSelf pointer will help the C++
* compiler to have dereferenceable hints, which can help to
* reduce the need for branch instructions in the generated
* code to allow the compiler to generate load store after the
* if condition (including short-circuit evaluation)
* speculatively and also reduce the data cache pollution when
* executing in the wrong path to make verilator-generated code
* run faster.
*/
puts("auto &vlSelfRef = std::ref(*vlSelf).get();\n");
}
if (nodep->initsp()) {
putsDecoration(nodep, "// Init\n");
iterateAndNextConstNull(nodep->initsp());
@@ -358,6 +374,8 @@ public:
iterateAndNextConstNull(nodep->finalsp());
}
m_usevlSelfRef = false;
puts("}\n");
if (nodep->ifdef() != "") puts("#endif // " + nodep->ifdef() + "\n");
}
@@ -367,32 +385,9 @@ public:
emitVarDecl(nodep);
}
void visit(AstCvtDynArrayToPacked* nodep) override {
putns(nodep, "VL_DYN_TO_");
emitIQW(nodep);
const AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep();
putns(elemDTypep, "<");
putbs(elemDTypep->cType("", false, false));
puts(">(");
iterateAndNextConstNull(nodep->fromp());
puts(", ");
putns(elemDTypep, cvtToStr(elemDTypep->widthMin()));
puts(")");
}
void visit(AstCvtUnpackArrayToPacked* nodep) override {
putns(nodep, "VL_UNPACK_TO_");
emitIQW(nodep);
const AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep();
putns(elemDTypep, "<");
putbs(elemDTypep->cType("", false, false));
puts(",");
puts(cvtToStr(nodep->fromp()->dtypep()->arrayUnpackedElements()));
puts(">(");
iterateAndNextConstNull(nodep->fromp());
puts(", ");
putns(elemDTypep, cvtToStr(elemDTypep->widthMin()));
puts(")");
void visit(AstCvtArrayToPacked* nodep) override {
AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep();
emitOpName(nodep, nodep->emitC(), nodep->fromp(), elemDTypep, nullptr);
}
void visit(AstNodeAssign* nodep) override {
@@ -449,30 +444,18 @@ public:
puts(cvtToStr(nodep->widthMin()) + ",");
iterateAndNextConstNull(nodep->lhsp());
puts(", ");
} else if (const AstCvtPackedToDynArray* const castp
= VN_CAST(nodep->rhsp(), CvtPackedToDynArray)) {
putns(castp, "VL_ASSIGN_DYN_Q<");
putbs(castp->dtypep()->subDTypep()->cType("", false, false));
puts(">(");
iterateAndNextConstNull(nodep->lhsp());
puts(", ");
} else if (const AstCvtPackedToArray* const castp
= VN_CAST(nodep->rhsp(), CvtPackedToArray)) {
putns(castp, "VL_UNPACK_");
emitIQW(nodep->dtypep()->subDTypep());
emitIQW(castp->fromp());
puts("(");
putns(castp->dtypep(), cvtToStr(castp->dtypep()->subDTypep()->widthMin()));
puts(", ");
puts(cvtToStr(castp->fromp()->widthMin()));
puts(", ");
rhs = false;
iterateAndNextConstNull(castp->fromp());
} else if (const AstCvtPackedToUnpackArray* const castp
= VN_CAST(nodep->rhsp(), CvtPackedToUnpackArray)) {
putns(castp, "VL_ASSIGN_UNPACK_Q<");
putbs(castp->dtypep()->subDTypep()->cType("", false, false));
puts(", ");
puts(cvtToStr(castp->dtypep()->arrayUnpackedElements()));
puts(">(");
iterateAndNextConstNull(nodep->lhsp());
puts(", ");
putns(castp->dtypep(), cvtToStr(castp->dtypep()->subDTypep()->widthMin()));
puts(", ");
rhs = false;
iterateAndNextConstNull(castp->fromp());
} else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) //
@@ -620,7 +603,7 @@ public:
if (!v3Global.opt.protectIds()) return;
}
if (!(nodep->protect() && v3Global.opt.protectIds())) {
putsDecoration(nodep, string{"// "} + nodep->name() + at + "\n");
putsDecoration(nodep, "// "s + nodep->name() + at + "\n");
}
iterateChildrenConst(nodep);
}
@@ -1280,8 +1263,7 @@ public:
return;
}
}
emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%lw, %P, %li, %ri)", nodep->lhsp(), nodep->rhsp(),
nullptr);
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nullptr);
}
void visit(AstCastDynamic* nodep) override {
putnbs(nodep, "VL_CAST_DYNAMIC(");
@@ -1402,9 +1384,12 @@ public:
void visit(AstConsDynArray* nodep) override {
putnbs(nodep, nodep->dtypep()->cType("", false, false));
if (!nodep->lhsp()) {
putns(nodep, "()");
putns(nodep, "{}");
} else {
putns(nodep, "::cons(");
puts("::cons");
puts(nodep->lhsIsValue() ? "V" : "C");
if (nodep->rhsp()) puts(nodep->rhsIsValue() ? "V" : "C");
puts("(");
iterateAndNextConstNull(nodep->lhsp());
if (nodep->rhsp()) {
puts(", ");
@@ -1433,9 +1418,12 @@ public:
void visit(AstConsQueue* nodep) override {
putnbs(nodep, nodep->dtypep()->cType("", false, false));
if (!nodep->lhsp()) {
puts("()");
puts("{}");
} else {
puts("::cons(");
puts("::cons");
puts(nodep->lhsIsValue() ? "V" : "C");
if (nodep->rhsp()) puts(nodep->rhsIsValue() ? "V" : "C");
puts("(");
iterateAndNextConstNull(nodep->lhsp());
if (nodep->rhsp()) {
puts(", ");
@@ -1457,15 +1445,13 @@ public:
}
// Default
void visit(AstNode* nodep) override {
putns(nodep, string{"\n???? // "} + nodep->prettyTypeName() + "\n");
void visit(AstNode* nodep) override { // LCOV_EXCL_START
putns(nodep, "\n???? // "s + nodep->prettyTypeName() + "\n");
iterateChildrenConst(nodep);
// LCOV_EXCL_START
if (!v3Global.opt.lintOnly()) { // An internal problem, so suppress
nodep->v3fatalSrc("Unknown node type reached emitter: " << nodep->prettyTypeName());
}
// LCOV_EXCL_STOP
}
} // LCOV_EXCL_STOP
EmitCFunc()
: m_lazyDecls(*this) {}
+1
View File
@@ -515,6 +515,7 @@ class EmitCHeader final : public EmitCConstInit {
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
if (v3Global.usesTiming()) puts("#include \"verilated_timing.h\"\n");
if (v3Global.useRandomizeMethods()) puts("#include \"verilated_random.h\"\n");
std::set<string> cuse_set;
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };
+4 -3
View File
@@ -389,6 +389,7 @@ class EmitCImp final : EmitCFunc {
} else if (varp->isParam()) {
} else if (varp->isStatic() && varp->isConst()) {
} else if (varp->basicp() && varp->basicp()->isTriggerVec()) {
} else if (VN_IS(varp->dtypep(), NBACommitQueueDType)) {
} else {
int vects = 0;
AstNodeDType* elementp = varp->dtypeSkipRefp();
@@ -397,7 +398,7 @@ class EmitCImp final : EmitCFunc {
const int vecnum = vects++;
UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp,
"Should have swapped msb & lsb earlier.");
const string ivar = string{"__Vi"} + cvtToStr(vecnum);
const string ivar = "__Vi"s + cvtToStr(vecnum);
puts("for (int __Vi" + cvtToStr(vecnum) + " = " + cvtToStr(0));
puts("; " + ivar + " < " + cvtToStr(arrayp->elementsConst()));
puts("; ++" + ivar + ") {\n");
@@ -411,7 +412,7 @@ class EmitCImp final : EmitCFunc {
if (elementp->isWide()
&& !(basicp && basicp->keyword() == VBasicDTypeKwd::STRING)) {
const int vecnum = vects++;
const string ivar = string{"__Vi"} + cvtToStr(vecnum);
const string ivar = "__Vi"s + cvtToStr(vecnum);
puts("for (int __Vi" + cvtToStr(vecnum) + " = " + cvtToStr(0));
puts("; " + ivar + " < " + cvtToStr(elementp->widthWords()));
puts("; ++" + ivar + ") {\n");
@@ -693,7 +694,7 @@ class EmitCTrace final : EmitCFunc {
const AstVarRef* const varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
if (!varrefp) return false;
AstVar* const varp = varrefp->varp();
return varp->isSc() && varp->isScUint();
return varp->isSc() && (varp->isScUint() || varp->isScUintBool());
}
void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) {
+14 -5
View File
@@ -81,10 +81,16 @@ class CMakeEmitter final {
*of << "\n### Constants...\n";
cmake_set(*of, "PERL", V3OutFormatter::quoteNameControls(V3Options::getenvPERL()),
"FILEPATH", "Perl executable (from $PERL)");
"FILEPATH", "Perl executable (from $PERL, defaults to 'perl' if not set)");
cmake_set(*of, "PYTHON3", V3OutFormatter::quoteNameControls(V3Options::getenvPYTHON3()),
"FILEPATH",
"Python3 executable (from $PYTHON3, defaults to 'python3' if not set)");
cmake_set(*of, "VERILATOR_ROOT",
V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_ROOT()), "PATH",
"Path to Verilator kit (from $VERILATOR_ROOT)");
cmake_set(*of, "VERILATOR_SOLVER",
V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_SOLVER()), "STRING",
"Default SMT solver for constrained randomization (from $VERILATOR_SOLVER)");
*of << "\n### Compiler flags...\n";
@@ -160,6 +166,9 @@ class CMakeEmitter final {
if (v3Global.usesTiming()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_timing.cpp");
}
if (v3Global.useRandomizeMethods()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_random.cpp");
}
global.emplace_back("${VERILATOR_ROOT}/include/verilated_threads.cpp");
if (v3Global.opt.usesProfiler()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_profiler.cpp");
@@ -206,7 +215,7 @@ class CMakeEmitter final {
<< hblockp->modp()->name() << " DIRECTORY "
<< v3Global.opt.makeDir() + "/" + prefix << " SOURCES ";
for (const auto& childr : children) {
*of << " " << v3Global.opt.makeDir() + "/" + childr->hierWrapper(true);
*of << " " << v3Global.opt.makeDir() + "/" + childr->hierWrapperFilename(true);
}
*of << " ";
const string vFile = hblockp->vFileIfNecessary();
@@ -214,7 +223,7 @@ class CMakeEmitter final {
const V3StringList& vFiles = v3Global.opt.vFiles();
for (const string& i : vFiles) *of << V3Os::filenameRealPath(i) << " ";
*of << " VERILATOR_ARGS ";
*of << "-f " << hblockp->commandArgsFileName(true)
*of << "-f " << hblockp->commandArgsFilename(true)
<< " -CFLAGS -fPIC" // hierarchical block will be static, but may be linked
// with .so
<< ")\n";
@@ -224,11 +233,11 @@ class CMakeEmitter final {
<< v3Global.rootp()->topModulep()->name() << " DIRECTORY "
<< v3Global.opt.makeDir() << " SOURCES ";
for (const auto& itr : *planp) {
*of << " " << v3Global.opt.makeDir() + "/" + itr.second->hierWrapper(true);
*of << " " << v3Global.opt.makeDir() + "/" + itr.second->hierWrapperFilename(true);
}
*of << " " << cmake_list(v3Global.opt.vFiles());
*of << " VERILATOR_ARGS ";
*of << "-f " << planp->topCommandArgsFileName(true);
*of << "-f " << planp->topCommandArgsFilename(true);
*of << ")\n";
}
}

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