Compare commits

...
148 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
462 changed files with 34708 additions and 40356 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
@@ -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
+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.026
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"
+87 -1
View File
@@ -8,6 +8,86 @@ 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
==========================
@@ -34,6 +114,7 @@ Verilator 5.026 2024-06-15
* 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.]
@@ -72,7 +153,11 @@ Verilator 5.026 2024-06-15
* 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
@@ -620,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]
@@ -1511,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
+1
View File
@@ -447,6 +447,7 @@ PY_PROGRAMS = \
nodist/fuzzer/actual_fail \
nodist/fuzzer/generate_dictionary \
nodist/install_test \
nodist/log_changes \
PY_FILES = \
$(PY_PROGRAMS) \
+6 -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
@@ -410,6 +413,7 @@ detailed descriptions of these arguments.
--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
@@ -459,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
+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
Executable
BIN
View File
Binary file not shown.
File diff suppressed because it is too large Load Diff
+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.:
::
+43 -26
View File
@@ -10,7 +10,7 @@
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.026 2024-06-15],
AC_INIT([Verilator],[5.028 2024-08-21],
[https://verilator.org],
[verilator],[https://verilator.org])
@@ -288,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
@@ -373,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
+12
View File
@@ -25,6 +25,7 @@ Aylon Chaim Porat
Bartłomiej Chmiel
Cameron Kirk
Chih-Mao Chen
Chris Bachhuber
Chris Randall
Christopher Taylor
Chuxuan Wang
@@ -76,6 +77,7 @@ Iru Cai
Ivan Vnučec
Iztok Jeras
Jake Merdich
James Bailey
James Hanlon
James Hutchinson
James Pallister
@@ -118,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
@@ -157,6 +162,7 @@ Pengcheng Xu
Peter Debacker
Peter Horvath
Peter Monsson
Philip Axer
Philipp Wagner
Pierre-Henri Horrein
Pieter Kapsenberg
@@ -166,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
@@ -194,6 +203,7 @@ Tudor Timi
Tymoteusz Blazejczyk
Udi Finkelstein
Unai Martinez-Corral
Udaya Raj Subedi
Valentin Atepalikhin
Varun Koyyalagunta
Vassilis Papaefstathiou
@@ -204,6 +214,7 @@ William D. Jones
Wilson Snyder
Xi Zhang
Yan Xu
Yangyu Chen
Yinan Xu
Yoda Lee
Yossi Nivin
@@ -215,3 +226,4 @@ Zhanglei Wang
Zhou Shen
Zixi Li
أحمد المحمودي
404allen404
+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)
+31
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`.
@@ -1004,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
@@ -1979,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
+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:
+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.
+58 -23
View File
@@ -419,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.
@@ -1264,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
@@ -1273,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".
@@ -1304,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
@@ -1346,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
@@ -1375,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
@@ -1897,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
@@ -1948,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
@@ -2078,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.
@@ -2089,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
@@ -2125,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
@@ -2136,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
@@ -2193,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
@@ -2214,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}};
+11
View File
@@ -379,6 +379,7 @@ Srini
Srinivasan
Stamness
Stephane
Sterin
Stoddard
Stroebel
Strouble
@@ -461,6 +462,7 @@ Wojciech
Wolfel
Wouters
Wpedantic
Wunused
Wwarn
XSim
Xcelium
@@ -497,6 +499,7 @@ architected
args
arrarys
assertOn
assertcontrol
astgen
async
atClone
@@ -589,6 +592,7 @@ defname
defparam
demangling
dep
deparametrized
der
dereference
desassign
@@ -701,6 +705,7 @@ genvar
genvars
getenv
getline
getter
ggdb
glibc
gmake
@@ -714,6 +719,7 @@ hh
hier
hierCMakeArgs
hierMkArgs
hierParameters
hierVer
hx
hyperthreading
@@ -845,6 +851,7 @@ parallelized
param
parameterization
parameterized
parameterized
parameterless
params
parens
@@ -891,6 +898,7 @@ prototyptes
ps
psprintf
pthread
pthreads
ptr
pulldown
pulldowns
@@ -973,7 +981,9 @@ submakes
submodule
submodules
substring
superclass
suspendable
suspendables
sv
svBitVal
svBitVecVal
@@ -1102,3 +1112,4 @@ zdave
Øyvind
Алексеевич
Исаак
+11 -1
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
@@ -4633,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));
@@ -6185,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;
@@ -6277,7 +6285,9 @@ for(;;)
}
blkpos += seclen;
#ifdef FST_DEBUG
secnum++;
#endif
}
xc->rvat_beg_tim = beg_tim;
+50 -2
View File
@@ -412,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;
@@ -421,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);
@@ -2494,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};
+88 -41
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
@@ -448,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.
@@ -470,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);
@@ -607,27 +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
void solverProgram(const std::string& flag) VL_MT_SAFE;
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;
@@ -804,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);
@@ -958,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
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) \
+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
+334 -26
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;
@@ -1577,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.
@@ -2059,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 {
+7 -1
View File
@@ -403,8 +403,12 @@ bool VlRandomizer::parseSolution(std::iostream& f) {
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;
}
it->second->set(std::move(value));
varr.set(std::move(value));
}
return true;
@@ -414,6 +418,8 @@ 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) {
+14 -6
View File
@@ -38,15 +38,19 @@ 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)
VlRandomVar(const char* name, int width, void* datap, std::uint32_t randModeIdx)
: m_name{name}
, m_datap{datap}
, m_width{width} {}
, 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;
};
@@ -94,8 +98,9 @@ public:
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
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);
@@ -106,12 +111,15 @@ public:
// Finds the next solution satisfying the constraints
bool next(VlRNG& rngr);
template <typename T>
void write_var(T& var, int width, const char* name) {
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);
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
+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
+36 -31
View File
@@ -512,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;
@@ -751,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{};
@@ -760,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>{};
@@ -769,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{};
@@ -778,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>{};
@@ -788,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 {
@@ -797,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 {
@@ -811,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 {
@@ -1092,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 {
@@ -1101,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 {
@@ -1109,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 {
@@ -1117,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
@@ -1128,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 {
@@ -1138,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>();
@@ -1147,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 {
@@ -1157,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 {
@@ -1438,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>{};
@@ -1447,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>{};
@@ -1455,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>{};
}
@@ -1470,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 {
@@ -1478,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 {
@@ -1490,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)
@@ -1786,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
+1 -1
View File
@@ -958,8 +958,8 @@ public:
}
static void dumpCbs() VL_MT_UNSAFE_ONE;
static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info
static void evalNeeded(bool evalNeeded) { s().m_evalNeeded = evalNeeded; }
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);
}
+4 -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
}
+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:
+4
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
@@ -488,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
+1
View File
@@ -284,6 +284,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3ProtectLib.o \
V3Randomize.o \
V3Reloop.o \
V3Sampled.o \
V3Sched.o \
V3SchedAcyclic.o \
V3SchedPartition.o \
+165 -58
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,9 +327,13 @@ 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);
@@ -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);
@@ -527,24 +607,51 @@ class AssertVisitor final : public VNVisitor {
iterateChildren(nodep);
if (const AstConst* const constp = VN_CAST(nodep->controlTypep(), Const)) {
nodep->ctlType(constp->toSInt());
} else if (nodep->ctlType() == VAssertCtlType::_TO_BE_EVALUATED) {
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);
FileLine* const fl = nodep->fileline();
const string assertOnStmt
= string{"vlSymsp->_vm_contextp__->assertOn("}
+ (nodep->ctlType() == VAssertCtlType::ON ? "true" : "false") + ");\n";
nodep->replaceWith(new AstCExpr{fl, assertOnStmt, 1});
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:
+87 -14
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,6 +157,8 @@ 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
@@ -175,21 +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();
m_clockingp->addVarsp(varp->unlinkFrBack());
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{
@@ -226,9 +259,8 @@ 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:
@@ -241,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()}};
@@ -265,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) {
@@ -317,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()) {
@@ -329,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)");
}
}
@@ -496,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;
}
+138 -18
View File
@@ -330,6 +330,7 @@ public:
ILLEGAL,
COVERAGE_BLOCK_OFF,
HIER_BLOCK,
HIER_PARAMS,
INLINE_MODULE,
NO_INLINE_MODULE,
NO_INLINE_TASK,
@@ -787,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;
}
@@ -1154,6 +1156,101 @@ constexpr bool operator==(VAssertCtlType::en lhs, const VAssertCtlType& rhs) {
// ######################################################################
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 };
@@ -1493,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
@@ -1584,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
@@ -2076,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);
@@ -2407,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.
@@ -2525,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
@@ -2543,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*)', "
@@ -2554,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*)', "
@@ -2565,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*)', "
@@ -2576,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*)', "
@@ -2591,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*)', "
@@ -2602,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*)', "
@@ -2616,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*)', "
@@ -2627,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) {
+5 -4
View File
@@ -219,6 +219,7 @@ public:
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)
@@ -875,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; }
@@ -956,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 {
+140 -76
View File
@@ -213,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(""); }
@@ -420,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; }
@@ -809,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]
@@ -872,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, '{}
@@ -1063,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
@@ -1531,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
@@ -1539,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(""); }
@@ -1563,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;
@@ -1744,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; }
@@ -1847,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(""); }
@@ -2135,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"; }
@@ -2173,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
@@ -4197,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; }
};
@@ -4218,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; }
};
@@ -4439,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
@@ -4460,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 ===
@@ -4619,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; }
@@ -5355,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()
@@ -5616,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;
@@ -5645,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; }
+134 -90
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
@@ -1022,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; }
@@ -1059,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.
@@ -1106,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
@@ -1771,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)
@@ -1794,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
@@ -1840,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;
@@ -1966,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; }
@@ -1982,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; }
@@ -2030,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(); }
@@ -2067,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;
@@ -2074,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; }
@@ -2092,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; }
@@ -2191,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; }
@@ -2346,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)
@@ -2588,16 +2616,18 @@ public:
};
class AstAssertCtl final : public AstNodeStmt {
// @astgen op1 := controlTypep : AstNodeExpr
// @astgen op2 := levelp : AstNodeExpr
// @astgen op3 := itemsp : List[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
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* assertionTypep = nullptr,
AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertTypesp = nullptr,
AstNodeExpr* directiveTypep = nullptr, AstNodeExpr* levelp = nullptr,
AstNodeExpr* itemsp = nullptr);
ASTGEN_MEMBERS_AstAssertCtl;
@@ -2608,6 +2638,10 @@ public:
bool isOutputter() override { return true; }
VAssertCtlType ctlType() const { return m_ctlType; }
void ctlType(int32_t type) { m_ctlType = VAssertCtlType{type}; }
VAssertType ctlAssertTypes() const { return m_assertTypes; }
void ctlAssertTypes(VAssertType types) { m_assertTypes = types; }
VAssertDirectiveType ctlDirectiveTypes() const { return m_directiveTypes; }
void ctlDirectiveTypes(VAssertDirectiveType types) { m_directiveTypes = types; }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
};
@@ -2688,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
@@ -2747,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; }
@@ -2786,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 {
@@ -2883,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; }
@@ -3021,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)
@@ -3036,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
@@ -3227,6 +3266,7 @@ public:
this->exprp(exprp);
}
ASTGEN_MEMBERS_AstStmtExpr;
bool isPure() override { return exprp()->isPure(); }
};
class AstStop final : public AstNodeStmt {
public:
@@ -3379,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; }
@@ -3461,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 ===
@@ -3619,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);
}
};
@@ -3633,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);
}
};
@@ -3642,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 ===
@@ -3792,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});
}
+60 -23
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) {
@@ -978,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();
@@ -1461,23 +1497,20 @@ void AstAlways::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "keyword", keyword().ascii());
dumpJsonGen(str);
}
AstAssertCtl::AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr* levelp,
AstNodeExpr* itemsp)
AstAssertCtl::AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr*, AstNodeExpr*)
: ASTGEN_SUPER_AssertCtl(fl)
, m_ctlType{ctlType} {
controlTypep(new AstConst{fl, ctlType});
if (!levelp) levelp = new AstConst{fl, 0};
this->levelp(levelp);
addItemsp(itemsp);
}
AstAssertCtl::AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr*, AstNodeExpr*,
AstNodeExpr* levelp, AstNodeExpr* itemsp)
AstAssertCtl::AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertTypesp,
AstNodeExpr* directiveTypep, AstNodeExpr*, AstNodeExpr*)
: ASTGEN_SUPER_AssertCtl(fl)
, m_ctlType{VAssertCtlType::_TO_BE_EVALUATED} {
, m_ctlType{VAssertCtlType::_TO_BE_EVALUATED}
, m_assertTypes{VAssertType::INTERNAL}
, m_directiveTypes{VAssertDirectiveType::INTERNAL} {
this->controlTypep(controlTypep);
if (!levelp) levelp = new AstConst{fl, 0};
this->levelp(levelp);
addItemsp(itemsp);
this->assertTypesp(assertTypesp);
this->directiveTypesp(directiveTypep);
}
void AstAssertCtl::dump(std::ostream& str) const {
this->AstNode::dump(str);
@@ -1636,10 +1669,10 @@ string AstClassRefDType::prettyDTypeName(bool) const { return "class{}"s + prett
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 {
@@ -1826,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())
@@ -2357,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]";
@@ -2801,6 +2837,7 @@ void AstCMethodHard::setPurity() {
{"reverse", false},
{"rsort", false},
{"set", false},
{"set_randmode", false},
{"shuffle", false},
{"size", true},
{"slice", true},
+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);
}
}
+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();
}
}
+21 -38
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) {
@@ -2170,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);
@@ -2211,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
@@ -2244,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;
@@ -2263,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)) {
@@ -2849,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)
+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);
}
+4 -12
View File
@@ -120,8 +120,6 @@ class DelayedVisitor final : public VNVisitor {
AstVarScope* delayVscp = nullptr;
// Points to AstActive block of the shadow variable 'delayVscp/post block 'postp'
AstActive* activep = nullptr;
// Post block for this variable used in suspendable processes
AstAlwaysPost* suspPostp = nullptr;
// Post block for this variable
AstAlwaysPost* postp = nullptr;
// First reference encountered to the VarScope
@@ -386,17 +384,11 @@ class DelayedVisitor final : public VNVisitor {
}
// Get/Create 'Post' ordered block to commit the delayed value
AstAlwaysPost* postp = m_vscpAux(vscp).suspPostp;
if (!postp) {
postp = new AstAlwaysPost{flp};
if (!m_procp->user2p()) {
m_procp->user2p(createActiveLike(lhsComponents.refp->fileline(), m_activep));
// TODO: Somebody needs to explain me how it makes sense to set this
// inside this 'if'. Shouldn't it be outside this 'if'? See #5084
m_vscpAux(vscp).suspPostp = postp;
}
VN_AS(m_procp->user2p(), Active)->addStmtsp(postp);
AstAlwaysPost* postp = new AstAlwaysPost{flp};
if (!m_procp->user2p()) {
m_procp->user2p(createActiveLike(lhsComponents.refp->fileline(), m_activep));
}
VN_AS(m_procp->user2p(), Active)->addStmtsp(postp);
// Create the flag denoting an update is pending - no reuse here
AstVarScope* const setVscp = createNewVarScope(vscp, "__VdlySet" + baseName, 1);
+1 -1
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: ");
+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
+3 -6
View File
@@ -160,13 +160,10 @@ 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 forced
if (astVarp->isForced()) continue;
if (driverVarp->varp()->isSc()) continue;
}
varp->forEachSinkEdge([=](DfgEdge& edge) { edge.relinkSource(driverp); });
+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.
+10 -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(),
@@ -764,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) {}
+3 -3
View File
@@ -398,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");
@@ -412,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");
@@ -694,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) {
+8 -5
View File
@@ -81,7 +81,10 @@ 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)");
@@ -212,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();
@@ -220,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";
@@ -230,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";
}
}
+13 -2
View File
@@ -118,6 +118,17 @@ class EmitCModel final : public EmitCFunc {
}
}
}
if (v3Global.opt.emitAccessors()) {
puts("\n// ACCESSORS\n"
"// The application code should use these methods to\n"
"// propagate new values into/out from the Verilated model\n"
"// instead of using signal variables directly.\n");
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
if (varp->isPrimaryIO()) emitVarAccessors(varp);
}
}
}
if (optSystemC() && v3Global.usesTiming()) puts("sc_core::sc_event trigger_eval;\n");
// Cells instantiated by the top level (for access to /* verilator public */)
@@ -540,11 +551,11 @@ class EmitCModel final : public EmitCFunc {
"0.\");\n");
puts("}\n");
puts("vlSymsp->__Vm_baseCode = code;\n");
puts("tracep->pushPrefix(std::string{vlSymsp->name()}, "
puts("if (strlen(vlSymsp->name())) tracep->pushPrefix(std::string{vlSymsp->name()}, "
"VerilatedTracePrefixType::SCOPE_MODULE);\n");
puts(topModNameProtected + "__" + protect("trace_decl_types") + "(tracep);\n");
puts(topModNameProtected + "__" + protect("trace_init_top") + "(vlSelf, tracep);\n");
puts("tracep->popPrefix();\n");
puts("if (strlen(vlSymsp->name())) tracep->popPrefix();\n");
puts("}\n");
// Forward declaration
+5
View File
@@ -57,6 +57,11 @@ public:
of.puts("#include \"" + symClassName() + ".h\"\n");
of.puts("#include \"" + topClassName() + ".h\"\n");
of.puts("\n// Additional include files added using '--compiler-include'\n");
for (const string& filename : v3Global.opt.compilerIncludes()) {
of.puts("#include \"" + filename + "\"\n");
}
of.putsEndGuard();
}
+1 -1
View File
@@ -537,7 +537,7 @@ void EmitCSyms::emitSymHdr() {
puts("\n// CONSTRUCTORS\n");
puts(symClassName() + "(VerilatedContext* contextp, const char* namep, " + topClassName()
+ "* modelp);\n");
puts(string{"~"} + symClassName() + "();\n");
puts("~"s + symClassName() + "();\n");
for (const auto& i : m_usesVfinal) {
puts("void " + symClassName() + "_" + cvtToStr(i.first) + "(");
+36 -26
View File
@@ -151,15 +151,18 @@ public:
of.puts("default: lib" + v3Global.opt.prefix() + "\n");
}
of.puts("\n### Constants...\n");
of.puts("# Perl executable (from $PERL)\n");
of.puts("# Perl executable (from $PERL, defaults to 'perl' if not set)\n");
of.puts("PERL = " + V3OutFormatter::quoteNameControls(V3Options::getenvPERL()) + "\n");
of.puts("# Python3 executable (from $PYTHON3, defaults to 'python3' if not set)\n");
of.puts("PYTHON3 = " + V3OutFormatter::quoteNameControls(V3Options::getenvPYTHON3())
+ "\n");
of.puts("# Path to Verilator kit (from $VERILATOR_ROOT)\n");
of.puts("VERILATOR_ROOT = "
+ V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_ROOT()) + "\n");
of.puts("# SystemC include directory with systemc.h (from $SYSTEMC_INCLUDE)\n");
of.puts(string{"SYSTEMC_INCLUDE ?= "} + V3Options::getenvSYSTEMC_INCLUDE() + "\n");
of.puts("SYSTEMC_INCLUDE ?= "s + V3Options::getenvSYSTEMC_INCLUDE() + "\n");
of.puts("# SystemC library directory with libsystemc.a (from $SYSTEMC_LIBDIR)\n");
of.puts(string{"SYSTEMC_LIBDIR ?= "} + V3Options::getenvSYSTEMC_LIBDIR() + "\n");
of.puts("SYSTEMC_LIBDIR ?= "s + V3Options::getenvSYSTEMC_LIBDIR() + "\n");
// Only check it if we really need the value
if (v3Global.opt.systemC() && !V3Options::systemCFound()) {
@@ -170,22 +173,22 @@ public:
of.puts("\n### Switches...\n");
of.puts("# C++ code coverage 0/1 (from --prof-c)\n");
of.puts(string{"VM_PROFC = "} + ((v3Global.opt.profC()) ? "1" : "0") + "\n");
of.puts("VM_PROFC = "s + ((v3Global.opt.profC()) ? "1" : "0") + "\n");
of.puts("# SystemC output mode? 0/1 (from --sc)\n");
of.puts(string{"VM_SC = "} + ((v3Global.opt.systemC()) ? "1" : "0") + "\n");
of.puts("VM_SC = "s + ((v3Global.opt.systemC()) ? "1" : "0") + "\n");
of.puts("# Legacy or SystemC output mode? 0/1 (from --sc)\n");
of.puts(string{"VM_SP_OR_SC = $(VM_SC)\n"});
of.puts("VM_SP_OR_SC = $(VM_SC)\n");
of.puts("# Deprecated\n");
of.puts(string{"VM_PCLI = "} + (v3Global.opt.systemC() ? "0" : "1") + "\n");
of.puts("VM_PCLI = "s + (v3Global.opt.systemC() ? "0" : "1") + "\n");
of.puts(
"# Deprecated: SystemC architecture to find link library path (from $SYSTEMC_ARCH)\n");
of.puts(string{"VM_SC_TARGET_ARCH = "} + V3Options::getenvSYSTEMC_ARCH() + "\n");
of.puts("VM_SC_TARGET_ARCH = "s + V3Options::getenvSYSTEMC_ARCH() + "\n");
of.puts("\n### Vars...\n");
of.puts("# Design prefix (from --prefix)\n");
of.puts(string{"VM_PREFIX = "} + v3Global.opt.prefix() + "\n");
of.puts("VM_PREFIX = "s + v3Global.opt.prefix() + "\n");
of.puts("# Module prefix (from --prefix)\n");
of.puts(string{"VM_MODPREFIX = "} + v3Global.opt.modPrefix() + "\n");
of.puts("VM_MODPREFIX = "s + v3Global.opt.modPrefix() + "\n");
of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n");
of.puts("VM_USER_CFLAGS = \\\n");
@@ -209,7 +212,7 @@ public:
of.puts("VM_USER_CLASSES = \\\n");
const V3StringSet& cppFiles = v3Global.opt.cppFiles();
for (const auto& cppfile : cppFiles) {
of.puts("\t" + V3Os::filenameNonExt(cppfile) + " \\\n");
of.puts("\t" + V3Os::filenameNonDirExt(cppfile) + " \\\n");
const string dir = V3Os::filenameDir(cppfile);
dirs.insert(dir);
}
@@ -236,11 +239,18 @@ public:
of.puts("\n");
}
const string compilerIncludePch
= v3Global.opt.compilerIncludes().empty() ? "" : "$(VK_PCH_H).fast.gch";
const string compilerIncludeFlag
= v3Global.opt.compilerIncludes().empty() ? "" : "$(VK_PCH_I_FAST)";
for (const string& cppfile : cppFiles) {
const string basename = V3Os::filenameNonExt(cppfile);
const string basename = V3Os::filenameNonDirExt(cppfile);
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
of.puts(basename + ".o: " + cppfile + "\n");
of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ $<\n");
of.puts(basename + ".o: " + cppfile + " " + compilerIncludePch + "\n");
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) "
+ compilerIncludeFlag + " -c -o $@ $<\n");
}
if (v3Global.opt.exe()) {
@@ -346,7 +356,7 @@ class EmitMkHierVerilation final {
= m_planp->hierBlocksSorted(); // leaf comes first
// List in order of leaf-last order so that linker can resolve dependency
for (const auto& block : vlstd::reverse_view(blocks)) {
of.puts("\t" + block->hierLib(true) + " \\\n");
of.puts("\t" + block->hierLibFilename(true) + " \\\n");
}
of.puts("\n");
@@ -365,12 +375,12 @@ class EmitMkHierVerilation final {
// Top level module
{
const string argsFile = v3Global.hierPlanp()->topCommandArgsFileName(false);
const string argsFile = v3Global.hierPlanp()->topCommandArgsFilename(false);
of.puts("\n# Verilate the top module\n");
of.puts(v3Global.opt.prefix()
+ ".mk: $(VM_HIER_INPUT_FILES) $(VM_HIER_VERILOG_LIBS) ");
of.puts(V3Os::filenameNonDir(argsFile) + " ");
for (const auto& itr : *m_planp) of.puts(itr.second->hierWrapper(true) + " ");
for (const auto& itr : *m_planp) of.puts(itr.second->hierWrapperFilename(true) + " ");
of.puts("\n");
emitLaunchVerilator(of, argsFile);
}
@@ -379,29 +389,29 @@ class EmitMkHierVerilation final {
of.puts("\n# Verilate hierarchical blocks\n");
for (const V3HierBlock* const blockp : m_planp->hierBlocksSorted()) {
const string prefix = blockp->hierPrefix();
const string argsFile = blockp->commandArgsFileName(false);
of.puts(blockp->hierGenerated(true));
const string argsFilename = blockp->commandArgsFilename(false);
of.puts(blockp->hierGeneratedFilenames(true));
of.puts(": $(VM_HIER_INPUT_FILES) $(VM_HIER_VERILOG_LIBS) ");
of.puts(V3Os::filenameNonDir(argsFile) + " ");
of.puts(V3Os::filenameNonDir(argsFilename) + " ");
const V3HierBlock::HierBlockSet& children = blockp->children();
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
child != children.end(); ++child) {
of.puts((*child)->hierWrapper(true) + " ");
of.puts((*child)->hierWrapperFilename(true) + " ");
}
of.puts("\n");
emitLaunchVerilator(of, argsFile);
emitLaunchVerilator(of, argsFilename);
// Rule to build lib*.a
of.puts(blockp->hierLib(true));
of.puts(blockp->hierLibFilename(true));
of.puts(": ");
of.puts(blockp->hierMk(true));
of.puts(blockp->hierMkFilename(true));
of.puts(" ");
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
child != children.end(); ++child) {
of.puts((*child)->hierLib(true));
of.puts((*child)->hierLibFilename(true));
of.puts(" ");
}
of.puts("\n\t$(MAKE) -f " + blockp->hierMk(false) + " -C " + prefix);
of.puts("\n\t$(MAKE) -f " + blockp->hierMkFilename(false) + " -C " + prefix);
of.puts(" VM_PREFIX=" + prefix);
of.puts("\n\n");
}
+8 -2
View File
@@ -602,7 +602,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
void visit(AstTypedef* nodep) override {
putfs(nodep, "typedef ");
iterateAndNextConstNull(nodep->dtypep());
iterateAndNextConstNull(nodep->subDTypep());
puts(" ");
puts(nodep->prettyName());
puts(";\n");
@@ -630,7 +630,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
iterateConst(nodep->subDTypep());
iterateAndNextConstNull(nodep->rangep());
}
void visit(AstRefDType* nodep) override { iterateConst(nodep->skipRefp()); }
void visit(AstRefDType* nodep) override {
if (nodep->subDTypep()) {
iterateConst(nodep->skipRefp());
} else {
puts("\n???? // "s + nodep->prettyTypeName() + " -> UNLINKED\n");
}
}
void visit(AstNodeUOrStructDType* nodep) override {
puts(nodep->verilogKwd() + " ");
if (nodep->packed()) puts("packed ");
+6 -20
View File
@@ -53,25 +53,11 @@ V3ErrorCode::V3ErrorCode(const char* msgp) {
//
bool V3ErrorGuarded::isError(V3ErrorCode code, bool supp) VL_REQUIRES(m_mutex) {
if (supp) {
return false;
} else if (code == V3ErrorCode::USERINFO) {
return false;
} else if (code == V3ErrorCode::EC_INFO) {
return false;
} else if (code == V3ErrorCode::EC_FATAL) {
return true;
} else if (code == V3ErrorCode::EC_FATALEXIT) {
return true;
} else if (code == V3ErrorCode::EC_FATALSRC) {
return true;
} else if (code == V3ErrorCode::EC_ERROR) {
return true;
} else if (code < V3ErrorCode::EC_FIRST_WARN || pretendError(code)) {
return true;
} else {
return false;
}
if (code.hardError()) return true;
if (supp) return false;
if (code == V3ErrorCode::USERINFO || code == V3ErrorCode::EC_INFO) return false;
if (pretendError(code)) return true;
return false;
}
string V3ErrorGuarded::msgPrefix() VL_REQUIRES(m_mutex) {
@@ -180,7 +166,7 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra)
}
if (!m_describedEachWarn[m_errorCode] && !m_pretendError[m_errorCode]) {
m_describedEachWarn[m_errorCode] = true;
if (m_errorCode >= V3ErrorCode::EC_FIRST_WARN && !m_describedWarnings) {
if (!m_errorCode.hardError() && !m_describedWarnings) {
m_describedWarnings = true;
std::cerr << warnMore() << "... Use \"/* verilator lint_off "
<< m_errorCode.ascii()
+8 -4
View File
@@ -226,6 +226,10 @@ public:
}
// Warnings that warn about nasty side effects
bool dangerous() const VL_MT_SAFE { return (m_e == COMBDLY); }
// Insuppressible error codes that should always stop elaboration
bool hardError() const VL_MT_SAFE {
return (m_e != EC_INFO && m_e < V3ErrorCode::EC_FIRST_WARN);
}
// Warnings we'll present to the user as errors
// Later -Werror- options may make more of these.
bool pretendError() const VL_MT_SAFE {
@@ -351,8 +355,8 @@ public:
void execErrorExitCb() VL_REQUIRES(m_mutex) {
if (m_errorExitCb) m_errorExitCb();
}
void errorExitCb(ErrorExitCb cb) VL_REQUIRES(m_mutex) { m_errorExitCb = cb; }
ErrorExitCb errorExitCb() VL_REQUIRES(m_mutex) { return m_errorExitCb; }
void errorExitCb(ErrorExitCb cb) VL_REQUIRES(m_mutex) { m_errorExitCb = cb; }
bool isError(V3ErrorCode code, bool supp) VL_REQUIRES(m_mutex);
void vlAbortOrExit() VL_REQUIRES(m_mutex);
void errorContexted(bool flag) VL_REQUIRES(m_mutex) { m_errorContexted = flag; }
@@ -379,12 +383,12 @@ public:
}
m_pretendError[code] = flag;
}
void debugDefault(int level) VL_MT_UNSAFE { m_debugDefault = level; }
int debugDefault() VL_MT_SAFE { return m_debugDefault; }
void errorLimit(int level) VL_REQUIRES(m_mutex) { m_errorLimit = level; }
void debugDefault(int level) VL_MT_UNSAFE { m_debugDefault = level; }
int errorLimit() VL_REQUIRES(m_mutex) { return m_errorLimit; }
void warnFatal(bool flag) VL_REQUIRES(m_mutex) { m_warnFatal = flag; }
void errorLimit(int level) VL_REQUIRES(m_mutex) { m_errorLimit = level; }
bool warnFatal() VL_REQUIRES(m_mutex) { return m_warnFatal; }
void warnFatal(bool flag) VL_REQUIRES(m_mutex) { m_warnFatal = flag; }
V3ErrorCode errorCode() VL_REQUIRES(m_mutex) { return m_errorCode; }
bool errorContexted() VL_REQUIRES(m_mutex) { return m_errorContexted; }
int warnCount() VL_REQUIRES(m_mutex) { return m_warnCount; }
+1 -1
View File
@@ -52,8 +52,8 @@ public:
void priority(uint32_t pri) { m_priority = pri; }
uint32_t cost() const { return m_cost; }
void cost(uint32_t cost) { m_cost = cost; }
void predictStart(uint64_t time) { m_predictStart = time; }
uint64_t predictStart() const { return m_predictStart; }
void predictStart(uint64_t time) { m_predictStart = time; }
string name() const override VL_MT_STABLE { return "mt"s + std::to_string(id()); }
string hashName() const { return m_hashName; }
void dump(std::ostream& str) const;
+1 -1
View File
@@ -371,7 +371,7 @@ bool FileLine::warnOff(const string& msg, bool flag) {
return true;
}
const V3ErrorCode code{cmsg};
if (code < V3ErrorCode::EC_FIRST_WARN) {
if (code.hardError()) {
return false;
} else {
warnOff(code, flag);
+35 -18
View File
@@ -46,7 +46,6 @@
#include "V3MemberMap.h"
#include <set>
#include <vector>
VL_DEFINE_DEBUG_FUNCTIONS;
@@ -108,7 +107,7 @@ public:
// Move variables into the class
for (AstVar* varp : m_captureOrder) {
if (varp->direction() == VDirection::INPUT) {
if (varp->direction().isAny()) {
varp = varp->cloneTree(false);
varp->direction(VDirection::NONE);
} else {
@@ -162,18 +161,28 @@ public:
AstNode* initsp = nullptr; // Arguments need to be copied
for (AstVar* varp : m_captureOrder) {
if (varp->direction() != VDirection::INPUT) continue;
if (!varp->direction().isAny()) continue;
AstMemberSel* const memberselp = new AstMemberSel{
varp->fileline(),
new AstVarRef{varp->fileline(), m_instance.m_handlep, VAccess::WRITE},
varp->dtypep()};
memberselp->name(varp->name());
memberselp->varp(VN_AS(memberMap.findMember(m_instance.m_classp, varp->name()), Var));
AstNode* initAsgnp
= new AstAssign{varp->fileline(), memberselp,
new AstVarRef{varp->fileline(), varp, VAccess::READ}};
initsp = AstNode::addNext(initsp, initAsgnp);
if (varp->direction().isNonOutput()) {
AstMemberSel* const memberselp = new AstMemberSel{
varp->fileline(),
new AstVarRef{varp->fileline(), m_instance.m_handlep, VAccess::WRITE},
VN_AS(memberMap.findMember(m_instance.m_classp, varp->name()), Var)};
AstNode* initAsgnp
= new AstAssign{varp->fileline(), memberselp,
new AstVarRef{varp->fileline(), varp, VAccess::READ}};
initsp = AstNode::addNext(initsp, initAsgnp);
}
if (varp->direction().isWritable()) {
AstMemberSel* const memberselp = new AstMemberSel{
varp->fileline(),
new AstVarRef{varp->fileline(), m_instance.m_handlep, VAccess::READ},
VN_AS(memberMap.findMember(m_instance.m_classp, varp->name()), Var)};
AstNode* writebackAsgnp = new AstAssign{
varp->fileline(), new AstVarRef{varp->fileline(), varp, VAccess::WRITE},
memberselp};
stmtp = AstNode::addNext(stmtp, writebackAsgnp);
}
}
if (initsp) AstNode::addNext(asgnp, initsp);
@@ -307,9 +316,8 @@ class DynScopeVisitor final : public VNVisitor {
refp->unlinkFrBack(&handle);
AstMemberSel* const membersel = new AstMemberSel{
refp->fileline(), new AstVarRef{refp->fileline(), dynScope.m_handlep, refp->access()},
refp->dtypep()};
membersel->name(refp->varp()->name());
if (refp->varp()->direction() == VDirection::INPUT) {
refp->varp()};
if (refp->varp()->direction().isAny()) {
membersel->varp(
VN_AS(m_memberMap.findMember(dynScope.m_classp, refp->varp()->name()), Var));
} else {
@@ -407,8 +415,17 @@ class DynScopeVisitor final : public VNVisitor {
void visit(AstVarRef* nodep) override {
ForkDynScopeFrame* const framep = frameOf(nodep->varp());
if (!framep) return;
if (needsDynScope(nodep)) {
if (m_afterTimingControl && nodep->varp()->isWritable()
&& nodep->access().isWriteOrRW()) {
// The output variable may not exist after a delay, so we can't just write to it
nodep->v3warn(
E_UNSUPPORTED,
"Unsupported: Writing to a captured "
<< (nodep->varp()->isInoutish() ? "inout" : "output") << " variable in a "
<< (VN_IS(nodep->backp(), AssignDly) ? "non-blocking assignment" : "fork")
<< " after a timing control");
}
if (!framep->instance().initialized()) framep->createInstancePrototype();
framep->captureVarInsert(nodep->varp());
}
@@ -434,7 +451,7 @@ class DynScopeVisitor final : public VNVisitor {
forkp->addStmtsp(nodep);
UINFO(9, "assign new fork " << forkp << endl);
} else {
iterateChildren(nodep);
visit(static_cast<AstNodeStmt*>(nodep));
}
}
void visit(AstNode* nodep) override {
+8
View File
@@ -47,4 +47,12 @@ struct FunctionTraits<ReturnType (ClassType::*)(Args...) const> VL_NOT_FINAL {
};
};
template <typename T_Callable, size_t index>
struct FunctionArgNoPointerNoCV final {
using Traits = FunctionTraits<T_Callable>;
using T_Arg = typename Traits::template arg<index>::type;
using T_ArgNoPtr = typename std::remove_pointer<T_Arg>::type;
using type = typename std::remove_cv<T_ArgNoPtr>::type;
};
#endif
+7
View File
@@ -79,6 +79,13 @@ void V3Global::readFiles() {
"Cannot find file containing library module: ");
}
// Read hierarchical type parameter file
const string filename = v3Global.opt.hierParamFile();
if (!filename.empty()) {
parser.parseFile(new FileLine{FileLine::commandLineFilename()}, filename, false,
"Cannot open file containing hierarchical parameter declarations: ");
}
// v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("parse.tree"));
V3Error::abortIfErrors();
+5 -2
View File
@@ -114,6 +114,7 @@ class V3Global final {
bool m_dpi = false; // Need __Dpi include files
bool m_hasEvents = false; // Design uses SystemVerilog named events
bool m_hasClasses = false; // Design uses SystemVerilog classes
bool m_hasSampled = false; // Design uses SAMPLED expresions
bool m_hasVirtIfaces = false; // Design uses virtual interfaces
bool m_usesProbDist = false; // Uses $dist_*
bool m_usesStdPackage = false; // Design uses the std package
@@ -168,6 +169,8 @@ public:
void setHasEvents() { m_hasEvents = true; }
bool hasClasses() const { return m_hasClasses; }
void setHasClasses() { m_hasClasses = true; }
bool hasSampled() const { return m_hasSampled; }
void setHasSampled() { m_hasSampled = true; }
bool hasVirtIfaces() const { return m_hasVirtIfaces; }
void setHasVirtIfaces() { m_hasVirtIfaces = true; }
bool usesProbDist() const { return m_usesProbDist; }
@@ -185,10 +188,10 @@ public:
UASSERT(!m_hierPlanp, "call once");
m_hierPlanp = plan;
}
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
bool useParallelBuild() const { return m_useParallelBuild; }
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
bool useRandomizeMethods() const { return m_useRandomizeMethods; }
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }
void saveJsonPtrFieldName(const std::string& fieldName);
void ptrNamesDumpJson(std::ostream& os);
void idPtrMapDumpJson(std::ostream& os);
+1 -1
View File
@@ -368,7 +368,7 @@ class HasherVisitor final : public VNVisitorConst {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstInitArray* nodep) override {
if (const AstAssocArrayDType* const dtypep = VN_CAST(nodep->dtypep(), AssocArrayDType)) {
if (VN_IS(nodep->dtypep(), AssocArrayDType)) {
if (nodep->defaultp()) {
m_hash
+= hashNodeAndIterate(nodep->defaultp(), HASH_DTYPE, HASH_CHILDREN, []() {});
+100 -41
View File
@@ -38,29 +38,36 @@
// Here is more detailed internal process.
// 1) Parser adds VPragmaType::HIER_BLOCK of AstPragma to modules
// that are marked with /*verilator hier_block*/ metacomment in Verilator run a).
// 2) AstModule with HIER_BLOCK pragma is marked modp->hier_block(true)
// 2) If module type parameters are present, V3Config marks hier param modules
// (marked with hier_params verilator config pragma) as modp->hierParams(true).
// This is done in run b), de-parametrized modules are mapped with their params one-to-one.
// 3) AstModule with HIER_BLOCK pragma is marked modp->hierBlock(true)
// in V3LinkResolve.cpp during run a).
// 3) In V3LinkCells.cpp, the following things are done during run b) and c).
// 3-1) Delete the upper modules of the hierarchical block because the top module in run b) is
// 4) In V3LinkCells.cpp, the following things are done during run b) and c).
// 4-1) Delete the upper modules of the hierarchical block because the top module in run b) is
// hierarchical block, not the top module of run c).
// 3-2) If the top module of the run b) or c) instantiates other hierarchical blocks that is
// 4-2) If the top module of the run b) or c) instantiates other hierarchical blocks that is
// parameterized,
// module and task names are renamed to the original name to be compatible with the
// hier module to be called.
//
// Parameterized modules have unique name by V3Param.cpp. The unique name contains '__' and
// Verilator encodes '__' when loading such symbols.
// 4) V3LinkDot.cpp checks dotted access across hierarchical block boundary.
// 5) In V3Dead.cpp, some parameters of parameterized modules are protected not to be deleted even
// 5) In V3LinkDot.cpp,
// 5-1) Dotted access across hierarchical block boundary is checked. Currently hierarchical
// block references are not supported.
// 5-2) If present, parameters in hier params module replace parameter values of de-parametrized
// module in run b).
// 6) In V3Dead.cpp, some parameters of parameterized modules are protected not to be deleted even
// if the parameter is not referred. This protection is necessary to match step 6) below.
// 6) In V3Param.cpp, use --lib-create wrapper of the parameterized module made in b) and c).
// 7) In V3Param.cpp, use --lib-create wrapper of the parameterized module made in b) and c).
// If a hierarchical block is a parameterized module and instantiated in multiple locations,
// all parameters must exactly match.
// 7) In V3HierBlock.cpp, relationship among hierarchical blocks are checked in run a).
// 8) In V3HierBlock.cpp, relationships among hierarchical blocks are checked in run a).
// (which block uses other blocks..)
// 8) In V3EmitMk.cpp, ${prefix}_hier.mk is created in run a).
// 9) In V3EmitMk.cpp, ${prefix}_hier.mk is created in run a).
//
// There are two hidden command options.
// There are three hidden command options:
// --hierarchical-child is added to Verilator run b).
// --hierarchical-block module_name,mangled_name,name0,value0,name1,value1,...
// module_name :The original modulename
@@ -70,12 +77,18 @@
// value :Overridden value of the parameter
//
// Used for b) and c).
// This options is repeated for all instantiating hierarchical blocks.
// These options are repeated for all instantiated hierarchical blocks.
// --hierarchical-params-file filename
// filename :Name of a hierarchical parameters file
//
// Added in a), used for b).
// Each de-parametrized module version has exactly one hier params file specified.
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3HierBlock.h"
#include "V3EmitV.h"
#include "V3File.h"
#include "V3Os.h"
#include "V3Stats.h"
@@ -88,11 +101,15 @@
VL_DEFINE_DEBUG_FUNCTIONS;
static string V3HierCommandArgsFileName(const string& prefix, bool forCMake) {
static string V3HierCommandArgsFilename(const string& prefix, bool forCMake) {
return v3Global.opt.makeDir() + "/" + prefix
+ (forCMake ? "__hierCMakeArgs.f" : "__hierMkArgs.f");
}
static string V3HierParametersFileName(const string& prefix) {
return v3Global.opt.makeDir() + "/" + prefix + "__hierParameters.v";
}
static void V3HierWriteCommonInputs(const V3HierBlock* hblockp, std::ostream* of, bool forCMake) {
string topModuleFile;
if (hblockp) topModuleFile = hblockp->vFileIfNecessary();
@@ -107,7 +124,8 @@ static void V3HierWriteCommonInputs(const V3HierBlock* hblockp, std::ostream* of
//######################################################################
V3HierBlock::StrGParams V3HierBlock::stringifyParams(const GParams& gparams, bool forGOption) {
V3HierBlock::StrGParams V3HierBlock::stringifyParams(const V3HierBlockParams::GParams& gparams,
bool forGOption) {
StrGParams strParams;
for (const auto& gparam : gparams) {
if (const AstConst* const constp = VN_CAST(gparam->valuep(), Const)) {
@@ -159,17 +177,21 @@ V3StringList V3HierBlock::commandArgs(bool forCMake) const {
opts.push_back(" --protect-key " + v3Global.opt.protectKeyDefaulted());
opts.push_back(" --hierarchical-child " + cvtToStr(v3Global.opt.threads()));
const StrGParams gparamsStr = stringifyParams(gparams(), true);
for (StrGParams::const_iterator paramIt = gparamsStr.begin(); paramIt != gparamsStr.end();
++paramIt) {
opts.push_back("-G" + paramIt->first + "=" + paramIt->second + "");
const StrGParams gparamsStr = stringifyParams(params().gparams(), true);
for (const StrGParam& param : gparamsStr) {
const string name = param.first;
const string value = param.second;
opts.push_back("-G" + name + "=" + value + "");
}
if (!params().gTypeParams().empty())
opts.push_back(" --hierarchical-params-file " + typeParametersFilename());
return opts;
}
V3StringList V3HierBlock::hierBlockArgs() const {
V3StringList opts;
const StrGParams gparamsStr = stringifyParams(gparams(), false);
const StrGParams gparamsStr = stringifyParams(params().gparams(), false);
opts.push_back("--hierarchical-block ");
string s = modp()->origName(); // origName
s += "," + modp()->name(); // mangledName
@@ -184,21 +206,27 @@ V3StringList V3HierBlock::hierBlockArgs() const {
string V3HierBlock::hierPrefix() const { return "V" + modp()->name(); }
string V3HierBlock::hierSomeFile(bool withDir, const char* prefix, const char* suffix) const {
string V3HierBlock::hierSomeFilename(bool withDir, const char* prefix, const char* suffix) const {
string s;
if (withDir) s = hierPrefix() + '/';
s += prefix + modp()->name() + suffix;
return s;
}
string V3HierBlock::hierWrapper(bool withDir) const { return hierSomeFile(withDir, "", ".sv"); }
string V3HierBlock::hierWrapperFilename(bool withDir) const {
return hierSomeFilename(withDir, "", ".sv");
}
string V3HierBlock::hierMk(bool withDir) const { return hierSomeFile(withDir, "V", ".mk"); }
string V3HierBlock::hierMkFilename(bool withDir) const {
return hierSomeFilename(withDir, "V", ".mk");
}
string V3HierBlock::hierLib(bool withDir) const { return hierSomeFile(withDir, "lib", ".a"); }
string V3HierBlock::hierLibFilename(bool withDir) const {
return hierSomeFilename(withDir, "lib", ".a");
}
string V3HierBlock::hierGenerated(bool withDir) const {
return hierWrapper(withDir) + ' ' + hierMk(withDir);
string V3HierBlock::hierGeneratedFilenames(bool withDir) const {
return hierWrapperFilename(withDir) + ' ' + hierMkFilename(withDir);
}
string V3HierBlock::vFileIfNecessary() const {
@@ -211,12 +239,12 @@ string V3HierBlock::vFileIfNecessary() const {
}
void V3HierBlock::writeCommandArgsFile(bool forCMake) const {
const std::unique_ptr<std::ofstream> of{V3File::new_ofstream(commandArgsFileName(forCMake))};
const std::unique_ptr<std::ofstream> of{V3File::new_ofstream(commandArgsFilename(forCMake))};
*of << "--cc\n";
if (!forCMake) {
for (const auto& hierblockp : m_children) {
*of << v3Global.opt.makeDir() << "/" << hierblockp->hierWrapper(true) << "\n";
*of << v3Global.opt.makeDir() << "/" << hierblockp->hierWrapperFilename(true) << "\n";
}
*of << "-Mdir " << v3Global.opt.makeDir() << "/" << hierPrefix() << " \n";
}
@@ -228,8 +256,31 @@ void V3HierBlock::writeCommandArgsFile(bool forCMake) const {
*of << v3Global.opt.allArgsStringForHierBlock(false, forCMake) << "\n";
}
string V3HierBlock::commandArgsFileName(bool forCMake) const {
return V3HierCommandArgsFileName(hierPrefix(), forCMake);
string V3HierBlock::commandArgsFilename(bool forCMake) const {
return V3HierCommandArgsFilename(hierPrefix(), forCMake);
}
string V3HierBlock::typeParametersFilename() const {
return V3HierParametersFileName(hierPrefix());
}
void V3HierBlock::writeParametersFile() const {
if (m_params.gTypeParams().empty()) return;
VHashSha256 hash{"type params"};
const string moduleName = "Vhsh" + hash.digestSymbol();
const std::unique_ptr<std::ofstream> of{V3File::new_ofstream(typeParametersFilename())};
*of << "module " << moduleName << ";\n";
for (const AstParamTypeDType* const gparam : m_params.gTypeParams()) {
AstTypedef* tdefp
= new AstTypedef(new FileLine{FileLine::builtInFilename()}, gparam->name(), nullptr,
VFlagChildDType{}, gparam->skipRefp()->cloneTreePure(true));
V3EmitV::verilogForTree(tdefp, *of);
VL_DO_DANGLING(tdefp->deleteTree(), tdefp);
}
*of << "endmodule\n\n";
*of << "`verilator_config\n";
*of << "hier_params -module \"" << moduleName << "\"\n";
}
//######################################################################
@@ -245,7 +296,7 @@ class HierBlockUsageCollectVisitor final : public VNVisitorConst {
AstModule* m_modp = nullptr; // The current module
AstModule* m_hierBlockp = nullptr; // The nearest parent module that is a hierarchical block
ModuleSet m_referred; // Modules that have hier_block pragma
V3HierBlock::GParams m_gparams; // list of variables that is VVarType::GPARAM
V3HierBlockParams m_params;
void visit(AstModule* nodep) override {
// Don't visit twice
@@ -256,23 +307,23 @@ class HierBlockUsageCollectVisitor final : public VNVisitorConst {
VL_RESTORER(m_modp);
AstModule* const prevHierBlockp = m_hierBlockp;
ModuleSet prevReferred;
V3HierBlock::GParams prevGParams;
V3HierBlockParams previousParams;
m_modp = nodep;
if (nodep->hierBlock()) {
m_hierBlockp = nodep;
prevReferred.swap(m_referred);
}
prevGParams.swap(m_gparams);
previousParams.swap(m_params);
iterateChildrenConst(nodep);
if (nodep->hierBlock()) {
m_planp->add(nodep, m_gparams);
m_planp->add(nodep, m_params);
for (const AstModule* modp : m_referred) m_planp->registerUsage(nodep, modp);
m_hierBlockp = prevHierBlockp;
m_referred = prevReferred;
}
m_gparams = prevGParams;
m_params.swap(previousParams);
}
void visit(AstCell* nodep) override {
// Visit used module here to know that the module is hier_block or not.
@@ -288,10 +339,13 @@ class HierBlockUsageCollectVisitor final : public VNVisitorConst {
if (m_modp && m_modp->hierBlock() && nodep->isIfaceRef() && !nodep->isIfaceParent()) {
nodep->v3error("Modport cannot be used at the hierarchical block boundary");
}
if (nodep->isGParam() && nodep->overriddenParam()) m_gparams.push_back(nodep);
if (nodep->isGParam() && nodep->overriddenParam()) m_params.add(nodep);
}
void visit(AstParamTypeDType* nodep) override { m_params.add(nodep); }
void visit(AstNodeExpr*) override {} // Accelerate
void visit(AstConstPool*) override {} // Accelerate
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
public:
@@ -303,11 +357,12 @@ public:
//######################################################################
void V3HierBlockPlan::add(const AstNodeModule* modp, const std::vector<AstVar*>& gparams) {
void V3HierBlockPlan::add(const AstNodeModule* modp, const V3HierBlockParams& params) {
const auto pair = m_blocks.emplace(modp, nullptr);
if (pair.second) {
V3HierBlock* hblockp = new V3HierBlock{modp, gparams};
UINFO(3, "Add " << modp->prettyNameQ() << " with " << gparams.size() << " parameters"
V3HierBlock* hblockp = new V3HierBlock{modp, params};
UINFO(3, "Add " << modp->prettyNameQ() << " with " << params.gparams().size()
<< " parameters and " << params.gTypeParams().size() << " type parameters"
<< std::endl);
pair.first->second = hblockp;
}
@@ -394,11 +449,11 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forCMake) const {
}
// For the top module
const std::unique_ptr<std::ofstream> of{
V3File::new_ofstream(topCommandArgsFileName(forCMake))};
V3File::new_ofstream(topCommandArgsFilename(forCMake))};
if (!forCMake) {
// Load wrappers first not to be overwritten by the original HDL
for (const_iterator it = begin(); it != end(); ++it) {
*of << it->second->hierWrapper(true) << "\n";
*of << it->second->hierWrapperFilename(true) << "\n";
}
}
V3HierWriteCommonInputs(nullptr, of.get(), forCMake);
@@ -425,6 +480,10 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forCMake) const {
*of << v3Global.opt.allArgsStringForHierBlock(true, forCMake) << "\n";
}
string V3HierBlockPlan::topCommandArgsFileName(bool forCMake) {
return V3HierCommandArgsFileName(v3Global.opt.prefix(), forCMake);
string V3HierBlockPlan::topCommandArgsFilename(bool forCMake) {
return V3HierCommandArgsFilename(v3Global.opt.prefix(), forCMake);
}
void V3HierBlockPlan::writeParametersFiles() const {
for (const auto& block : *this) { block.second->writeParametersFile(); }
}
+46 -15
View File
@@ -32,19 +32,44 @@
class AstNetlist;
class AstNodeModule;
class AstParamTypeDType;
class AstVar;
//######################################################################
class V3HierBlock final {
class V3HierBlockParams final {
public:
using GParams = std::vector<AstVar*>;
using GTypeParams = std::vector<AstParamTypeDType*>;
private:
GParams m_params;
GTypeParams m_typeParams;
public:
void add(AstVar* param) { m_params.push_back(param); }
void add(AstParamTypeDType* param) { m_typeParams.push_back(param); }
const GParams& gparams() const { return m_params; };
const GTypeParams& gTypeParams() const { return m_typeParams; };
GParams& gparams() { return m_params; };
GTypeParams& gTypeParams() { return m_typeParams; };
void swap(V3HierBlockParams& other) {
m_params.swap(other.m_params);
m_typeParams.swap(other.m_typeParams);
}
};
class V3HierBlock final {
public:
using HierBlockSet = std::unordered_set<V3HierBlock*>;
private:
// TYPES
// Parameter name, stringified value
using StrGParams = std::vector<std::pair<std::string, std::string>>;
using StrGParam = std::pair<string, string>;
using StrGParams = std::vector<StrGParam>;
// MEMBERS
const AstNodeModule* const m_modp; // Hierarchical block module
@@ -53,16 +78,18 @@ private:
// Hierarchical blocks that this block directly or indirectly instantiates
HierBlockSet m_children;
// Parameters that are overridden by #(.param(value)) syntax.
const GParams m_gparams;
const V3HierBlockParams m_params;
// METHODS
VL_UNCOPYABLE(V3HierBlock);
static StrGParams stringifyParams(const GParams& gparams, bool forGOption) VL_MT_DISABLED;
static StrGParams stringifyParams(const V3HierBlockParams::GParams& params,
bool forGOption) VL_MT_DISABLED;
public:
V3HierBlock(const AstNodeModule* modp, const GParams& gparams)
V3HierBlock(const AstNodeModule* modp, const V3HierBlockParams& params)
: m_modp{modp}
, m_gparams{gparams} {}
, m_params{params} {}
~V3HierBlock() VL_MT_DISABLED;
void addParent(V3HierBlock* parentp) { m_parents.insert(parentp); }
@@ -70,23 +97,26 @@ public:
bool hasChild() const { return !m_children.empty(); }
const HierBlockSet& parents() const { return m_parents; }
const HierBlockSet& children() const { return m_children; }
const GParams& gparams() const { return m_gparams; }
const V3HierBlockParams& params() const { return m_params; }
const AstNodeModule* modp() const { return m_modp; }
// For emitting Makefile and CMakeLists.txt
V3StringList commandArgs(bool forCMake) const VL_MT_DISABLED;
V3StringList hierBlockArgs() const VL_MT_DISABLED;
string hierPrefix() const VL_MT_DISABLED;
string hierSomeFile(bool withDir, const char* prefix, const char* suffix) const VL_MT_DISABLED;
string hierWrapper(bool withDir) const VL_MT_DISABLED;
string hierMk(bool withDir) const VL_MT_DISABLED;
string hierLib(bool withDir) const VL_MT_DISABLED;
string hierGenerated(bool withDir) const VL_MT_DISABLED;
string hierSomeFilename(bool withDir, const char* prefix,
const char* suffix) const VL_MT_DISABLED;
string hierWrapperFilename(bool withDir) const VL_MT_DISABLED;
string hierMkFilename(bool withDir) const VL_MT_DISABLED;
string hierLibFilename(bool withDir) const VL_MT_DISABLED;
string hierGeneratedFilenames(bool withDir) const VL_MT_DISABLED;
// Returns the original HDL file if it is not included in v3Global.opt.vFiles().
string vFileIfNecessary() const VL_MT_DISABLED;
// Write command line arguments to .f file for this hierarchical block
void writeCommandArgsFile(bool forCMake) const VL_MT_DISABLED;
string commandArgsFileName(bool forCMake) const VL_MT_DISABLED;
void writeParametersFile() const VL_MT_DISABLED;
string commandArgsFilename(bool forCMake) const VL_MT_DISABLED;
string typeParametersFilename() const VL_MT_DISABLED;
};
//######################################################################
@@ -104,7 +134,7 @@ public:
using const_iterator = HierMap::const_iterator;
using HierVector = std::vector<const V3HierBlock*>;
void add(const AstNodeModule* modp, const std::vector<AstVar*>& gparams) VL_MT_DISABLED;
void add(const AstNodeModule* modp, const V3HierBlockParams& params) VL_MT_DISABLED;
void registerUsage(const AstNodeModule* parentp, const AstNodeModule* childp) VL_MT_DISABLED;
const_iterator begin() const { return m_blocks.begin(); }
@@ -117,7 +147,8 @@ public:
// Write command line arguments to .f files for child Verilation run
void writeCommandArgsFiles(bool forCMake) const VL_MT_DISABLED;
static string topCommandArgsFileName(bool forCMake) VL_MT_DISABLED;
void writeParametersFiles() const VL_MT_DISABLED;
static string topCommandArgsFilename(bool forCMake) VL_MT_DISABLED;
static void createPlan(AstNetlist* nodep) VL_MT_DISABLED;
};
+3 -5
View File
@@ -46,17 +46,13 @@ class LifeState final {
public:
VDouble0 m_statAssnDel; // Statistic tracking
VDouble0 m_statAssnCon; // Statistic tracking
std::vector<AstNode*> m_unlinkps;
// CONSTRUCTORS
LifeState() = default;
~LifeState() {
V3Stats::addStatSum("Optimizations, Lifetime assign deletions", m_statAssnDel);
V3Stats::addStatSum("Optimizations, Lifetime constant prop", m_statAssnCon);
for (AstNode* ip : m_unlinkps) VL_DO_DANGLING(ip->unlinkFrBack()->deleteTree(), ip);
}
// METHODS
void pushUnlinkDeletep(AstNode* nodep) { m_unlinkps.push_back(nodep); }
};
//######################################################################
@@ -124,6 +120,7 @@ class LifeBlock final {
LifeBlock* const m_aboveLifep; // Upper life, or nullptr
LifeState* const m_statep; // Current global state
bool m_replacedVref = false; // Replaced a variable reference since last clearing
VNDeleter m_deleter; // Used to delay deletion of nodes
public:
LifeBlock(LifeBlock* aboveLifep, LifeState* statep)
@@ -145,7 +142,8 @@ public:
// above our current iteration point.
if (debug() > 4) oldassp->dumpTree("- REMOVE/SAMEBLK: ");
entp->complexAssign();
VL_DO_DANGLING(m_statep->pushUnlinkDeletep(oldassp), oldassp);
oldassp->unlinkFrBack();
VL_DO_DANGLING(m_deleter.pushDeletep(oldassp), oldassp);
++m_statep->m_statAssnDel;
}
}
+44 -5
View File
@@ -398,6 +398,7 @@ class LinkCellsVisitor final : public VNVisitor {
for (AstNode* portnodep = nodep->modp()->stmtsp(); portnodep;
portnodep = portnodep->nextp()) {
if (const AstPort* const portp = VN_CAST(portnodep, Port)) {
if (ports.find(portp->name()) == ports.end()
&& ports.find("__pinNumber" + cvtToStr(portp->pinNum())) == ports.end()) {
if (pinStar) {
@@ -411,11 +412,49 @@ class LinkCellsVisitor final : public VNVisitor {
newp->svImplicit(true);
nodep->addPinsp(newp);
} else { // warn on the CELL that needs it, not the port
nodep->v3warn(PINMISSING,
"Cell has missing pin: " << portp->prettyNameQ());
AstPin* const newp
= new AstPin{nodep->fileline(), 0, portp->name(), nullptr};
nodep->addPinsp(newp);
// We *might* not want to warn on this port, if it happened to be
// an input with a default value in the module declaration. Our
// AstPort* (portp) doesn't have that information, but the Module
// (nodep->modp()) statements do that have information in an AstVar*
// with the same name() as the port. We'll look for that in-line here,
// if a port is missing on this instance.
// Get the AstVar for this AstPort, if it exists, using this
// inefficient O(n) lookup to match the port name.
const AstVar* portp_varp = nullptr;
for (AstNode* module_stmtsp = nodep->modp()->stmtsp(); module_stmtsp;
module_stmtsp = module_stmtsp->nextp()) {
if (const AstVar* const varp = VN_CAST(module_stmtsp, Var)) {
if (!varp->isParam() && varp->name() == portp->name()) {
// not a parameter, same name, break, this is our varp
// (AstVar*)
portp_varp = varp;
break;
}
}
}
// Is the matching Module port: an INPUT, with default value (in
// valuep):
if (portp_varp && portp_varp->isInput() && portp_varp->valuep()) {
// Do not warn
// Create b/c not already connected, and it does exist.
AstPin* const newp
= new AstPin{nodep->fileline(), 0, portp->name(), nullptr};
nodep->addPinsp(newp);
} else {
nodep->v3warn(PINMISSING,
"Cell has missing pin: "
<< portp->prettyNameQ() << '\n'
<< nodep->warnContextPrimary() << '\n'
<< portp->warnOther()
<< "... Location of port declaration\n"
<< portp->warnContextSecondary());
AstPin* const newp
= new AstPin{nodep->fileline(), 0, portp->name(), nullptr};
nodep->addPinsp(newp);
}
}
}
}
+176 -40
View File
@@ -65,8 +65,10 @@
#include "V3LinkDot.h"
#include "V3Global.h"
#include "V3Graph.h"
#include "V3MemberMap.h"
#include "V3Parse.h"
#include "V3String.h"
#include "V3SymTable.h"
@@ -749,6 +751,8 @@ class LinkDotFindVisitor final : public VNVisitor {
AstClocking* m_clockingp = nullptr; // Current clocking block
VSymEnt* m_modSymp = nullptr; // Symbol Entry for current module
VSymEnt* m_curSymp = nullptr; // Symbol Entry for current table, where to lookup/insert
string
m_hierParamsName; // Name of module with hierarchical type parameters, empty when not used
string m_scope; // Scope text
const AstNodeBlock* m_blockp = nullptr; // Current Begin/end block
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
@@ -757,6 +761,7 @@ class LinkDotFindVisitor final : public VNVisitor {
bool m_explicitNew = false; // Hit a "new" function
int m_modBlockNum = 0; // Begin block number in module, 0=none seen
int m_modWithNum = 0; // With block number, 0=none seen
int m_modArgNum = 0; // Arg block number for randomize(), 0=none seen
// METHODS
void makeImplicitNew(AstClass* nodep) {
@@ -869,6 +874,7 @@ class LinkDotFindVisitor final : public VNVisitor {
VL_RESTORER(m_paramNum);
VL_RESTORER(m_modBlockNum);
VL_RESTORER(m_modWithNum);
VL_RESTORER(m_modArgNum);
if (doit && nodep->user2()) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Identically recursive module (module instantiates "
@@ -895,6 +901,7 @@ class LinkDotFindVisitor final : public VNVisitor {
m_paramNum = 0;
m_modBlockNum = 0;
m_modWithNum = 0;
m_modArgNum = 0;
// m_modSymp/m_curSymp for non-packages set by AstCell above this module
// Iterate
nodep->user2(true);
@@ -914,9 +921,22 @@ class LinkDotFindVisitor final : public VNVisitor {
iterateChildren(nodep);
nodep->user4(true);
} else { // !doit
// Will be optimized away later
// Can't remove now, as our backwards iterator will throw up
UINFO(5, "Module not under any CELL or top - dead module: " << nodep << endl);
if (nodep->hierParams()) {
UINFO(1, "Found module with hier type parameters" << endl);
m_hierParamsName = nodep->name();
for (const AstNode* node = nodep->op2p(); node; node = node->nextp()) {
if (const AstTypedef* const tdef = VN_CAST(node, Typedef)) {
UINFO(1, "Inserting hier type parameter typedef: " << tdef << endl);
VSymEnt* const upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp();
m_curSymp = m_modSymp = m_statep->insertBlock(upperSymp, nodep->name(),
nodep, m_classOrPackagep);
}
}
} else {
// Will be optimized away later
// Can't remove now, as our backwards iterator will throw up
UINFO(5, "Module not under any CELL or top - dead module: " << nodep << endl);
}
}
}
@@ -930,6 +950,7 @@ class LinkDotFindVisitor final : public VNVisitor {
VL_RESTORER(m_paramNum);
VL_RESTORER(m_modBlockNum);
VL_RESTORER(m_modWithNum);
VL_RESTORER(m_modArgNum);
{
UINFO(4, " Link Class: " << nodep << endl);
VSymEnt* const upperSymp = m_curSymp;
@@ -943,6 +964,7 @@ class LinkDotFindVisitor final : public VNVisitor {
m_paramNum = 0;
m_modBlockNum = 0;
m_modWithNum = 0;
m_modArgNum = 0;
m_explicitNew = false;
// m_modSymp/m_curSymp for non-packages set by AstCell above this module
// Iterate
@@ -1360,6 +1382,29 @@ class LinkDotFindVisitor final : public VNVisitor {
}
void visit(AstParamTypeDType* nodep) override {
UASSERT_OBJ(m_curSymp, nodep, "Parameter type not under module/package/$unit");
// Replace missing param types with provided hierarchical type params.
if (!m_hierParamsName.empty()) {
if (const VSymEnt* const typedefEntp = m_curSymp->findIdFallback(m_hierParamsName)) {
const AstModule* modp = VN_CAST(typedefEntp->nodep(), Module);
for (const AstNode* node = modp ? modp->stmtsp() : nullptr; node;
node = node->nextp()) {
const AstTypedef* tdefp = VN_CAST(node, Typedef);
if (tdefp && tdefp->name() == nodep->name() && m_statep->forPrimary()) {
UINFO(8, "Replacing type of" << nodep << endl
<< " with " << tdefp << endl);
AstNodeDType* const newType = tdefp->childDTypep();
AstNodeDType* const oldType = nodep->childDTypep();
oldType->replaceWith(newType->cloneTree(false));
oldType->deleteTree();
}
}
}
}
iterateChildren(nodep);
m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_classOrPackagep);
if (m_statep->forPrimary() && nodep->isGParam()) {
@@ -1486,6 +1531,7 @@ class LinkDotFindVisitor final : public VNVisitor {
// We use an int type, this might get changed in V3Width when types resolve
argrefp = new AstVar{parserefp->fileline(), VVarType::BLOCKTEMP,
parserefp->name(), argp->findSigned32DType()};
argrefp->lifetime(VLifetime::AUTOMATIC);
parserefp->replaceWith(argrefp);
VL_DO_DANGLING(parserefp->deleteTree(), parserefp);
// Insert argref's name into symbol table
@@ -1505,11 +1551,15 @@ class LinkDotFindVisitor final : public VNVisitor {
void visit(AstWithParse* nodep) override {
// Change WITHPARSE(FUNCREF, equation) to FUNCREF(WITH(equation))
const auto funcrefp = VN_AS(nodep->funcrefp(), NodeFTaskRef);
AstNodeFTaskRef* funcrefp = VN_CAST(nodep->funcrefp(), NodeFTaskRef);
if (const AstDot* const dotp = VN_CAST(nodep->funcrefp(), Dot))
funcrefp = VN_CAST(dotp->rhsp(), NodeFTaskRef);
UASSERT_OBJ(funcrefp, nodep, "'with' only can operate on a function/task");
string name = "item";
FileLine* argFl = nodep->fileline();
if (const auto argp = VN_CAST(funcrefp->pinsp(), Arg)) {
AstArg* argp = VN_CAST(funcrefp->pinsp(), Arg);
if (argp) argp->unlinkFrBackWithNext();
if (argp && funcrefp->name() != "randomize") {
if (const auto parserefp = VN_CAST(argp->exprp(), ParseRef)) {
name = parserefp->name();
argFl = parserefp->fileline();
@@ -1518,7 +1568,7 @@ class LinkDotFindVisitor final : public VNVisitor {
}
if (argp->nextp())
argp->nextp()->v3error("'with' function expects only up to one argument");
VL_DO_DANGLING(argp->unlinkFrBackWithNext()->deleteTree(), argp);
VL_DO_DANGLING(argp->deleteTree(), argp);
}
// Type depends on the method used, let V3Width figure it out later
if (nodep->exprsp()) { // Else empty expression and pretend no "with"
@@ -1528,7 +1578,8 @@ class LinkDotFindVisitor final : public VNVisitor {
nodep->exprsp()->unlinkFrBackWithNext()};
funcrefp->addPinsp(newp);
}
nodep->replaceWith(funcrefp->unlinkFrBack());
funcrefp->addPinsp(argp);
nodep->replaceWith(nodep->funcrefp()->unlinkFrBack());
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void visit(AstWith* nodep) override {
@@ -1602,7 +1653,7 @@ class LinkDotParamVisitor final : public VNVisitor {
void visit(AstConstPool*) override {}
void visit(AstNodeModule* nodep) override {
UINFO(5, " " << nodep << endl);
if (nodep->dead() || !nodep->user4()) {
if ((nodep->dead() || !nodep->user4()) && !nodep->hierParams()) {
UINFO(4, "Mark dead module " << nodep << endl);
UASSERT_OBJ(m_statep->forPrearray(), nodep,
"Dead module persisted past where should have removed");
@@ -1839,6 +1890,7 @@ class LinkDotScopeVisitor final : public VNVisitor {
symp->fallbackp(m_modSymp);
// No recursion, we don't want to pick up variables
}
void visit(AstConstraintForeach* nodep) override { iterateChildren(nodep); }
void visit(AstWith* nodep) override {
VSymEnt* const symp = m_statep->insertBlock(m_modSymp, nodep->name(), nodep, nullptr);
symp->fallbackp(m_modSymp);
@@ -2043,11 +2095,14 @@ class LinkDotResolveVisitor final : public VNVisitor {
VSymEnt* m_curSymp = nullptr; // SymEnt for current lookup point
VSymEnt* m_modSymp = nullptr; // SymEnt for current module
VSymEnt* m_pinSymp = nullptr; // SymEnt for pin lookups
VSymEnt* m_fromSymp = nullptr; // SymEnt for randomize lookups
const AstCell* m_cellp = nullptr; // Current cell
AstNodeModule* m_modp = nullptr; // Current module
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
AstMethodCall* m_randMethodCallp = nullptr; // Current randomize() method call
int m_modportNum = 0; // Uniqueify modport numbers
bool m_inSens = false; // True if in senitem
bool m_inWith = false; // True if in with
std::map<std::string, AstNode*> m_ifClassImpNames; // Names imported from interface class
std::set<AstClass*> m_extendsParam; // Classes that have a parameterized super class
// (except the default instances)
@@ -2209,9 +2264,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
new AstVarRef{clockingp->fileline(), eventp, VAccess::WRITE},
false}});
v3Global.setHasEvents();
eventp->user1p(new VSymEnt{m_statep->symsp(), eventp});
}
return reinterpret_cast<VSymEnt*>(clockingp->eventp()->user1p());
AstVar* const eventp = clockingp->eventp();
if (!eventp->user1p()) eventp->user1p(new VSymEnt{m_statep->symsp(), eventp});
return reinterpret_cast<VSymEnt*>(eventp->user1p());
}
bool isParamedClassRefDType(const AstNode* classp) {
@@ -2461,6 +2517,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
UINFO(8, " " << nodep << endl);
const DotStates lastStates = m_ds;
const bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed
VL_RESTORER(m_fromSymp);
{
if (start) { // Starting dot sequence
if (debug() >= 9) nodep->dumpTree("- dot-in: ");
@@ -2576,6 +2633,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
"ParseRefs should no longer exist");
const DotStates lastStates = m_ds;
const bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed
if (start) {
m_ds.init(m_curSymp);
// Note m_ds.m_dot remains nullptr; this is a reference not under a dot
@@ -2644,12 +2702,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
classOrPackagep = cpackagerefp->classOrPackagep();
UASSERT_OBJ(classOrPackagep, m_ds.m_dotp->lhsp(), "Bad package link");
if (cpackagerefp->name() == "local::") {
if (m_pinSymp) {
m_ds.m_dotSymp = m_curSymp->fallbackp();
} else {
nodep->v3error("Illegal 'local::' outside 'randomize() with'");
m_ds.m_dotErr = true;
}
m_fromSymp = nullptr;
} else {
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep);
}
@@ -2671,6 +2724,34 @@ class LinkDotResolveVisitor final : public VNVisitor {
VSymEnt* foundp;
string baddot;
VSymEnt* okSymp = nullptr;
if (m_fromSymp) {
foundp = m_fromSymp->findIdFlat(nodep->name());
if (foundp) {
if (!m_inWith) {
UASSERT_OBJ(m_randMethodCallp, nodep, "Expected to be under randomize()");
// This will start failing once complex expressions are allowed on the LHS
// of randomize() with args
UASSERT_OBJ(VN_IS(m_randMethodCallp->fromp(), VarRef), m_randMethodCallp,
"Expected simple randomize target");
// A ParseRef is used here so that the dot RHS gets resolved
nodep->replaceWith(new AstMemberSel{
nodep->fileline(),
new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT,
m_randMethodCallp->fromp()->name()},
VFlagChildDType{}, nodep->name()});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
UINFO(9, " randomize-with fromSym " << foundp->nodep() << endl);
if (m_ds.m_dotPos != DP_NONE) m_ds.m_dotPos = DP_MEMBER;
AstLambdaArgRef* const lambdaRefp
= new AstLambdaArgRef{nodep->fileline(), "item", false};
nodep->replaceWith(new AstMemberSel{nodep->fileline(), lambdaRefp,
VFlagChildDType{}, nodep->name()});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
}
if (allowScope) {
foundp = m_statep->findDotted(nodep->fileline(), m_ds.m_dotSymp, nodep->name(),
baddot, okSymp); // Maybe nullptr
@@ -2726,8 +2807,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
}
} else if (allowFTask && VN_IS(foundp->nodep(), NodeFTask)) {
AstTaskRef* const taskrefp
= new AstTaskRef{nodep->fileline(), nodep->name(), nullptr};
AstNodeFTaskRef* taskrefp;
if (VN_IS(foundp->nodep(), Task)) {
taskrefp = new AstTaskRef{nodep->fileline(), nodep->name(), nullptr};
} else {
taskrefp = new AstFuncRef{nodep->fileline(), nodep->name(), nullptr};
}
nodep->replaceWith(taskrefp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
if (start) m_ds = lastStates;
@@ -2847,7 +2932,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
} else if (AstConstraint* const consp = VN_CAST(foundp->nodep(), Constraint)) {
AstNode* const newp = new AstConstraintRef{nodep->fileline(), consp->name()};
AstNode* const newp = new AstConstraintRef{nodep->fileline(), nullptr, consp};
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
ok = true;
@@ -2875,6 +2960,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
} else if (VN_IS(foundp->nodep(), Clocking)) {
m_ds.m_dotSymp = foundp;
if (m_ds.m_dotText != "") m_ds.m_dotText += "." + nodep->name();
ok = m_ds.m_dotPos == DP_SCOPE;
} else if (const AstNodeFTask* const ftaskp = VN_CAST(foundp->nodep(), NodeFTask)) {
@@ -2974,6 +3060,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
UINFO(4, "(Backto) Link ClassOrPackageRef: " << nodep << endl);
iterateChildren(nodep);
if (nodep->name() == "local::") {
if (!m_fromSymp) {
nodep->v3error("Illegal 'local::' outside 'randomize() with'");
m_ds.m_dotErr = true;
}
}
AstClass* const modClassp = VN_CAST(m_modp, Class);
if (m_statep->forPrimary() && refClassp && !nodep->paramsp()
&& nodep->classOrPackagep()->hasGParam()
@@ -3044,7 +3136,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot,
okSymp); // Maybe nullptr
if (!m_statep->forScopeCreation()) {
VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
if (m_inSens && foundp) {
if (AstClocking* const clockingp = VN_CAST(foundp->nodep(), Clocking)) {
foundp = getCreateClockingEventSymEnt(clockingp);
}
}
AstVar* const varp
= foundp ? foundToVarp(foundp, nodep, nodep->access()) : nullptr;
nodep->varp(varp);
@@ -3061,11 +3158,14 @@ class LinkDotResolveVisitor final : public VNVisitor {
// AstVarXRef's even though they are in the same module detect
// this and convert to normal VarRefs
if (!m_statep->forPrearray() && !m_statep->forScopeCreation()) {
if (VN_IS(nodep->dtypep(), IfaceRefDType)) {
AstVarRef* const newrefp
= new AstVarRef{nodep->fileline(), nodep->varp(), nodep->access()};
nodep->replaceWith(newrefp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
if (const AstIfaceRefDType* const ifaceDtp
= VN_CAST(nodep->dtypep(), IfaceRefDType)) {
if (!ifaceDtp->isVirtual()) {
AstVarRef* const newrefp
= new AstVarRef{nodep->fileline(), nodep->varp(), nodep->access()};
nodep->replaceWith(newrefp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
}
} else {
@@ -3111,10 +3211,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
void visit(AstMethodCall* nodep) override {
// Created here so should already be resolved.
VL_RESTORER(m_ds);
VL_RESTORER(m_pinSymp);
VL_RESTORER(m_fromSymp);
VL_RESTORER(m_randMethodCallp);
{
m_ds.init(m_curSymp);
if (nodep->name() == "randomize" && VN_IS(nodep->pinsp(), With)) {
if (nodep->name() == "randomize" && nodep->pinsp()) {
m_randMethodCallp = nodep;
const AstNodeDType* fromDtp = nodep->fromp()->dtypep();
if (!fromDtp) {
if (const AstNodeVarRef* const varRefp = VN_CAST(nodep->fromp(), NodeVarRef)) {
@@ -3131,9 +3233,17 @@ class LinkDotResolveVisitor final : public VNVisitor {
fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep();
}
}
if (!fromDtp)
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: 'randomize() with' on complex expressions");
if (!fromDtp) {
if (VN_IS(nodep->pinsp(), With)) {
nodep->v3warn(
E_UNSUPPORTED,
"Unsupported: 'randomize() with' on complex expressions");
} else {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Inline random variable control with "
"'randomize()' called on complex expressions");
}
}
}
if (m_statep->forPrimary() && isParamedClassRefDType(fromDtp)) {
m_ds.m_unresolvedClass = true;
@@ -3144,7 +3254,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
nodep->v3error("'randomize() with' on a non-class-instance "
<< fromDtp->prettyNameQ());
else
m_pinSymp = m_statep->getNodeSym(classDtp->classp());
m_fromSymp = m_statep->getNodeSym(classDtp->classp());
}
}
iterateChildren(nodep);
@@ -3178,6 +3288,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
}
VL_RESTORER(m_fromSymp);
bool staticAccess = false;
if (m_ds.m_unresolvedClass) {
// Unable to link before V3Param
@@ -3197,12 +3309,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
staticAccess = true;
AstClassOrPackageRef* const cpackagerefp
= VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef);
if (cpackagerefp->name() == "local") {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: " << AstNode::prettyNameQ(cpackagerefp->name()));
}
UASSERT_OBJ(cpackagerefp->classOrPackagep(), m_ds.m_dotp->lhsp(), "Bad package link");
nodep->classOrPackagep(cpackagerefp->classOrPackagep());
if (cpackagerefp->name() == "local::") {
m_fromSymp = nullptr;
} else {
nodep->classOrPackagep(cpackagerefp->classOrPackagep());
}
// Class/package :: HERE function() . method_called_on_function_return_value()
m_ds.m_dotPos = DP_MEMBER;
m_ds.m_dotp = nullptr;
@@ -3271,6 +3383,24 @@ class LinkDotResolveVisitor final : public VNVisitor {
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot,
okSymp); // Maybe nullptr
}
if (m_fromSymp) {
VSymEnt* const foundp = m_fromSymp->findIdFlat(nodep->name());
if (foundp && m_inWith) {
UINFO(9, " randomize-with fromSym " << foundp->nodep() << endl);
AstNodeExpr* argsp = nullptr;
if (nodep->pinsp()) {
iterateAndNextNull(nodep->pinsp());
argsp = nodep->pinsp()->unlinkFrBackWithNext();
}
if (m_ds.m_dotPos != DP_NONE) m_ds.m_dotPos = DP_MEMBER;
AstNode* const newp = new AstMethodCall{
nodep->fileline(), new AstLambdaArgRef{nodep->fileline(), "item", false},
VFlagChildDType{}, nodep->name(), argsp};
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
}
VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
AstNodeFTask* const taskp
= foundp ? VN_CAST(foundp->nodep(), NodeFTask) : nullptr; // Maybe nullptr
@@ -3309,8 +3439,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
} else if (VN_IS(nodep, New) && m_statep->forPrearray()) {
// Resolved in V3Width
} else if (nodep->name() == "randomize" || nodep->name() == "srandom"
|| nodep->name() == "get_randstate"
|| nodep->name() == "set_randstate") {
|| nodep->name() == "get_randstate" || nodep->name() == "set_randstate"
|| nodep->name() == "rand_mode" || nodep->name() == "constraint_mode") {
if (AstClass* const classp = VN_CAST(m_modp, Class)) {
nodep->classOrPackagep(classp);
} else {
@@ -3344,8 +3474,13 @@ class LinkDotResolveVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return;
} else {
VSpellCheck speller;
V3Parse::candidatePli(&speller);
const string suggest = speller.bestCandidateMsg(nodep->prettyName());
nodep->v3error(
"Unsupported or unknown PLI call: " << nodep->prettyNameQ());
"Unsupported or unknown PLI call: "
<< nodep->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : nodep->warnMore() + suggest));
}
} else {
const string suggest = m_statep->suggestSymFallback(
@@ -3504,9 +3639,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
UINFO(5, " " << nodep << endl);
checkNoDot(nodep);
VL_RESTORER(m_curSymp);
VL_RESTORER(m_inWith);
{
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
if (m_pinSymp) m_curSymp->importFromClass(m_statep->symsp(), m_pinSymp);
m_inWith = true;
iterateChildren(nodep);
}
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);
+1 -1
View File
@@ -246,7 +246,7 @@ class LinkIncVisitor final : public VNVisitor {
// Prepare a temporary variable
FileLine* const fl = nodep->fileline();
const string name = string{"__Vincrement"} + cvtToStr(++m_modIncrementsNum);
const string name = "__Vincrement"s + cvtToStr(++m_modIncrementsNum);
AstVar* const varp = new AstVar{
fl, VVarType::BLOCKTEMP, name, VFlagChildDType{},
new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{}, readp->cloneTree(true)}};
+1 -1
View File
@@ -201,7 +201,7 @@ class LinkJumpVisitor final : public VNVisitor {
// REPEAT(count,body) -> loop=count,WHILE(loop>0) { body, loop-- }
// Note var can be signed or unsigned based on original number.
AstNodeExpr* const countp = nodep->countp()->unlinkFrBackWithNext();
const string name = string{"__Vrepeat"} + cvtToStr(m_modRepeatNum++);
const string name = "__Vrepeat"s + cvtToStr(m_modRepeatNum++);
// Spec says value is integral, if negative is ignored
AstVar* const varp
= new AstVar{nodep->fileline(), VVarType::BLOCKTEMP, name, nodep->findSigned32DType()};
+57 -3
View File
@@ -36,12 +36,16 @@ class LinkLValueVisitor final : public VNVisitor {
bool m_setContinuously = false; // Set that var has some continuous assignment
bool m_setStrengthSpecified = false; // Set that var has assignment with strength specified.
bool m_setForcedByCode = false; // Set that var is the target of an AstAssignForce/AstRelease
bool m_setIfRand = false; // Update VarRefs if var declared as rand
bool m_inInitialStatic = false; // Set if inside AstInitialStatic
bool m_inFunc = false; // Set if inside AstNodeFTask
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
// VISITs
// Result handing
void visit(AstNodeVarRef* nodep) override {
// VarRef: LValue its reference
if (m_setIfRand && !(nodep->varp() && nodep->varp()->isRand())) return;
if (m_setRefLvalue != VAccess::NOCHANGE) nodep->access(m_setRefLvalue);
if (nodep->varp() && nodep->access().isWriteOrRW()) {
if (m_setContinuously) {
@@ -50,15 +54,19 @@ class LinkLValueVisitor final : public VNVisitor {
// so it is needed to check only if m_setContinuously is true
if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true);
}
if (const AstClockingItem* itemp = VN_CAST(nodep->varp()->backp(), ClockingItem)) {
if (const AstClockingItem* const itemp
= VN_CAST(nodep->varp()->backp(), ClockingItem)) {
UINFO(5, "ClkOut " << nodep << endl);
if (itemp->outputp()) nodep->varp(itemp->outputp()->varp());
}
if (m_setForcedByCode) {
nodep->varp()->setForcedByCode();
} else if (!nodep->varp()->isFuncLocal() && nodep->varp()->isReadOnly()) {
nodep->v3warn(ASSIGNIN,
"Assigning to input/const variable: " << nodep->prettyNameQ());
// This is allowed with IEEE 1800-2009 module input with default value.
// the checking now happens in V3Width::visit(AstNodeVarRef*)
// If you were to check here, it would fail on module inputs with default value,
// because Inputs are isReadOnly()=true, and we don't yet have visibility into
// it being an Initial style procedure.
}
}
iterateChildren(nodep);
@@ -95,6 +103,35 @@ class LinkLValueVisitor final : public VNVisitor {
m_setStrengthSpecified = false;
iterateAndNextNull(nodep->rhsp());
}
if (m_inInitialStatic && m_inFunc) {
const bool rhsHasIO = nodep->rhsp()->exists([](const AstNodeVarRef* const refp) {
// Exclude module I/O referenced from a function/task.
return refp->varp() && refp->varp()->isIO()
&& refp->varp()->lifetime() != VLifetime::NONE;
});
if (rhsHasIO) {
nodep->rhsp()->v3warn(E_UNSUPPORTED,
"Static variable initializer\n"
<< nodep->rhsp()->warnMore()
<< "is dependent on function/task I/O variable");
} else {
const bool rhsHasAutomatic
= nodep->rhsp()->exists([](const AstNodeVarRef* const refp) {
return refp->varp() && refp->varp()->lifetime() == VLifetime::AUTOMATIC;
});
if (rhsHasAutomatic) {
nodep->rhsp()->v3error("Static variable initializer\n"
<< nodep->rhsp()->warnMore()
<< "is dependent on automatic variable");
}
}
}
}
void visit(AstInitialStatic* nodep) override {
VL_RESTORER(m_inInitialStatic);
m_inInitialStatic = true;
iterateChildren(nodep);
}
void visit(AstRelease* nodep) override {
VL_RESTORER(m_setRefLvalue);
@@ -285,6 +322,13 @@ class LinkLValueVisitor final : public VNVisitor {
void visit(AstMemberSel* nodep) override {
if (m_setRefLvalue != VAccess::NOCHANGE) {
nodep->access(m_setRefLvalue);
if (nodep->varp() && nodep->access().isWriteOrRW()) {
if (const AstClockingItem* const itemp
= VN_CAST(nodep->varp()->backp(), ClockingItem)) {
UINFO(5, "ClkOut " << nodep << endl);
if (itemp->outputp()) nodep->varp(itemp->outputp()->varp());
}
}
} else {
// It is the only place where the access is set to member select nodes.
// If it doesn't have to be set to WRITE, it means that it is READ.
@@ -313,6 +357,16 @@ class LinkLValueVisitor final : public VNVisitor {
}
}
}
void visit(AstConstraint* nodep) override {
VL_RESTORER(m_setIfRand);
m_setIfRand = true;
iterateChildren(nodep);
}
void visit(AstNodeFTask* nodep) override {
VL_RESTORER(m_inFunc);
m_inFunc = true;
iterateChildren(nodep);
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
+6 -3
View File
@@ -371,9 +371,12 @@ class LinkParseVisitor final : public VNVisitor {
FileLine* const fl = nodep->valuep()->fileline();
if (nodep->isParam() || (m_ftaskp && nodep->isNonOutput())) {
// 1. Parameters and function inputs: It's a default to use if not overridden
} else if (!m_ftaskp && !VN_IS(m_modp, Class) && nodep->isNonOutput()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Default value on module input: "
<< nodep->prettyNameQ());
} else if (!m_ftaskp && !VN_IS(m_modp, Class) && nodep->isNonOutput()
&& !nodep->isInput()) {
// Module inout/ref/constref: const default to use
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Default value on module inout/ref/constref: "
<< nodep->prettyNameQ());
nodep->valuep()->unlinkFrBack()->deleteTree();
} // 2. Under modules/class, it's an initial value to be loaded at time 0 via an
// AstInitial
+5
View File
@@ -65,6 +65,11 @@ class NameVisitor final : public VNVisitorConst {
nodep->name(newname);
nodep->editCountInc();
} else if (VN_IS(nodep, CFunc) && VN_AS(nodep, CFunc)->isConstructor()) {
} else if (v3Global.opt.emitAccessors() && VN_IS(nodep, Var)
&& VN_AS(nodep, Var)->isSigPublic()) {
const string newname = "__Vm_sig_" + nodep->name();
nodep->name(newname);
nodep->editCountInc();
} else {
renameKeywordCheck(nodep);
}
+13 -19
View File
@@ -570,7 +570,7 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const VL_MT_STABLE {
}
if (isNull()) {
if (VL_UNCOVERABLE(!isEqZero())) {
out << "-%E-null-not-zero";
out << "-%E-null-not-zero"; // LCOV_EXCL_LINE
} else {
out << " [null]";
}
@@ -1202,16 +1202,6 @@ V3Number& V3Number::opBitsZ(const V3Number& lhs) { // 0/1->1, X/Z->0
}
return *this;
}
V3Number& V3Number::opBitsNonZ(const V3Number& lhs) { // 0/1->1, X/Z->0
// op i, L(lhs) bit return
NUM_ASSERT_OP_ARGS1(lhs);
NUM_ASSERT_LOGIC_ARGS1(lhs);
setZero();
for (int bit = 0; bit < width(); bit++) {
if (!lhs.bitIsZ(bit)) setBit(bit, 1);
}
return *this;
}
//======================================================================
// Operators - Simple per-bit logical ops
@@ -1706,11 +1696,13 @@ V3Number& V3Number::opWildEq(const V3Number& lhs, const V3Number& rhs) {
NUM_ASSERT_LOGIC_ARGS2(lhs, rhs);
char outc = 1;
for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); bit++) {
if (!rhs.bitIsXZ(bit) && lhs.bitIs(bit) != rhs.bitIs(bit)) {
outc = 0;
goto last;
if (!rhs.bitIsXZ(bit)) {
if (lhs.bitIs(bit) != rhs.bitIs(bit)) {
outc = 0;
goto last;
}
if (lhs.bitIsXZ(bit)) outc = 'x';
}
if (lhs.bitIsXZ(bit)) outc = 'x';
}
last:
return setSingleBits(outc);
@@ -1721,11 +1713,13 @@ V3Number& V3Number::opWildNeq(const V3Number& lhs, const V3Number& rhs) {
NUM_ASSERT_LOGIC_ARGS2(lhs, rhs);
char outc = 0;
for (int bit = 0; bit < std::max(lhs.width(), rhs.width()); bit++) {
if (!rhs.bitIsXZ(bit) && lhs.bitIs(bit) != rhs.bitIs(bit)) {
outc = 1;
goto last;
if (!rhs.bitIsXZ(bit)) {
if (lhs.bitIs(bit) != rhs.bitIs(bit)) {
outc = 1;
goto last;
}
if (lhs.bitIsXZ(bit)) outc = 'x';
}
if (lhs.bitIsXZ(bit)) outc = 'x';
}
last:
return setSingleBits(outc);
-1
View File
@@ -670,7 +670,6 @@ public:
V3Number& opBitsOne(const V3Number& lhs); // 1->1, 0/X/Z->0
V3Number& opBitsXZ(const V3Number& lhs); // 0/1->0, X/Z->1
V3Number& opBitsZ(const V3Number& lhs); // Z->1, 0/1/X->0
V3Number& opBitsNonZ(const V3Number& lhs); // Z->0, 0/1/X->1
//
V3Number& opAssign(const V3Number& lhs);
V3Number& opAssignNonXZ(const V3Number& lhs, bool ignoreXZ = true);
+28 -11
View File
@@ -361,6 +361,9 @@ void V3Options::checkParameters() {
void V3Options::addCppFile(const string& filename) { m_cppFiles.insert(filename); }
void V3Options::addCFlags(const string& filename) { m_cFlags.push_back(filename); }
void V3Options::addCompilerIncludes(const string& filename) {
m_compilerIncludes.insert(filename);
}
void V3Options::addLdLibs(const string& filename) { m_ldLibs.push_back(filename); }
void V3Options::addMakeFlags(const string& filename) { m_makeFlags.push_back(filename); }
void V3Options::addFuture(const string& flag) { m_futures.insert(flag); }
@@ -550,7 +553,7 @@ string V3Options::filePath(FileLine* fl, const string& modname, const string& la
// Find a filename to read the specified module name,
// using the incdir and libext's.
// Return "" if not found.
const string filename = V3Os::filenameCleanup(modname);
const string filename = V3Os::filenameCleanup(VName::dehash(modname));
if (!V3Os::filenameIsRel(filename)) {
// filename is an absolute path, so can find getStdPackagePath()
string exists = filePathCheckOneDir(filename, "");
@@ -572,7 +575,7 @@ string V3Options::filePath(FileLine* fl, const string& modname, const string& la
// Warn and return not found
if (errmsg != "") {
fl->v3error(errmsg + filename);
fl->v3error(errmsg + "'"s + filename + "'"s);
filePathLookedMsg(fl, filename);
}
return "";
@@ -582,8 +585,8 @@ void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) {
static bool shown_notfound_msg = false;
if (modname.find("__Vhsh") != string::npos) {
std::cerr << V3Error::warnMoreStandalone()
<< "... Unsupported: Name is longer than 127 characters;"
<< " automatic file lookup not supported.\n";
<< "... Note: Name is longer than 127 characters; automatic"
<< " file lookup may have failed due to OS filename length limits.\n";
std::cerr << V3Error::warnMoreStandalone()
<< "... Suggest putting filename with this module/package"
<< " onto command line instead.\n";
@@ -634,6 +637,8 @@ string V3Options::getenvBuiltins(const string& var) {
return getenvMAKE();
} else if (var == "PERL") {
return getenvPERL();
} else if (var == "PYTHON3") {
return getenvPYTHON3();
} else if (var == "SYSTEMC") {
return getenvSYSTEMC();
} else if (var == "SYSTEMC_ARCH") {
@@ -663,6 +668,10 @@ string V3Options::getenvPERL() { //
return V3Os::filenameCleanup(V3Os::getenvStr("PERL", "perl"));
}
string V3Options::getenvPYTHON3() { //
return V3Os::filenameCleanup(V3Os::getenvStr("PYTHON3", "python3"));
}
string V3Options::getenvSYSTEMC() {
string var = V3Os::getenvStr("SYSTEMC", "");
// Treat compiled-in DEFENV string literals as C-strings to enable
@@ -698,9 +707,9 @@ string V3Options::getenvSYSTEMC_ARCH() {
uname(&uts);
const string sysname = VString::downcase(uts.sysname); // aka 'uname -s'
if (VL_UNCOVERABLE(VString::wildmatch(sysname.c_str(), "*solaris*"))) {
var = "gccsparcOS5";
var = "gccsparcOS5"; // LCOV_EXCL_LINE
} else if (VL_UNCOVERABLE(VString::wildmatch(sysname.c_str(), "*cygwin*"))) {
var = "cygwin";
var = "cygwin"; // LCOV_EXCL_LINE
} else {
var = "linux";
}
@@ -855,8 +864,11 @@ void V3Options::notify() VL_MT_DISABLED {
cmdfl->v3error(
"--hierarchical must not be set with --hierarchical-child or --hierarchical-block");
}
if (m_hierChild && m_hierBlocks.empty()) {
cmdfl->v3error("--hierarchical-block must be set when --hierarchical-child is set");
if (m_hierChild) {
if (m_hierBlocks.empty()) {
cmdfl->v3error("--hierarchical-block must be set when --hierarchical-child is set");
}
m_main = false;
}
if (protectIds()) {
@@ -1022,10 +1034,9 @@ void V3Options::parseOpts(FileLine* fl, int argc, char** argv) VL_MT_DISABLED {
}
// Default prefix to the filename
if (prefix() == "" && topModule() != "")
m_prefix = string{"V"} + AstNode::encodeName(topModule());
if (prefix() == "" && topModule() != "") m_prefix = "V"s + AstNode::encodeName(topModule());
if (prefix() == "" && vFilesList.size() >= 1)
m_prefix = string{"V"} + AstNode::encodeName(V3Os::filenameNonExt(*(vFilesList.begin())));
m_prefix = "V"s + AstNode::encodeName(V3Os::filenameNonDirExt(*(vFilesList.begin())));
if (modPrefix() == "") m_modPrefix = prefix();
// Find files in makedir
@@ -1186,6 +1197,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
<< fl->warnMore() << "... Suggest 'clang', 'gcc', or 'msvc'");
}
});
DECL_OPTION("-compiler-include", CbVal, callStrSetter(&V3Options::addCompilerIncludes));
DECL_OPTION("-coverage", CbOnOff, [this](bool flag) { coverage(flag); });
DECL_OPTION("-converge-limit", Set, &m_convergeLimit);
DECL_OPTION("-coverage-line", OnOff, &m_coverageLine);
@@ -1236,6 +1248,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
if (flag) m_std = false;
m_preprocOnly = flag;
});
DECL_OPTION("-emit-accessors", OnOff, &m_emitAccessors);
DECL_OPTION("-error-limit", CbVal, static_cast<void (*)(int)>(&V3Error::errorLimit));
DECL_OPTION("-exe", OnOff, &m_exe);
DECL_OPTION("-expand-limit", CbVal,
@@ -1316,6 +1329,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
m_hierBlocks.emplace(opt.mangledName(), opt);
});
DECL_OPTION("-hierarchical-child", Set, &m_hierChild);
DECL_OPTION("-hierarchical-params-file", CbVal,
[this](const char* optp) { m_hierParamsFile = optp; });
DECL_OPTION("-I", CbPartialMatch,
[this, &optdir](const char* optp) { addIncDirUser(parseFileArg(optdir, optp)); });
@@ -1406,6 +1421,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
m_pinsScUint = flag;
if (!m_pinsScBigUint) m_pinsBv = 65;
});
DECL_OPTION("-pins-sc-uint-bool", CbOnOff, [this](bool flag) { m_pinsScUintBool = flag; });
DECL_OPTION("-pins-sc-biguint", CbOnOff, [this](bool flag) {
m_pinsScBigUint = flag;
m_pinsBv = 513;
@@ -1950,6 +1966,7 @@ void V3Options::showVersion(bool verbose) {
cout << "Environment:\n";
cout << " MAKE = " << V3Os::getenvStr("MAKE", "") << "\n";
cout << " PERL = " << V3Os::getenvStr("PERL", "") << "\n";
cout << " PYTHON3 = " << V3Os::getenvStr("PYTHON3", "") << "\n";
cout << " SYSTEMC = " << V3Os::getenvStr("SYSTEMC", "") << "\n";
cout << " SYSTEMC_ARCH = " << V3Os::getenvStr("SYSTEMC_ARCH", "") << "\n";
cout << " SYSTEMC_INCLUDE = " << V3Os::getenvStr("SYSTEMC_INCLUDE", "") << "\n";
+10
View File
@@ -203,6 +203,7 @@ private:
V3StringList m_cFlags; // argument: user CFLAGS
V3StringList m_ldLibs; // argument: user LDFLAGS
V3StringList m_makeFlags; // argument: user MAKEFLAGS
V3StringSet m_compilerIncludes; // argument: user --compiler-include
V3StringSet m_futures; // argument: -Wfuture- list
V3StringSet m_future0s; // argument: -future list
V3StringSet m_future1s; // argument: -future1 list
@@ -249,6 +250,7 @@ private:
bool m_decoration = true; // main switch: --decoration
bool m_decorationNodes = false; // main switch: --decoration=nodes
bool m_dpiHdrOnly = false; // main switch: --dpi-hdr-only
bool m_emitAccessors = false; // main switch: --emit-accessors
bool m_exe = false; // main switch: --exe
bool m_flatten = false; // main switch: --flatten
bool m_hierarchical = false; // main switch: --hierarchical
@@ -260,6 +262,7 @@ private:
bool m_pedantic = false; // main switch: --Wpedantic
bool m_pinsInoutEnables = false;// main switch: --pins-inout-enables
bool m_pinsScUint = false; // main switch: --pins-sc-uint
bool m_pinsScUintBool = false; // main switch: --pins-sc-uint-bool
bool m_pinsScBigUint = false; // main switch: --pins-sc-biguint
bool m_pinsUint8 = false; // main switch: --pins-uint8
bool m_ppComments = false; // main switch: --pp-comments
@@ -343,6 +346,7 @@ private:
string m_buildDepBin; // main switch: --build-dep-bin {filename}
string m_exeName; // main switch: -o {name}
string m_flags; // main switch: -f {name}
string m_hierParamsFile; // main switch: --hierarchical-params-file
string m_l2Name; // main switch: --l2name; "" for top-module's name
string m_libCreate; // main switch: --lib-create {lib_name}
string m_mainTopName; // main switch: --main-top-name
@@ -434,6 +438,7 @@ public:
// METHODS
void addCppFile(const string& filename);
void addCFlags(const string& filename);
void addCompilerIncludes(const string& filename);
void addLdLibs(const string& filename);
void addMakeFlags(const string& filename);
void addLibraryFile(const string& filename);
@@ -496,6 +501,7 @@ public:
bool dumpTreeDot() const {
return m_dumpLevel.count("tree-dot") && m_dumpLevel.at("tree-dot");
}
bool emitAccessors() const { return m_emitAccessors; }
bool exe() const { return m_exe; }
bool flatten() const { return m_flatten; }
bool gmake() const { return m_gmake; }
@@ -514,6 +520,7 @@ public:
bool pedantic() const { return m_pedantic; }
bool pinsInoutEnables() const { return m_pinsInoutEnables; }
bool pinsScUint() const { return m_pinsScUint; }
bool pinsScUintBool() const { return m_pinsScUintBool; }
bool pinsScBigUint() const { return m_pinsScBigUint; }
bool pinsUint8() const { return m_pinsUint8; }
bool ppComments() const { return m_ppComments; }
@@ -594,6 +601,7 @@ public:
int compLimitParens() const { return m_compLimitParens; }
string exeName() const { return m_exeName != "" ? m_exeName : prefix(); }
string hierParamFile() const { return m_hierParamsFile; }
string l2Name() const { return m_l2Name; }
string libCreate() const { return m_libCreate; }
string libCreateName(bool shared) {
@@ -626,6 +634,7 @@ public:
const V3StringSet& cppFiles() const { return m_cppFiles; }
const V3StringList& cFlags() const { return m_cFlags; }
const V3StringSet& compilerIncludes() const { return m_compilerIncludes; }
const V3StringList& ldLibs() const { return m_ldLibs; }
const V3StringList& makeFlags() const { return m_makeFlags; }
const V3StringSet& libraryFiles() const { return m_libraryFiles; }
@@ -713,6 +722,7 @@ public:
static string getenvMAKE();
static string getenvMAKEFLAGS();
static string getenvPERL();
static string getenvPYTHON3();
static string getenvSYSTEMC();
static string getenvSYSTEMC_ARCH();
static string getenvSYSTEMC_INCLUDE();
+14 -6
View File
@@ -58,6 +58,15 @@ class V3OrderProcessDomains final {
// METHODS
// Dump a domain for debugging
std::string debugDomain(AstSenTree* domainp) {
return cvtToHex(domainp)
+ (domainp == m_deleteDomainp ? " [DEL]"
: domainp->hasCombo() ? " [COMB]"
: domainp->isMulti() ? " [MULT]"
: "");
}
// Make a domain that merges the two domains
AstSenTree* combineDomains(AstSenTree* ap, AstSenTree* bp) {
if (ap == bp) return ap;
@@ -93,6 +102,7 @@ class V3OrderProcessDomains final {
// For logic, start with the explicit hybrid sensitivities
OrderLogicVertex* const lvtxp = vtxp->cast<OrderLogicVertex>();
if (lvtxp) domainp = lvtxp->hybridp();
if (domainp) UINFO(6, " hybr d=" << debugDomain(domainp) << " " << vtxp << endl);
// For each incoming edge, examine the source vertex
for (V3GraphEdge& edge : vtxp->inEdges()) {
@@ -104,6 +114,7 @@ class V3OrderProcessDomains final {
AstSenTree* fromDomainp = fromVtxp->domainp();
UINFO(6, " from d=" << debugDomain(fromDomainp) << " " << fromVtxp << endl);
UASSERT(fromDomainp == m_deleteDomainp || !fromDomainp->hasCombo(),
"There should be no need for combinational domains");
@@ -113,6 +124,8 @@ class V3OrderProcessDomains final {
externalDomainps.clear();
m_externalDomains(vscp, externalDomainps);
for (AstSenTree* const externalDomainp : externalDomainps) {
UINFO(6, " xtrn d=" << debugDomain(externalDomainp) << " " << fromVtxp
<< " because of " << vscp << endl);
UASSERT_OBJ(!externalDomainp->hasCombo(), vscp,
"There should be no need for combinational domains");
fromDomainp = combineDomains(fromDomainp, externalDomainp);
@@ -140,12 +153,7 @@ class V3OrderProcessDomains final {
// Set the domain of the vertex
vtxp->domainp(domainp);
UINFO(5, " done d=" << cvtToHex(domainp)
<< (domainp == m_deleteDomainp ? " [DEL]"
: domainp->hasCombo() ? " [COMB]"
: domainp->isMulti() ? " [MULT]"
: "")
<< " " << vtxp << endl);
UINFO(5, " done d=" << debugDomain(domainp) << " " << vtxp << endl);
}
}
+1 -1
View File
@@ -34,7 +34,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
std::vector<AstActive*> V3Order::createSerial(OrderGraph& graph, const std::string& tag,
const TrigToSenMap& trigToSen, bool slow) {
UINFO(2, " Constructing serial code for '" + tag + "'");
UINFO(2, " Constructing serial code for '" + tag + "'\n");
// Build the move graph
OrderMoveDomScope::clear();

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