Compare commits

...
221 Commits
Author SHA1 Message Date
Wilson Snyder cd693ce02b Version bump 2024-06-15 08:43:45 -04:00
Wilson Snyder 2641bc98aa Update include/gtkwave from upstream 2024-06-15 08:14:43 -04:00
Wilson Snyder ad2862ce3f Fix DPI import of null C-string (#5179). 2024-06-14 22:50:54 -04:00
Bartłomiej Chmiel 4695967185 Fix signed types emitted in hierarchical Verilation (#5178)
Signed-off-by: Bartłomiej Chmiel <[email protected]>
2024-06-14 08:07:32 -04:00
Wilson Snyder f96e99542c Internals: Fix cppcheck warnings. No functional change. 2024-06-13 21:29:03 -04:00
Wilson Snyder 7de193c2e9 Commentary: Changes update 2024-06-13 18:04:57 -04:00
Arkadiusz Kozdra 442c9bc316 Support parsing and otherwise ignoring inline constraints (#5126) 2024-06-13 08:38:20 -04:00
Arkadiusz Kozdra 3203019408 Fix coroutines without awaits to have a co_return (#4208) (#5175)
After the V3Timing refactoring the V3SchedTiming phase could apparently
move away all awaits from a coroutine without adding a co_return
statement.

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

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

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

* wording/formatting

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

---------

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

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

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

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

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

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

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

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

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

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

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

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

Output code perturbed due to variable ordering.

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

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

Also renamed Vdlyvfoo to VdlyFoo for easier readability of the generated
code.
2024-05-01 23:06:25 +01:00
Geza Lore b5b937e2f2 Fix missed optimization in V3Delayed (#5089)
Checking the wrong node meant we never actually pushed constant
bit-select indices into the delayed update, as was the intention, but
always generated a temporary instead.
2024-05-01 16:22:56 +01:00
Todd Strader 4a41f69293 Internals: Remove C-style cast in VPI_HANDLE (#5088) 2024-05-01 10:11:44 -04:00
Wilson Snyder 8fd038f88e Add --localize-max-size option and optimization (#5072). 2024-04-30 19:46:54 -04:00
Wilson Snyder 4982f63da2 Update coverage for last commit 2024-04-30 19:34:41 -04:00
Wilson Snyder 71bc60fb91 Add error on zero width select (#5028). 2024-04-30 18:38:37 -04:00
Wilson Snyder 0da7ecf753 Commentary: Changes update 2024-04-30 07:37:06 -04:00
Andrea Calabrese 25ca1af8b3 Internals: Fix missing const (#4899) 2024-04-29 22:57:56 -04:00
Wilson Snyder 5d54fa8e6f Fix missing parameters with comma to be errors (#4979) (#5012). 2024-04-29 22:41:16 -04:00
Christopher Taylor e8c5b9faad Fix missing flex include path variable (#4970) (#4971) 2024-04-29 22:01:09 -04:00
Aleksander Kiryk 8e0301c287 Fix bound queue printing (#5032) 2024-04-29 21:52:06 -04:00
Iztok Jeras 3f625fc359 Tests: Add unsupported streaming LHS tests (#4302) 2024-04-29 21:35:19 -04:00
Geza Lore 72b96d5069 tests: disable ASLR for t_trace_ub_misaligned_address (#5075)
This works around an address-sanitizer bug hit with some GCC versions
2024-04-29 15:38:00 +01:00
Bartłomiej Chmiel d1b45b8d32 Fix attempted to destroy locked thread pool error (#5040) 2024-04-29 07:59:24 -04:00
Wilson Snyder 5601056ed0 Tests: Check for bad event methods 2024-04-28 13:10:25 -04:00
Wilson Snyder 8ed269c77f Make 'disable isn't underneath a begin' into UNSUPPORTED error (#4699). 2024-04-27 21:30:40 -04:00
Geza Lore 27b7e70218 Fix DFG assertion with SystemC (#5076)
Fixes #5050
2024-04-27 13:41:10 +01:00
Geza Lore 97680656d3 Remove unknown '--' form cmake -E (#5073)
After #5063, with cmake 3.22.1, I get test failures, saying:
    cmake -E env: unknown option '--'
The '--' is necessary so removing.
2024-04-27 11:09:59 +01:00
Michael Bikovitsky 4202ec2ab2 Fix CMake builds to export VERILATOR_ROOT (#5063) 2024-04-26 19:29:10 -04:00
Wilson Snyder 4f3a816fb0 Fix false ASSIGNIN on functions with explicit port map (#5069). 2024-04-26 19:26:21 -04:00
Todd Strader 25fd8ef5c0 Add VPI eval needed tracking (#5065) 2024-04-25 09:07:31 -04:00
Gökçe Aydos a9664b8d16 Remove $(info) which cannot be silenced by -MAKEFLAGS '--quiet' (#5059) 2024-04-20 08:48:16 -05:00
Wilson Snyder 26a5729514 Add CITATION.cff (#5057) (#5058). 2024-04-19 20:33:11 -04:00
Arkadiusz Kozdra 5b839699ac Support empty queue as dynarray default value (#5055)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-04-18 11:53:23 -05:00
Wilson Snyder 1315aa31ed Commentary 2024-04-13 08:18:45 -04:00
Wilson Snyder 5d8da6b4ac Fix width extension on delays (#5045). 2024-04-13 08:16:59 -04:00
Wilson Snyder aac2563a07 Commentary: Changes update 2024-04-13 08:03:57 -04:00
Wilson Snyder 8e44487354 Tests: update style 2024-04-13 08:02:25 -04:00
Wilson Snyder 1012c054e6 Fix $system with string argument (#5042). 2024-04-11 17:31:14 -04:00
Paul Wright a8b5738b44 Support __en/__out signals on top level inout ports (#4812) (#4856) 2024-04-11 09:02:58 -04:00
Wilson Snyder 72845167e6 Commenary: Fix README badge paragraph 2024-04-10 17:02:07 -04:00
Krzysztof Bieganski 7ca2d6470a Fix consecutive zero-delays (#5038)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2024-04-05 16:48:47 -04:00
Wilson Snyder 33e999e01a devel release 2024-04-05 06:23:30 -04:00
Wilson Snyder 522bead374 Version bump 2024-04-05 05:52:00 -04:00
Wilson Snyder 030286142f Commentary: Changes update 2024-04-04 22:11:33 -04:00
Wilson Snyder 8a4ceb5717 Tests: Fix test failure, last commit. 2024-04-02 07:04:27 -04:00
Andrew Nolte 63fa6accc4 [Vpi] Fix missing scopes 2 (#4965) 2024-04-01 23:11:15 -04:00
Wilson Snyder 28718f964a Fix tracing replicated hierarchical models (#5027) (#5029) 2024-03-30 16:00:52 -04:00
Wilson Snyder 1d486e3a59 Rename statsPrintSummary() 2024-03-30 13:19:47 -04:00
Wilson Snyder 9b396f7125 Fix pylint warning 2024-03-30 13:13:51 -04:00
Wilson Snyder e16cbce52e Internals: Fix clang_check_attributes false error print 2024-03-30 11:58:34 -04:00
Wilson Snyder 840970e8f7 Internals: Add VL_MT_SAFE flags 2024-03-30 11:54:29 -04:00
Wilson Snyder e390d6779d Internals: Rename threadsInModel() 2024-03-30 11:13:14 -04:00
Wilson Snyder 39ee522f92 Commentary: Changes update 2024-03-28 08:22:15 -04:00
Wilson Snyder 9526efcf72 Fix V3Hash MacOS ambiguity (#5011) 2024-03-28 08:11:48 -04:00
github action d397e0d838 Apply 'make format' 2024-03-28 11:32:57 +00:00
Szymon Gizler 8301fdc6d3 Add JSON AST dumps (#5020) 2024-03-28 07:32:18 -04:00
Arkadiusz Kozdra f645382f11 Support inside range with implicit type conversion (#5026)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-03-28 07:31:45 -04:00
Wilson Snyder 1ed5557d2d Support 1800-2023 class and function :initial, :extends, :final virtual overrides (#5025). (#5025) 2024-03-27 23:57:58 -04:00
Wilson Snyder 28b9216f8a Fix tracing class parameters (#5014). 2024-03-27 20:07:46 -04:00
Wilson Snyder 0114cb67ca Fix $readmem with missing newline (#5019). 2024-03-27 18:42:20 -04:00
Wilson Snyder ea8f86dd30 Internals: Prefer '(void)' to avoid unused var. No functional change. 2024-03-27 18:07:14 -04:00
Wilson Snyder 0ff77fc352 Internals: Remove extra single-line {}. No functional change. 2024-03-27 17:57:49 -04:00
Wilson Snyder 341963e820 Internals: Avoid '%Error' so can grep logs for real errors 2024-03-27 09:00:34 -04:00
Wilson Snyder db60b92613 Fix internal error on missing pattern key (#5023) 2024-03-27 08:41:58 -04:00
Geza Lore 98206a4f04 Improve V3List user interface (#4996) 2024-03-25 23:06:25 +00:00
Wilson Snyder 4df9e2e0e5 Add printing summary reports (#4909) (#5018) 2024-03-25 07:03:17 -04:00
Wilson Snyder 34e4cffb62 Add stats on output size 2024-03-24 10:50:53 -04:00
Wilson Snyder 71d76c993c Fix MSVC++ warnings. No functional change. 2024-03-24 10:19:26 -04:00
Wilson Snyder e67bdb4c08 Commentary 2024-03-24 09:23:37 -04:00
Wilson Snyder 23f013f2d7 Add stats on input size etc (#5017) 2024-03-24 09:14:31 -04:00
Geza Lore 9b729b80e0 Plug memory leaks (#5016) 2024-03-23 22:12:43 +00:00
Wilson Snyder 38ad328956 Remove duplicate stop ignored messages 2024-03-22 19:35:42 -04:00
Wilson Snyder f767f0cfaa Commentary 2024-03-22 19:35:13 -04:00
Fuad Ismail 1c79df8630 Support stream operation on unpacked array (#4714) (#5006) 2024-03-21 18:26:42 -04:00
Wilson Snyder 248573205b Commentary: Changes update 2024-03-20 18:27:35 -04:00
Arkadiusz Kozdra 88831ca21b Fix preprocessor to respect strings in joins (#5007)
This adheres more to the wording used in IEEE 1800-2017 22.5.1,
specifying the join operator to be more of a delimiter than join:

> A `` delimits lexical tokens without introducing white space,
> allowing identifiers to be constructed from arguments.

Before, string RHS arguments to the join operator were silently dropped.

Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-03-20 08:54:23 -04:00
Arkadiusz Kozdra 26f15e11c4 Fix unique {} constraints missing semicolon (#5001)
Signed-off-by: Arkadiusz Kozdra <[email protected]>
2024-03-19 08:22:39 -04:00
Ryszard Rozak 5c0a7e06dc Fix inout ports of unpacked stuct type (#5000) 2024-03-19 06:43:06 -04:00
Andrew Nolte 290b313dc0 Fix missing VPI scopes (#4918) 2024-03-18 20:47:28 -04:00
Wilson Snyder 93e5ca3f6d Fix class type as an associative array parameter (#4997). 2024-03-18 20:44:18 -04:00
Geza Lore 6ffff8565f Use the same serial ordering within MTasks as we use in serial mode (#4994)
The goal here is to use as single ordering heuristic (which can be
improved later) within MTasks as we do for serial code ordering. The
heuristic itself is factored out into the new OrderMoveGraphSerializer.
This also yields slightly nicer ordering than the previously use
GraphStream, so we end up with fewer trigger (domain) conditionals in
the MTasks, this can be worth a few percent speedup.

This has the somewhat nice side-effect of reusing OrderMoveGraphVertex
for both serial and parallel mode, so MTaskMoveGraphVertex can be
removed.

Serial mode yields identical output.
2024-03-17 13:15:39 +00:00
Geza Lore 494e05b326 Fix install instructions for attribute checks, do the same in CI (#4995)
On Ubuntu 22.04, pip install clang picks up a mismatched version by
default. There are 'apt' packages with correct dependencies, so use
those instead.
2024-03-17 12:10:13 +00:00
Kevin Nygaard a24f61403a Support implicitly-typed variable definitions in for-loop initializers (#4945) (#4986)
- Adds support for C-style for-loop initializers
    - Current implementation supports: for (x a = 1, y b = 2, ...)
    - This patch extends support to:   for (x a = 1,   b = 2, ...)
- Adds unit test for new feature
2024-03-16 19:02:37 -04:00
Geza Lore 292cc54768 Compute MTask affinity in V3VariableOrder (#4991)
Instead of carrying around MTask affinity from scheduling, compute it in
V3VariableOrder (where it is used), by tracing through the code. This
simplifies some code and has the benefit of handling variables
introduced after scheduling. It's worth a few % speed at run-time, and
the new implementation of V3VariableOrder is slightly more efficient,
though the speed/space is still dominated by the TSP sort.
2024-03-16 16:32:12 +00:00
Geza Lore e8a9662eb5 Simplify LogicMTask/ExecMTask IDs (#4990)
There is no strong need to re-map LogicMTask IDs and it just adds extra
processing. Instead we just allocate a separate set of ExecMTask IDs as
they are created, which can also be used as the unique profiling ID as
well. The only effect on the output of this is the change in mtask IDs
emitted, which was fairly arbitrary to begin with.
2024-03-16 14:02:17 +00:00
Wilson Snyder 0262819c20 Fix object assignment from conditionals (#4968). 2024-03-16 09:12:18 -04:00
Wilson Snyder d3b93a1113 Fix object reference to conditional null : null 2024-03-16 09:12:18 -04:00
Wilson Snyder 5f8c06c805 Internals: Add AstNode::isNull(). No functional change. 2024-03-16 09:12:18 -04:00
Kevin Nygaard a5ad26cd28 Fix tests on macOS (#4984) (#4985) 2024-03-16 09:08:03 -04:00
Fuad Ismail 5802818b9a Add error when pass net to function argument (#4132) (#4966) 2024-03-16 08:25:42 -04:00
Geza Lore df519ff16e Fix --prof-exec predicted time values (#4988)
Wrapping the functions in #4933 broke --prof-exec report as the
predicted MTask times are computed during thread packing, but are
emitted in the wrapping functions.
2024-03-16 12:17:24 +00:00
Geza Lore bf8a88a0ad Do not create any empty ExecMTasks (#4987) 2024-03-16 12:00:32 +00:00
Geza Lore 878204db73 Do not feed any empty logic into scheduling (#4972) 2024-03-16 10:35:56 +00:00
Nolan Poe 08c76b1da6 Fix GCC14 warnings on template specialization syntax (#4974) (#4975) 2024-03-15 22:21:08 -04:00
Wilson Snyder e82c9db6da Fix unpacked structure upper bit cleaning (#4978). 2024-03-15 21:56:24 -04:00
Wilson Snyder 65c3cb4708 Tests: stop testing if --fail-max tests fail 2024-03-15 21:11:24 -04:00
Wilson Snyder 0cab076b86 CI: Reenable address sanitizer and fix core dumps (#4983) 2024-03-15 21:06:21 -04:00
Wilson Snyder 3a28ed24ea Revert last bin/verilator change. 2024-03-15 10:34:50 -04:00
Wilson Snyder 71775eae4c sanitize 2024-03-15 10:32:31 -04:00
Wilson Snyder 953249aa43 CI: Add dist-vlt-3 to avoid out of disk space 2024-03-12 07:49:01 -04:00
Wilson Snyder 229dbbb100 Tests: RUn same job count as CPU instead of +1 to reduce CI congestion 2024-03-11 20:13:23 -04:00
Wilson Snyder 4a439beae5 Add error on missing pure virtual functions (#4961). 2024-03-11 18:56:30 -04:00
Wilson Snyder 49022c3e73 Add error on calling static function without object (#4962). 2024-03-11 18:23:55 -04:00
Geza Lore c92ced4ac7 Do not defer deletion in const-bit-op-tree
Deferring deletion of unused terms is not necessary, but can cause huge
memory usage increases in some bad cases.
2024-03-11 17:33:40 +00:00
Wilson Snyder ba47da6587 Internals: Move vl_timescaled_double to verilated_funcs.h. No functional change intended 2024-03-10 22:34:32 -04:00
Wilson Snyder 2c27f22900 Remove unused vltstd::exchange, should be using C++14 std::exchange instead 2024-03-10 22:32:51 -04:00
Geza Lore c0391990ad Increase graph ParallelismReprot values to uint64_t 2024-03-10 18:56:31 +00:00
Geza Lore 2247e1e345 Cleanup/simplify V3OrderParallel (#4959)
No functional change.
2024-03-10 18:15:45 +00:00
Geza Lore e4847464d4 Split V3Partition into logically separate pieces (#4958)
V3Partition used to contain 2 conceptually separate set of algorithms

- The MTask partitioning/coarsening algorithm used by V3Order. This has
  been moved to V3OrderParallel.cpp

- The lowering of AstExecGraph into per thread functions by packing
  tasks into threads and creating additional code
  (V3Partition::finalize). This has been moved to the new
  V3ExecGraph.cpp

This patch is just code movement/rename with minimal fixes required to
do so.
2024-03-10 15:58:58 +00:00
Wilson Snyder 7694a7bf41 Fix suggestion of ulimit -s (#4956) 2024-03-10 11:20:25 -04:00
Geza Lore a686e547cf Factor out graph parallelism report into a generic algorithm (#4957)
This is a generic algorithm parametrised by a cost function, so
implement it as such for easy reuse.
2024-03-10 14:56:43 +00:00
Geza Lore 1481d34959 Add candidate item count to MergeCond stats 2024-03-09 22:11:04 +00:00
Nolan Poe 04512e5d04 Add custom version for verilator --version packaging (#4954) 2024-03-09 16:44:25 -05:00
Geza Lore 9a3aed57b1 Internals: Simplify/cleanup V3OrderSerial (#4955). No functional change.
Remove redundant data structures and simplify/cleanup implementation.

No functional change. Output is identical.
2024-03-09 16:19:35 -05:00
Geza Lore 5a69321be3 Split V3Order into further part and decouple various components (#4953)
Continuing the idea of decoupling the implementations of the various algorithms.

The main points:

-Move the former "processDomain" stuff, dealing with assigning combinational logic into the relevant sensitivity domains into V3OrderProcessDomains.cpp

-Move the parallel code construction in V3OrderParallel.cpp (Could combine this with some parts of V3Partition - those not called from V3Partition::finalize - but that's not for this patch).

-Move the serial code construction into V3OrderSerial.cpp

-Factored the very small common code between the parallel and serial code construction (processMoveOneLogic) into V3OrderCFuncEmitter.cpp
2024-03-09 12:43:09 +00:00
Wilson Snyder 5ee938fd1c Fix assignment of null into struct member (#4952). 2024-03-08 17:33:51 -05:00
Arkadiusz Kozdra f78783e187 Internals: Use LogOr/LogAnd on inside operator expansion (#4951)
This will help introducing constrained randomization, as SMT solvers
treat booleans and 1-bit vectors as incompatible types.
2024-03-08 08:17:41 -05:00
Wilson Snyder 11bfe7a7bb cppcheck fixes. No functional change. 2024-03-07 19:20:00 -05:00
Wilson Snyder 8ba494c71f Fix null characters in shortened identifiers (#4946). 2024-03-07 18:09:55 -05:00
Geza Lore acb63c929b Split V3Order.cpp into multiple smaller files (#4950). No functional change intended.
Move OrderBuildVisitor into V3OrderGraphBuilder.cpp (and rename to
V3OrderGraphBuilder). Move ProcessMoveBuildGraph to
V3OrderMoveGraphBuilder.cpp (and rename to V3OrderGraphBuilder).

This patch is pure code movement/rename, no refactoring at all.
2024-03-07 17:12:19 -05:00
Geza Lore 26dd5d4550 Add some missing VNUserInUse (#4949) 2024-03-07 20:26:58 +00:00
Bartłomiej Chmiel 32f288084a Add UNUSEDLOOP when unused loop is removed (#4926) 2024-03-07 08:33:49 -05:00
Andrew Nolte 6db149c588 Fix generate blocks in vpi_iterate (#3609) (#4913) 2024-03-06 18:33:30 -05:00
Geza Lore 0fed5f8b3e Avoid creating redundant vertices in V3DfgPeephole (#4944)
Add a new data-structure V3DfgCache, which can be used to retrieve
existing vertices with some given inputs vertices. Use this in
V3DfgPeephole to eliminate the creation of redundant vertices.

Overall this is performance neutral, but is in prep for some future
work.
2024-03-06 18:01:52 +00:00
Geza Lore a30930bdcc Fix unsound temporary name generation in V3DfgRegularize
Sadly used to produce some conflicting names.
2024-03-05 19:17:41 +00:00
Geza Lore 3a1355fb54 Fix bad assertion in DFG variable elimination
Fixes #4943
2024-03-05 18:51:46 +00:00
Andrew Nolte 97db128d66 Tests: Add VPI dump testcases (#4838) 2024-03-05 12:08:22 -05:00
Wilson Snyder 3d57256070 Commentary: Changes update 2024-03-04 08:22:36 -05:00
Wilson Snyder 5dc8fb5b4f Commentary (#4930) 2024-03-04 08:04:39 -05:00
Wilson Snyder 3f0d434f82 Gtkwave: Update from upstream for warning cleanup (#4941) 2024-03-03 19:24:03 -05:00
Wilson Snyder 2cdc5ea48b Fix unaligned access in --trace --runtime-debug (#4941). 2024-03-03 12:33:11 -05:00
Geza Lore 745605efe3 Fix DFG removing forceable signals (#4942)
DFG could remove forceable signals by replacing them with their
in-design driver. This is a bit of a pain to prevent, and ideally the
forcing transform should happen before DFG, but implementing it there is
a pain due to having to rewrite ports based on direction.  This is an
attempted fix in DFG. More cases might remain.
2024-03-03 16:22:41 +00:00
Wilson Snyder 0fbd4313b2 Fix __Vlip undefined error in --freloop (#4824). 2024-03-03 11:10:46 -05:00
Wilson Snyder 9f8f61ef2f Internals: Only show SimulateVisitor note once per type 2024-03-03 11:10:19 -05:00
github action 4b1f4938b8 Apply 'make format' 2024-03-03 15:23:47 +00:00
Kefa Chen 5f1dc73a1b Support public packed struct / union (#860) (#4878) 2024-03-03 10:23:04 -05:00
Geza Lore 5e1fc6e24d Add DFG 'regularize' pass, and improve variable removal (#4937)
This functionality used to be distributed in the removeVars pass and the
final dfgToAst conversion. Instead added a new 'regularize' pass to
convert DFGs into forms that can be trivially converted back to Ast, and
a new 'eliminateVars' pass to remove/repalce redundant variables. This
simplifies dfgToAst significantly and makes the code a bit easier to
follow.

The new 'regularize' pass will ensure that every sub-expression with
multiple uses is assigned to a temporary (unless it's a trivial memory
reference or constant), and will also eliminate or replace redundant
variables. Overall it is a performance neutral change but it does
enable some later improvements which required the graph to be in this
form, and this also happens to be the form required for the dfgToAst
conversion.
2024-03-02 19:49:29 +00:00
Wilson Snyder 0ec32ee404 Parse 1800-2023 map expressions and throw UNSUPPORTED 2024-03-02 10:15:19 -05:00
Wilson Snyder 97b21b3849 Parse 1800-2023 2024-03-02 10:15:19 -05:00
Wilson Snyder 214173c6b8 Support 1800-2023 preprocessor ifdef expressions; add PREPROC zero warning. 2024-03-02 10:15:19 -05:00
Wilson Snyder 3786f59e03 Change to IEEE 1800-2023 warning mentions 2024-03-02 10:15:19 -05:00
Wilson Snyder fa7234ff68 Support 1800-2023 DPI headers, svGetTime/svgGetTimeUnit/svGetTimePrecision methods. 2024-03-02 10:15:19 -05:00
Wilson Snyder 91dd3c5fac Support 1800-2023 keywords. 2024-03-02 10:15:19 -05:00
Wilson Snyder a378dbd9b2 Tests: Fix t_dist_whitespace 2024-03-02 10:14:54 -05:00
Yutetsu TAKATSUKASA da9521a351 Internals: Add --debug-width option for developers to check width consistency (#4923) 2024-03-02 08:57:26 -05:00
Wilson Snyder af51107587 Fix statistics missing some additions. 2024-03-01 20:27:57 -05:00
Wilson Snyder e60f652a64 Commentary (#4936) 2024-03-01 17:32:26 -05:00
Wilson Snyder a4813ec677 Add warning on TOP-named modules (#4935). 2024-03-01 17:28:12 -05:00
Kamil Rakoczy 01dadb0a8d Fix try-lock spuriously fails (#4931) (#4938) 2024-03-01 15:29:13 -05:00
Geza Lore 9dcb7ea6a9 Fix minor V3DfgPatternStats coding issues 2024-03-01 14:56:35 +00:00
github action adfe072b89 Apply 'make format' 2024-03-01 14:15:34 +00:00
Yan Xu b12f8b3d73 Fix V3Unknown unpacked struct x-assign (#4934) 2024-03-01 09:14:49 -05:00
Geza Lore f56f318217 Make installation relocatable, and the installation testable (#4927)
Fixes #4893
2024-03-01 00:08:28 +00:00
Geza Lore 3af0eb7fa0 Internals: Emit a separate CFunc for each MTask body (#4932) (#4933) 2024-02-29 16:53:53 -05:00
Wilson Snyder a69cb9b044 Tests: Avoid verilated.v include in most tests 2024-02-27 18:08:37 -05:00
Geza Lore b68a696859 Ignore all JSON tree dumps in test-snap/test-diff 2024-02-27 17:16:34 +00:00
Wilson Snyder e320b0b581 Internals: Favor const_iterators. No functional change intended. 2024-02-25 17:12:13 -05:00
Wilson Snyder 42041f2403 Fix invalid cast on string structure creation (#4921). 2024-02-25 08:19:53 -05:00
Wilson Snyder 881c6ee655 devel release 2024-02-24 07:52:19 -05:00
797 changed files with 61536 additions and 35014 deletions
+2
View File
@@ -18,3 +18,5 @@ What 'verilator --version' are you using? Did you try it with the git master ve
What OS and distribution are you using?
May we assist you in trying to fix this in Verilator yourself?
(Please avoid attaching screenshots that show text - you can convert images to text using e.g. https://ocr.space)
+16 -4
View File
@@ -17,6 +17,8 @@ env:
CCACHE_COMPRESS: 1
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_LIMIT_MULTIPLE: 0.95
INSTALL_DIR: ${{ github.workspace }}/install
RELOC_DIR: ${{ github.workspace }}/relloc
defaults:
run:
@@ -94,22 +96,32 @@ jobs:
compiler:
- { cc: clang, cxx: clang++ }
- { cc: gcc, cxx: g++ }
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, vltmt-0, vltmt-1]
reloc: [0]
suite: [dist-vlt-0, dist-vlt-1, dist-vlt-2, dist-vlt-3, vltmt-0, vltmt-1]
include:
# Test with GCC 10 on ubuntu-20.04
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: dist-vlt-0}
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: dist-vlt-1}
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: dist-vlt-2}
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: dist-vlt-3}
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: vltmt-0}
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, suite: vltmt-1}
# Test relocated installation - on most common platform only
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: dist-vlt-0}
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: dist-vlt-1}
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: dist-vlt-2}
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: dist-vlt-3}
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: vltmt-0}
- {os: ubuntu-22.04, compiler: { cc: gcc, cxx: g++ }, reloc: 1, suite: vltmt-1}
runs-on: ${{ matrix.os }}
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }}
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.reloc && 'reloc | ' || '' }} ${{ matrix.suite }}
env:
CI_BUILD_STAGE_NAME: test
CI_RUNS_ON: ${{ matrix.os }}
CI_RELOC: ${{ matrix.reloc }}
CC: ${{ matrix.compiler.cc }}
CXX: ${{ matrix.compiler.cxx }}
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.suite }}
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-${{ matrix.reloc }}-${{ matrix.suite }}
CCACHE_MAXSIZE: 100M # Per build per suite (* 5 * 5 = 2500M in total)
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}.tar.gz
steps:
@@ -127,7 +139,7 @@ jobs:
- name: Cache $CCACHE_DIR
uses: actions/cache@v4
env:
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache
CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache2
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ env.CACHE_KEY }}-${{ github.sha }}
+2
View File
@@ -44,3 +44,5 @@ verilator-config-version.cmake
/.idea/
/cmake-build-*/
/test_regress/snapshot/
xmverilog.*
xrun.history
+27
View File
@@ -0,0 +1,27 @@
# See https://citation-file-format.github.io/
cff-version: 1.2.0
title: Verilator
message: >-
If you use this software, please cite it using the
metadata from this file.
type: software
authors:
- given-names: Wilson
family-names: Snyder
email: [email protected]
affiliation: Veripool
- given-names: Paul
family-names: Wasson
- given-names: Duane
family-names: Galbi
- name: 'et al'
repository-code: 'https://github.com/verilator/verilator'
url: 'https://verilator.org'
abstract: >-
The Verilator package converts Verilog and SystemVerilog hardware
description language (HDL) designs into a fast C++ or SystemC model
that, after compiling, can be executed. Verilator is not a
traditional simulator but a compiler.
license:
- LGPL-3.0-only
- Artistic-2.0
+1 -1
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.022
VERSION 5.026
HOMEPAGE_URL https://verilator.org
LANGUAGES CXX
)
+122 -1
View File
@@ -8,7 +8,127 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 5.022 2024-04-24
Verilator 5.026 2024-06-15
==========================
**Major:**
* Support constrained randomization with external solvers (#4947). [Arkadiusz Kozdra, Antmicro Ltd.]
**Minor:**
* Support `$psprintf` system function (#4314) (#5169). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support 2D dynamic array initialization (#4700) (#5122). [Valentin Atepalikhin]
* Support __en/__out signals on top level inout ports (#4812) (#4856). [Paul Wright]
* Support empty queue as dynarray default value (#5055). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support vpiInertialDelay (#5087). [Todd Strader]
* Support NBAs to arrays inside loops (#5092). [Geza Lore]
* Support parsing and otherwise ignoring inline constraints (#5126). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support `inout` clocking items (#5160). [Arkadiusz Kozdra, Antmicro Ltd.]
* Support StructSel in unpacked array assignments (#5176). [Geza Lore]
* Add error on zero width select (#5028).
* Add CITATION.cff (#5057) (#5058). [Gijs Burghoorn]
* Add VPI eval needed tracking (#5065). [Todd Strader]
* Add `--localize-max-size` option and optimization (#5072).
* Add parameterless assert control system tasks (#5010). [Bartłomiej Chmiel]
* Add traceCapable indication to model header (#5053). [Vito Gamberini]
* Add increasing of stack size when possible (#5071) (#5104). [Yinan Xu]
* Add assertion on reusing VerilatedContext (#5167).
* Improve DFG regularization in cyclic graphs (#5142). [Geza Lore]
* Improve VerilatedVpiPutHolder storage requirements (#5144). [Kaleb Barrett]
* Fix coroutines without awaits to have a co_return (#4208) (#5175). [Arkadiusz Kozdra, Antmicro Ltd.]
* Fix missing flex include path variable (#4970) (#4971). [Christopher Taylor]
* Fix missing parameters with comma to be errors (#4979) (#5012). [Paul Swirhun]
* Fix 'experimental/coroutine' file not found on MacOS (#5030) (#5031) (#5151). [Paul Bowen-Huggett]
* Fix bound queue printing (#5032). [Aleksander Kiryk, Antmicro Ltd.]
* Fix consecutive zero-delays (#5038). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix attempted to destroy locked thread pool error (#5040). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix `$system` with string argument (#5042).
* Fix width extension on delays (#5043).
* Fix `$typename` on array.min and others (#5049). [Gökçe Aydos]
* Fix `make $(info)` which cannot be silenced (#5059). [Gökçe Aydos]
* Fix CMake builds to export VERILATOR_ROOT (#5063). [Michael Bikovitsky]
* Fix false ASSIGNIN on functions with explicit port map (#5069).
* Fix 4-state value support for $readmem (#5070) (#5078). [Ethan Sifferman]
* Fix DFG assertion with SystemC (#5076). [Geza Lore]
* Fix `$typename` string to be more standard (#5082) (#5083). [Andrew Nolte]
* Fix missed optimization in V3Delayed (#5089). [Geza Lore]
* Fix macro expansion in strings per 1800-2023 (#5094). [Geza Lore]
* Fix width extension of unpacked array select (#5095). [Varun Koyyalagunta]
* Fix MacOS missing <type_traits> header (#5096) (#5097). [Vito Gamberini]
* Fix assertion failure in V3Gate (#5101). [Yutetsu TAKATSUKASA]
* Fix aliases for forced port signals (#5105). [Geza Lore]
* Fix tracing interface functions (#5108). [Alex Solomatnikov]
* Fix method calls parsing in constraints (#5110). [Arkadiusz Kozdra, Antmicro Ltd.]
* Fix vpiInertialDelay for memories (#5113). [Todd Strader]
* Fix hierarchical compilation with nested -F (#5114) (#5124). [Alex Solomatnikov]
* Fix references to ports in forks (#5123). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix output C++ type error on change detect of I/O arrays (#5125) (#5131). [Pawel Jewstafjew]
* Fix x-valued parameters with `--x-assign unique` (#5129). [Ethan Sifferman]
* Fix overflow of string on VPI reads (#5145) (#5146). [Kaleb Barrett]
* Fix VerilatedVpiPutHolder class (#5156). [Kaleb Barrett]
* Fix extending out-of-range select (#5159) (#5164). [Geza Lore]
* Fix radix in width warnings (#5166). [Geza Lore]
* Fix SystemC BITS_PER_DIGIT in VL_ASSIGN_SBW (#5170). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix non-constant replication in concats (#5171). [Arkadiusz Kozdra, Antmicro Ltd.]
* Fix table optimization when applied on real data type (#5172) (#5173). [Arthur Rosa]
* Fix DPI import of null C-string (#5179).
Verilator 5.024 2024-04-05
==========================
**Major:**
* Add printing summary reports, use `--quiet` or `+verilator+quiet` to suppress (#4909).
* Support 1800-2023 keywords, and parsing with UNDEFINED warnings.
* Support 1800-2023 preprocessor ifdef expressions.
**Minor:**
* Change 1800-2023 to be default language version.
* Add DFG 'regularize' pass, and improve variable removal (#4937). [Geza Lore]
* Add error when pass net to function argument (#4132) (#4966). [Fuad Ismail]
* Add `UNUSEDLOOP` when unused loop is removed (#4926). [Bartłomiej Chmiel, Antmicro Ltd.]
* Add custom version for verilator --version packaging (#4954). [Nolan Poe]
* Add error on missing pure virtual functions (#4961).
* Add error on calling static function without object (#4962).
* Add JSON AST dumps (#5020). [Szymon Gizler]
* Support 1800-2023 DPI headers, svGetTime/svgGetTimeUnit/svGetTimePrecision methods.
* Support 1800-2023 class and function :initial, :extends, :final virtual overrides (#5025).
* Support public packed struct / union (#860) (#4878). [Kefa Chen]
* Support stream operation on unpacked array (#4714) (#5006). [Fuad Ismail]
* Support implicitly-typed variable definitions in for-loop initializers (#4945) (#4986). [Kevin Nygaard]
* Support inside range with implicit type conversion (#5026). [Arkadiusz Kozdra, Antmicro Ltd.]
* Improve installation to be relocatable (#4927). [Geza Lore]
* Improve internal ordering code (#4957) (#4990) (#4994) et al. [Geza Lore]
* Fix generate blocks in vpi_iterate (#3609) (#4913). [Andrew Nolte]
* Fix __Vlip undefined error in --freloop (#4824). [Justin Yao Du]
* Fix missing VPI scopes (#4918). [Andrew Nolte]
* Fix invalid cast on string structure creation (#4921).
* Fix try-lock spuriously fails (#4931) (#4938). [Kamil Rakoczy]
* Fix V3Unknown unpacked struct x-assign (#4934). [Yan Xu]
* Fix DFG removing forceable signals (#4942). [Geza Lore]
* Fix null characters in shortened identifiers (#4946). [Abdul Hameed]
* Fix assignment of null into struct member (#4952).
* Fix VPI missing scopes 2 (#4965). [Andrew Nolte]
* Fix object assignment from conditionals (#4968).
* Fix GCC14 warnings on template specialization syntax (#4974) (#4975). [Nolan Poe]
* Fix unpacked structure upper bit cleaning (#4978).
* Fix tests on MacOS (#4984) (#4985). [Kevin Nygaard]
* Fix `--prof-exec` predicted time values (#4988). [Geza Lore]
* Fix class type as an associative array parameter (#4997).
* Fix inout ports of unpacked struct type (#5000). [Ryszard Rozak, Antmicro Ltd.]
* Fix `unique {}` constraints missing semicolon (#5001).
* Fix preprocessor to respect strings in joins (#5007).
* Fix tracing class parameters (#5014).
* Fix memory leaks (#5016). [Geza Lore]
* Fix $readmem with missing newline (#5019). [Josse Van Delm]
* Fix internal error on missing pattern key (#5023).
* Fix tracing replicated hierarchical models (#5027).
Verilator 5.022 2024-02-24
==========================
**Minor:**
@@ -127,6 +247,7 @@ Verilator 5.020 2024-01-01
* Fix dynamic NBA conditions (#4773). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix `V3Fork` stage to run only if `--timing` is set (#4778). [Krzysztof Bieganski, Antmicro Ltd.]
* Fix max multiply width and add runtime assertions if too small. (#4781)
* Fix select value too wide (#5148) (#5153). [Dercury]
Verilator 5.018 2023-10-30
+50 -20
View File
@@ -91,6 +91,7 @@ datarootdir = @datarootdir@
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
CFG_WITH_SOLVER = @CFG_WITH_SOLVER@
PACKAGE_VERSION = @PACKAGE_VERSION@
#### End of system configuration section. ####
@@ -203,11 +204,21 @@ verilator.html:
verilator.pdf: Makefile
$(MAKE) -C docs verilator.pdf
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
VL_INST_BIN_FILES = verilator verilator_bin$(EXEEXT) verilator_bin_dbg$(EXEEXT) verilator_coverage_bin_dbg$(EXEEXT) \
verilator_ccache_report verilator_coverage verilator_difftree verilator_gantt verilator_includer verilator_profcfunc
# Some scripts go into both the search path and pkgdatadir,
# so they can be found by the user, and under $VERILATOR_ROOT.
# Public executables intended to be invoked directly by the user
# Don't put wildcards in these variables, it might cause an uninstall of other stuff
VL_INST_PUBLIC_SCRIPT_FILES = verilator \
verilator_coverage \
verilator_gantt \
verilator_profcfunc \
VL_INST_PUBLIC_BIN_FILES = verilator_bin$(EXEEXT) \
verilator_bin_dbg$(EXEEXT) \
verilator_coverage_bin_dbg$(EXEEXT) \
# Private executabels intended to be invoked by internals
# Don't put wildcards in these variables, it might cause an uninstall of other stuff
VL_INST_PRIVATE_SCRIPT_FILES = verilator_ccache_report \
verilator_includer \
VL_INST_INC_BLDDIR_FILES = \
include/verilated_config.h \
@@ -226,19 +237,34 @@ VL_INST_DATA_SRCDIR_FILES = \
examples/*/Makefile* \
examples/*/vl_* \
installbin:
$(MKINSTALLDIRS) $(DESTDIR)$(bindir)
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator $(DESTDIR)$(bindir)/verilator )
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_coverage $(DESTDIR)$(bindir)/verilator_coverage )
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_gantt $(DESTDIR)$(bindir)/verilator_gantt )
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_profcfunc $(DESTDIR)$(bindir)/verilator_profcfunc )
( cd bin ; $(INSTALL_PROGRAM) verilator_bin$(EXEEXT) $(DESTDIR)$(bindir)/verilator_bin$(EXEEXT) )
( cd bin ; $(INSTALL_PROGRAM) verilator_bin_dbg$(EXEEXT) $(DESTDIR)$(bindir)/verilator_bin_dbg$(EXEEXT) )
( cd bin ; $(INSTALL_PROGRAM) verilator_coverage_bin_dbg$(EXEEXT) $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg$(EXEEXT) )
mkbindirs:
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/bin
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_includer $(DESTDIR)$(pkgdatadir)/bin/verilator_includer )
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_ccache_report $(DESTDIR)$(pkgdatadir)/bin/verilator_ccache_report )
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_difftree $(DESTDIR)$(pkgdatadir)/bin/verilator_difftree )
$(MKINSTALLDIRS) $(DESTDIR)$(bindir)
installbin: | mkbindirs
cd $(srcdir)/bin; \
for p in $(VL_INST_PUBLIC_SCRIPT_FILES) ; do \
$(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkgdatadir)/bin/$$p; \
done
cd bin; \
for p in $(VL_INST_PUBLIC_BIN_FILES) ; do \
$(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkgdatadir)/bin/$$p; \
done
cd $(srcdir)/bin; \
for p in $(VL_INST_PRIVATE_SCRIPT_FILES) ; do \
$(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkgdatadir)/bin/$$p; \
done
installredirect: installbin | mkbindirs
cp ${srcdir}/bin/redirect ${srcdir}/bin/redirect.tmp
perl -p -i -e 'use File::Spec;' \
-e' $$path = File::Spec->abs2rel("$(realpath $(DESTDIR)$(pkgdatadir)/bin)", "$(realpath $(DESTDIR)$(bindir))");' \
-e 's/RELPATH.*/"$$path";/g' -- "${srcdir}/bin/redirect.tmp"
cd $(srcdir)/bin; \
for p in $(VL_INST_PUBLIC_SCRIPT_FILES) $(VL_INST_PUBLIC_BIN_FILES) ; do \
$(INSTALL_PROGRAM) redirect.tmp $(DESTDIR)$(bindir)/$$p; \
done
rm ${srcdir}/bin/redirect.tmp
# Man files can either be part of the original kit, or built in current directory
# So important we use $^ so VPATH is searched
@@ -281,8 +307,11 @@ installdata:
# We don't trust rm -rf, so rmdir instead as it will fail if user put in other files
uninstall:
-cd $(DESTDIR)$(bindir) && rm -f $(VL_INST_BIN_FILES)
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_BIN_FILES)
-cd $(DESTDIR)$(bindir) && rm -f $(VL_INST_PUBLIC_SCRIPT_FILES)
-cd $(DESTDIR)$(bindir) && rm -f $(VL_INST_PUBLIC_BIN_FILES)
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_PUBLIC_SCRIPT_FILES)
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_PUBLIC_BIN_FILES)
-cd $(DESTDIR)$(pkgdatadir)/bin && rm -f $(VL_INST_PRIVATE_SCRIPT_FILES)
-cd $(DESTDIR)$(mandir)/man1 && rm -f $(VL_INST_MAN_FILES)
-cd $(DESTDIR)$(pkgdatadir) && rm -f $(VL_INST_INC_BLDDIR_FILES)
-cd $(DESTDIR)$(pkgdatadir) && rm -f $(VL_INST_INC_SRCDIR_FILES)
@@ -311,7 +340,7 @@ uninstall:
-rmdir $(DESTDIR)$(pkgconfigdir)
install: all_nomsg install-all
install-all: installbin installman installdata install-msg
install-all: installbin installredirect installman installdata install-msg
install-here: installman info
@@ -410,6 +439,7 @@ PY_PROGRAMS = \
src/cppcheck_filtered \
src/flexfix \
src/vlcovgen \
src/.gdbinit.py \
test_regress/t/*.pf \
nodist/clang_check_attributes \
nodist/code_coverage \
+15 -14
View File
@@ -1,21 +1,23 @@
.. Github doesn't render images unless absolute URL
.. Do not know of a conditional tag, "only: github" nor "github display" works
.. image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
|badge1| |badge2| |badge3| |badge4| |badge5| |badge6| |badge7| |badge8|
.. |badge1| image:: https://img.shields.io/badge/Website-Verilator.org-181717.svg
:target: https://verilator.org
.. image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
.. |badge2| image:: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
:target: https://www.gnu.org/licenses/lgpl-3.0
.. image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
.. |badge3| image:: https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg
:target: https://opensource.org/licenses/Artistic-2.0
.. image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
.. |badge4| image:: https://repology.org/badge/tiny-repos/verilator.svg?header=distro%20packages
:target: https://repology.org/project/verilator/versions
.. image:: https://img.shields.io/docker/pulls/verilator/verilator
.. |badge5| image:: https://img.shields.io/docker/pulls/verilator/verilator
:target: https://hub.docker.com/r/verilator/verilator
.. image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
.. |badge6| image:: https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f
:target: https://www.codacy.com/gh/verilator/verilator
.. image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
.. |badge7| image:: https://codecov.io/gh/verilator/verilator/branch/master/graph/badge.svg
:target: https://codecov.io/gh/verilator/verilator
.. image:: https://github.com/verilator/verilator/workflows/build/badge.svg
.. |badge8| image:: https://github.com/verilator/verilator/workflows/build/badge.svg
:target: https://github.com/verilator/verilator/actions?query=workflow%3Abuild
@@ -85,12 +87,11 @@ thread is about 100 times faster than interpreted Verilog simulators such
as `Icarus Verilog`_. Another 2-10x speedup might be gained from
multithreading (yielding 200-1000x total over interpreted simulators).
Verilator has typically similar or better performance versus
closed-source Verilog simulators (e.g., Carbon Design Systems Carbonator,
Modelsim/Questa, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and
Pragmatic CVer/CVC). But, Verilator is open-sourced, so you can spend on
computes rather than licenses. Thus, Verilator gives you the best
simulation cycles/dollar.
Verilator has typically similar or better performance versus closed-source
Verilog simulators (e.g., Aldec Riviera-Pro, Cadence Incisive/NC-Verilog,
Mentor ModelSim/Questa, Synopsys VCS, VTOC, and Pragmatic CVer/CVC). But,
Verilator is open-sourced, so you can spend on computes rather than
licenses. Thus, Verilator gives you the best simulation cycles/dollar.
Installation & Documentation
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env perl
######################################################################
#
# Copyright 2003-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
#
######################################################################
require 5.006_001;
use warnings;
use FindBin qw($RealBin $RealScript);
use strict;
my $relpath = RELPATH; # Substituted during Verilator 'make install'
unshift @ARGV, $RealScript;
exec { "$RealBin/$relpath/$RealScript" } @ARGV;
die "%Error: Exec failed,";
+37 -32
View File
@@ -15,6 +15,7 @@ use Getopt::Long;
use FindBin qw($RealBin $RealScript);
use IO::File;
use Pod::Usage;
use Cwd qw(realpath);
use strict;
use vars qw($Debug @Opt_Verilator_Sw);
@@ -57,6 +58,7 @@ if (! GetOptions(
# Switches
"gdb!" => \$opt_gdb,
"gdbbt!" => \$opt_gdbbt,
"quiet!" => \$opt_quiet_exit, # As -quiet implies -quiet-exit
"quiet-exit!" => \$opt_quiet_exit,
"rr!" => \$opt_rr,
"unlimited-stack!" => \$opt_unlimited_stack,
@@ -67,6 +69,18 @@ if (! GetOptions(
pod2usage(-exitstatus => 2, -verbose => 0);
}
my $verilator_root = realpath("$RealBin/..");
if (defined $ENV{VERILATOR_ROOT}) {
if ((!-d $ENV{VERILATOR_ROOT}) || $verilator_root ne realpath($ENV{VERILATOR_ROOT})) {
warn "%Error: verilator: VERILATOR_ROOT is set to inconsistent path. Suggest leaving it unset.\n";
warn "%Error: VERILATOR_ROOT=$ENV{VERILATOR_ROOT}\n";
exit 1;
}
} else {
print "export VERILATOR_ROOT='$verilator_root'\n" if $Debug;
$ENV{VERILATOR_ROOT} = $verilator_root;
}
if ($opt_gdbbt && !gdb_works()) {
warn "-Info: --gdbbt ignored: gdb doesn't seem to be working\n" if $Debug;
$opt_gdbbt = 0;
@@ -151,30 +165,13 @@ sub debug {
# Builds
sub verilator_bin {
my $bin = "";
# Use VERILATOR_ROOT if defined, else assume verilator_bin is in the search path
my $basename = ($ENV{VERILATOR_BIN}
|| ($Debug ? "verilator_bin_dbg" : "verilator_bin"));
if (defined($ENV{VERILATOR_ROOT})) {
my $dir = $ENV{VERILATOR_ROOT};
if (-x "$dir/bin/$basename"
|| -x "$dir/bin/$basename.exe") { # From a "make install" into VERILATOR_ROOT
$bin = "$dir/bin/$basename";
} else {
$bin = "$dir/$basename"; # From pointing to kit directory
}
if (-x "$RealBin/$basename" || -x "$RealBin/$basename.exe") {
return "$RealBin/$basename";
} else {
if (-x "$RealBin/$basename"
|| -x "$RealBin/$basename.exe") {
$bin = "$RealBin/$basename"; # From path/to/verilator with verilator_bin installed
} else {
$bin = $basename; # Find in PATH
}
# Note we don't look under bin/$basename which would be right if running
# in the kit dir. Running that would likely break, since
# VERILATOR_ROOT wouldn't be set and Verilator won't find internal files.
return $basename; # Find in PATH
}
return $bin;
}
#######################################################################
@@ -312,6 +309,7 @@ detailed descriptions of these arguments.
+1800-2009ext+<ext> Use SystemVerilog 2009 with file extension <ext>
+1800-2012ext+<ext> Use SystemVerilog 2012 with file extension <ext>
+1800-2017ext+<ext> Use SystemVerilog 2017 with file extension <ext>
+1800-2023ext+<ext> Use SystemVerilog 2023 with file extension <ext>
--assert Enable all assertions
--assert-case Enable unique/unique0/priority case related checks
--autoflush Flush streams after all $displays
@@ -350,10 +348,12 @@ detailed descriptions of these arguments.
--dump-tree Enable dumping Ast .tree files
--dump-tree-addrids Use short identifiers instead of addresses
--dump-tree-dot Enable dumping Ast .tree.dot debug files
--dump-tree-json Enable dumping Ast .tree.json files and .tree.meta.json file
--dump-<srcfile> Enable dumping everything in source file
--dumpi-dfg <level> Enable dumping DfgGraphs to .dot files at level
--dumpi-graph <level> Enable dumping V3Graphs to .dot files at level
--dumpi-tree <level> Enable dumping Ast .tree files at level
--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
--error-limit <value> Abort after this number of errors
@@ -373,7 +373,7 @@ detailed descriptions of these arguments.
--generate-key Create random key for --protect-key
--getenv <var> Get environment variable with defaults
--get-supported <feature> Get if feature is supported
--help Display this help
--help Show this help
--hierarchical Enable hierarchical Verilation
-I<dir> Directory to search for includes
--if-depth <value> Tune IFDEPTH warning
@@ -387,6 +387,7 @@ detailed descriptions of these arguments.
--lib-create <name> Create a DPI library
+libext+<ext>+[ext]... Extensions for finding modules
--lint-only Lint, but do not make output
--localize-max-size <value> Tune localize optimization variable size
--make <build-tool> Generate scripts for specified build tool
-MAKEFLAGS <flags> Arguments to pass to make during --build
--main Generate C++ main() file
@@ -406,6 +407,7 @@ detailed descriptions of these arguments.
--output-split-ctrace <statements> Split tracing functions
-P Disable line numbers and blanks with -E
--pins-bv <bits> Specify types for top-level ports
--pins-inout-enables Specify that __en and __out signals be created for inouts
--pins-sc-biguint Specify types for top-level ports
--pins-sc-uint Specify types for top-level ports
--pins-uint8 Specify types for top-level ports
@@ -426,7 +428,9 @@ detailed descriptions of these arguments.
--public-params Mark all parameters as public_flat
--public-flat-rw Mark all variables, etc as public_flat_rw
-pvalue+<name>=<value> Overwrite toplevel parameter
--quiet Minimize additional printing
--quiet-exit Don't print the command on failure
--quiet-stats Don't print statistics
--relative-includes Resolve includes relative to current file
--reloop-limit Minimum iterations for forming loops
--report-unoptflat Extra diagnostics for UNOPTFLAT
@@ -441,7 +445,7 @@ detailed descriptions of these arguments.
--no-stop-fail Do not call $stop when assertion fails
--structs-packed Convert all unpacked structures to packed structures
-sv Enable SystemVerilog parsing
+systemverilogext+<ext> Synonym for +1800-2017ext+<ext>
+systemverilogext+<ext> Synonym for +1800-2023ext+<ext>
--threads <threads> Enable multithreading
--threads-dpi <mode> Enable multithreaded DPI
--threads-max-mtasks <mtasks> Tune maximum mtask partitioning
@@ -474,7 +478,7 @@ detailed descriptions of these arguments.
--no-verilate Skip Verilation and just compile previously Verilated code
+verilog1995ext+<ext> Synonym for +1364-1995ext+<ext>
+verilog2001ext+<ext> Synonym for +1364-2001ext+<ext>
--version Displays program version and exits
--version Show program version and exits
--vpi Enable VPI compiles
--waiver-output <filename> Create a waiver file based on the linter warnings
-Wall Enable all style warnings
@@ -508,20 +512,21 @@ description of these arguments.
=for VL_SPHINX_EXTRACT "_build/gen/args_verilated.rst"
+verilator+debug Enable debugging
+verilator+debugi+<value> Enable debugging at a level
+verilator+debug Enable debugging
+verilator+debugi+<value> Enable debugging at a level
+verilator+coverage+file+<filename> Set coverage output filename
+verilator+error+limit+<value> Set error limit
+verilator+help Display help
+verilator+noassert Disable assert checking
+verilator+error+limit+<value> Set error limit
+verilator+help Show help
+verilator+noassert Disable assert checking
+verilator+prof+exec+file+<filename> Set execution profile filename
+verilator+prof+exec+start+<value> Set execution profile starting point
+verilator+prof+exec+window+<value> Set execution profile duration
+verilator+prof+vlt+file+<filename> Set PGO profile filename
+verilator+rand+reset+<value> Set random reset technique
+verilator+seed+<value> Set random seed
+verilator+V Verbose version and config
+verilator+version Show version and exit
+verilator+quiet Minimize additional printing
+verilator+rand+reset+<value> Set random reset technique
+verilator+seed+<value> Set random seed
+verilator+V Show verbose version and config
+verilator+version Show version and exit
=head1 DISTRIBUTION
+2 -2
View File
@@ -154,9 +154,9 @@ verilator_coverage - Verilator coverage analyzer
verilator_coverage --annotate <obj>
verilator_coverage -write merged.dat -read <datafiles>...
verilator_coverage -write merged.dat <datafiles>...
verilator_coverage -write-info merged.info -read <datafiles>...
verilator_coverage -write-info merged.info <datafiles>...
Verilator_coverage processes Verilated model-generated coverage reports.
+8 -7
View File
@@ -92,13 +92,12 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
sudo apt-get update ||
sudo apt-get update
# libfl-dev needed for internal coverage's test runs
sudo apt-get install gdb gtkwave lcov libfl-dev ccache ||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3 ||
sudo apt-get install gdb gtkwave lcov libfl-dev ccache jq z3
# Required for test_regress/t/t_dist_attributes.pl
if [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
sudo apt-get install libclang-dev mold ||
sudo apt-get install libclang-dev mold
pip3 install clang==14.0
sudo apt-get install python3-clang mold ||
sudo apt-get install python3-clang mold
fi
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
sudo apt-get install libsystemc-dev ||
@@ -107,10 +106,10 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
elif [ "$CI_OS_NAME" = "osx" ]; then
brew update
# brew cask install gtkwave # fst2vcd hangs at launch, so don't bother
brew install ccache perl
brew install ccache perl jq z3
elif [ "$CI_OS_NAME" = "freebsd" ]; then
# fst2vcd fails with "Could not open '<input file>', exiting."
sudo pkg install -y ccache gmake perl5 python3
sudo pkg install -y ccache gmake perl5 python3 jq z3
else
fatal "Unknown os: '$CI_OS_NAME'"
fi
@@ -120,6 +119,8 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
fi
yes yes | sudo cpan -M $CI_CPAN_REPO -fi Parallel::Forker
install-vcddiff
# Workaround -fsanitize=address crash
sudo sysctl -w vm.mmap_rnd_bits=28
else
##############################################################################
# Unknown build stage
+23 -6
View File
@@ -40,7 +40,7 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
if [ "$COVERAGE" != 1 ]; then
autoconf
./configure --enable-longtests --enable-ccwarn
./configure --enable-longtests --enable-ccwarn --prefix="$INSTALL_DIR"
ccache -z
"$MAKE" -j "$NPROC" -k
# 22.04: ccache -s -v
@@ -87,23 +87,40 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
# Run sanitize on Ubuntu 22.04 only
[ "$CI_RUNS_ON" = 'ubuntu-22.04' ] && sanitize='--sanitize' || sanitize=''
TEST_REGRESS=test_regress
if [ "$CI_RELOC" == 1 ]; then
# Testing that the installation is relocatable.
"$MAKE" install
mkdir -p "$RELOC_DIR"
mv "$INSTALL_DIR" "$RELOC_DIR/relocated-install"
export VERILATOR_ROOT="$RELOC_DIR/relocated-install/share/verilator"
TEST_REGRESS="$RELOC_DIR/test_regress"
mv test_regress "$TEST_REGRESS"
# Feeling brave?
find . -delete
ls -la .
fi
# Run the specified test
ccache -z
case $TESTS in
dist-vlt-0)
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/3
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=0/4
;;
dist-vlt-1)
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=1/3
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=1/4
;;
dist-vlt-2)
"$MAKE" -C test_regress SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=2/3
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=2/4
;;
dist-vlt-3)
"$MAKE" -C "$TEST_REGRESS" SCENARIOS="--dist --vlt $sanitize" DRIVER_HASHSET=--hashset=3/4
;;
vltmt-0)
"$MAKE" -C test_regress SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=0/2
"$MAKE" -C "$TEST_REGRESS" SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=0/2
;;
vltmt-1)
"$MAKE" -C test_regress SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=1/2
"$MAKE" -C "$TEST_REGRESS" SCENARIOS=--vltmt DRIVER_HASHSET=--hashset=1/2
;;
coverage-all)
nodist/code_coverage --stages 1-
+13
View File
@@ -1,3 +1,15 @@
# DESCRIPTION: Verilator: CI Windows Power Shell - Compile Verilator
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
#
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
Set-PSDebug -Trace 1
if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
git clone --depth 1 https://github.com/lexxmark/winflexbison
cd winflexbison
@@ -8,6 +20,7 @@ if (-Not (Test-Path $PWD/../.ccache/win_bison.exe)) {
cmake --install . --prefix $PWD/../../../.ccache
cd ../..
}
mkdir build
cd build
cmake .. --install-prefix $PWD/../install
+17 -1
View File
@@ -1,3 +1,16 @@
# DESCRIPTION: Verilator: CI Windows Power Shell - Verilate a test
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
#
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
################################################################################
Set-PSDebug -Trace 1
cd install
$Env:VERILATOR_ROOT=$PWD
cd examples/cmake_tracing_c
@@ -5,6 +18,9 @@ mkdir build
cd build
cmake ..
cmake --build . --config Release -j 3
Release/example.exe
# TODO put this back in, see issue# 5163
# Release/example.exe
cd ..
Remove-Item -path build -recurse
+1
View File
@@ -43,6 +43,7 @@ RUN apt-get update \
perl \
python3 \
wget \
z3 \
zlib1g \
zlib1g-dev \
&& apt-get clean \
+24 -2
View File
@@ -5,12 +5,12 @@
# General Public License Version 3 or the Perl Artistic License Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
# When releasing, also update header of Changes file, and CmakeLists.txt,
# When releasing, also update header of Changes file, and CMakeLists.txt,
# and commit using "devel release" or "Version bump" message
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[5.022 2024-02-24],
AC_INIT([Verilator],[5.026 2024-06-15],
[https://verilator.org],
[verilator],[https://verilator.org])
@@ -134,6 +134,28 @@ AC_ARG_ENABLE([longtests],
AC_SUBST(CFG_WITH_LONGTESTS)
AC_MSG_RESULT($CFG_WITH_LONGTESTS)
AC_CHECK_PROG(HAVE_Z3,z3,yes)
AC_CHECK_PROG(HAVE_CVC5,cvc5,yes)
AC_CHECK_PROG(HAVE_CVC4,cvc4,yes)
# Special Substitutions - CFG_WITH_SOLVER
AC_MSG_CHECKING(for SMT solver)
AC_ARG_WITH([solver],
[AS_HELP_STRING([--with-solver='z3 --in'],
[set default SMT solver for constrained randomization])],
[CFG_WITH_SOLVER="${withval}"],
[CFG_WITH_SOLVER=no
if test "x$HAVE_Z3" = "xyes"; then
CFG_WITH_SOLVER="z3 --in"
elif test "x$HAVE_CVC5" = "xyes"; then
CFG_WITH_SOLVER="cvc5 --incremental"
elif test "x$HAVE_CVC4" = "xyes"; then
CFG_WITH_SOLVER="cvc4 --lang=smt2 --incremental"
fi]
)
AC_SUBST(CFG_WITH_SOLVER)
AC_MSG_RESULT($CFG_WITH_SOLVER)
# Compiler flags (ensure they are not empty to avoid configure defaults)
CFLAGS="$CFLAGS "
CPPFLAGS="$CPPFLAGS "
+15
View File
@@ -9,20 +9,24 @@ Adrien Le Masle
Ahmed El-Mahmoudy
Aleksander Kiryk
Alex Chadwick
Alex Solomatnikov
Àlex Torregrosa
Aliaksei Chapyzhenka
Ameya Vikram Singh
Andrea Calabrese
Andreas Kuster
Andrei Kostovski
Andrew Miloradovsky
Andrew Nolte
Anthony Donlon
Arkadiusz Kozdra
Arthur Rosa
Aylon Chaim Porat
Bartłomiej Chmiel
Cameron Kirk
Chih-Mao Chen
Chris Randall
Christopher Taylor
Chuxuan Wang
Chykon
Conor McCullough
@@ -33,6 +37,7 @@ David Ledger
David Metz
David Stanford
David Turner
Dercury
Don Williamson
Drew Ranck
Drew Taussig
@@ -46,10 +51,12 @@ february cozzocrea
Felix Neumärker
Felix Yan
Frans Skarman
Fuad Ismail
G-A. Kamendje
Garrett Smith
Geza Lore
Gianfranco Costamagna
Gijs Burghoorn
Glen Gibb
Gökçe Aydos
Graham Rushton
@@ -103,8 +110,10 @@ Justin Thiel
Kaleb Barrett
Kamil Rakoczy
Kanad Kanhere
Kefa Chen
Keith Colbert
Kevin Kiningham
Kevin Nygaard
Kritik Bhimani
Krzysztof Bieganski
Krzysztof Boronski
@@ -124,6 +133,7 @@ Marshal Qiao
Martin Schmidt
Martin Stadler
Matthew Ballance
Michael Bikovitsky
Michael Killough
Michaël Lefebvre
Michal Czyz
@@ -135,10 +145,13 @@ Mostafa Gamal
Nandu Raj
Nathan Kohagen
Nathan Myers
Nolan Poe
Oleh Maksymenko
Patrick Stewart
Paul Bowen-Huggett
Paul Swirhun
Paul Wright
Pawel Jewstafjew
Pawel Sagan
Pengcheng Xu
Peter Debacker
@@ -181,6 +194,7 @@ Tudor Timi
Tymoteusz Blazejczyk
Udi Finkelstein
Unai Martinez-Corral
Valentin Atepalikhin
Varun Koyyalagunta
Vassilis Papaefstathiou
Veripool API Bot
@@ -189,6 +203,7 @@ Vito Gamberini
William D. Jones
Wilson Snyder
Xi Zhang
Yan Xu
Yinan Xu
Yoda Lee
Yossi Nivin
+1 -1
View File
@@ -1,6 +1,6 @@
.. comment: generated by t_lint_multidriven_bad
.. code-block::
%Warning-MULTIDRIVEN: example.v:1:22 Signal has multiple driving blocks with different clocking: 'out2'
%Warning-MULTIDRIVEN: example.v:1:22 Signal has multiple driving blocks with different clocking: 't.mem'
example.v:1:7 ... Location of first driving block
example.v:1:7 ... Location of other driving block
+10
View File
@@ -407,6 +407,16 @@ only a couple of instructions.
For signal callbacks to work the main loop of the program must call
:code:`VerilatedVpi::callValueCbs()`.
Verilator also tracks when the model state has been modified via the VPI with
an :code:`evalNeeded` flag. This flag can be checked with :code:`VerilatedVpi::evalNeeded()`
and it can be cleared with :code:`VerilatedVpi::clearEvalNeeded()`. Used together
it is possible to skip :code:`eval()` calls if no model state has been changed
since the last :code:`eval()`.
Any data written via :code:`vpi_put_value` with :code:`vpiInertialDelay` will
be deferred for later. These delayed values can be flushed to the model with
:code:`VerilatedVpi::doInertialPuts()`.
.. _VPI Example:
+6
View File
@@ -118,6 +118,12 @@ associated programs.
See :ref:`Installation` for more details.
.. option:: VERILATOR_SOLVER
If set, the command to run as a constrained randomization backend, such
as :command:`cvc4 --lang=smt2 --incremental`. If not specified, it will use
the one supplied or found during configure, or :command:`z3 --in` if empty.
.. option:: VERILATOR_VALGRIND
If set, the command to run when using the :vlopt:`--valgrind` option, such as
+5
View File
@@ -92,6 +92,11 @@ Summary:
profile-guided optimization data runtime filename to dump to. Defaults
to :file:`profile.vlt`.
.. option:: +verilator+quiet
Disable printing the simulation summary report, see :ref:`Simulation
Summary Report`.
.. option:: +verilator+rand+reset+<value>
When a model was Verilated using
+47 -11
View File
@@ -51,6 +51,8 @@ Summary:
.. option:: +1800-2017ext+<ext>
.. option:: +1800-2023ext+<ext>
Specifies the language standard to be used with a specific filename
extension, <ext>.
@@ -199,9 +201,10 @@ Summary:
.. option:: --compiler <compiler-name>
Enables workarounds for the specified C++ compiler (list below).
This does not change any performance tuning options, but it may
in the future.
Enables workarounds for the specified C++ compiler (list below). This
does not change any performance tuning options, but it may in the
future. This also does not change default compiler flags; these are
determined when Verilator was configured.
clang
Tune for clang. This may reduce execution speed as it enables several
@@ -348,7 +351,7 @@ Summary:
Select the language used by default when first processing each
Verilog file. The language value must be "VAMS", "1364-1995",
"1364-2001", "1364-2001-noconfig", "1364-2005", "1800-2005",
"1800-2009", "1800-2012", "1800-2017", or "1800+VAMS".
"1800-2009", "1800-2012", "1800-2017", "1800-2023", or "1800+VAMS".
Any language associated with a particular file extension (see the
various +<lang>*\ ext+ options) will be used in preference to the
@@ -361,7 +364,7 @@ Summary:
``+<lang>ext+`` options should be used.
If no language is specified, either by this option or ``+<lang>ext+``
options, then the latest SystemVerilog language (IEEE 1800-2017) is
options, then the latest SystemVerilog language (IEEE 1800-2023) is
used.
.. option:: +define+<var>=<value>
@@ -410,6 +413,12 @@ Summary:
:vlopt:`--debug --no-dump-tree <--dump-tree>` may be useful if the dump
files are large and not desired.
.. option:: --dump-tree-json
Rarely needed. Enable dumping Ast .json.tree debug files with dumping level 3,
which dumps the standard critical stages. For details on the format, see
the Verilator Internals manual.
.. option:: --dump-tree-dot
Rarely needed. Enable dumping Ast .tree.dot debug files in Graphviz
@@ -445,6 +454,11 @@ Summary:
Rarely needed - for developer use. Set internal Ast dumping level
globally to the specified value.
.. option:: --dumpi-tree-json <level>
Rarely needed - for developer use. Set internal Ast JSON dumping level
globally to the specified value.
.. option:: --dumpi-<srcfile> <level>
Rarely needed - for developer use. Set the dumping level in the
@@ -803,6 +817,11 @@ Summary:
If the design is not to be completely Verilated, see also the
:vlopt:`--bbox-sys` and :vlopt:`--bbox-unsup` options.
.. option:: --localize-max-size <value>
Rarely needed. Set the maximum variable size in bytes for it to be
subject to localizing-to-stack optimization. Defaults to 1024.
.. option:: --make <build-tool>
Generates a script for the specified build tool.
@@ -971,6 +990,13 @@ Summary:
:option:`/*verilator&32;sc_bv*/` metacomment to select specific ports to
be sc_bv.
.. option:: --pins-inout-enables
Specifies that the __en and __out outputs will always be created for
inouts in the top-level module. The __en variable has a one in a bit
position to indicate the corresponding bit of the __out variable has
a value being driven from within the Verilated model.
.. option:: --pins-sc-uint
Specifies SystemC inputs/outputs greater than 2 bits wide should use
@@ -1160,11 +1186,20 @@ Summary:
Overwrites the given parameter(s) of the top-level module. See
:vlopt:`-G <-G<name>>` for a detailed description.
.. option:: --quiet
Alias for :vlopt:`--quiet-exit` :vlopt:`--quiet-stats`.
.. option:: --quiet-exit
When exiting due to an error, do not display the "Exiting due to Errors"
nor "Command Failed" messages.
.. option:: --quiet-stats
Disable printing the Verilation statistics report, see :ref:`Verilation
Summary Report`.
.. option:: --relative-includes
When a file references an include file, resolve the filename relative to
@@ -1298,12 +1333,12 @@ Summary:
.. option:: -sv
Specifies SystemVerilog language features should be enabled; equivalent
to :vlopt:`--language 1800-2017 <--language>`. This option is selected
to :vlopt:`--language 1800-2023 <--language>`. This option is selected
by default; it exists for compatibility with other simulators.
.. option:: +systemverilogext+<ext>
A synonym for :vlopt:`+1800-2017ext+\<ext\>`.
A synonym for :vlopt:`+1800-2023ext+\<ext\>`.
.. option:: --threads <threads>
@@ -1709,9 +1744,9 @@ Summary:
.. note::
This option applies only to values explicitly written as X
in modules (not classes) in the Verilog source code. Initial values
of clocks are set to 0 unless `--x-initial-edge` is
This option applies only to values explicitly written as X in modules
(not classes, nor parameters) in the Verilog source code. Initial
values of clocks are set to 0 unless `--x-initial-edge` is
specified. Initial values of all other state holding variables are
controlled with `--x-initial`.
@@ -1786,7 +1821,8 @@ Summary:
format is still evolving; there will be some changes in future versions.
This option disables some more aggressive transformations and dumps only
the final state of the AST.
the final state of the AST. For more granular and unaltered dumps, meant
mainly for debugging see :vlopt:`--dump-tree-json`.
.. option:: --json-only-meta-output <filename>
+2 -2
View File
@@ -37,9 +37,9 @@ verilator_coverage Example Usage
verilator_coverage --annotate <obj>
verilator_coverage -write merged.dat -read <datafiles>...
verilator_coverage -write merged.dat <datafiles>...
verilator_coverage -write-info merged.info -read <datafiles>...
verilator_coverage -write-info merged.info <datafiles>...
verilator_coverage Arguments
+6 -3
View File
@@ -36,8 +36,7 @@ or "`ifdef`"'s may break other tools.
.. option:: """ [string] """
A triple-quoted block specifies a string that may include newlines and
single quotes. This extension is experimental and may be removed
without deprecation.
single quotes. This extension was standardized in IEEE 1800-2023.
.. option:: $c([string], ...);
@@ -577,6 +576,10 @@ or "`ifdef`"'s may break other tools.
.. option:: /*verilator&32;trace_init_task*/
Removed.
In versions before 5.024:
Attached to a DPI import to indicate that function should be called when
initializing tracing. This attribute is indented only to be used
internally in code that Verilator generates when :vlopt:`--lib-create`
@@ -619,4 +622,4 @@ or "`ifdef`"'s may break other tools.
symbols. Also, the data represents the C++ stack; the SystemVerilog
functions/tasks involved may be renamed and/or inlined before becoming
the C++ functions that may be visible in the stack trace. This
extension is experimental and may be removed without deprecation.
extension was standardized in IEEE 1800-2023.
+7 -4
View File
@@ -128,10 +128,13 @@ How do I generate waveforms (traces) in C++?
See also the next question for tracing in SystemC mode.
A. Pass the :vlopt:`--trace` option to Verilator, and in your top-level C
code, call ``Verilated::traceEverOn(true)``. Then you may use
``$dumpfile`` and ``$dumpvars`` to enable traces, the same as with any
Verilog simulator. See ``examples/make_tracing_c`` in the distribution.
A. Pass the :vlopt:`--trace` option to Verilator. Then you may use ``$dumpfile`` and
``$dumpvars`` to enable traces, the same as with any Verilog simulator,
although Verilator ignores the arguments to ``$dumpvars``. See
``examples/make_tracing_c`` in the distribution.
If writing the top-level C code, call ``Verilated::traceEverOn(true)``;
this is done for you if using :vlopt:`--binary`.
B. Or, for finer-grained control, or C++ files with multiple Verilated
modules, you may also create the trace purely from C++. Create a
+17 -4
View File
@@ -126,8 +126,9 @@ Those developing Verilator itself may also want these (see internals.rst):
::
sudo apt-get install clang clang-format-14 cmake gdb gprof graphviz lcov
sudo apt-get install libclang-dev yapf3 bear
sudo pip3 install clang sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
sudo apt-get install python3-clang yapf3 bear jq
sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe ruff
sudo pip3 install git+https://github.com/antmicro/astsee.git
cpan install Pod::Perldoc
cpan install Parallel::Forker
@@ -151,6 +152,17 @@ To make use of Verilator FST tracing you will want `GTKwave
required at Verilator build time.
Install Z3
^^^^^^^^^^
In order to use constrained randomization the `Z3 Theorem Prover
<https://github.com/z3prover/z3#readme>`__ must be installed, however this is
not required at Verilator build time. There are other compatible SMT solvers,
like CVC5/CVC4, but they are not guaranteed to work. Since different solvers are
faster for different scenarios, the solver to use at run-time can be specified
by the environment variable :option:`VERILATOR_SOLVER`.
.. _Obtain Sources:
Obtain Sources
@@ -192,8 +204,9 @@ Eventual Installation Options
Before configuring the build, you must decide how you're going to
eventually install Verilator onto your system. Verilator will be compiling
the current value of the environment variables :option:`VERILATOR_ROOT`,
:option:`SYSTEMC_INCLUDE`, and :option:`SYSTEMC_LIBDIR` as defaults into
the executable, so they must be correct before configuring.
:option:`VERILATOR_SOLVER`, :option:`SYSTEMC_INCLUDE`, and
:option:`SYSTEMC_LIBDIR` as defaults into the executable, so they must be
correct before configuring.
These are the installation options:
+12 -1
View File
@@ -62,12 +62,23 @@ Verilator supports the 2017 "for" loop constructs and several cleanups IEEE
made in 1800-2017.
SystemVerilog 2023 (IEEE 1800-2023) Support
-------------------------------------------
Verilator supports some of the 2023 improvements, including triple-quoted
string blocks that may include newlines and single quotes.
Verilator implements a full IEEE 1800-2023 compliant preprocessor,
including triple-quoted strings, and \`ifdef expressions.
Verilog AMS Support
-------------------
Verilator implements a very small subset of Verilog AMS (Verilog Analog and
Mixed-Signal Extensions) with the subset corresponding to those VMS
keywords with near-equivalents in Verilog 2005 or SystemVerilog 2017.
keywords with near-equivalents in Verilog IEEE 1364 or SystemVerilog
IEEE 1800.
AMS parsing is enabled with :vlopt:`--language VAMS <--language>` or
:vlopt:`--language 1800+VAMS <--language>`.
+3 -3
View File
@@ -50,6 +50,6 @@ The best place to get started is to try the :ref:`Examples`.
.. [#] SystemVerilog is defined by the `Institute of Electrical and
Electronics Engineers (IEEE) Standard for SystemVerilog - Unified
Hardware Design, Specification, and Verification Language`, Standard
1800, released in 2005, 2009, 2012, and 2017. The Verilator
documentation uses the shorthand e.g., "IEEE 1800-2017", to refer to
the, e.g., 2017 version of this standard.
1800, released in 2005, 2009, 2012, 2017, and 2023. The Verilator
documentation uses the shorthand e.g., "IEEE 1800-2023", to refer to
the, e.g., 2023 version of this standard.
+54
View File
@@ -12,6 +12,60 @@ Verilated model's executable. For the runtime arguments to a simulated
model, see :ref:`Simulation Runtime Arguments`.
.. _Simulation Summary Report:
Simulation Summary Report
=========================
When simulation finishes, it will print a report to stdout summarizing the
simulation. This requires the model being Verilated with :vlopt:`--main`.
The report may be disabled with :vlopt:`+verilator+quiet`.
For example:
.. code-block::
- S i m u l a t i o n R e p o r t: Verilator ...
- Verilator: End at simtime 123 ns; walltime 1234.001 s; speed 123 ns/s
- Verilator: cpu 22.001 s on 4 threads; alloced 123 MB
The information in this report is:
.. describe:: "Verilator ..."
Program version.
.. describe:: "End at simtime 123 ns"
Verilog $time at which the model finished or stopped.
.. describe:: "walltime 1234.001 s"
Real elapsed wall time in seconds.
.. describe:: "speed 123.1 ns/s"
Simulated time (if non-zero) divided by wall time. e.g. `123 ns/s` means
123 simulated nanoseconds took 1 second of wall time; for a model with
only a 1 GHz clock that would be equivalent to 123.1 cycles per
second. The units are automatically selected to give a number between 1
and 1000. The wall time includes initialization, initial and final
process blocks, so indicates a slower speed than if the model had a
longer runtime.
.. describe:: "cpu 22 s"
CPU time used total across all CPU threads in seconds.
.. describe:: "4 threads"
Number of simultaneous threads used.
.. describe:: "alloced 123 MB"
Total memory used during simulation in megabytes.
.. _Benchmarking & Optimization:
Benchmarking & Optimization
+75
View File
@@ -498,3 +498,78 @@ The search paths can be configured by setting some variables:
Sets the installation prefix of an installed SystemC library. (Same as
SYSTEMC_ROOT).
.. _Verilation Summary Report:
Verilation Summary Report
=========================
When Verilator generates code, unless :vlopt:`--quiet-stats` is used, it
will print a report to stdout summarizing the build. For example:
.. code-block::
- V e r i l a t i o n R e p o r t: Verilator ....
- Verilator: Built from 354 MB sources in 247 modules,
into 74 MB in 89 C++ files needing 0.192 MB
- Verilator: Walltime 26.580 s (elab=2.096, cvt=18.268,
bld=2.100); cpu 26.548 s on 1 threads; alloced 2894.672 MB
The information in this report is:
.. describe:: "Verilator ..."
Program version.
.. describe:: "234 MB sources"
Characters of post-preprocessed text in all input
Verilog and Verilator Control files in megabytes.
.. describe:: "247 modules"
Number of interfaces/modules/classes/packages in design before
elaboration.
.. describe:: "into 74 MB"
Characters of output C++ code, including comments in megabytes.
.. describe:: "89 C++ files"
Number of .cpp files created.
.. describe:: "needing 192MB"
Verilation-time minimum-bound estimate of memory needed to run model in
megabytes. (Expect to need significantly more.)
.. describe:: "Walltime 26.580 s"
Real elapsed wall time for Verilation and build.
.. describe:: "elab=2.096"
Wall time to read in files and complete elaboration.
.. describe:: "cvt=18.268"
Wall time for Verilator to process and write output.
.. describe:: "bld=2.1"
Wall time to compile gcc/clang (if using :vlopt:`--build`).
.. describe:: "cpu 22.548 s"
CPU time used, total across all CPU threads.
.. describe:: "4 threads"
Number of simultaneous threads used.
.. describe:: "alloced 123 MB"
Total memory used during build by Verilator executable (excludes
:vlopt:`--build` compiler's usage) in megabytes.
+69 -23
View File
@@ -161,8 +161,8 @@ List Of Warnings
.. option:: BADSTDPRAGMA
An error that a pragma is badly formed, for pragmas defined by IEEE
1800-2017. For example, an empty pragma line, or an incorrectly used
'pragma protect'. Third-party pragmas not defined by IEEE 1800-2017 are
1800-2023. For example, an empty pragma line, or an incorrectly used
'pragma protect'. Third-party pragmas not defined by IEEE 1800-2023 are
ignored.
@@ -210,22 +210,35 @@ List Of Warnings
.. option:: BLKLOOPINIT
.. TODO better example
This indicates that the initialization of an array needs to use
non-delayed assignments. This is done in the interest of speed; if
delayed assignments were used, the simulator would have to copy large
arrays every cycle. (In smaller loops, loop unrolling allows the
delayed assignment to work, though it's a bit slower than a non-delayed
assignment.) Here's an example
Indicates certain constructs where non-blocking assignments to unpacked
arrays (memories) are not supported inside loops. These typically appear in
initialization/reset code:
.. code-block:: sv
always @(posedge clk)
if (~reset_l)
for (i=0; i<`ARRAY_SIZE; i++)
array[i] = 0; // Non-delayed for verilator
array[i] <= 0; // Non-blocking assignment inside loop
else
array[address] <= data;
While this is supported in typical synthesizeable code (including the
example above), some complicated cases are not supported. Namely:
1. If the above loop is inside a suspendable process or fork statement.
2. If the variable is also the target of a '<=' non-blocking assignment
in a suspendable process or fork statement (in addition to a synthesizable
loop).
3. If the element type of the array is a compound type.
4. In versions before 5.026, any delayed assignment to an array.
It might slightly improve run-time performance if you change the
non-blocking assignment inside the loop into a blocking assignment
(that is: use '=' instead of '<='), if possible.
This message is only seen on large or complicated loops because
Verilator generally unrolls small loops. You may want to try increasing
@@ -283,7 +296,7 @@ List Of Warnings
Unique case statements that select on an enumerated variable, where all
of the enumerated values are covered by case items, are considered
complete even if the case statement does not cover illegal
non-enumerated values (IEEE 1800-2017 12.5.3). To check that illegal
non-enumerated values (IEEE 1800-2023 12.5.3). To check that illegal
values are not hit, use :vlopt:`--assert`.
Ignoring this warning will only suppress the lint check; it will
@@ -479,7 +492,7 @@ List Of Warnings
.. code-block::
%Warning-DEFPARAM: example.v:5:15: defparam is deprecated (IEEE 1800-2017 C.4.1)
%Warning-DEFPARAM: example.v:5:15: defparam is deprecated (IEEE 1800-2023 C.4.1)
: ... Suggest use instantiation with #(.MY_PARAM(...etc...))
To repair use :code:`#(.PARAMETER(...))` syntax. Repaired Example:
@@ -707,7 +720,7 @@ List Of Warnings
.. code-block::
%Warning-GENUNNAMED: example.v:2:9: Unnamed generate block (IEEE 1800-2017 27.6)
%Warning-GENUNNAMED: example.v:2:9: Unnamed generate block (IEEE 1800-2023 27.6)
To fix this assign a label (often with the naming convention prefix of
:code:`gen_` or :code:`g_`), for example:
@@ -763,7 +776,7 @@ List Of Warnings
.. code-block::
%Warning-IGNOREDRETURN: example.v:5:9: Ignoring return value of non-void function (IEEE 1800-2017 13.4.1)
%Warning-IGNOREDRETURN: example.v:5:9: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1)
The portable way to suppress this warning (in SystemVerilog) is to use a
void cast, for example:
@@ -1204,6 +1217,14 @@ List Of Warnings
:vlopt:`--no-timing` option.
.. option:: NONSTD
Warns when a non-standard language feature is used that has a standard
equivalent, which might behave differently in corner cases. For example
:code:`$psprintf` system function is replaced by its standard equivalent
:code:`$sformatf`.
.. option:: NULLPORT
Warns that a null port was detected in the module definition port
@@ -1328,10 +1349,35 @@ List Of Warnings
.. TODO better example
An error that a package/class appears to have been referenced that has not
yet been declared. According to IEEE 1800-2017 26.3, all packages must
yet been declared. According to IEEE 1800-2023 26.3, all packages must
be declared before being used.
.. option:: PREPROCZERO
Warns that a preprocessor \`ifdef/\`ifndef expression (added in IEEE
1800-2023) evaluates a define value which has a value of :code:`0`.
This will evaluate in the expression as :code:`1` because the define has
a definition, unlike in the C preprocessor, which evaluates using the
define's value (of :code:`1`).
Referring to a define with an empty value does not give this warning, as
in C, the preprocessor will give an error on a preprocessor expression
of a define that is empty.
.. code-block:: sv
:linenos:
:emphasize-lines: 2
`define ZERO 0
`ifdef (ZERO || ZERO) //<--- warning PREPROCZERO
`error This_will_error_which_might_be_not_the_intent
`endif
The portable way to suppress this warning is to use a define value other
than zero to when used in a preprocessor expression.
.. option:: PROCASSWIRE
.. TODO better example
@@ -1707,7 +1753,7 @@ List Of Warnings
.. code-block::
%Warning-TIMESCALEMOD: example.v:1:8: Timescale missing on this module as other modules have it (IEEE 1800-2017 3.14.2.3)
%Warning-TIMESCALEMOD: example.v:1:8: Timescale missing on this module as other modules have it (IEEE 1800-2023 3.14.2.3)
Recommend using :vlopt:`--timescale` argument, or in front of all
modules use:
@@ -1949,7 +1995,7 @@ List Of Warnings
.. option:: USERERROR
A SystemVerilog elaboration-time assertion error was executed.
IEEE 1800-2017 20.11 requires this error.
IEEE 1800-2023 20.11 requires this error.
Faulty example:
@@ -1965,7 +2011,7 @@ List Of Warnings
.. option:: USERFATAL
A SystemVerilog elaboration-time assertion fatal was executed.
IEEE 1800-2017 20.11 requires this error.
IEEE 1800-2023 20.11 requires this error.
Faulty example:
@@ -1981,7 +2027,7 @@ List Of Warnings
.. option:: USERINFO
A SystemVerilog elaboration-time assertion print was executed. This is
not an error or warning, and IEEE 1800-2017 20.11 requires this
not an error or warning, and IEEE 1800-2023 20.11 requires this
behavior.
Example:
@@ -1996,7 +2042,7 @@ List Of Warnings
.. option:: USERWARN
A SystemVerilog elaboration-time assertion warning was executed.
IEEE 1800-2017 20.11 requires this warning.
IEEE 1800-2023 20.11 requires this warning.
Faulty example:
@@ -2135,7 +2181,7 @@ List Of Warnings
.. option:: ZEROREPL
Warns that zero is used as the replication value in the replication
operator. This is specified as an error by IEEE 1800-2017 11.4.12.1.
operator. This is specified as an error by IEEE 1800-2023 11.4.12.1.
Faulty example:
@@ -2155,7 +2201,7 @@ List Of Warnings
.. code-block::
%Error-ZEROREPL: test.v:5:22: Replication value of 0 is only legal under a concatenation (IEEE 1800-2017 11.4.12.1)
%Error-ZEROREPL: test.v:5:22: Replication value of 0 is only legal under a concatenation (IEEE 1800-2023 11.4.12.1)
Note that in some cases, this warning may be false, when a condition
upstream or downstream of the access means the zero replication will
+126 -19
View File
@@ -224,7 +224,7 @@ Scheduling
----------
Verilator implements the Active and NBA regions of the SystemVerilog scheduling
model as described in IEEE 1800-2017 chapter 4, and in particular sections
model as described in IEEE 1800-2023 chapter 4, and in particular sections
4.5 and Figure 4.1. The static (Verilation time) scheduling of SystemVerilog
processes is performed by code in the ``V3Sched`` namespace. The single
entry point to the scheduling algorithm is ``V3Sched::schedule``. Some
@@ -312,7 +312,7 @@ Scheduling of clocked and combinational logic
For performance, clocked and combinational logic needs to be ordered.
Conceptually this minimizes the iterations through the evaluation loop
presented in the reference algorithm in the SystemVerilog standard (IEEE
1800-2017 section 4.5), by evaluating logic constructs in data-flow order.
1800-2023 section 4.5), by evaluating logic constructs in data-flow order.
Without going into a lot of detail here, accept that well thought out ordering
is crucial to good simulation performance, and also enables further
optimizations later on.
@@ -407,7 +407,7 @@ triggered due to a combinational signal change from the previous evaluation
pass, and a combinational loop settling due to hybrid logic, if the clocked
logic reads the not yet settled combinationally driven signal. Such a race
is indeed possible, but our evaluation is consistent with the SystemVerilog
scheduling semantics (IEEE 1800-2017 chapter 4), and therefore any program
scheduling semantics (IEEE 1800-2023 chapter 4), and therefore any program
that exhibits such a race has non-deterministic behavior according to the
SystemVerilog semantics, so we accept this.
@@ -1031,6 +1031,91 @@ the evaluation process records a bitmask of variables that might have
changed; if clear, checking those signals for changes may be skipped.
Constrained randomization
-------------------------
Because general constrained randomization is a co-NP-hard problem, not all
cases are implemented in Verilator, and an external specialized SMT solver is
used for any non-obvious ones.
The ``randomize()`` method spawns an SMT solver in a sub-process. Then the
solver gets a setup query, then the definition of variables, then all the
constraints (SMT assertions) about the variables. Since the solver has no
information about the class' PRNG state, if the problem is satisfiable,
the solution space is further constrained by adding extra random constraints,
and querying the values satisfying the problem statement.
The constraint is currently constructed as fixing a simple xor of randomly
chosen bits of the variables being randomized.
The runtime classes used for handling the randomization are defined in
``verilated_random.h`` and ``verilated_random.cpp``.
``VlSubprocess``
~~~~~~~~~~~~~~~~
Subprocess handle, responsible for keeping track of the resources like child
PID, read and write file descriptors, and presenting them as a C++ iostream.
``VlRandomizer``
~~~~~~~~~~~~~~~~
Randomizer class, responsible for keeping track of variables and constraints,
and communicating with the solver subprocess.
The solver gets the constraints in `SMT-LIB2
<https://smtlib.cs.uiowa.edu/>`__ textual format in the following syntax:
::
(set-info :smt-lib-version 2.0)
(set-option :produce-models true)
(set-logic QF_BV)
(declare-fun v () (_ BitVec 16))
(declare-fun w () (_ BitVec 64))
(declare-fun x () (_ BitVec 48))
(declare-fun z () (_ BitVec 24))
(declare-fun t () (_ BitVec 23))
(assert (or (= v #x0003) (= v #x0008)))
(assert (= w #x0000000000000009))
(assert (or (or (= x #x000000000001) (= x #x000000000002)) (or (= x #x000000000004) (= x #x000000000009))))
(assert (bvult ((_ zero_extend 8) z) #x00000015))
(assert (bvugt ((_ zero_extend 8) z) #x0000000d))
(check-sat)
The solver responds with either ``sat`` or ``unsat``. Then the initial solution
is queried with:
::
(get-value (v w x z t ))
The solver then responds with e.g.:
::
((v #x0008)
(w #x0000000000000005)
(x #x000000000002)
(z #x000010)
(t #b00000000000000000000000))
And then a follow-up query (or a series thereof) is asked, and the solver gets
reset, so that it can be reused by subsequent randomization attempts:
::
(assert (= (bvxor (bvxor <...> (bvxor ((_ extract 21 21) z) ((_ extract 39 39) x)) ((_ extract 5 5) w)) <...> ((_ extract 10 10) w)) #b0))
(check-sat)
(get-value)
...
(reset)
Coding Conventions
==================
@@ -1701,7 +1786,7 @@ Similarly, the ``NETLIST`` has a list of modules referred to by its
``.tree.json``` is an alternative dump format to ``.tree`` that is meant for
programmatic processing (e.g. with `astsee <https://github.com/antmicro/astsee>`_).
To enable this dump format, use :vlopt:`--json-only`.
To enable this dump format, use :vlopt:`--dump-tree-json` or :vlopt:`--json-only`.
Structure:
::
@@ -1743,8 +1828,8 @@ Structure:
.tree.meta.json Output
----------------
.tree.meta.json contains metadata that is common across the whole AST tree.
.tree.meta.json contains metadata that is common across the whole AST tree
(in case of --dump-tree-json, multiple trees share one meta file).
Besides de-duplication of data shared between multiple stages, .meta.json enables offloading
unstable data (that can vary from machine-to-machine or run-to-run) from main .tree.json.
@@ -1754,11 +1839,11 @@ This offloading allows, for example, to use byte-to-byte comparisons of AST dump
{"files": {
/* Map id to filename, and other metadata */
"d": {"filename":"/home/ant/tmp/verilator/include/verilated_std.sv", "realpath":"/home/ant/tmp/verilator/include/verilated_std.sv", "language":"1800-2017"},
"a": {"filename":"<built-in>", "realpath":"<built-in>", "language":"1800-2017"},
"b": {"filename":"<command-line>", "realpath":"<command-line>", "language":"1800-2017"},
"c": {"filename":"input.vc", "realpath":"/home/ant/tmp/verilator/test_regress/input.vc", "language":"1800-2017"},
"e": {"filename":"t/t_EXAMPLE.v", "realpath":"/home/ant/tmp/verilator/test_regress/t/t_EXAMPLE.v", "language":"1800-2017"}
"d": {"filename":"/home/ant/tmp/verilator/include/verilated_std.sv", "realpath":"/home/ant/tmp/verilator/include/verilated_std.sv", "language":"1800-2023"},
"a": {"filename":"<built-in>", "realpath":"<built-in>", "language":"1800-2023"},
"b": {"filename":"<command-line>", "realpath":"<command-line>", "language":"1800-2023"},
"c": {"filename":"input.vc", "realpath":"/home/ant/tmp/verilator/test_regress/input.vc", "language":"1800-2023"},
"e": {"filename":"t/t_EXAMPLE.v", "realpath":"/home/ant/tmp/verilator/test_regress/t/t_EXAMPLE.v", "language":"1800-2023"}
},"pointers": {
/* Map id to real address */
"(AG)": "0x562997289180",
@@ -1846,6 +1931,20 @@ To print a node:
pnt nodep
# or: call dumpTreeGdb(nodep) # aliased to "pnt" in src/.gdbinit
``src/.gdbinit`` and ``src/.gdbinit.py`` define handy utilities for working with
JSON AST dumps. For example:
* ``jstash nodep`` - Perform a JSON AST dump and save it into GDB value history (e.g. ``$1``)
* ``jtree nodep`` - Perform a JSON AST dump and pretty print it using ``astsee_verilator``.
* ``jtree $1`` - Pretty print a dump that was previously saved by ``jstash``.
* ``jtree nodep -d '.file, .timeunit'`` - Perform a JSON AST dump, filter out some fields and pretty print it.
* ``jtree 0x55555613dca0`` - Pretty print using address literal (rather than actual pointer).
* ``jtree $1 nodep`` - Diff ``nodep`` against an older dump.
A detailed description of ``jstash`` and ``jtree`` can be displayed using ``gdb``'s ``help`` command.
These commands require `astsee <https://github.com/antmicro/astsee>`_ to be installed.
When GDB halts, it is useful to understand that the backtrace will commonly
show the iterator functions between each invocation of ``visit`` in the
backtrace. You will typically see a frame sequence something like:
@@ -1907,21 +2006,21 @@ Verilator ideally would support all of IEEE, and has the goal to get close
to full support. However the following IEEE sections and features are not
anticipated to be ever implemented for the reasons indicated.
IEEE 1800-2017 3.3 modules within modules
IEEE 1800-2023 3.3 modules within modules
Little/no tool support, and arguably not a good practice.
IEEE 1800-2017 6.12 "shortreal"
IEEE 1800-2023 6.12 "shortreal"
Little/no tool support, and easily promoted to real.
IEEE 1800-2017 11.11 Min, typ, max
IEEE 1800-2023 11.11 Min, typ, max
No SDF support, so will always use typical.
IEEE 1800-2017 20.16 Stochastic analysis
IEEE 1800-2023 20.16 Stochastic analysis
Little industry use.
IEEE 1800-2017 20.17 PLA modeling
IEEE 1800-2023 20.17 PLA modeling
Little industry use and outdated technology.
IEEE 1800-2017 31 Timing checks
IEEE 1800-2023 31 Timing checks
No longer relevant with static timing analysis tools.
IEEE 1800-2017 32 SDF annotation
IEEE 1800-2023 32 SDF annotation
No longer relevant with static timing analysis tools.
IEEE 1800-2017 33 Config
IEEE 1800-2023 33 Config
Little industry use.
@@ -1962,6 +2061,10 @@ driver.pl Non-Scenario Arguments
files with dumping level 3, which dumps the standard critical stages.
For details on the format see `.tree Output`.
--fail-max <numtests>
Set the number of failing tests, after which the driver will stop running
additional tests. Defaults to 20, 0 disables.
--gdb
Same as ``verilator --gdb``: Run Verilator under the debugger.
@@ -2107,6 +2210,10 @@ VERILATOR_ROOT
Standard path to Verilator distribution root; see primary Verilator
documentation.
VERILATOR_SOLVER
SMT solver command for constrained randomization; see primary Verilator
documentation.
VERILATOR_TESTS_SITE
Used with ``--site``, a colon-separated list of directories with tests to
be added to testlist.
+19
View File
@@ -40,6 +40,7 @@ Brej
Briquet
Brownlee
Buckenmaier
Burghoorn
Bybell
CLang
CMake
@@ -71,6 +72,7 @@ Deadman
Debacker
Deepa
Defparams
Delm
Denio
Deprecations
Deroo
@@ -130,6 +132,7 @@ Gernot
Gerst
Gielda
Gigerl
Gijs
Gileadi
Giri
Gisselquist
@@ -146,6 +149,7 @@ Grulfen
Gu
Gunter
Guo
Hameed
Hao
Haojin
Harboe
@@ -162,6 +166,7 @@ Homebrew
Hornung
Hossell
Hsu
Huggett
Hutt
Hyperthreading
Ibrahim
@@ -184,6 +189,7 @@ Jiaxun
Jiuyang
Joannou
Joly
Josse
Jullien
Junji
Junyi
@@ -438,6 +444,7 @@ Verilog
Vighnesh
Viktor
Vilp
Vlip
Vm
Vukobratovic
Wai
@@ -461,6 +468,7 @@ Xiaoliang
Xiaoyi
Xuan
Xuanqi
Yao
Yazdanbakhsh
Yernagula
Yi
@@ -639,6 +647,7 @@ envvar
eof
errae
erroring
esynr
et
eval
evals
@@ -670,6 +679,7 @@ foreach
fprintf
fprofile
fread
freloop
frewind
fs
fscanf
@@ -835,6 +845,7 @@ parallelized
param
parameterization
parameterized
parameterless
params
parens
pawel
@@ -878,6 +889,7 @@ profcfunc
profiler
prototyptes
ps
psprintf
pthread
ptr
pulldown
@@ -961,13 +973,18 @@ submakes
submodule
submodules
substring
suspendable
sv
svBitVal
svBitVecVal
svGet
svGetTime
svGetTimePrecision
svLogicVal
svdpi
svgGetTimeUnit
swrite
synthesizeable
sys
systemc
tenghtt
@@ -986,6 +1003,7 @@ toolchain
topcell
toplevel
toupper
traceCapable
traceEverOn
tran
treei
@@ -1050,6 +1068,7 @@ vluint
vpi
vpiConstType
vpiDefName
vpiInertialDelay
vpiInstance
vpiLeftRange
vpiModule
+1
View File
@@ -30,6 +30,7 @@ endif()
# Create a new executable target that will contain all your sources
add_executable(example ../make_hello_c/sim_main.cpp)
target_compile_features(example PUBLIC cxx_std_14)
# Add the Verilated circuit to the target
verilate(example
+1 -1
View File
@@ -31,7 +31,7 @@ endif
ifeq ($(shell which cmake),)
TARGET := nocmake
else
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
+1
View File
@@ -37,6 +37,7 @@ find_package(SystemCLanguage QUIET)
# Create a new executable target that will contain all your sources
add_executable(example ../make_hello_sc/sc_main.cpp)
target_compile_features(example PUBLIC cxx_std_14)
set_property(
TARGET example
+1 -1
View File
@@ -31,7 +31,7 @@ endif
ifeq ($(shell which cmake),)
TARGET := nocmake
else
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
@@ -30,9 +30,12 @@ endif()
# Create the main executable target
add_executable(example ../make_protect_lib/sim_main.cpp)
target_compile_features(example PUBLIC cxx_std_14)
# Create a secret library
add_library(verilated_secret STATIC) # or SHARED for a shared library
target_compile_features(verilated_secret PUBLIC cxx_std_14)
target_link_libraries(example PRIVATE verilated_secret)
# To create both libraries on CMake >= 3.12 replace the above 2 lines with the following:
# add_library(verilated_secret OBJECT)
+1 -1
View File
@@ -31,7 +31,7 @@ endif
ifeq ($(shell which cmake),)
TARGET := nocmake
else
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
+1
View File
@@ -30,6 +30,7 @@ endif()
# Create a new executable target that will contain all your sources
add_executable(example ../make_tracing_c/sim_main.cpp)
target_compile_features(example PUBLIC cxx_std_14)
# Add the Verilated circuit to the target
verilate(example COVERAGE TRACE
+1 -1
View File
@@ -31,7 +31,7 @@ endif
ifeq ($(shell which cmake),)
TARGET := nocmake
else
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
+1
View File
@@ -38,6 +38,7 @@ find_package(SystemCLanguage QUIET)
# Create a new executable target that will contain all your sources
add_executable(example ../make_tracing_sc/sc_main.cpp)
target_compile_features(example PUBLIC cxx_std_14)
set_property(
TARGET example
+1 -1
View File
@@ -31,7 +31,7 @@ endif
ifeq ($(shell which cmake),)
TARGET := nocmake
else
CMAKE_VERSION := $(shell cmake --version | grep -Po '(\d[\.\d]+)')
CMAKE_VERSION := $(shell cmake --version | grep -o '[0-9][.0-9]\+')
CMAKE_MAJOR := $(shell echo $(CMAKE_VERSION) | cut -f1 -d.)
CMAKE_MINOR := $(shell echo $(CMAKE_VERSION) | cut -f2 -d.)
CMAKE_GT_3_8 := $(shell [ $(CMAKE_MAJOR) -gt 3 -o \( $(CMAKE_MAJOR) -eq 3 -a $(CMAKE_MINOR) -ge 8 \) ] && echo true)
+3 -2
View File
@@ -17,7 +17,8 @@
#endif
int main(int argc, char** argv) {
if (false && argc && argv) {}
(void)argc;
(void)argv;
// Construct context to hold simulation time, etc
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
@@ -46,12 +47,12 @@ int main(int argc, char** argv) {
// Simulate until $finish
while (!contextp->gotFinish()) {
contextp->timeInc(1);
top->clk = ~top->clk & 0x1;
top->eval();
#if VM_TRACE
if (tfp) tfp->dump(contextp->time());
#endif
contextp->timeInc(1);
}
// Final model cleanup
+3
View File
@@ -117,6 +117,9 @@ int main(int argc, char** argv) {
contextp->coveragep()->write("logs/coverage.dat");
#endif
// Final simulation summary
contextp->statsPrintSummary();
// Return good completion status
// Don't use exit() or destructor won't get called
return 0;
+7 -2
View File
@@ -347,8 +347,9 @@ return(NULL);
static void *fstMmap2(size_t __len, int __fd, fst_off_t __off)
{
DWORD64 len64 = __len; /* Must be 64-bit for shift below */
HANDLE handle = CreateFileMapping((HANDLE)_get_osfhandle(__fd), NULL,
PAGE_READWRITE, (DWORD)(__len >> 32),
PAGE_READWRITE, (DWORD)(len64 >> 32),
(DWORD)__len, NULL);
if (!handle) { return NULL; }
@@ -1646,7 +1647,11 @@ if(1)
{
if(vm4ip[2] != prev_alias)
{
fpos += fstWriterSVarint(f, (((int64_t)((int32_t)(prev_alias = vm4ip[2]))) << 1) | 1);
int32_t t_i32 = ((int32_t)(prev_alias = vm4ip[2])); /* vm4ip is generic unsigned data */
int64_t t_i64 = (int64_t)t_i32; /* convert to signed */
uint64_t t_u64 = (uint64_t)t_i64; /* sign extend through 64b */
fpos += fstWriterSVarint(f, (int64_t)((t_u64 << 1) | 1)); /* all in this block was: fpos += fstWriterSVarint(f, (((int64_t)((int32_t)(prev_alias = vm4ip[2]))) << 1) | 1); */
}
else
{
+206 -68
View File
@@ -82,6 +82,12 @@
#include "verilated_trace.h"
#ifdef VM_SOLVER_DEFAULT
#define VL_SOLVER_DEFAULT VM_SOLVER_DEFAULT
#else
#define VL_SOLVER_DEFAULT "z3 --in"
#endif
// Max characters in static char string for VL_VALUE_STRING
constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192;
@@ -169,9 +175,12 @@ void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool may
Verilated::threadContextp()->errorCountInc();
if (maybe
&& Verilated::threadContextp()->errorCount() < Verilated::threadContextp()->errorLimit()) {
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
"-Info: %s:%d: %s\n", filename, linenum,
"Verilog $stop, ignored due to +verilator+error+limit");
// Do just once when cross error limit
if (Verilated::threadContextp()->errorCount() == 1) {
VL_PRINTF( // Not VL_PRINTF_MT, already on main thread
"-Info: %s:%d: %s\n", filename, linenum,
"Verilog $stop, ignored due to +verilator+error+limit");
}
} else {
vl_stop(filename, linenum, hier);
}
@@ -602,7 +611,7 @@ WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP
const int words = VL_WORDS_I(obits);
VL_ZERO_W(obits, owp);
EData lor = 0; // 0=all zeros, ~0=all ones, else mix
for (int i = 1; i < (words - 1); ++i) { lor |= lwp[i]; }
for (int i = 1; i < (words - 1); ++i) lor |= lwp[i];
lor |= ((lwp[words - 1] == VL_MASK_E(rbits)) ? ~VL_EUL(0) : 0);
if (lor == 0 && lwp[0] == 0) { // "X" so return 0
return owp;
@@ -814,7 +823,7 @@ void _vl_vsformat(std::string& output, const std::string& format, va_list ap) VL
bool widthSet = false;
bool left = false;
size_t width = 0;
for (std::string::const_iterator pos = format.begin(); pos != format.end(); ++pos) {
for (std::string::const_iterator pos = format.cbegin(); pos != format.cend(); ++pos) {
if (!inPct && pos[0] == '%') {
pctit = pos;
inPct = true;
@@ -883,7 +892,7 @@ void _vl_vsformat(std::string& output, const std::string& format, va_list ap) VL
case '^': { // Realtime
const int lbits = va_arg(ap, int);
const double d = va_arg(ap, double);
if (lbits) {} // UNUSED - always 64
(void)lbits; // UNUSED - always 64
if (fmt == '^') { // Realtime
if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
const int timeunit = va_arg(ap, int);
@@ -1168,8 +1177,8 @@ static char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp, const
static void _vl_vsss_setbit(WDataOutP iowp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE {
for (; nbits && lsb < obits; nbits--, lsb++, ld >>= 1) VL_ASSIGNBIT_WI(lsb, iowp, ld & 1);
}
static void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
size_t posstart, size_t posend) VL_MT_SAFE {
void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp, size_t posstart,
size_t posend) VL_MT_SAFE {
// Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
VL_ZERO_W(obits, owp);
int lsb = 0;
@@ -1212,15 +1221,15 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
IData got = 0;
bool inPct = false;
bool inIgnore = false;
std::string::const_iterator pos = format.begin();
for (; pos != format.end() && !_vl_vsss_eof(fp, floc); ++pos) {
std::string::const_iterator pos = format.cbegin();
for (; pos != format.cend() && !_vl_vsss_eof(fp, floc); ++pos) {
// VL_DBG_MSGF("_vlscan fmt='"<<pos[0]<<"' floc="<<floc<<" file='"<<_vl_vsss_peek(fp, floc,
// fromp, fstr)<<"'\n");
if (!inPct && pos[0] == '%') {
inPct = true;
inIgnore = false;
} else if (!inPct && std::isspace(pos[0])) { // Format spaces
while (std::isspace(pos[1])) pos++;
while (std::isspace(pos[1])) ++pos;
_vl_vsss_skipspace(fp, floc, fromp, fstr);
} else if (!inPct) { // Expected Format
_vl_vsss_skipspace(fp, floc, fromp, fstr);
@@ -1436,8 +1445,8 @@ void _vl_string_to_vint(int obits, void* destp, size_t srclen, const char* srcp)
char* op = reinterpret_cast<char*>(destp);
if (srclen > bytes) srclen = bytes; // Don't overflow destination
size_t i = 0;
for (i = 0; i < srclen; ++i) { *op++ = srcp[srclen - 1 - i]; }
for (; i < bytes; ++i) { *op++ = 0; }
for (i = 0; i < srclen; ++i) *op++ = srcp[srclen - 1 - i];
for (; i < bytes; ++i) *op++ = 0;
}
static IData getLine(std::string& str, IData fpi, size_t maxLen) VL_MT_SAFE {
@@ -1652,7 +1661,8 @@ IData VL_SSCANF_INNX(int, const std::string& ld, const std::string& format, int
...) VL_MT_SAFE {
va_list ap;
va_start(ap, argc);
const IData got = _vl_vsscanf(nullptr, ld.length() * 8, nullptr, ld, format, ap);
const IData got
= _vl_vsscanf(nullptr, static_cast<int>(ld.length() * 8), nullptr, ld, format, ap);
va_end(ap);
return got;
}
@@ -1745,7 +1755,10 @@ IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
char filenamez[VL_VALUE_STRING_MAX_CHARS + 1];
_vl_vint_to_string(lhswords * VL_EDATASIZE, filenamez, lhsp);
const int code = std::system(filenamez); // Yes, std::system() is threadsafe
return VL_SYSTEM_IN(filenamez);
}
IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE {
const int code = std::system(lhs.c_str()); // Yes, std::system() is threadsafe
return code >> 8; // Want exit status
}
@@ -1941,7 +1954,7 @@ std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE {
IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
std::string str_mod = str;
// IEEE 1800-2017 6.16.9 says '_' may exist.
// IEEE 1800-2023 6.16.9 says '_' may exist.
str_mod.erase(std::remove(str_mod.begin(), str_mod.end(), '_'), str_mod.end());
errno = 0;
@@ -2033,22 +2046,26 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
if (VL_UNLIKELY(!m_fp)) return false;
valuer = "";
// Prep for reading
bool indata = false;
bool ignore_to_eol = false;
bool ignore_to_cmt = false;
bool reading_addr = false;
int lastc = ' ';
bool inData = false;
bool ignoreToEol = false;
bool ignoreToComment = false;
bool readingAddress = false;
int lastCh = ' ';
// Read the data
// We process a character at a time, as then we don't need to deal
// with changing buffer sizes dynamically, etc.
while (true) {
int c = std::fgetc(m_fp);
if (VL_UNLIKELY(c == EOF)) break;
const bool chIs4StateBin
= c == '0' || c == '1' || c == 'x' || c == 'X' || c == 'z' || c == 'Z';
const bool chIs2StateHex = std::isxdigit(c);
const bool chIs4StateHex = std::isxdigit(c) || chIs4StateBin;
// printf("%d: Got '%c' Addr%lx IN%d IgE%d IgC%d\n",
// m_linenum, c, m_addr, indata, ignore_to_eol, ignore_to_cmt);
// m_linenum, c, m_addr, inData, ignoreToEol, ignoreToComment);
// See if previous data value has completed, and if so return
if (c == '_') continue; // Ignore _ e.g. inside a number
if (indata && !std::isxdigit(c) && c != 'x' && c != 'X') {
if (inData && !chIs4StateHex) {
// printf("Got data @%lx = %s\n", m_addr, valuer.c_str());
ungetc(c, m_fp);
addrr = m_addr;
@@ -2058,58 +2075,55 @@ bool VlReadMem::get(QData& addrr, std::string& valuer) {
// Parse line
if (c == '\n') {
++m_linenum;
ignore_to_eol = false;
reading_addr = false;
ignoreToEol = false;
readingAddress = false;
} else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') {
reading_addr = false;
readingAddress = false;
}
// Skip // comments and detect /* comments
else if (ignore_to_cmt && lastc == '*' && c == '/') {
ignore_to_cmt = false;
reading_addr = false;
} else if (!ignore_to_eol && !ignore_to_cmt) {
if (lastc == '/' && c == '*') {
ignore_to_cmt = true;
} else if (lastc == '/' && c == '/') {
ignore_to_eol = true;
else if (ignoreToComment && lastCh == '*' && c == '/') {
ignoreToComment = false;
readingAddress = false;
} else if (!ignoreToEol && !ignoreToComment) {
if (lastCh == '/' && c == '*') {
ignoreToComment = true;
} else if (lastCh == '/' && c == '/') {
ignoreToEol = true;
} else if (c == '/') { // Part of /* or //
} else if (c == '#') {
ignore_to_eol = true;
ignoreToEol = true;
} else if (c == '@') {
reading_addr = true;
readingAddress = true;
m_anyAddr = true;
m_addr = 0;
}
// Check for hex or binary digits as file format requests
else if (std::isxdigit(c) || (!reading_addr && (c == 'x' || c == 'X'))) {
} else if (readingAddress && chIs2StateHex) {
c = std::tolower(c);
const int value
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
if (reading_addr) {
// Decode @ addresses
m_addr = (m_addr << 4) + value;
} else {
indata = true;
valuer += static_cast<char>(c);
// printf(" Value width=%d @%x = %c\n", width, m_addr, c);
if (VL_UNLIKELY(value > 1 && !m_hex)) {
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
"$readmemb (binary) file contains hex characters");
}
const int addressValue = (c >= 'a') ? (c - 'a' + 10) : (c - '0');
m_addr = (m_addr << 4) + addressValue;
} else if (readingAddress && chIs4StateHex) {
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
"$readmem address contains 4-state characters");
} else if (chIs4StateHex) {
inData = true;
valuer += static_cast<char>(c);
if (VL_UNLIKELY(!m_hex && !chIs4StateBin)) {
VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
"$readmemb (binary) file contains hex characters");
}
} else {
VL_FATAL_MT(m_filename.c_str(), m_linenum, "", "$readmem file syntax error");
}
}
lastc = c;
lastCh = c;
}
if (VL_UNLIKELY(m_end != ~0ULL && m_addr <= m_end && !m_anyAddr)) {
VL_WARN_MT(m_filename.c_str(), m_linenum, "",
"$readmem file ended before specified final address (IEEE 1800-2017 21.4)");
"$readmem file ended before specified final address (IEEE 1800-2023 21.4)");
}
return false; // EOF
addrr = m_addr;
return inData; // EOF
}
void VlReadMem::setData(void* valuep, const std::string& rhs) {
const QData shift = m_hex ? 4ULL : 1ULL;
@@ -2117,8 +2131,9 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
// Shift value in
for (const auto& i : rhs) {
const char c = std::tolower(i);
const int value
= (c >= 'a' ? (c == 'x' ? VL_RAND_RESET_I(4) : (c - 'a' + 10)) : (c - '0'));
const int value = (c == 'x' || c == 'z') ? VL_RAND_RESET_I(m_hex ? 4 : 1)
: (c >= 'a') ? (c - 'a' + 10)
: (c - '0');
if (m_bits <= 8) {
CData* const datap = reinterpret_cast<CData*>(valuep);
if (!innum) *datap = 0;
@@ -2258,6 +2273,7 @@ void VL_READMEM_N(bool hex, // Hex format, else binary
QData addr = 0;
std::string value;
if (rmem.get(addr /*ref*/, value /*ref*/)) {
// printf("readmem.get [%" PRIu64 "]=%s\n", addr, value.c_str());
if (VL_UNLIKELY(addr < static_cast<QData>(array_lsb)
|| addr >= static_cast<QData>(array_lsb + depth))) {
VL_FATAL_MT(filename.c_str(), rmem.linenum(), "",
@@ -2412,6 +2428,22 @@ uint64_t vl_time_pow10(int n) {
return pow10[n];
}
std::string vl_timescaled_double(double value, const char* format) VL_PURE {
const char* suffixp = "s";
// clang-format off
if (value >= 1e0) { suffixp = "s"; value *= 1e0; }
else if (value >= 1e-3) { suffixp = "ms"; value *= 1e3; }
else if (value >= 1e-6) { suffixp = "us"; value *= 1e6; }
else if (value >= 1e-9) { suffixp = "ns"; value *= 1e9; }
else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
// clang-format on
char valuestr[100];
VL_SNPRINTF(valuestr, 100, format, value, suffixp);
return std::string{valuestr}; // Gets converted to string, so no ref to stack
}
void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
const VerilatedContext* contextp) VL_MT_SAFE {
VL_PRINTF_MT("Time scale of %s is %s / %s\n", namep, timeunitp,
@@ -2435,10 +2467,12 @@ VerilatedContext::VerilatedContext()
m_ns.m_coverageFilename = "coverage.dat";
m_ns.m_profExecFilename = "profile_exec.dat";
m_ns.m_profVltFilename = "profile.vlt";
m_ns.m_solverProgram = VlOs::getenvStr("VERILATOR_SOLVER", VL_SOLVER_DEFAULT);
m_fdps.resize(31);
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
m_fdFreeMct.resize(30);
for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id;
IData id = 1;
for (std::size_t i = 0; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id;
}
// Must declare here not in interface, as otherwise forward declarations not known
@@ -2544,6 +2578,18 @@ std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_profVltFilename;
}
void VerilatedContext::solverProgram(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_ns.m_solverProgram = flag;
}
std::string VerilatedContext::solverProgram() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_solverProgram;
}
void VerilatedContext::quiet(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_s.m_quiet = flag;
}
void VerilatedContext::randReset(int val) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_s.m_randReset = val;
@@ -2611,8 +2657,24 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE {
}
void VerilatedContext::addModel(VerilatedModel* modelp) {
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
if (!quiet()) {
// CPU time isn't read as starting point until model creation, so that quiet() is set
// Thus if quiet(), avoids slow OS read affecting some usages that make many models
const VerilatedLockGuard lock{m_mutex};
m_ns.m_cpuTimeStart.start();
m_ns.m_wallTimeStart.start();
}
// We look for time passing, as opposed to post-eval(), as embedded
// models might get added inside initial blocks.
if (VL_UNLIKELY(time()))
VL_FATAL_MT(
"", 0, "",
"Adding model when time is non-zero. ... Suggest check time(), or for restarting"
" model use a new VerilatedContext");
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
m_threadsInModels += modelp->threads();
if (VL_UNLIKELY(modelp->threads() > m_threads)) {
std::ostringstream msg;
msg << "VerilatedContext has " << m_threads << " threads but model '"
@@ -2645,6 +2707,7 @@ VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(Ver
//======================================================================
// VerilatedContextImp:: Methods - command line
void VerilatedContextImp::commandArgsGuts(int argc, const char** argv)
VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock{m_argMutex};
@@ -2741,6 +2804,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
profExecFilename(str);
} else if (commandArgVlString(arg, "+verilator+prof+vlt+file+", str)) {
profVltFilename(str);
} else if (arg == "+verilator+quiet") {
quiet(true);
} else if (commandArgVlUint64(arg, "+verilator+rand+reset+", u64, 0, 2)) {
randReset(static_cast<int>(u64));
} else if (commandArgVlUint64(arg, "+verilator+seed+", u64, 1,
@@ -2786,7 +2851,7 @@ bool VerilatedContextImp::commandArgVlUint64(const std::string& arg, const std::
VL_FATAL_MT("COMMAND_LINE", 0, "", msg.c_str());
};
if (std::any_of(str.begin(), str.end(), [](int c) { return !std::isdigit(c); })) fail();
if (std::any_of(str.cbegin(), str.cend(), [](int c) { return !std::isdigit(c); })) fail();
char* end;
valuer = std::strtoull(str.c_str(), &end, 10);
if (errno == ERANGE) fail("Value out of range of uint64_t");
@@ -2818,6 +2883,36 @@ uint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
}
}
//======================================================================
// VerilatedContext:: Statistics
double VerilatedContext::statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_cpuTimeStart.deltaTime();
}
double VerilatedContext::statWallTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_wallTimeStart.deltaTime();
}
void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
if (quiet()) return;
VL_PRINTF("- S i m u l a t i o n R e p o r t: %s %s\n", Verilated::productName(),
Verilated::productVersion());
const std::string endwhy = gotError() ? "$stop" : gotFinish() ? "$finish" : "end";
const double simtimeInUnits = VL_TIME_Q() * vl_time_multiplier(timeunit())
* vl_time_multiplier(timeprecision() - timeunit());
const std::string simtime = vl_timescaled_double(simtimeInUnits);
const double walltime = statWallTimeSinceStart();
const double cputime = statCpuTimeSinceStart();
const std::string simtimePerf
= vl_timescaled_double((cputime != 0.0) ? (simtimeInUnits / cputime) : 0, "%0.3f %s");
VL_PRINTF("- Verilator: %s at %s; walltime %0.3f s; speed %s/s\n", endwhy.c_str(),
simtime.c_str(), walltime, simtimePerf.c_str());
const double modelMB = VlOs::memUsageBytes() / 1024.0 / 1024.0;
VL_PRINTF("- Verilator: cpu %0.3f s on %u threads; alloced %0.0f MB\n", cputime,
threadsInModels(), modelMB);
}
//======================================================================
// VerilatedContext:: Methods - scopes
@@ -2856,6 +2951,36 @@ const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
return &(impp()->m_impdatap->m_nameMap);
}
//======================================================================
// VerilatedContext:: Methods - trace
void VerilatedContext::trace(VerilatedTraceBaseC* tfp, int levels, int options) VL_MT_SAFE {
VL_DEBUG_IF(VL_DBG_MSGF("+ VerilatedContext::trace\n"););
if (tfp->isOpen()) {
VL_FATAL_MT("", 0, "",
"Testbench C call to 'VerilatedContext::trace()' must not be called"
" after 'VerilatedTrace*::open()'\n");
}
{
// Legacy usage may call {modela}->trace(...) then {modelb}->trace(...)
// So check for and suppress second and later calls
if (tfp->modelConnected()) return;
tfp->modelConnected(true);
}
// We rely on m_ns.m_traceBaseModelCbs being stable when trace() is called
// nope: const VerilatedLockGuard lock{m_mutex};
if (m_ns.m_traceBaseModelCbs.empty())
VL_FATAL_MT("", 0, "",
"Testbench C call to 'VerilatedContext::trace()' requires model(s) Verilated"
" with --trace or --trace-vcd option");
for (auto& cbr : m_ns.m_traceBaseModelCbs) cbr(tfp, levels, options);
}
void VerilatedContext::traceBaseModelCbAdd(traceBaseModelCb_t cb) VL_MT_SAFE {
// Model creation registering a callback for when Verilated::trace() called
const VerilatedLockGuard lock{m_mutex};
m_ns.m_traceBaseModelCbs.push_back(cb);
}
//======================================================================
// VerilatedSyms:: Methods
@@ -3027,15 +3152,24 @@ void Verilated::stackCheck(QData needSize) VL_MT_UNSAFE {
if (haveSize == RLIM_INFINITY) haveSize = 0;
}
// VL_PRINTF_MT("-Info: stackCheck(%" PRIu64 ") have %" PRIu64 "\n", needSize, haveSize);
// Check for 1.5x need, but suggest 2x so small model increase won't cause warning
// if the user follows the suggestions
if (VL_UNLIKELY(haveSize && needSize && haveSize < (needSize + needSize / 2))) {
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
" which may be too small; suggest 'ulimit -c %" PRIu64 "' or larger\n",
haveSize / 1024, (needSize * 2) / 1024);
// Check and request for 1.5x need. This is automated so the user doesn't need to do anything.
QData requestSize = needSize + needSize / 2;
if (VL_UNLIKELY(haveSize && needSize && haveSize < requestSize)) {
// Try to increase the stack limit to the requested size
rlim.rlim_cur = requestSize;
if (
#ifdef _VL_TEST_RLIMIT_FAIL
true ||
#endif
setrlimit(RLIMIT_STACK, &rlim)) {
VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
" which may be too small; failed to request more"
" using 'ulimit -s %" PRIu64 "'\n",
haveSize / 1024, requestSize);
}
}
#else
if (false && needSize) {} // Unused argument
(void)needSize; // Unused argument
#endif
}
@@ -3234,7 +3368,7 @@ void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
// Slowpath - Called only when find has failed
const std::string msg = ("Testbench C called '"s + VerilatedImp::exportName(funcnum)
+ "' but scope wasn't set, perhaps due to dpi import call without "
+ "'context', or missing svSetScope. See IEEE 1800-2017 35.5.3.");
+ "'context', or missing svSetScope. See IEEE 1800-2023 35.5.3.");
VL_FATAL_MT("unknown", 0, "", msg.c_str());
return nullptr;
}
@@ -3300,3 +3434,7 @@ void VlDeleter::deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_M
}
//===========================================================================
// OS functions (last, so we have minimal OS dependencies above)
#define VL_ALLOW_VERILATEDOS_C
#include "verilatedos_c.h"
+36 -2
View File
@@ -101,6 +101,7 @@ class VerilatedScope;
class VerilatedScopeNameMap;
template <class, class>
class VerilatedTrace;
class VerilatedTraceBaseC;
class VerilatedTraceConfig;
class VerilatedVar;
class VerilatedVarNameMap;
@@ -347,6 +348,10 @@ class VerilatedContext VL_NOT_FINAL {
friend class VerilatedContextImp;
protected:
// TYPES
using traceBaseModelCb_t
= std::function<void(VerilatedTraceBaseC*, int, int)>; // Type of traceBaseModel callbacks
// MEMBERS
// Slow path variables
mutable VerilatedMutex m_mutex; // Mutex for most s_s/s_ns members
@@ -354,13 +359,14 @@ protected:
struct Serialized final { // All these members serialized/deserialized
// 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
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
bool m_gotError = false; // A $finish statement executed
bool m_gotFinish = false; // A $finish or $stop statement executed
uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
bool m_quiet = false; // Quiet, no summary report
// Slow path
int8_t m_timeunit; // Time unit as 0..15
int8_t m_timeprecision; // Time precision as 0..15
@@ -390,6 +396,10 @@ protected:
std::string m_coverageFilename; // +coverage+file filename
std::string m_profExecFilename; // +prof+exec+file filename
std::string m_profVltFilename; // +prof+vlt filename
std::string m_solverProgram; // SMT solver program
VlOs::DeltaCpuTime m_cpuTimeStart{false}; // CPU time, starts when create first model
VlOs::DeltaWallTime m_wallTimeStart{false}; // Wall time, starts when create first model
std::vector<traceBaseModelCb_t> m_traceBaseModelCbs; // Callbacks to traceRegisterModel
} m_ns;
mutable VerilatedMutex m_argMutex; // Protect m_argVec, m_argVecLoaded
@@ -405,6 +415,8 @@ protected:
const std::unique_ptr<VerilatedContextImpData> m_impdatap;
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
unsigned m_threads = std::thread::hardware_concurrency();
// Number of threads in added models
unsigned m_threadsInModels = 0;
// The thread pool shared by all models added to this context
std::unique_ptr<VerilatedVirtualBase> m_threadPool;
// The execution profiler shared by all models added to this context
@@ -489,6 +501,10 @@ public:
void gotFinish(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;
/// Return if quiet enabled
bool quiet() const VL_MT_SAFE { return m_s.m_quiet; }
/// Select initial value of otherwise uninitialized signals.
/// 0 = Set to zeros
/// 1 = Set all bits to one
@@ -501,6 +517,13 @@ public:
/// Set default random seed, 0 = seed it automatically
int randSeed() const VL_MT_SAFE { return m_s.m_randSeed; }
/// Return statistic: CPU time delta from model created until now
double statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex);
/// Return statistic: Wall time delta from model created until now
double statWallTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex);
/// Print statistics summary (if not quiet)
void statsPrintSummary() VL_MT_UNSAFE;
// Time handling
/// Returns current simulation time in units of timeprecision().
///
@@ -544,10 +567,14 @@ public:
/// Get number of threads used for simulation (including the main thread)
unsigned threads() const { return m_threads; }
/// Get number of threads in added models (for statistical use only)
unsigned threadsInModels() const { return m_threadsInModels; }
/// Set number of threads used for simulation (including the main thread)
/// Can only be called before the thread pool is created (before first model is added).
void threads(unsigned n);
/// Trace signals in models within the context; called by application code
void trace(VerilatedTraceBaseC* tfp, int levels, int options = 0);
/// Allow traces to at some point be enabled (disables some optimizations)
void traceEverOn(bool flag) VL_MT_SAFE {
if (flag) calcUnusedSigs(true);
@@ -571,8 +598,8 @@ public:
return reinterpret_cast<const VerilatedContextImp*>(this);
}
// Internal: Model and thread setup
void addModel(VerilatedModel*);
VerilatedVirtualBase* threadPoolp();
void prepareClone();
VerilatedVirtualBase* threadPoolpOnClone();
@@ -598,6 +625,10 @@ public:
void profVltFilename(const std::string& flag) VL_MT_SAFE;
std::string profVltFilename() const VL_MT_SAFE;
// Internal: SMT solver program
void solverProgram(const std::string& flag) VL_MT_SAFE;
std::string solverProgram() const VL_MT_SAFE;
// Internal: Find scope
const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE;
@@ -606,6 +637,9 @@ public:
static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); }
void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; }
// Internal: trace registration
void traceBaseModelCbAdd(traceBaseModelCb_t cb) VL_MT_SAFE;
// Internal: Check magic number
static void checkMagic(const VerilatedContext* contextp);
void selfTestClearMagic() { m_magic = 0x2; }
-1
View File
@@ -225,7 +225,6 @@ endif
# 1. Extract object files from .a
# 2. Create a new archive from extracted .o and given .o
%.a: | %.verilator_deplist.tmp
$(info Archive $(AR) -rcs $@ $^)
$(foreach L, $(filter-out %.a,$^), $(shell echo $L >>[email protected]_deplist.tmp))
@if test $(words $(filter %.a,$^)) -eq 0; then \
$(RM) -f $@; \
+1 -1
View File
@@ -121,7 +121,7 @@ private:
public:
// CONSTRUCTORS
VerilatedCovImp(VerilatedContext* contextp)
explicit VerilatedCovImp(VerilatedContext* contextp)
: m_contextp{contextp} {}
VL_UNCOPYABLE(VerilatedCovImp);
+1 -1
View File
@@ -165,7 +165,7 @@ public:
/// Return default filename for the current thread
static std::string defaultFilename() VL_MT_SAFE { return threadCovp()->defaultFilename(); }
/// Write all coverage data to a file for the current thread
static void write(const std::string filename) VL_MT_SAFE { threadCovp()->write(filename); }
static void write(const std::string& filename) VL_MT_SAFE { threadCovp()->write(filename); }
static void write() VL_MT_SAFE { write(defaultFilename()); }
/// Clear coverage points (and call delete on all items) for the current thread
static void clear() VL_MT_SAFE { threadCovp()->clear(); }
+30
View File
@@ -770,6 +770,36 @@ int svGetCallerInfo(const char** fileNamepp, int* lineNumberp) {
return true;
}
//======================================================================
// Time
int svGetTime(const svScope scope, svTimeVal* time) {
if (VL_UNLIKELY(!time)) return -1;
const QData qtime = VL_TIME_Q();
VlWide<2> itime;
VL_SET_WQ(itime, qtime);
time->low = itime[0];
time->high = itime[1];
return 0;
}
int svGetTimeUnit(const svScope scope, int32_t* time_unit) {
if (VL_UNLIKELY(!time_unit)) return -1;
const VerilatedScope* const vscopep = reinterpret_cast<const VerilatedScope*>(scope);
if (!vscopep) { // Null asks for global, not unlikely
*time_unit = Verilated::threadContextp()->timeunit();
} else {
*time_unit = vscopep->timeunit();
}
return 0;
}
int svGetTimePrecision(const svScope scope, int32_t* time_precision) {
if (VL_UNLIKELY(!time_precision)) return -1;
*time_precision = Verilated::threadContextp()->timeprecision();
return 0;
}
//======================================================================
// Disables
+6 -3
View File
@@ -192,7 +192,7 @@ class VerilatedFstBuffer VL_NOT_FINAL {
/// Create a FST dump file in C standalone (no SystemC) simulations.
/// Also derived for use in SystemC simulations.
class VerilatedFstC VL_NOT_FINAL {
class VerilatedFstC VL_NOT_FINAL : public VerilatedTraceBaseC {
VerilatedFst m_sptrace; // Trace file being created
// CONSTRUCTORS
@@ -208,11 +208,14 @@ public:
// METHODS - User called
/// Return if file is open
bool isOpen() const VL_MT_SAFE { return m_sptrace.isOpen(); }
bool isOpen() const override VL_MT_SAFE { return m_sptrace.isOpen(); }
/// Open a new FST file
virtual void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); }
/// Close dump
void close() VL_MT_SAFE { m_sptrace.close(); }
void close() VL_MT_SAFE {
m_sptrace.close();
modelConnected(false);
}
/// Flush dump
void flush() VL_MT_SAFE { m_sptrace.flush(); }
/// Write one cycle of dump data
+149 -8
View File
@@ -115,6 +115,9 @@ extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP const lwp, WDataInP const rwp,
bool is_modulus) VL_MT_SAFE;
extern void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
size_t posstart, size_t posend) VL_MT_SAFE;
extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE;
extern void VL_FFLUSH_I(IData fdi) VL_MT_SAFE;
@@ -152,6 +155,7 @@ extern std::string VL_STACKTRACE_N() VL_MT_SAFE;
extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp) VL_MT_SAFE;
extern IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE;
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
extern IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE;
extern IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE;
extern const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE; // PLIish
@@ -215,6 +219,10 @@ static inline QData VL_CVT_Q_D(double lhs) VL_PURE {
return u.q;
}
// clang-format on
// Return string from DPI char*
static inline std::string VL_CVT_N_CSTR(const char* lhsp) VL_PURE {
return lhsp ? std::string{lhsp} : ""s;
}
// Return double from lhs (numeric) unsigned
double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
@@ -238,7 +246,7 @@ static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_MT_SAFE {
VL_SET_WQ(lwp, lhs);
return VL_ISTOR_D_W(lbits, lwp);
}
// Return QData from double (numeric)
// Return IData truncated from double (numeric)
static inline IData VL_RTOI_I_D(double lhs) VL_PURE { return static_cast<int32_t>(VL_TRUNC(lhs)); }
// Sign extend such that if MSB set, we get ffff_ffff, else 0s
@@ -263,7 +271,7 @@ static inline QData VL_EXTENDSIGN_Q(int lbits, QData lhs) VL_PURE {
extern void _vl_debug_print_w(int lbits, WDataInP const iwp) VL_MT_SAFE;
//=========================================================================
// Pli macros
// Time handling
// clang-format off
@@ -321,6 +329,8 @@ uint64_t VerilatedContext::time() const VL_MT_SAFE {
double vl_time_multiplier(int scale) VL_PURE;
// Return power of 10. e.g. returns 100 if n==2
uint64_t vl_time_pow10(int n) VL_PURE;
// Return time as string with timescale suffix
std::string vl_timescaled_double(double value, const char* format = "%0.0f%s") VL_PURE;
//=========================================================================
// Functional macros/routines
@@ -502,19 +512,18 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
{ (svar).write(rd); }
#define VL_ASSIGN_SBQ(obits, svar, rd) \
{ (svar).write(rd); }
#define VL_SC_BITS_PER_DIGIT 30 // This comes from sc_nbdefs.h BITS_PER_DIGIT
#define VL_ASSIGN_SBW(obits, svar, rwp) \
{ \
sc_dt::sc_biguint<(obits)> _butemp; \
int32_t lsb = 0; \
uint32_t* chunkp = _butemp.get_raw(); \
while (lsb + VL_SC_BITS_PER_DIGIT < (obits)) { \
while (lsb + BITS_PER_DIGIT < (obits)) { \
static_assert(std::is_same<IData, EData>::value, "IData and EData mismatch"); \
const uint32_t data = VL_SEL_IWII(lsb + VL_SC_BITS_PER_DIGIT + 1, (rwp).data(), lsb, \
VL_SC_BITS_PER_DIGIT); \
*chunkp = data & VL_MASK_E(VL_SC_BITS_PER_DIGIT); \
const uint32_t data \
= VL_SEL_IWII(lsb + BITS_PER_DIGIT + 1, (rwp).data(), lsb, BITS_PER_DIGIT); \
*chunkp = data & VL_MASK_E(BITS_PER_DIGIT); \
++chunkp; \
lsb += VL_SC_BITS_PER_DIGIT; \
lsb += BITS_PER_DIGIT; \
} \
if (lsb < (obits)) { \
const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits)-lsb); \
@@ -1309,6 +1318,26 @@ static inline void _vl_insert_WI(WDataOutP iowp, IData ld, int hbit, int lbit,
}
}
// Copy bits from lwp[hbit:lbit] to low bits of lhsr. rbits is real width of lshr
static inline void _vl_insert_IW(IData& lhsr, WDataInP const lwp, int hbit, int lbit,
int rbits = 0) VL_MT_SAFE {
const int hoffset = VL_BITBIT_E(hbit);
const int loffset = VL_BITBIT_E(lbit);
const int hword = VL_BITWORD_E(hbit);
const int lword = VL_BITWORD_E(lbit);
const IData cleanmask = VL_MASK_I(rbits);
if (hword == lword) {
const IData insmask = (VL_MASK_I(hoffset - loffset + 1));
lhsr = (lhsr & ~insmask) | ((lwp[lword] >> loffset) & (insmask & cleanmask));
} else {
const int nbitsonright = VL_IDATASIZE - loffset; // bits that filled by lword
const IData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << nbitsonright;
const IData linsmask = VL_MASK_E(VL_EDATASIZE - loffset);
lhsr = (lhsr & ~linsmask) | ((lwp[lword] >> loffset) & (linsmask & cleanmask));
lhsr = (lhsr & ~hinsmask) | ((lwp[hword] << nbitsonright) & (hinsmask & cleanmask));
}
}
// INTERNAL: Stuff large LHS bit 0++ into OUTPUT at specified offset
// lwp may be "dirty"
static inline void _vl_insert_WW(WDataOutP iowp, WDataInP const lwp, int hbit, int lbit,
@@ -1556,6 +1585,13 @@ static inline void VL_ASSIGN_DYN_Q(VlQueue<T>& q, int elem_size, int lbits, QDat
for (int i = 0; i < size; ++i) q.at(i) = (T)((from >> (i * elem_size)) & mask);
}
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);
for (size_t i = 0; i < T_Depth; ++i)
q[i] = (T)((from >> ((T_Depth - 1 - i) * elem_size)) & mask);
}
template <typename T>
static inline IData VL_DYN_TO_I(const VlQueue<T>& q, int elem_size) {
IData ret = 0;
@@ -1570,6 +1606,20 @@ static inline QData VL_DYN_TO_Q(const VlQueue<T>& q, int 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) {
IData ret = 0;
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * elem_size);
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) {
QData ret = 0;
for (size_t i = 0; i < T_Depth; ++i) ret |= q[T_Depth - 1 - i] << (i * elem_size);
return ret;
}
// 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.
@@ -2083,6 +2133,97 @@ static inline void VL_ASSIGNSEL_WW(int rbits, int obits, int lsb, WDataOutP iowp
_vl_insert_WW(iowp, rwp, lsb + obits - 1, lsb, rbits);
}
//====================================================
// Range assignments
// These additional functions copy bits range [obis+roffset-1:roffset] from rhs to lower bits
// of lhs(select before assigning). Rhs should always be wider than lhs.
static inline void VL_SELASSIGN_II(int rbits, int obits, CData& lhsr, IData rhs,
int roffset) VL_PURE {
_vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
}
static inline void VL_SELASSIGN_II(int rbits, int obits, SData& lhsr, IData rhs,
int roffset) VL_PURE {
_vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
}
static inline void VL_SELASSIGN_II(int rbits, int obits, IData& lhsr, IData rhs,
int roffset) VL_PURE {
_vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
}
static inline void VL_SELASSIGN_IQ(int rbits, int obits, CData& lhsr, QData rhs,
int roffset) VL_PURE {
// it will be truncated to right CData mask
const CData cleanmask = VL_MASK_I(rbits);
const CData insmask = VL_MASK_I(obits);
lhsr = (lhsr & ~insmask) | (static_cast<CData>(rhs >> roffset) & (insmask & cleanmask));
}
static inline void VL_SELASSIGN_IQ(int rbits, int obits, SData& lhsr, QData rhs,
int roffset) VL_PURE {
// it will be truncated to right CData mask
const SData cleanmask = VL_MASK_I(rbits);
const SData insmask = VL_MASK_I(obits);
lhsr = (lhsr & ~insmask) | (static_cast<SData>(rhs >> roffset) & (insmask & cleanmask));
}
static inline void VL_SELASSIGN_IQ(int rbits, int obits, IData& lhsr, QData rhs,
int roffset) VL_PURE {
const IData cleanmask = VL_MASK_I(rbits);
const IData insmask = VL_MASK_I(obits);
lhsr = (lhsr & ~insmask) | (static_cast<IData>(rhs >> roffset) & (insmask & cleanmask));
}
static inline void VL_SELASSIGN_QQ(int rbits, int obits, QData& lhsr, QData rhs,
int roffset) VL_PURE {
_vl_insert_QQ(lhsr, rhs >> roffset, obits - 1, 0, rbits);
}
static inline void VL_SELASSIGN_IW(int rbits, int obits, CData& lhsr, WDataInP const rhs,
int roffset) VL_MT_SAFE {
IData l = static_cast<IData>(lhsr);
_vl_insert_IW(l, rhs, roffset + obits - 1, roffset, rbits);
lhsr = static_cast<CData>(l);
}
static inline void VL_SELASSIGN_IW(int rbits, int obits, SData& lhsr, WDataInP const rhs,
int roffset) VL_MT_SAFE {
IData l = static_cast<IData>(lhsr);
_vl_insert_IW(l, rhs, roffset + obits - 1, roffset, rbits);
lhsr = static_cast<SData>(l);
}
static inline void VL_SELASSIGN_IW(int rbits, int obits, IData& lhsr, WDataInP const rhs,
int roffset) VL_MT_SAFE {
_vl_insert_IW(lhsr, rhs, roffset + obits - 1, roffset, rbits);
}
static inline void VL_SELASSIGN_QW(int rbits, int obits, QData& lhsr, WDataInP const rhs,
int roffset) VL_MT_SAFE {
// assert VL_QDATASIZE >= rbits > VL_IDATASIZE;
IData low = static_cast<IData>(lhsr);
IData high = static_cast<IData>(lhsr >> VL_IDATASIZE);
if (obits <= VL_IDATASIZE) {
_vl_insert_IW(low, rhs, obits + roffset - 1, roffset, VL_IDATASIZE);
} else {
_vl_insert_IW(low, rhs, roffset + VL_IDATASIZE - 1, roffset, VL_IDATASIZE);
_vl_insert_IW(high, rhs, roffset + obits - 1, roffset + VL_IDATASIZE,
rbits - VL_IDATASIZE);
}
lhsr = (static_cast<QData>(high) << VL_IDATASIZE) | low;
}
static inline void VL_SELASSIGN_WW(int rbits, int obits, WDataOutP iowp, WDataInP const rwp,
int roffset) VL_MT_SAFE {
// assert rbits > VL_QDATASIZE
const int wordoff = roffset / VL_EDATASIZE;
const int lsb = roffset & VL_SIZEBITS_E;
const int upperbits = lsb == 0 ? 0 : VL_EDATASIZE - lsb;
// If roffset is not aligned, we copy some bits to align it.
if (lsb != 0) {
const int w = obits < upperbits ? obits : upperbits;
const int insmask = VL_MASK_E(w);
iowp[0] = (iowp[0] & ~insmask) | ((rwp[wordoff] >> lsb) & insmask);
if (w == obits) return;
obits -= w;
}
_vl_insert_WW(iowp, rwp + wordoff + (lsb != 0), upperbits + obits - 1, upperbits, rbits);
}
//======================================================================
// Triops
+1 -1
View File
@@ -116,7 +116,7 @@ IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE {
return 0;
}
double x = 1.0;
for (int32_t i = 1; i <= k; i++) { x = x * _vl_dbase_uniform(seedr, 0, 1); }
for (int32_t i = 1; i <= k; i++) x = x * _vl_dbase_uniform(seedr, 0, 1);
const double a = static_cast<double>(mean);
const double b = static_cast<double>(k);
double r = -a * log(x) / b;
+424
View File
@@ -0,0 +1,424 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2024 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU Lesser
// General Public License Version 3 or the Perl Artistic License Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//=========================================================================
///
/// \file
/// \brief Verilated randomization implementation code
///
/// This file must be compiled and linked against all Verilated objects
/// that use randomization features.
///
/// See the internals documentation docs/internals.rst for details.
///
//=========================================================================
#include "verilated_random.h"
#include <iostream>
#include <sstream>
#include <streambuf>
#define _VL_SOLVER_HASH_LEN 1
#define _VL_SOLVER_HASH_LEN_TOTAL 4
// clang-format off
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
# define _VL_SOLVER_PIPE // Allow pipe SMT solving. Needs fork()
#endif
#ifdef _VL_SOLVER_PIPE
# include <sys/wait.h>
# include <fcntl.h>
#endif
#if defined(_WIN32) || defined(__MINGW32__)
# include <io.h> // open, read, write, close
#endif
// clang-format on
class Process final : private std::streambuf, public std::iostream {
static constexpr int BUFFER_SIZE = 4096;
const char* const* m_cmd = nullptr; // fork() process argv
#ifdef _VL_SOLVER_PIPE
pid_t m_pid = 0; // fork() process id
#else
int m_pid = 0; // fork() process id - always zero as disabled
#endif
bool m_pidExited = true; // If subprocess has exited and can be opened
int m_pidStatus = 0; // fork() process exit status, valid if m_pidExited
int m_writeFd = -1; // File descriptor TO subprocess
int m_readFd = -1; // File descriptor FROM subprocess
char m_readBuf[BUFFER_SIZE];
char m_writeBuf[BUFFER_SIZE];
public:
typedef std::streambuf::traits_type traits_type;
protected:
int overflow(int c = traits_type::eof()) override {
char c2 = static_cast<char>(c);
if (pbase() == pptr()) return 0;
size_t size = pptr() - pbase();
ssize_t n = ::write(m_writeFd, pbase(), size);
if (n == -1) perror("write");
if (n <= 0) {
wait_report();
return traits_type::eof();
}
if (n == size)
setp(m_writeBuf, m_writeBuf + sizeof(m_writeBuf));
else
setp(m_writeBuf + n, m_writeBuf + sizeof(m_writeBuf));
if (c != traits_type::eof()) sputc(c2);
return 0;
}
int underflow() override {
sync();
ssize_t n = ::read(m_readFd, m_readBuf, sizeof(m_readBuf));
if (n == -1) perror("read");
if (n <= 0) {
wait_report();
return traits_type::eof();
}
setg(m_readBuf, m_readBuf, m_readBuf + n);
return traits_type::to_int_type(m_readBuf[0]);
}
int sync() override {
overflow();
return 0;
}
public:
explicit Process(const char* const* const cmd = nullptr)
: std::streambuf{}
, std::iostream{this}
, m_cmd{cmd} {
open(cmd);
}
void wait_report() {
if (m_pidExited) return;
#ifdef _VL_SOLVER_PIPE
if (waitpid(m_pid, &m_pidStatus, 0) != m_pid) return;
if (m_pidStatus) {
std::stringstream msg;
msg << "Subprocess command `" << m_cmd[0];
for (const char* const* arg = m_cmd + 1; *arg; arg++) msg << ' ' << *arg;
msg << "' failed: ";
if (WIFSIGNALED(m_pidStatus))
msg << strsignal(WTERMSIG(m_pidStatus))
<< (WCOREDUMP(m_pidStatus) ? " (core dumped)" : "");
else if (WIFEXITED(m_pidStatus))
msg << "exit status " << WEXITSTATUS(m_pidStatus);
const std::string str = msg.str();
VL_WARN_MT("", 0, "Process", str.c_str());
}
#endif
m_pidExited = true;
m_pid = 0;
closeFds();
}
void closeFds() {
if (m_writeFd != -1) {
close(m_writeFd);
m_writeFd = -1;
}
if (m_readFd != -1) {
close(m_readFd);
m_readFd = -1;
}
}
bool open(const char* const* const cmd) {
setp(std::begin(m_writeBuf), std::end(m_writeBuf));
setg(m_readBuf, m_readBuf, m_readBuf);
#ifdef _VL_SOLVER_PIPE
if (!cmd || !cmd[0]) return false;
m_cmd = cmd;
int fd_stdin[2]; // Can't use std::array
int fd_stdout[2]; // Can't use std::array
constexpr int P_RD = 0;
constexpr int P_WR = 1;
if (pipe(fd_stdin) != 0) {
perror("Process::open: pipe");
return false;
}
if (pipe(fd_stdout) != 0) {
perror("Process::open: pipe");
close(fd_stdin[P_RD]);
close(fd_stdin[P_WR]);
return false;
}
if (fd_stdin[P_RD] <= 2 || fd_stdin[P_WR] <= 2 || fd_stdout[P_RD] <= 2
|| fd_stdout[P_WR] <= 2) {
// We'd have to rearrange all of the FD usages in this case.
// Too unlikely; verilator isn't a daemon.
fprintf(stderr, "stdin/stdout closed before pipe opened\n");
close(fd_stdin[P_RD]);
close(fd_stdin[P_WR]);
close(fd_stdout[P_RD]);
close(fd_stdout[P_WR]);
return false;
}
const pid_t pid = fork();
if (pid < 0) {
perror("Process::open: fork");
close(fd_stdin[P_RD]);
close(fd_stdin[P_WR]);
close(fd_stdout[P_RD]);
close(fd_stdout[P_WR]);
return false;
}
if (pid == 0) {
// Child
close(fd_stdin[P_WR]);
dup2(fd_stdin[P_RD], STDIN_FILENO);
close(fd_stdout[P_RD]);
dup2(fd_stdout[P_WR], STDOUT_FILENO);
execvp(cmd[0], const_cast<char* const*>(cmd));
std::stringstream msg;
msg << "Process::open: execvp(" << cmd[0] << ")";
const std::string str = msg.str();
perror(str.c_str());
_exit(127);
}
// Parent
m_pid = pid;
m_pidExited = false;
m_pidStatus = 0;
m_readFd = fd_stdout[P_RD];
m_writeFd = fd_stdin[P_WR];
close(fd_stdin[P_RD]);
close(fd_stdout[P_WR]);
return true;
#else
return false;
#endif
}
};
static Process& getSolver() {
static Process s_solver;
static bool s_done = false;
if (s_done) return s_solver;
s_done = true;
static std::vector<const char*> s_argv;
static std::string s_program = Verilated::threadContextp()->solverProgram();
s_argv.emplace_back(&s_program[0]);
for (char* arg = &s_program[0]; *arg; arg++) {
if (*arg == ' ') {
*arg = '\0';
s_argv.emplace_back(arg + 1);
}
}
s_argv.emplace_back(nullptr);
const char* const* const cmd = &s_argv[0];
s_solver.open(cmd);
s_solver << "(set-logic QF_BV)\n";
s_solver << "(check-sat)\n";
s_solver << "(reset)\n";
std::string s;
getline(s_solver, s);
if (s == "sat") return s_solver;
std::stringstream msg;
msg << "Unable to communicate with SAT solver, please check its installation or specify a "
"different one in VERILATOR_SOLVER environment variable.\n";
msg << " ... Tried: $";
for (const char* const* arg = cmd; *arg; arg++) msg << ' ' << *arg;
msg << '\n';
const std::string str = msg.str();
VL_WARN_MT("", 0, "randomize", str.c_str());
while (getline(s_solver, s)) {}
return s_solver;
}
//======================================================================
// VlRandomizer:: Methods
void VlRandomVar::emit(std::ostream& s) const { s << m_name; }
void VlRandomConst::emit(std::ostream& s) const {
s << "#b";
for (int i = 0; i < m_width; i++) s << (VL_BITISSET_Q(m_val, m_width - i - 1) ? '1' : '0');
}
void VlRandomBinOp::emit(std::ostream& s) const {
s << '(' << m_op << ' ';
m_lhs->emit(s);
s << ' ';
m_rhs->emit(s);
s << ')';
}
void VlRandomExtract::emit(std::ostream& s) const {
s << "((_ extract " << m_idx << ' ' << m_idx << ") ";
m_expr->emit(s);
s << ')';
}
bool VlRandomVar::set(std::string&& val) const {
VlWide<VL_WQ_WORDS_E> qowp;
VL_SET_WQ(qowp, 0ULL);
WDataOutP owp = qowp;
int obits = width();
if (obits > VL_QUADSIZE) owp = reinterpret_cast<WDataOutP>(datap());
int i;
for (i = 0; val[i] && val[i] != '#'; i++) {}
if (val[i++] != '#') return false;
switch (val[i++]) {
case 'b': _vl_vsss_based(owp, obits, 1, &val[i], 0, val.size() - i); break;
case 'o': _vl_vsss_based(owp, obits, 3, &val[i], 0, val.size() - i); break;
case 'h': // FALLTHRU
case 'x': _vl_vsss_based(owp, obits, 4, &val[i], 0, val.size() - i); break;
default:
VL_WARN_MT(__FILE__, __LINE__, "randomize",
"Internal: Unable to parse solver's randomized number");
return false;
}
if (obits <= VL_BYTESIZE) {
CData* const p = static_cast<CData*>(datap());
*p = VL_CLEAN_II(obits, obits, owp[0]);
} else if (obits <= VL_SHORTSIZE) {
SData* const p = static_cast<SData*>(datap());
*p = VL_CLEAN_II(obits, obits, owp[0]);
} else if (obits <= VL_IDATASIZE) {
IData* const p = static_cast<IData*>(datap());
*p = VL_CLEAN_II(obits, obits, owp[0]);
} else if (obits <= VL_QUADSIZE) {
QData* const p = static_cast<QData*>(datap());
*p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp));
} else {
_vl_clean_inplace_w(obits, owp);
}
return true;
}
std::shared_ptr<const VlRandomExpr> VlRandomizer::randomConstraint(VlRNG& rngr, int bits) {
unsigned long long hash = VL_RANDOM_RNG_I(rngr) & ((1 << bits) - 1);
std::shared_ptr<const VlRandomExpr> concat = nullptr;
std::vector<std::shared_ptr<const VlRandomExpr>> varbits;
for (const auto& var : m_vars) {
for (int i = 0; i < var.second->width(); i++)
varbits.emplace_back(std::make_shared<const VlRandomExtract>(var.second, i));
}
for (int i = 0; i < bits; i++) {
std::shared_ptr<const VlRandomExpr> bit = nullptr;
for (unsigned j = 0; j * 2 < varbits.size(); j++) {
unsigned idx = j + VL_RANDOM_RNG_I(rngr) % (varbits.size() - j);
auto sel = varbits[idx];
std::swap(varbits[idx], varbits[j]);
bit = bit == nullptr ? sel : std::make_shared<const VlRandomBinOp>("bvxor", bit, sel);
}
concat = concat == nullptr ? bit
: std::make_shared<const VlRandomBinOp>("concat", concat, bit);
}
return std::make_shared<const VlRandomBinOp>(
"=", concat, std::make_shared<const VlRandomConst>(hash, bits));
}
bool VlRandomizer::next(VlRNG& rngr) {
if (m_vars.empty()) return true;
std::iostream& f = getSolver();
if (!f) return false;
f << "(set-option :produce-models true)\n";
f << "(set-logic QF_BV)\n";
for (const auto& var : m_vars) {
f << "(declare-fun " << var.second->name() << " () (_ BitVec " << var.second->width()
<< "))\n";
}
for (const std::string& constraint : m_constraints) { f << "(assert " << constraint << ")\n"; }
f << "(check-sat)\n";
bool sat = parseSolution(f);
if (!sat) {
f << "(reset)\n";
return false;
}
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; i++) {
f << "(assert ";
randomConstraint(rngr, _VL_SOLVER_HASH_LEN)->emit(f);
f << ")\n";
f << "\n(check-sat)\n";
sat = parseSolution(f);
}
f << "(reset)\n";
return true;
}
bool VlRandomizer::parseSolution(std::iostream& f) {
std::string sat;
do { std::getline(f, sat); } while (sat == "");
if (sat == "unsat") return false;
if (sat != "sat") {
std::stringstream msg;
msg << "Internal: Solver error: " << sat;
const std::string str = msg.str();
VL_WARN_MT(__FILE__, __LINE__, "randomize", str.c_str());
return false;
}
f << "(get-value (";
for (const auto& var : m_vars) f << var.second->name() << ' ';
f << "))\n";
// Quasi-parse S-expression of the form ((x #xVALUE) (y #bVALUE) (z #xVALUE))
char c;
f >> c;
if (c != '(') {
VL_WARN_MT(__FILE__, __LINE__, "randomize",
"Internal: Unable to parse solver's response: invalid S-expression");
return false;
}
while (true) {
f >> c;
if (c == ')') break;
if (c != '(') {
VL_WARN_MT(__FILE__, __LINE__, "randomize",
"Internal: Unable to parse solver's response: invalid S-expression");
return false;
}
std::string name, value;
f >> name;
std::getline(f, value, ')');
auto it = m_vars.find(name);
if (it == m_vars.end()) continue;
it->second->set(std::move(value));
}
return true;
}
void VlRandomizer::hard(std::string&& constraint) {
m_constraints.emplace_back(std::move(constraint));
}
#ifdef VL_DEBUG
void VlRandomizer::dump() const {
for (const auto& var : m_vars) {
VL_PRINTF("Variable (%d): %s\n", var.second->width(), var.second->name());
}
for (const std::string& c : m_constraints) VL_PRINTF("Constraint: %s\n", c.c_str());
}
#endif
+120
View File
@@ -0,0 +1,120 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2024 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU Lesser
// General Public License Version 3 or the Perl Artistic License Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilated randomization header
///
/// This file is included automatically by Verilator in some of the C++ files
/// it generates if randomization features are used.
///
/// This file is not part of the Verilated public-facing API.
/// It is only for internal use.
///
/// See the internals documentation docs/internals.rst for details.
///
//*************************************************************************
#ifndef VERILATOR_VERILATED_RANDOM_H_
#define VERILATOR_VERILATED_RANDOM_H_
#include "verilated.h"
//=============================================================================
// VlRandomExpr and subclasses represent expressions for the constraint solver.
class VlRandomExpr VL_NOT_FINAL {
public:
virtual void emit(std::ostream& s) const = 0;
};
class VlRandomVar final : public VlRandomExpr {
const char* const m_name; // Variable name
void* const m_datap; // Reference to variable data
const int m_width; // Variable width in bits
public:
VlRandomVar(const char* name, int width, void* datap)
: m_name{name}
, m_datap{datap}
, m_width{width} {}
const char* name() const { return m_name; }
int width() const { return m_width; }
void* datap() const { return m_datap; }
bool set(std::string&&) const;
void emit(std::ostream& s) const override;
};
class VlRandomConst final : public VlRandomExpr {
const QData m_val; // Constant value
const int m_width; // Constant width in bits
public:
VlRandomConst(QData val, int width)
: m_val{val}
, m_width{width} {
assert(width <= sizeof(m_val) * 8);
}
void emit(std::ostream& s) const override;
};
class VlRandomExtract final : public VlRandomExpr {
const std::shared_ptr<const VlRandomExpr> m_expr; // Sub-expression
const unsigned m_idx; // Extracted index
public:
VlRandomExtract(std::shared_ptr<const VlRandomExpr> expr, unsigned idx)
: m_expr{expr}
, m_idx{idx} {}
void emit(std::ostream& s) const override;
};
class VlRandomBinOp final : public VlRandomExpr {
const char* const m_op; // Binary operation identifier
const std::shared_ptr<const VlRandomExpr> m_lhs, m_rhs; // Sub-expressions
public:
VlRandomBinOp(const char* op, std::shared_ptr<const VlRandomExpr> lhs,
std::shared_ptr<const VlRandomExpr> rhs)
: m_op{op}
, m_lhs{lhs}
, m_rhs{rhs} {}
void emit(std::ostream& s) const override;
};
//=============================================================================
// VlRandomizer is the object holding constraints and variable references.
class VlRandomizer final {
// MEMBERS
std::vector<std::string> m_constraints; // Solver-dependent constraints
std::map<std::string, std::shared_ptr<const VlRandomVar>>
m_vars; // Solver-dependent variables
// PRIVATE METHODS
std::shared_ptr<const VlRandomExpr> randomConstraint(VlRNG& rngr, int bits);
bool parseSolution(std::iostream& file);
public:
// METHODS
// Finds the next solution satisfying the constraints
bool next(VlRNG& rngr);
template <typename T>
void write_var(T& var, int width, const char* name) {
auto it = m_vars.find(name);
if (it != m_vars.end()) return;
m_vars[name] = std::make_shared<const VlRandomVar>(name, width, &var);
}
void hard(std::string&& constraint);
#ifdef VL_DEBUG
void dump() const;
#endif
};
#endif // Guard
+11 -4
View File
@@ -59,7 +59,7 @@ void VlDelayScheduler::resume() {
#endif
bool resumed = false;
while (!m_queue.empty() && (m_queue.begin()->first == m_context.time())) {
while (!m_queue.empty() && (m_queue.cbegin()->first == m_context.time())) {
VlCoroutineHandle handle = std::move(m_queue.begin()->second);
m_queue.erase(m_queue.begin());
handle.resume();
@@ -67,9 +67,16 @@ void VlDelayScheduler::resume() {
}
if (!m_zeroDelayed.empty()) {
for (auto&& handle : m_zeroDelayed) handle.resume();
m_zeroDelayed.clear();
// First, we need to move the coroutines out of the queue, as a resumed coroutine can
// suspend on #0 again, adding itself to the queue, which can result in reallocating the
// queue mid-iteration.
// We swap with the m_zeroDlyResumed field to keep the allocated buffer.
m_zeroDlyResumed.swap(m_zeroDelayed);
for (auto&& handle : m_zeroDlyResumed) handle.resume();
m_zeroDlyResumed.clear();
resumed = true;
// We are now in the Active region, so any coroutines added to m_zeroDelayed in the
// meantime will have to wait until the next Inactive region.
}
if (!resumed) {
@@ -80,7 +87,7 @@ void VlDelayScheduler::resume() {
}
uint64_t VlDelayScheduler::nextTimeSlot() const {
if (!m_queue.empty()) return m_queue.begin()->first;
if (!m_queue.empty()) return m_queue.cbegin()->first;
if (m_zeroDelayed.empty())
VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: There is no next time slot scheduled");
return m_context.time();
+19 -12
View File
@@ -32,14 +32,18 @@
// clang-format off
// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
#if defined _LIBCPP_VERSION // libc++
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
# if defined(__has_include) && !__has_include(<coroutine>) && __has_include(<experimental/coroutine>)
# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
# endif
# include <experimental/coroutine>
namespace std {
using namespace experimental; // Bring std::experimental into the std namespace
}
# else
# include <coroutine>
# endif
# include <experimental/coroutine>
namespace std {
using namespace experimental; // Bring std::experimental into the std namespace
}
#else
# if defined __clang__ && defined __GLIBCXX__ && !defined __cpp_impl_coroutine
# define __cpp_impl_coroutine 1 // Clang doesn't define this, but it's needed for libstdc++
@@ -165,6 +169,9 @@ class VlDelayScheduler final {
VerilatedContext& m_context;
VlDelayedCoroutineQueue m_queue; // Coroutines to be restored at a certain simulation time
std::vector<VlCoroutineHandle> m_zeroDelayed; // Coroutines waiting for #0
std::vector<VlCoroutineHandle> m_zeroDlyResumed; // Coroutines that waited for #0 and are
// to be resumed. Kept as a field to avoid
// reallocation.
public:
// CONSTRUCTORS
@@ -180,7 +187,7 @@ public:
bool empty() const { return m_queue.empty() && m_zeroDelayed.empty(); }
// Are there coroutines to resume at the current simulation time?
bool awaitingCurrentTime() const {
return (!m_queue.empty() && (m_queue.begin()->first <= m_context.time()))
return (!m_queue.empty() && (m_queue.cbegin()->first <= m_context.time()))
|| !m_zeroDelayed.empty();
}
#ifdef VL_DEBUG
@@ -193,9 +200,9 @@ public:
VlProcessRef process; // Data of the suspended process, null if not needed
VlDelayedCoroutineQueue& queue;
std::vector<VlCoroutineHandle>& queueZeroDelay;
uint64_t delay;
VlDelayPhase phase;
VlFileLineDebug fileline;
const uint64_t delay;
const VlDelayPhase phase;
const VlFileLineDebug fileline;
bool await_ready() const { return false; } // Always suspend
void await_suspend(std::coroutine_handle<> coro) {
@@ -208,7 +215,7 @@ public:
void await_resume() const {}
};
VlDelayPhase phase = (delay == 0) ? VlDelayPhase::INACTIVE : VlDelayPhase::ACTIVE;
const VlDelayPhase phase = (delay == 0) ? VlDelayPhase::INACTIVE : VlDelayPhase::ACTIVE;
#ifdef VL_DEBUG
if (phase == VlDelayPhase::INACTIVE) {
VL_WARN_MT(filename, lineno, VL_UNKNOWN,
+20 -8
View File
@@ -30,8 +30,9 @@
#include <condition_variable>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <type_traits>
#include <map>
#include <set>
#include <vector>
#include <deque>
@@ -179,6 +180,21 @@ public:
, m_useFstWriterThread{useFstWriterThread} {}
};
//=============================================================================
// VerilatedTraceBaseC - base class of all Verilated*C trace classes
// Internal use only
class VerilatedTraceBaseC VL_NOT_FINAL {
bool m_modelConnected = false; // Model connected by calling Verilated::trace()
public:
/// True if file currently open
virtual bool isOpen() const VL_MT_SAFE = 0;
// internal use only
bool modelConnected() const VL_MT_SAFE { return m_modelConnected; }
void modelConnected(bool flag) VL_MT_SAFE { m_modelConnected = flag; }
};
//=============================================================================
// VerilatedTrace
@@ -205,10 +221,6 @@ private:
friend OffloadBuffer;
struct CallbackRecord final {
// Note: would make these fields const, but some old STL implementations
// (the one in Ubuntu 14.04 with GCC 4.8.4 in particular) use the
// assignment operator on inserting into collections, so they don't work
// with const fields...
union { // The callback
const initCb_t m_initCb;
const dumpCb_t m_dumpCb;
@@ -283,7 +295,7 @@ private:
uint64_t m_timeLastDump = 0; // Last time we did a dump
bool m_didSomeDump = false; // Did at least one dump (i.e.: m_timeLastDump is valid)
VerilatedContext* m_contextp = nullptr; // The context used by the traced models
std::unordered_set<const VerilatedModel*> m_models; // The collection of models being traced
std::set<const VerilatedModel*> m_models; // The collection of models being traced
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex);
@@ -582,7 +594,7 @@ public:
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep += 2;
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_offloadBufferWritep++ = newvalp[i]; }
for (int i = 0; i < (bits + 31) / 32; ++i) *m_offloadBufferWritep++ = newvalp[i];
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgDouble(uint32_t code, double newval) {
+15 -24
View File
@@ -60,22 +60,6 @@ static double timescaleToDouble(const char* unitp) VL_PURE {
return value;
}
static std::string doubleToTimescale(double value) VL_PURE {
const char* suffixp = "s";
// clang-format off
if (value >= 1e0) { suffixp = "s"; value *= 1e0; }
else if (value >= 1e-3) { suffixp = "ms"; value *= 1e3; }
else if (value >= 1e-6) { suffixp = "us"; value *= 1e6; }
else if (value >= 1e-9) { suffixp = "ns"; value *= 1e9; }
else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
// clang-format on
char valuestr[100];
VL_SNPRINTF(valuestr, 100, "%0.0f%s", value, suffixp);
return valuestr; // Gets converted to string, so no ref to stack
}
//=========================================================================
// Buffer management
@@ -190,13 +174,16 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
//===
// Rare commands
case VerilatedTraceOffloadCommand::TIME_CHANGE:
case VerilatedTraceOffloadCommand::TIME_CHANGE: {
VL_TRACE_OFFLOAD_DEBUG("Command TIME_CHANGE " << top);
readp -= 1; // No code in this command, undo increment
emitTimeChange(*reinterpret_cast<const uint64_t*>(readp));
const uint64_t timeui
= static_cast<uint64_t>(*reinterpret_cast<const uint32_t*>(readp)) << 32ULL
| static_cast<uint64_t>(*reinterpret_cast<const uint32_t*>(readp + 1));
emitTimeChange(timeui);
readp += 2;
continue;
}
case VerilatedTraceOffloadCommand::TRACE_BUFFER:
VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
readp -= 1; // No code in this command, undo increment
@@ -418,7 +405,7 @@ bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const std::stri
template <>
std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
return doubleToTimescale(m_timeRes);
return vl_timescaled_double(m_timeRes);
}
//=========================================================================
@@ -574,7 +561,10 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
// Tell worker to update time point
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
*reinterpret_cast<uint32_t*>(m_offloadBufferWritep + 1)
= static_cast<uint32_t>(timeui >> 32ULL);
*reinterpret_cast<uint32_t*>(m_offloadBufferWritep + 2)
= static_cast<uint32_t>(timeui);
m_offloadBufferWritep += 3;
} else {
// Update time point
@@ -643,12 +633,13 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
// Validate
if (!newModel) { // LCOV_EXCL_START
VL_FATAL_MT(__FILE__, __LINE__, "",
"The same model has already been added to this trace file");
VL_FATAL_MT(
__FILE__, __LINE__, "",
"The same model has already been added to this trace file or VerilatedContext");
}
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"A trace file instance can only handle models from the same context");
"A trace file instance can only handle models from the same VerilatedContext");
}
if (VL_UNCOVERABLE(m_didSomeDump)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
+239 -42
View File
@@ -36,7 +36,6 @@
#include <memory>
#include <set>
#include <string>
#include <unordered_set>
#include <utility>
//=========================================================================
@@ -415,8 +414,20 @@ public:
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
template <std::size_t T_Words>
struct VlWide;
// Type trait to check if a type is VlWide
template <typename>
struct VlIsVlWide : public std::false_type {};
template <std::size_t T_Words>
struct VlIsVlWide<VlWide<T_Words>> : public std::true_type {};
template <std::size_t T_Words>
struct VlWide final {
static constexpr size_t Words = T_Words;
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
EData m_storage[T_Words]; // Contents of the packed array
@@ -615,7 +626,7 @@ public:
// inside (set membership operator)
bool inside(const T_Value& value) const {
return std::find(m_deque.begin(), m_deque.end(), value) != m_deque.end();
return std::find(m_deque.cbegin(), m_deque.cend(), value) != m_deque.cend();
}
// Return slice q[lsb:msb]
@@ -776,13 +787,13 @@ public:
// Reduction operators
VlQueue min() const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::min_element(m_deque.begin(), m_deque.end());
const auto it = std::min_element(m_deque.cbegin(), m_deque.cend());
return VlQueue::cons(*it);
}
template <typename Func>
VlQueue min(Func with_func) const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::min_element(m_deque.begin(), m_deque.end(),
const auto it = std::min_element(m_deque.cbegin(), m_deque.cend(),
[&with_func](const IData& a, const IData& b) {
return with_func(0, a) < with_func(0, b);
});
@@ -790,13 +801,13 @@ public:
}
VlQueue max() const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::max_element(m_deque.begin(), m_deque.end());
const auto it = std::max_element(m_deque.cbegin(), m_deque.cend());
return VlQueue::cons(*it);
}
template <typename Func>
VlQueue max(Func with_func) const {
if (m_deque.empty()) return VlQueue{};
const auto it = std::max_element(m_deque.begin(), m_deque.end(),
const auto it = std::max_element(m_deque.cbegin(), m_deque.cend(),
[&with_func](const IData& a, const IData& b) {
return with_func(0, a) < with_func(0, b);
});
@@ -817,40 +828,40 @@ public:
}
T_Value r_product() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
auto it = m_deque.cbegin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out *= *it;
for (; it != m_deque.cend(); ++it) out *= *it;
return out;
}
template <typename Func>
T_Value r_product(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
auto it = m_deque.cbegin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out *= with_func(index++, *it);
for (; it != m_deque.cend(); ++it) out *= with_func(index++, *it);
return out;
}
T_Value r_and() const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
auto it = m_deque.cbegin();
T_Value out{*it};
++it;
for (; it != m_deque.end(); ++it) out &= *it;
for (; it != m_deque.cend(); ++it) out &= *it;
return out;
}
template <typename Func>
T_Value r_and(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
auto it = m_deque.cbegin();
IData index = 0;
T_Value out{with_func(index, *it)};
++it;
++index;
for (; it != m_deque.end(); ++it) out &= with_func(index, *it);
for (; it != m_deque.cend(); ++it) out &= with_func(index, *it);
return out;
}
T_Value r_or() const {
@@ -891,8 +902,8 @@ public:
}
};
template <class T_Value>
std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
template <class T_Value, size_t T_MaxSize>
std::string VL_TO_STRING(const VlQueue<T_Value, T_MaxSize>& obj) {
return obj.to_string();
}
@@ -948,7 +959,7 @@ public:
// Return last element. Verilog: function int last(ref index)
int last(T_Key& indexr) const {
const auto it = m_map.crbegin();
if (it == m_map.rend()) return 0;
if (it == m_map.crend()) return 0;
indexr = it->first;
return 1;
}
@@ -1077,7 +1088,7 @@ public:
template <typename Func>
VlQueue<T_Value> find_first(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
= std::find_if(m_map.cbegin(), m_map.cend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
@@ -1086,7 +1097,7 @@ public:
template <typename Func>
VlQueue<T_Key> find_first_index(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
= std::find_if(m_map.cbegin(), m_map.cend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
if (it == m_map.end()) return VlQueue<T_Value>{};
@@ -1094,19 +1105,17 @@ public:
}
template <typename Func>
VlQueue<T_Value> find_last(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
const auto it = std::find_if(
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);
}
template <typename Func>
VlQueue<T_Key> find_last_index(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
});
const auto it = std::find_if(
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);
}
@@ -1115,7 +1124,7 @@ public:
VlQueue<T_Value> min() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::min_element(
m_map.begin(), m_map.end(),
m_map.cbegin(), m_map.cend(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
@@ -1125,7 +1134,7 @@ public:
VlQueue<T_Value> min(Func with_func) const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::min_element(
m_map.begin(), m_map.end(),
m_map.cbegin(), m_map.cend(),
[&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);
});
@@ -1134,7 +1143,7 @@ public:
VlQueue<T_Value> max() const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element(
m_map.begin(), m_map.end(),
m_map.cbegin(), m_map.cend(),
[](const std::pair<T_Key, T_Value>& a, const std::pair<T_Key, T_Value>& b) {
return a.second < b.second;
});
@@ -1144,7 +1153,7 @@ public:
VlQueue<T_Value> max(Func with_func) const {
if (m_map.empty()) return VlQueue<T_Value>();
const auto it = std::max_element(
m_map.begin(), m_map.end(),
m_map.cbegin(), m_map.cend(),
[&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);
});
@@ -1164,36 +1173,36 @@ public:
}
T_Value r_product() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
auto it = m_map.cbegin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out *= it->second;
for (; it != m_map.cend(); ++it) out *= it->second;
return out;
}
template <typename Func>
T_Value r_product(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
auto it = m_map.cbegin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out *= with_func(it->first, it->second);
for (; it != m_map.cend(); ++it) out *= with_func(it->first, it->second);
return out;
}
T_Value r_and() const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
auto it = m_map.cbegin();
T_Value out{it->second};
++it;
for (; it != m_map.end(); ++it) out &= it->second;
for (; it != m_map.cend(); ++it) out &= it->second;
return out;
}
template <typename Func>
T_Value r_and(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
auto it = m_map.cbegin();
T_Value out{with_func(it->first, it->second)};
++it;
for (; it != m_map.end(); ++it) out &= with_func(it->first, it->second);
for (; it != m_map.cend(); ++it) out &= with_func(it->first, it->second);
return out;
}
T_Value r_or() const {
@@ -1311,7 +1320,11 @@ public:
// Similar to 'neq' above, *this = that used for change detection
void assign(const VlUnpacked<T_Value, T_Depth>& that) { *this = that; }
bool operator==(const VlUnpacked<T_Value, T_Depth>& that) const { return !neq(that); }
bool operator!=(const VlUnpacked<T_Value, T_Depth>& that) { return neq(that); }
bool operator!=(const VlUnpacked<T_Value, T_Depth>& that) const { return neq(that); }
// interface to C style arrays (used in ports), see issue #5125
bool neq(const T_Value that[T_Depth]) const { return neq(*this, that); }
void assign(const T_Value that[T_Depth]) { std::copy_n(that, T_Depth, m_storage); }
void operator=(const T_Value that[T_Depth]) { assign(that); }
// inside (set membership operator)
bool inside(const T_Value& value) const {
@@ -1388,7 +1401,7 @@ public:
VlQueue<T_Key> unique_index(Func with_func) const {
VlQueue<T_Key> out;
IData index = 0;
std::unordered_set<T_Value> saw;
std::set<T_Value> saw;
for (const auto& i : m_storage) {
const auto i_mapped = with_func(index, i);
auto it = saw.find(i_mapped);
@@ -1501,6 +1514,15 @@ private:
return false;
}
template <typename T_Val, std::size_t T_Dep>
static bool neq(const VlUnpacked<T_Val, T_Dep>& a, const T_Val b[T_Dep]) {
for (size_t i = 0; i < T_Dep; ++i) {
// Recursive 'neq', in case T_Val is also a VlUnpacked<_, _>
if (neq(a.m_storage[i], b[i])) return true;
}
return false;
}
template <typename T_Other> //
static bool neq(const T_Other& a, const T_Other& b) {
// Base case (T_Other is not VlUnpacked<_, _>), fall back on !=
@@ -1513,6 +1535,181 @@ std::string VL_TO_STRING(const VlUnpacked<T_Value, T_Depth>& obj) {
return obj.to_string();
}
//===================================================================
// Helper to apply the given indices to a target expression
template <size_t Curr, size_t Rank, typename T_Target>
struct VlApplyIndices final {
VL_ATTR_ALWINLINE
static auto& apply(T_Target& target, const size_t* indicesp) {
return VlApplyIndices<Curr + 1, Rank, decltype(target[indicesp[Curr]])>::apply(
target[indicesp[Curr]], indicesp);
}
};
template <size_t Rank, typename T_Target>
struct VlApplyIndices<Rank, Rank, T_Target> final {
VL_ATTR_ALWINLINE
static T_Target& apply(T_Target& target, const size_t*) { return target; }
};
//===================================================================
// Commit queue for NBAs - currently only for unpacked arrays
//
// This data-structure is used to handle non-blocking assignments
// that might execute a variable number of times in a single
// evaluation. It has 2 operations:
// - 'enqueue' will add an update to the queue
// - 'commit' will apply all enqueued updates to the target variable,
// in the order they were enqueued. This ensures the last NBA
// takes effect as it is expected.
// There are 2 specializations of this class below:
// - A version when a partial element update is not required,
// e.g, to handle:
// logic [31:0] array[N];
// for (int i = 0 ; i < N ; ++i) array[i] <= x;
// Here 'enqueue' takes the RHS ('x'), and the array indices ('i')
// as arguments.
// - A different version when a partial element update is required,
// e.g. for:
// logic [31:0] array[N];
// for (int i = 0 ; i < N ; ++i) array[i][3:1] <= y;
// Here 'enqueue' takes one additional argument, which is a bitmask
// derived from the bit selects (_[3:1]), which masks the bits that
// need to be updated, and additionally the RHS is widened to a full
// element size, with the bits inserted into the masked region.
template <typename T_Target, // Type of the variable this commit queue updates
bool Partial, // Whether partial element updates are necessary
// The following we could figure out from 'T_Target using type traits, but passing
// explicitly to avoid template expansion, as Verilator already knows them
typename T_Element, // Non-array leaf element type of T_Target array
std::size_t T_Rank // Rank of T_Target (i.e.: how many dimensions it has)
>
class VlNBACommitQueue;
// Specialization for whole element updates only
template <typename T_Target, typename T_Element, std::size_t T_Rank>
class VlNBACommitQueue<T_Target, /* Partial: */ false, T_Element, T_Rank> final {
// TYPES
struct Entry final {
T_Element value;
size_t indices[T_Rank];
};
// STATE
std::vector<Entry> m_pending; // Pending updates, in program order
public:
// CONSTRUCTOR
VlNBACommitQueue() = default;
VL_UNCOPYABLE(VlNBACommitQueue);
// METHODS
template <typename... Args>
void enqueue(const T_Element& value, Args... indices) {
m_pending.emplace_back(Entry{value, {indices...}});
}
// Note: T_Commit might be different from T_Target. Specifically, when the signal is a
// top-level IO port, T_Commit will be a native C array, while T_Target, will be a VlUnpacked
template <typename T_Commit>
void commit(T_Commit& target) {
if (m_pending.empty()) return;
for (const Entry& entry : m_pending) {
VlApplyIndices<0, T_Rank, T_Commit>::apply(target, entry.indices) = entry.value;
}
m_pending.clear();
}
};
// With partial element updates
template <typename T_Target, typename T_Element, std::size_t T_Rank>
class VlNBACommitQueue<T_Target, /* Partial: */ true, T_Element, T_Rank> final {
// TYPES
struct Entry final {
T_Element value;
T_Element mask;
size_t indices[T_Rank];
};
// STATE
std::vector<Entry> m_pending; // Pending updates, in program order
// STATIC METHODS
// Binary & | ~ for elements to use for masking in partial updates. Sorry for the templates.
template <typename T>
VL_ATTR_ALWINLINE static typename std::enable_if<!VlIsVlWide<T>::value, T>::type
bAnd(const T& a, const T& b) {
return a & b;
}
template <typename T>
VL_ATTR_ALWINLINE static typename std::enable_if<VlIsVlWide<T>::value, T>::type
bAnd(const T& a, const T& b) {
T result;
for (size_t i = 0; i < T::Words; ++i) {
result.m_storage[i] = a.m_storage[i] & b.m_storage[i];
}
return result;
}
template <typename T>
VL_ATTR_ALWINLINE static typename std::enable_if<!VlIsVlWide<T>::value, T>::type
bOr(const T& a, const T& b) {
return a | b;
}
template <typename T>
VL_ATTR_ALWINLINE static typename std::enable_if<VlIsVlWide<T>::value, T>::type //
bOr(const T& a, const T& b) {
T result;
for (size_t i = 0; i < T::Words; ++i) {
result.m_storage[i] = a.m_storage[i] | b.m_storage[i];
}
return result;
}
template <typename T>
VL_ATTR_ALWINLINE static typename std::enable_if<!VlIsVlWide<T>::value, T>::type
bNot(const T& a) {
return ~a;
}
template <typename T>
VL_ATTR_ALWINLINE static typename std::enable_if<VlIsVlWide<T>::value, T>::type
bNot(const T& a) {
T result;
for (size_t i = 0; i < T::Words; ++i) result.m_storage[i] = ~a.m_storage[i];
return result;
}
public:
// CONSTRUCTOR
VlNBACommitQueue() = default;
VL_UNCOPYABLE(VlNBACommitQueue);
// METHODS
template <typename... Args>
void enqueue(const T_Element& value, const T_Element& mask, Args... indices) {
m_pending.emplace_back(Entry{value, mask, {indices...}});
}
// Note: T_Commit might be different from T_Target. Specifically, when the signal is a
// top-level IO port, T_Commit will be a native C array, while T_Target, will be a VlUnpacked
template <typename T_Commit>
void commit(T_Commit& target) {
if (m_pending.empty()) return;
for (const Entry& entry : m_pending) { //
auto& ref = VlApplyIndices<0, T_Rank, T_Commit>::apply(target, entry.indices);
// Maybe inefficient, but it works for now ...
const auto oldValue = ref;
ref = bOr(bAnd(entry.value, entry.mask), bAnd(oldValue, bNot(entry.mask)));
}
m_pending.clear();
}
};
//===================================================================
// Object that VlDeleter is capable of deleting
@@ -1707,7 +1904,7 @@ public:
T_Class* operator->() const { return m_objp; }
// For 'if (ptr)...'
operator bool() const { return m_objp; }
// In SV A == B iff both are handles to the same object (IEEE 1800-2017 8.4)
// In SV A == B iff both are handles to the same object (IEEE 1800-2023 8.4)
template <typename T_OtherClass>
bool operator==(const VlClassRef<T_OtherClass>& rhs) const {
return m_objp == rhs.m_objp;
+1 -1
View File
@@ -268,7 +268,7 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
// We add output data to m_writep.
// When it gets nearly full we dump it using this routine which calls write()
// This is much faster than using buffered I/O
if (VL_UNLIKELY(!isOpen())) return;
if (VL_UNLIKELY(!m_isOpen)) return;
const char* wp = m_wrBufp;
while (true) {
const ssize_t remaining = (m_writep - wp);
+6 -3
View File
@@ -251,7 +251,7 @@ public:
/// Class representing a VCD dump file in C standalone (no SystemC)
/// simulations. Also derived for use in SystemC simulations.
class VerilatedVcdC VL_NOT_FINAL {
class VerilatedVcdC VL_NOT_FINAL : public VerilatedTraceBaseC {
VerilatedVcd m_sptrace; // Trace file being created
// CONSTRUCTORS
@@ -267,7 +267,7 @@ public:
// METHODS - User called
/// Return if file is open
bool isOpen() const VL_MT_SAFE { return m_sptrace.isOpen(); }
bool isOpen() const override VL_MT_SAFE { return m_sptrace.isOpen(); }
/// Open a new VCD file
/// This includes a complete header dump each time it is called,
/// just as if this object was deleted and reconstructed.
@@ -283,7 +283,10 @@ public:
/// first may be removed. Cat files together to create viewable vcd.
void rolloverSize(size_t size) VL_MT_SAFE { m_sptrace.rolloverSize(size); }
/// Close dump
void close() VL_MT_SAFE { m_sptrace.close(); }
void close() VL_MT_SAFE {
m_sptrace.close();
modelConnected(false);
}
/// Flush dump
void flush() VL_MT_SAFE { m_sptrace.flush(); }
/// Write one cycle of dump data
+292 -117
View File
@@ -31,11 +31,14 @@
#include "verilated.h"
#include "verilated_imp.h"
#include "vltstd/vpi_user.h"
#include <cstdarg>
#include <cstdio>
#include <list>
#include <map>
#include <set>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -263,33 +266,24 @@ class VerilatedVpioScope VL_NOT_FINAL : public VerilatedVpio {
protected:
const VerilatedScope* const m_scopep;
bool m_toplevel = false;
const char* m_name;
const char* m_fullname;
public:
explicit VerilatedVpioScope(const VerilatedScope* scopep)
: m_scopep{scopep} {
std::string scopename = m_scopep->name();
std::string::size_type pos = std::string::npos;
// Look for '.' not inside escaped identifier
size_t i = 0;
while (i < scopename.length()) {
if (scopename[i] == '\\') {
while (i < scopename.length() && scopename[i] != ' ') ++i;
++i; // Proc ' ', it should always be there. Then grab '.' on next cycle
} else {
while (i < scopename.length() && scopename[i] != '.') ++i;
if (i < scopename.length()) pos = i++;
}
}
if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true;
m_fullname = m_scopep->name();
if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
m_name = m_scopep->identifier();
}
~VerilatedVpioScope() override = default;
static VerilatedVpioScope* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioScope*>(reinterpret_cast<VerilatedVpio*>(h));
}
uint32_t type() const override { return vpiScope; }
uint32_t type() const override { return vpiGenScope; }
const VerilatedScope* scopep() const { return m_scopep; }
const char* name() const override { return m_scopep->name(); }
const char* fullname() const override { return m_scopep->name(); }
const char* name() const override { return m_name; }
const char* fullname() const override { return m_fullname; }
bool toplevel() const { return m_toplevel; }
};
@@ -469,22 +463,29 @@ public:
};
class VerilatedVpioModule final : public VerilatedVpioScope {
const char* m_name;
const char* m_fullname;
public:
explicit VerilatedVpioModule(const VerilatedScope* modulep)
: VerilatedVpioScope{modulep} {
m_fullname = m_scopep->name();
if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
m_name = m_scopep->identifier();
// Look for '.' not inside escaped identifier
const std::string scopename = m_fullname;
std::string::size_type pos = std::string::npos;
size_t i = 0;
while (i < scopename.length()) {
if (scopename[i] == '\\') {
while (i < scopename.length() && scopename[i] != ' ') ++i;
++i; // Proc ' ', it should always be there. Then grab '.' on next cycle
} else {
while (i < scopename.length() && scopename[i] != '.') ++i;
if (i < scopename.length()) pos = i++;
}
}
if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true;
}
static VerilatedVpioModule* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModule*>(reinterpret_cast<VerilatedVpio*>(h));
}
uint32_t type() const override { return vpiModule; }
const char* name() const override { return m_name; }
const char* fullname() const override { return m_fullname; }
};
class VerilatedVpioModuleIter final : public VerilatedVpio {
@@ -507,8 +508,8 @@ public:
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
const VerilatedScope::Type itype = (*m_it)->type();
const VerilatedScope* const modp = *m_it++;
const VerilatedScope::Type itype = modp->type();
if (itype == VerilatedScope::SCOPE_MODULE) {
return (new VerilatedVpioModule{modp})->castVpiHandle();
}
@@ -516,26 +517,54 @@ public:
}
};
class VerilatedVpioScopeIter final : public VerilatedVpio {
const std::vector<const VerilatedScope*>* m_vec;
std::vector<const VerilatedScope*>::const_iterator m_it;
public:
explicit VerilatedVpioScopeIter(const std::vector<const VerilatedScope*>& vec)
: m_vec{&vec} {
m_it = m_vec->begin();
}
~VerilatedVpioScopeIter() override = default;
static VerilatedVpioScopeIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioScopeIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
while (true) {
if (m_it == m_vec->end()) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
}
const VerilatedScope* const modp = *m_it++;
const VerilatedScope::Type itype = modp->type();
if (itype == VerilatedScope::SCOPE_OTHER) {
return (new VerilatedVpioScope{modp})->castVpiHandle();
} else if (itype == VerilatedScope::SCOPE_MODULE) {
return (new VerilatedVpioModule{modp})->castVpiHandle();
}
}
}
};
static const char* d_unit = "$unit";
class VerilatedVpioPackage final : public VerilatedVpioScope {
std::string m_name;
std::string m_fullname;
std::string m_fullname_string;
public:
explicit VerilatedVpioPackage(const VerilatedScope* modulep)
: VerilatedVpioScope{modulep} {
const char* sfullname = m_scopep->name();
if (std::strncmp(sfullname, "TOP.", 4) == 0) sfullname += 4;
m_fullname = std::string{sfullname} + "::";
if (m_fullname == "\\$unit ::") m_fullname = "$unit::";
m_name = std::string(m_scopep->identifier());
if (m_name == "\\$unit ") m_name = "$unit";
m_fullname_string = std::string{m_fullname} + "::";
if (m_fullname_string == "\\$unit ::") m_fullname_string = "$unit::";
if (strcmp(m_name, "\\$unit ") == 0) m_name = d_unit;
}
static VerilatedVpioPackage* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioPackage*>(reinterpret_cast<VerilatedVpio*>(h));
}
const char* fullname() const override { return m_fullname_string.c_str(); }
uint32_t type() const override { return vpiPackage; }
const char* name() const override { return m_name.c_str(); }
const char* fullname() const override { return m_fullname.c_str(); }
};
class VerilatedVpioInstanceIter final : public VerilatedVpio {
@@ -604,6 +633,146 @@ public:
void invalidate() { m_id = 0; }
};
class VerilatedVpiPutHolder final {
VerilatedVpioVar m_var;
s_vpi_value m_value;
union Storage {
char init = 0; // to ensure trivial constructor
std::string str;
std::vector<s_vpi_vecval> vec;
~Storage() noexcept {/* handled by VerilatedVpiPutHolder */};
} m_storage{};
public:
VerilatedVpiPutHolder(const VerilatedVpioVar* vop, p_vpi_value valuep)
: m_var{vop} {
m_value.format = valuep->format;
switch (valuep->format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: {
new (&m_storage.str) std::string{valuep->value.str};
m_value.value.str = const_cast<char*>(m_storage.str.c_str());
break;
}
case vpiScalarVal: {
m_value.value.scalar = valuep->value.scalar;
break;
}
case vpiIntVal: {
m_value.value.integer = valuep->value.integer;
break;
}
case vpiRealVal: {
m_value.value.real = valuep->value.real;
break;
}
case vpiVectorVal: {
size_t words = 0;
switch (vop->varp()->vltype()) {
case VLVT_UINT8:
case VLVT_UINT16:
case VLVT_UINT32: {
words = 1;
break;
}
case VLVT_UINT64: {
words = 2;
break;
}
case VLVT_WDATA: {
words = VL_WORDS_I(vop->varp()->packed().elements());
break;
}
default: break;
}
new (&m_storage.vec)
std::vector<s_vpi_vecval>{valuep->value.vector, &valuep->value.vector[words]};
m_value.value.vector = m_storage.vec.data();
break;
}
}
}
VerilatedVpiPutHolder(VerilatedVpiPutHolder const& o)
: m_var{o.m_var}
, m_value{o.m_value} {
switch (m_value.format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: {
new (&m_storage.str) std::string{o.m_storage.str};
break;
}
case vpiVectorVal: {
new (&m_storage.vec) std::vector<s_vpi_vecval>{o.m_storage.vec};
break;
}
}
}
VerilatedVpiPutHolder(VerilatedVpiPutHolder&& o) noexcept
: m_var{std::move(o.m_var)}
, m_value{std::move(o.m_value)} {
switch (m_value.format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: {
new (&m_storage.str) std::string{std::move(o.m_storage.str)};
break;
}
case vpiVectorVal: {
new (&m_storage.vec) std::vector<s_vpi_vecval>{std::move(o.m_storage.vec)};
break;
}
}
}
~VerilatedVpiPutHolder() noexcept {
switch (m_value.format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: m_storage.str.~basic_string(); break;
case vpiVectorVal: m_storage.vec.~vector(); break;
}
}
VerilatedVpioVar* varp() { return &m_var; }
p_vpi_value valuep() { return &m_value; }
static bool canInertialDelay(p_vpi_value valuep) {
switch (valuep->format) {
case vpiBinStrVal: // FALLTHRU
case vpiOctStrVal: // FALLTHRU
case vpiDecStrVal: // FALLTHRU
case vpiHexStrVal: // FALLTHRU
case vpiStringVal: {
if (VL_UNLIKELY(!valuep->value.str)) return false;
break;
}
case vpiScalarVal: // FALLTHRU
case vpiIntVal: // FALLTHRU
case vpiRealVal: break;
case vpiVectorVal: {
if (VL_UNLIKELY(!valuep->value.vector)) return false;
break;
}
default: {
return false;
}
}
return true;
}
};
struct VerilatedVpiTimedCbsCmp final {
// Ordering sets keyed by time, then callback unique id
bool operator()(const std::pair<QData, uint64_t>& a,
@@ -626,9 +795,11 @@ class VerilatedVpiImp final {
std::array<VpioCbList, CB_ENUM_MAX_VALUE> m_cbCurrentLists;
VpioFutureCbs m_futureCbs; // Time based callbacks for future timestamps
VpioFutureCbs m_nextCbs; // cbNextSimTime callbacks
std::list<VerilatedVpiPutHolder> m_inertialPuts; // Pending vpi puts due to vpiInertialDelay
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
uint64_t m_nextCallbackId = 1; // Id to identify callback
bool m_evalNeeded = false; // Model has had signals updated via vpi_put_value()
static VerilatedVpiImp& s() { // Singleton
static VerilatedVpiImp s_s;
@@ -750,7 +921,7 @@ public:
assertOneCheck();
VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
bool called = false;
std::unordered_set<VerilatedVpioVar*> update; // set of objects to update after callbacks
std::set<VerilatedVpioVar*> update; // set of objects to update after callbacks
if (cbObjList.empty()) return called;
const auto last = std::prev(cbObjList.end()); // prevent looping over newly added elements
for (auto it = cbObjList.begin(); true;) {
@@ -787,6 +958,17 @@ public:
}
static void dumpCbs() VL_MT_UNSAFE_ONE;
static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info
static void evalNeeded(bool evalNeeded) { s().m_evalNeeded = evalNeeded; }
static bool evalNeeded() { return s().m_evalNeeded; }
static void inertialDelay(const VerilatedVpioVar* vop, p_vpi_value valuep) {
s().m_inertialPuts.emplace_back(vop, valuep);
}
static void doInertialPuts() {
for (auto& it : s().m_inertialPuts) {
vpi_put_value(it.varp()->castVpiHandle(), it.valuep(), nullptr, vpiNoDelay);
}
s().m_inertialPuts.clear();
}
};
//======================================================================
@@ -889,6 +1071,11 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
return 1;
}
void VerilatedVpi::clearEvalNeeded() VL_MT_UNSAFE_ONE { VerilatedVpiImp::evalNeeded(false); }
bool VerilatedVpi::evalNeeded() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::evalNeeded(); }
void VerilatedVpi::doInertialPuts() VL_MT_UNSAFE_ONE { VerilatedVpiImp::doInertialPuts(); }
//======================================================================
// VerilatedVpiImp implementation
@@ -1978,13 +2165,21 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
return ((new VerilatedVpioVarIter{vop, true})->castVpiHandle());
}
case vpiModule: {
const VerilatedVpioModule* const vop = VerilatedVpioModule::castp(object);
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
if (it == map->end()) return nullptr;
return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
}
case vpiInternalScope: {
const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
const auto it = vlstd::as_const(map)->find(const_cast<VerilatedScope*>(modp));
if (it == map->end()) return nullptr;
return ((new VerilatedVpioScopeIter{it->second})->castVpiHandle());
}
case vpiInstance: {
if (object) return nullptr;
const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
@@ -2160,13 +2355,28 @@ bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const c
return status;
}
static void vl_strprintf(std::string& buffer, char const* fmt, ...) {
va_list args, args_copy;
va_start(args, fmt);
buffer.clear();
// Make copy of args since we may need to call VL_VSNPRINTF more than once
va_copy(args_copy, args);
// Try VL_VSNPRINTF in existing buffer
const int result
= VL_VSNPRINTF(const_cast<char*>(buffer.data()), buffer.capacity(), fmt, args_copy);
va_end(args_copy);
const int required = result + 1; // Returned size doesn't include NUL terminator
// If there wasn't enough space, reallocate and try again
if (buffer.capacity() < required) {
buffer.reserve(required * 2);
VL_VSNPRINTF(const_cast<char*>(buffer.data()), buffer.capacity(), fmt, args);
}
va_end(args);
}
void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
const char* fullname) {
if (!vl_check_format(varp, valuep, fullname, true)) return;
// Maximum required size is for binary string, one byte per bit plus null termination
static thread_local char t_outStr[VL_VALUE_STRING_MAX_WORDS * VL_EDATASIZE + 1];
// cppcheck-suppress variableScope
static const thread_local int t_outStrSz = sizeof(t_outStr) - 1;
// string data type is dynamic and may vary in size during simulation
static thread_local std::string t_outDynamicStr;
// We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
@@ -2210,43 +2420,21 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
return;
}
} else if (valuep->format == vpiBinStrVal) {
valuep->value.str = t_outStr;
int bits = varp->packed().elements();
t_outDynamicStr.resize(bits);
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (bits > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_VALUE_STRING_MAX_WORDS, bits);
bits = t_outStrSz;
}
for (i = 0; i < bits; ++i) {
for (size_t i = 0; i < bits; ++i) {
const char val = (datap[i >> 3] >> (i & 7)) & 1;
t_outStr[bits - i - 1] = val ? '1' : '0';
t_outDynamicStr[bits - i - 1] = val ? '1' : '0';
}
t_outStr[i] = '\0';
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiOctStrVal) {
valuep->value.str = t_outStr;
int chars = (varp->packed().elements() + 2) / 3;
t_outDynamicStr.resize(chars);
const int bytes = VL_BYTES_I(varp->packed().elements());
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (chars > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_VALUE_STRING_MAX_WORDS, chars);
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
for (size_t i = 0; i < chars; ++i) {
const div_t idx = div(i * 3, 8);
int val = datap[idx.quot];
if ((idx.quot + 1) < bytes) {
@@ -2265,46 +2453,31 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
val &= (1 << rem) - 1;
}
}
t_outStr[chars - i - 1] = '0' + (val & 7);
t_outDynamicStr[chars - i - 1] = '0' + (val & 7);
}
t_outStr[i] = '\0';
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiDecStrVal) {
valuep->value.str = t_outStr;
// outStrSz does not include nullptr termination so add one
if (varp->vltype() == VLVT_UINT8) {
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%hhu",
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
return;
vl_strprintf(t_outDynamicStr, "%hhu",
static_cast<unsigned char>(*(reinterpret_cast<CData*>(varDatap))));
} else if (varp->vltype() == VLVT_UINT16) {
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%hu",
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
return;
vl_strprintf(t_outDynamicStr, "%hu",
static_cast<unsigned short>(*(reinterpret_cast<SData*>(varDatap))));
} else if (varp->vltype() == VLVT_UINT32) {
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%u",
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
return;
vl_strprintf(t_outDynamicStr, "%u",
static_cast<unsigned int>(*(reinterpret_cast<IData*>(varDatap))));
} else if (varp->vltype() == VLVT_UINT64) {
VL_SNPRINTF(t_outStr, t_outStrSz + 1, "%llu",
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
return;
vl_strprintf(t_outDynamicStr, "%llu",
static_cast<unsigned long long>(*(reinterpret_cast<QData*>(varDatap))));
}
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiHexStrVal) {
valuep->value.str = t_outStr;
int chars = (varp->packed().elements() + 3) >> 2;
t_outDynamicStr.resize(chars);
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (chars > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, t_outStrSz,
VL_VALUE_STRING_MAX_WORDS, chars);
chars = t_outStrSz;
}
for (i = 0; i < chars; ++i) {
for (size_t i = 0; i < chars; ++i) {
char val = (datap[i >> 1] >> ((i & 1) << 2)) & 15;
if (i == (chars - 1)) {
// most significant char, mask off nonexistent bits when vector
@@ -2315,9 +2488,9 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
val &= (1 << rem) - 1;
}
}
t_outStr[chars - i - 1] = "0123456789abcdef"[static_cast<int>(val)];
t_outDynamicStr[chars - i - 1] = "0123456789abcdef"[static_cast<int>(val)];
}
t_outStr[i] = '\0';
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
} else if (valuep->format == vpiStringVal) {
if (varp->vltype() == VLVT_STRING) {
@@ -2330,26 +2503,15 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep,
return;
}
} else {
valuep->value.str = t_outStr;
int bytes = VL_BYTES_I(varp->packed().elements());
t_outDynamicStr.resize(bytes);
const CData* datap = (reinterpret_cast<CData*>(varDatap));
int i;
if (bytes > t_outStrSz) {
// limit maximum size of output to size of buffer to prevent overrun.
VL_VPI_WARNING_(__FILE__, __LINE__,
"%s: Truncating string value of %s for %s"
" as buffer size (%d, VL_VALUE_STRING_MAX_WORDS=%d) is less than "
"required (%d)",
__func__, VerilatedVpiError::strFromVpiVal(valuep->format),
fullname, t_outStrSz, VL_VALUE_STRING_MAX_WORDS, bytes);
bytes = t_outStrSz;
}
for (i = 0; i < bytes; ++i) {
for (size_t i = 0; i < bytes; ++i) {
const char val = datap[bytes - i - 1];
// other simulators replace [leading?] zero chars with spaces, replicate here.
t_outStr[i] = val ? val : ' ';
t_outDynamicStr[i] = val ? val : ' ';
}
t_outStr[i] = '\0';
valuep->value.str = const_cast<PLI_BYTE8*>(t_outDynamicStr.c_str());
return;
}
} else if (valuep->format == vpiIntVal) {
@@ -2398,7 +2560,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
}
vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/,
PLI_INT32 /*flags*/) {
PLI_INT32 flags) {
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, valuep););
VerilatedVpiImp::assertOneCheck();
VL_VPI_ERROR_RESET_();
@@ -2406,11 +2568,24 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer");
return nullptr;
}
PLI_INT32 delay_mode = flags & 0xfff;
if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
if (delay_mode == vpiInertialDelay) {
if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) {
VL_VPI_WARNING_(
__FILE__, __LINE__,
"%s: Unsupported p_vpi_value as requested for '%s' with vpiInertialDelay",
__func__, vop->fullname());
return nullptr;
}
VerilatedVpiImp::inertialDelay(vop, valuep);
return object;
}
VL_DEBUG_IF_PLI(
VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(),
valuep->format, valuep->value.integer);
VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap()););
VerilatedVpiImp::evalNeeded(true);
if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
VL_VPI_WARNING_(__FILE__, __LINE__,
+8 -1
View File
@@ -30,7 +30,7 @@
#include "verilated_syms.h"
//======================================================================
// From IEEE 1800-2017 annex M
// From IEEE 1800-2023 annex M
#include "vltstd/sv_vpi_user.h"
@@ -54,6 +54,13 @@ public:
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
/// Debug dump of callbacks
static void dumpCbs() VL_MT_UNSAFE_ONE;
/// Checks VPI dirty state (i.e. whether vpi_put_value() has
/// been called since the last clearEvalNeeded())
static bool evalNeeded() VL_MT_UNSAFE_ONE;
/// Clears VPI dirty state (see evalNeeded())
static void clearEvalNeeded() VL_MT_UNSAFE_ONE;
/// Perform inertially delayed puts
static void doInertialPuts() VL_MT_UNSAFE_ONE;
// Self test, for internal use only
static void selfTest() VL_MT_UNSAFE_ONE;
+51 -8
View File
@@ -58,6 +58,9 @@
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__((format(printf, (fmtArgNum), (fmtArgNum) + 1)))
# define VL_ATTR_PURE __attribute__((pure))
# define VL_ATTR_UNUSED __attribute__((unused))
#ifndef VL_ATTR_WARN_UNUSED_RESULT
# define VL_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#endif
# if !defined(_WIN32) && !defined(__MINGW32__)
// All VL_ATTR_WEAK symbols must be marked with the macOS -U linker flag in verilated.mk.in
# define VL_ATTR_WEAK __attribute__((weak))
@@ -164,6 +167,9 @@
#ifndef VL_ATTR_UNUSED
# define VL_ATTR_UNUSED ///< Attribute that function that may be never used
#endif
#ifndef VL_ATTR_WARN_UNUSED_RESULT
# define VL_ATTR_WARN_UNUSED_RESULT ///< Attribute that return value of function must be used
#endif
#ifndef VL_ATTR_WEAK
# define VL_ATTR_WEAK ///< Attribute that function external that is optionally defined
#endif
@@ -337,6 +343,7 @@ extern "C" void __gcov_dump();
#include <cstdint>
#include <cinttypes>
#include <cmath>
#include <ctime>
#ifndef VL_NO_LEGACY
using vluint8_t = uint8_t; ///< 8-bit unsigned type (backward compatibility)
@@ -604,6 +611,50 @@ static inline double VL_ROUND(double n) {
#define VL_OFFSETOF(type, field) \
(reinterpret_cast<size_t>(&(reinterpret_cast<type*>(0x10000000)->field)) - 0x10000000)
//=========================================================================
// Time and performance
#include <string>
namespace VlOs {
/// Get environment variable
extern std::string getenvStr(const std::string& envvar,
const std::string& defaultValue) VL_MT_SAFE;
extern uint64_t memUsageBytes() VL_MT_SAFE; ///< Return memory usage in bytes, or 0 if unknown
// Internal: Record CPU time, starting point on construction, and current delta from that
class DeltaCpuTime final {
double m_start{}; // Time constructed at
static double gettime() VL_MT_SAFE;
public:
// Construct, and if startit is true, start() timer
explicit DeltaCpuTime(bool startit) {
if (startit) start();
}
void start() VL_MT_SAFE { m_start = gettime(); } // Start timer; record current time
double deltaTime() const VL_MT_SAFE { // Return time between now and start()
return (m_start == 0.0) ? 0.0 : gettime() - m_start;
}
};
// Internal: Record wall time, starting point on construction, and current delta from that
class DeltaWallTime final {
double m_start{}; // Time constructed at
static double gettime() VL_MT_SAFE;
public:
// Construct, and if startit is true, start() timer
explicit DeltaWallTime(bool startit) {
if (startit) start();
}
void start() VL_MT_SAFE { m_start = gettime(); } // Start timer; record current time
double deltaTime() const VL_MT_SAFE { // Return time between now and start()
return (m_start == 0.0) ? 0.0 : gettime() - m_start;
}
};
} //namespace VlOs
//=========================================================================
// Conversions
@@ -637,14 +688,6 @@ T const& as_const(T& v) VL_MT_SAFE {
return v;
}
// C++14's std::exchange
template <class T, class U = T>
T exchange(T& obj, U&& new_value) {
T old_value = std::move(obj);
obj = std::forward<U>(new_value);
return old_value;
}
}; // namespace vlstd
//=========================================================================
+129
View File
@@ -0,0 +1,129 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Code available from: https://verilator.org
//
// Copyright 2003-2024 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
///
/// \file
/// \brief Verilated/Verilator common implementation for OS portability
///
/// This is compiled as part of other .cpp files to reduce compile time
/// and as such is a .h file rather than .cpp file.
///
//*************************************************************************
#ifndef VL_ALLOW_VERILATEDOS_C
#error "This file should be included only from V3Os.cpp/Verilated.cpp"
#endif
#include "verilatedos.h"
// clang-format off
#if defined(_WIN32) || defined(__MINGW32__)
# include <windows.h> // LONG for bcrypt.h on MINGW
# include <processthreadsapi.h> // GetProcessTimes
# include <psapi.h> // GetProcessMemoryInfo
#endif
// clang-format on
namespace VlOs {
//=========================================================================
// VlOs::VlGetCpuTime/VlGetWallTime implementation
double DeltaCpuTime::gettime() VL_MT_SAFE {
#if defined(_WIN32) || defined(__MINGW32__)
FILETIME lpCreationTime, lpExitTime, lpKernelTime, lpUserTime;
if (0
!= GetProcessTimes(GetCurrentProcess(), &lpCreationTime, &lpExitTime, &lpKernelTime,
&lpUserTime)) {
return static_cast<double>(static_cast<uint64_t>(lpUserTime.dwLowDateTime)
| static_cast<uint64_t>(lpUserTime.dwHighDateTime) << 32ULL)
* 1e-7;
}
#else
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
timespec ts;
if (0 != clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)) // MT-Safe
return ts.tv_sec + ts.tv_nsec * 1e-9;
#endif
return 0.0;
}
double DeltaWallTime::gettime() VL_MT_SAFE {
#if defined(_WIN32) || defined(__MINGW32__)
FILETIME ft; // contains number of 0.1us intervals since the beginning of 1601 UTC.
GetSystemTimeAsFileTime(&ft);
const uint64_t tenthus
= ((static_cast<uint64_t>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime + 5ULL);
return static_cast<double>(tenthus) * 1e-7;
#else
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
timespec ts;
if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) // MT-Safe
return ts.tv_sec + ts.tv_nsec * 1e-9;
return 0.0;
#endif
}
//=========================================================================
// VlOs::memUsageBytes implementation
uint64_t memUsageBytes() VL_MT_SAFE {
#if defined(_WIN32) || defined(__MINGW32__)
const HANDLE process = GetCurrentProcess();
PROCESS_MEMORY_COUNTERS pmc;
if (GetProcessMemoryInfo(process, &pmc, sizeof(pmc))) {
// The best we can do using simple Windows APIs is to get the size of the working set.
return pmc.WorkingSetSize;
}
return 0;
#else
// Highly unportable. Sorry
const char* const statmFilename = "/proc/self/statm";
FILE* const fp = fopen(statmFilename, "r");
if (!fp) return 0;
uint64_t size, resident, share, text, lib, data, dt; // All in pages
const int items = fscanf(
fp, "%" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64,
&size, &resident, &share, &text, &lib, &data, &dt);
fclose(fp);
if (VL_UNCOVERABLE(7 != items)) return 0;
return (text + data) * getpagesize();
#endif
}
//=========================================================================
// VlOs::getenvStr implementation
std::string getenvStr(const std::string& envvar, const std::string& defaultValue) VL_MT_SAFE {
std::string ret;
#if defined(_MSC_VER)
// Note: MinGW does not offer _dupenv_s
const char* envvalue = nullptr;
_dupenv_s((char**)&envvalue, nullptr, envvar.c_str());
if (envvalue != nullptr) {
const std::string result{envvalue};
free((void*)envvalue);
ret = result;
} else {
ret = defaultValue;
}
#else
if (const char* const envvalue = getenv(envvar.c_str())) {
ret = envvalue;
} else {
ret = defaultValue;
}
#endif
return ret;
}
//=========================================================================
} //namespace VlOs
+17 -2
View File
@@ -150,10 +150,21 @@ extern "C" {
#define vpiIntegerNet 681
#define vpiLogicNet vpiNet
#define vpiTimeNet 682
#define vpiUnionNet 525
#define vpiShortRealNet 526
#define vpiRealNet 527
#define vpiByteNet 528
#define vpiShortIntNet 529
#define vpiIntNet 530
#define vpiLongIntNet 531
#define vpiBitNet 532
#define vpiInterconnectNet 533
#define vpiInterconnectArray 534
#define vpiStructNet 683
#define vpiBreak 684
#define vpiContinue 685
#define vpiPackedArrayNet 693
#define vpiNettypeDecl 523
#define vpiConstraintExpr 747
#define vpiElseConst 748
#define vpiImplication 749
@@ -174,6 +185,8 @@ extern "C" {
#define vpiBaseTypespec 703
#define vpiElemTypespec 704
#define vpiNetTypedefAlias 705
#define vpiInputSkew 706
#define vpiOutputSkew 707
#define vpiGlobalClocking 708
@@ -206,7 +219,8 @@ extern "C" {
#define vpiMessages 735
#define vpiLoopVars 737
#define vpiConcurrentAssertions 740
#define vpiConcurrentAssertion 740
#define vpiConcurrentAssertions vpiConcurrentAssertion
#define vpiMatchItem 741
#define vpiMember 742
#define vpiElement 743
@@ -221,6 +235,7 @@ extern "C" {
/**************************************************************************/
/************************ generic object properties ***********************/
/**************************************************************************/
#define vpiTop 600
#define vpiUnit 602
@@ -255,7 +270,6 @@ extern "C" {
#define vpiModportPort 2
/* vpiPort is also a port type. It is defined in vpi_user.h */
#define vpiConstantVariable 612
#define vpiStructUnionMember 615
@@ -407,6 +421,7 @@ extern "C" {
/***************************** structure *****************************/
/**************************** CALLBACK REASONS ****************************/
#define vpiMethodFuncCall 648
#define cbStartOfThread 600 /* callback on thread creation */
#define cbEndOfThread 601 /* callback on thread termination */
#define cbEnterThread 602 /* callback on reentering thread */
+51 -6
View File
@@ -7,7 +7,7 @@
* This file contains the constant definitions, structure definitions,
* and routine declarations used by SystemVerilog DPI.
*
* This file is from the SystemVerilog IEEE 1800-2017 Annex I.
* This file is from the SystemVerilog IEEE 1800-2023 Annex I.
*/
#ifndef INCLUDED_SVDPI
@@ -111,6 +111,26 @@ typedef uint32_t svBitVecVal;
((N) == 32 ? (VALUE) : \
(((VALUE) & (1 << (N))) ? ((VALUE) | ~SV_MASK(N)) : ((VALUE) & SV_MASK(N))))
#ifndef VPI_TIME
#define VPI_TIME
typedef struct t_vpi_time {
int32_t type;
uint32_t high, low;
double real;
} s_vpi_time, *p_vpi_time;
#define vpiScaledRealTime 1
#define vpiSimTime 2
#define vpiSuppressTime 3
#endif
/* time value */
typedef s_vpi_time svTimeVal;
/* time value types */
#define sv_scaled_real_time vpiScaledRealTime
#define sv_sim_time vpiSimTime
/*
* Implementation-dependent representation.
*/
@@ -277,7 +297,7 @@ XXTERN svScope svGetScope( void );
/*
* Set context for subsequent export function execution.
* This function must be called before calling an export function, unless
* This function shall be called before calling an export function, unless
* the export function is called while executing an import function. In that
* case the export function shall inherit the scope of the surrounding import
* function. This is known as the "default scope".
@@ -297,7 +317,7 @@ XXTERN svScope svGetScopeFromName(const char* scopeName);
/*
* Store an arbitrary user data pointer for later retrieval by svGetUserData()
* The userKey is generated by the user. It must be guaranteed by the user to
* The userKey is generated by the user. It needs to be guaranteed by the user to
* be unique from all other userKey's for all unique data storage requirements
* It is recommended that the address of static functions or variables in the
* user's C code be used as the userKey.
@@ -329,24 +349,49 @@ XXTERN void* svGetUserData(const svScope scope, void* userKey);
* modified. Whether this information is available or not is implementation-
* specific. Note that the string provided (if any) is owned by the SV
* implementation and is valid only until the next call to any SV function.
* Applications must not modify this string or free it
* Applications shall not modify this string or free it.
*/
XXTERN int svGetCallerInfo(const char** fileName, int *lineNumber);
/*
* Returns 1 if the current execution thread is in the disabled state.
* Disable protocol must be adhered to if in the disabled state.
* Disable protocol shall be adhered to if in the disabled state.
*/
XXTERN int svIsDisabledState( void );
/*
* Imported functions call this API function during disable processing to
* acknowledge that they are correctly participating in the DPI disable protocol.
* This function must be called before returning from an imported function that is
* This function shall be called before returning from an imported function that is
* in the disabled state.
*/
XXTERN void svAckDisabledState( void );
/* Mantis 5713/D9
* Retrieve the current simulation time, scaled to the time unit of the scope.
* If scope is NULL, then time is scaled to the simulation time unit.
* It is an error to call svGetTime() with an invalid svScope.
* This function returns -1 for all error cases, 0 upon success.
*/
XXTERN int svGetTime(const svScope scope, svTimeVal* time);
/*
* Retrieve the time unit for scope.
* If scope is NULL, then simulation time unit is retrieved.
* It is an error to call svGetTimeUnit() with an invalid svScope.
* This function returns -1 for all error cases, 0 upon success.
*/
XXTERN int svGetTimeUnit(const svScope scope, int32_t* time_unit);
/*
* Retrieve the time precision for scope.
* If scope is NULL, then simulation time unit is retrieved.
* It is an error to call svGetTimePrecision() with an invalid svScope.
* This function returns -1 for all error cases, 0 upon success.
*/
XXTERN int svGetTimePrecision(const svScope scope, int32_t* time_precision);
/*
**********************************************************
* DEPRECATED PORTION OF FILE STARTS FROM HERE.
+11 -4
View File
@@ -1,7 +1,7 @@
/*******************************************************************************
* vpi_user.h
*
* IEEE Std 1800-2017 Programming Language Interface (PLI)
* IEEE Std 1800-2023 Programming Language Interface (PLI)
*
* This file contains the constant definitions, structure definitions, and
* routine declarations used by the SystemVerilog Verification Procedural
@@ -344,6 +344,9 @@ typedef PLI_UINT32 *vpiHandle;
#define vpiSupply0 11 /* supply-0 net */
#define vpiNone 12 /* no default net type (1364-2001) */
#define vpiUwire 13 /* unresolved wire net (1364-2005) */
#define vpiNettypeNet 14 /* user-defined nettype net */
#define vpiNettypeNetSelect 15 /* user-defined nettype net subelement */
#define vpiInterconnect 16 /* interconnect net */
#define vpiExplicitScalared 23 /* explicitly scalared (Boolean) */
#define vpiExplicitVectored 24 /* explicitly vectored (Boolean) */
@@ -573,6 +576,9 @@ typedef PLI_UINT32 *vpiHandle;
/******************************* time structure *******************************/
#ifndef VPI_TIME /* added in 1800-2023 */
#define VPI_TIME
typedef struct t_vpi_time
{
PLI_INT32 type; /* [vpiScaledRealTime, vpiSimTime,
@@ -587,6 +593,8 @@ typedef struct t_vpi_time
#define vpiSimTime 2
#define vpiSuppressTime 3
#endif
/****************************** delay structures ******************************/
typedef struct t_vpi_delay
@@ -741,9 +749,8 @@ typedef struct t_vpi_arrayvalue
typedef struct t_vpi_systf_data
{
PLI_INT32 type; /* vpiSysTask, vpiSysFunc */
PLI_INT32 sysfunctype; /* vpiSysTask, vpi[Int,Real,Time,Sized,
SizedSigned]Func */
PLI_BYTE8 *tfname; /* first character must be '$' */
PLI_INT32 sysfunctype; /* vpi[Int,Real,Time,Sized,SizedSigned]Func */
PLI_BYTE8 *tfname; /* first character has to be '$' */
PLI_INT32 (*calltf)(PLI_BYTE8 *);
PLI_INT32 (*compiletf)(PLI_BYTE8 *);
PLI_INT32 (*sizetf)(PLI_BYTE8 *); /* for sized function callbacks only */
+1 -1
View File
@@ -434,7 +434,7 @@ class CallAnnotationsValidator:
self.process_translation_unit(translation_unit)
self._main_source_file = ""
self._defines = {}
else:
elif len(errors) != 0:
print(f"%Error: parsing failed: {filename}", file=sys.stderr)
for error in errors:
print(f" {error}", file=sys.stderr)
+20
View File
@@ -20,6 +20,26 @@ document pnt
Verilator: Print AstNode NODEP's tree
end
# Source python-based gdb config with jshow/jdiff definitions
# (Stored in separate file, so it can be highlighted/linted/formatted as Python)
python
import os
if "VERILATOR_ROOT" in os.environ:
gdbinit_py = os.environ["VERILATOR_ROOT"] + "/src/.gdbinit.py"
gdb.execute("source" + gdbinit_py)
end
define jstash
call (char*) &(AstNode::dumpTreeJsonGdb($arg0)[0])
end
document jstash
Verilator: Perform a JSON dump of the given AST node and save it in value history (e.g. $1) for later
inspection using jtree. The node can be a pointer identifier or an address literal.
end
alias -a js=jstash
alias -a jt=jtree
define dtf
call AstNode::dumpTreeFileGdb($arg0, 0)
end
+80
View File
@@ -0,0 +1,80 @@
# pylint: disable=line-too-long,invalid-name,multiple-statements,missing-function-docstring,missing-class-docstring,missing-module-docstring,no-else-return,too-few-public-methods,unused-argument
# DESCRIPTION: Verilator: GDB startup file with useful define
#
# Copyright 2023 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify the Verilator internals 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
######################################################################
import tempfile
import gdb # pylint: disable=import-error
def _vltgdb_get_dump(node):
gdb.execute(f'set $_gdb_dump_json_str = AstNode::dumpTreeJsonGdb({node})')
dump = gdb.execute('printf "%s", $_gdb_dump_json_str', to_string=True)
gdb.execute('call free($_gdb_dump_json_str)')
return dump
def _vltgdb_tmpfile():
return tempfile.NamedTemporaryFile(mode="wt") # write, text mode
def _vltgdb_fwrite(file, s):
"""Write to file and flush buffer before passing the file to astsee"""
file.write(s)
file.flush()
class AstseeCmd(gdb.Command):
"""Verilator: Pretty print or diff nodes using `astsee`. A node can be:
* an pointer identifier,
* an address literal,
* a GDB value (like `$1`) that stores a dump previously done by the `jstash` command.
Apart from not taking input from a file, it works exactly like `verilator_astsee`:
* passing one node gives you a pretty print,
* passing two nodes gives you a diff,
* for more options see `astsee` readme/help.
For examples see internals.rst
"""
def __init__(self):
super().__init__("jtree", gdb.COMMAND_USER, gdb.COMPLETE_EXPRESSION)
def _null_check(self, old, new):
err = ""
if old == "<nullptr>\n": err += "old == <nullptr>\n"
if new == "<nullptr>\n": err += "new == <nullptr>"
if err: raise gdb.GdbError(err.strip("\n"))
def invoke(self, arg_str, from_tty):
from astsee import verilator_cli as astsee # pylint: disable=import-error,import-outside-toplevel
# we import it here so "no module X" error would occur on typing `jtree` rather than on every gdb start
# We hack `astsee_verilator`'s arg parser to find arguments with nodes
# After finding them, we replace them with proper files
astsee_args = astsee.parser.parse_args(gdb.string_to_argv(arg_str))
with _vltgdb_tmpfile() as oldfile, _vltgdb_tmpfile(
) as newfile, _vltgdb_tmpfile() as metafile:
if astsee_args.file:
_vltgdb_fwrite(oldfile, _vltgdb_get_dump(astsee_args.file))
astsee_args.file = oldfile.name
if astsee_args.newfile:
_vltgdb_fwrite(newfile, _vltgdb_get_dump(astsee_args.newfile))
astsee_args.newfile = newfile.name
if astsee_args.meta is None:
# pass
gdb.execute(
f'call AstNode::dumpJsonMetaFileGdb("{metafile.name}")')
astsee_args.meta = metafile.name
try:
astsee.main(astsee_args)
except SystemExit as e: # astsee prints nice errmsgs on exit(), so rethrow it as GdbError to suppress cryptic python trace
raise gdb.GdbError("astsee exited with non-zero code") from e
AstseeCmd()
+14 -3
View File
@@ -62,8 +62,10 @@ set(HEADERS
V3DepthBlock.h
V3Descope.h
V3Dfg.h
V3DfgCache.h
V3DfgOptimizer.h
V3DfgPasses.h
V3DfgPatternStats.h
V3DfgPeephole.h
V3DfgVertices.h
V3DupFinder.h
@@ -77,6 +79,7 @@ set(HEADERS
V3EmitV.h
V3EmitXml.h
V3Error.h
V3ExecGraph.h
V3Expand.h
V3File.h
V3FileLine.h
@@ -119,6 +122,7 @@ set(HEADERS
V3OptionParser.h
V3Options.h
V3Order.h
V3OrderInternal.h
V3OrderGraph.h
V3OrderMoveGraph.h
V3Os.h
@@ -127,10 +131,9 @@ set(HEADERS
V3Parse.h
V3ParseImp.h
V3ParseSym.h
V3Partition.h
V3PartitionGraph.h
V3PchAstMT.h
V3PchAstNoMT.h
V3PreExpr.h
V3PreLex.h
V3PreProc.h
V3PreShell.h
@@ -212,11 +215,13 @@ set(COMMON_SOURCES
V3Descope.cpp
V3Dfg.cpp
V3DfgAstToDfg.cpp
V3DfgCache.cpp
V3DfgDecomposition.cpp
V3DfgDfgToAst.cpp
V3DfgOptimizer.cpp
V3DfgPasses.cpp
V3DfgPeephole.cpp
V3DfgRegularize.cpp
V3DupFinder.cpp
V3Timing.cpp
V3EmitCBase.cpp
@@ -234,6 +239,7 @@ set(COMMON_SOURCES
V3EmitV.cpp
V3EmitXml.cpp
V3Error.cpp
V3ExecGraph.cpp
V3Expand.cpp
V3File.cpp
V3FileLine.cpp
@@ -270,9 +276,13 @@ set(COMMON_SOURCES
V3OptionParser.cpp
V3Options.cpp
V3Order.cpp
V3OrderGraphBuilder.cpp
V3OrderMoveGraph.cpp
V3OrderParallel.cpp
V3OrderProcessDomains.cpp
V3OrderSerial.cpp
V3Os.cpp
V3Param.cpp
V3Partition.cpp
V3PreShell.cpp
V3Premit.cpp
V3ProtectLib.cpp
@@ -470,6 +480,7 @@ set_target_properties(${verilator} PROPERTIES
MSVC_RUNTIME_LIBRARY MultiThreaded$<IF:$<CONFIG:Release>,,DebugDLL>
#JOB_POOL_LINK one_job # Linking takes lots of resources
INTERPROCEDURAL_OPTIMIZATION_RELEASE $<IF:MINGW,FALSE,TRUE>
INCLUDE_DIRECTORIES ${FLEX_INCLUDE_DIR}
)
add_dependencies(${verilator}
+16 -1
View File
@@ -56,6 +56,7 @@ pkgdatadir = @pkgdatadir@
# Compile options
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
CFG_WITH_DEFENV = @CFG_WITH_DEFENV@
CFG_WITH_SOLVER = @CFG_WITH_SOLVER@
CPPFLAGS += @CPPFLAGS@
CFLAGS += @CFLAGS@
CXXFLAGS += @CXXFLAGS@
@@ -125,6 +126,13 @@ ifeq ($(CFG_WITH_DEFENV),yes)
else
CPPFLAGS += -DDEFENV_VERILATOR_ROOT=\"$(VERILATOR_ROOT)\"
endif
ifneq ($(CFG_WITH_SOLVER),no)
CPPFLAGS += -DDEFENV_VERILATOR_SOLVER='"$(CFG_WITH_SOLVER)"'
else
ifneq ($(VERILATOR_SOLVER),)
CPPFLAGS += -DDEFENV_VERILATOR_SOLVER='"$(VERILATOR_SOLVER)"'
endif
endif
endif
HEADERS = $(wildcard V*.h v*.h)
@@ -228,11 +236,13 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Descope.o \
V3Dfg.o \
V3DfgAstToDfg.o \
V3DfgCache.o \
V3DfgDecomposition.o \
V3DfgDfgToAst.o \
V3DfgOptimizer.o \
V3DfgPasses.o \
V3DfgPeephole.o \
V3DfgRegularize.o \
V3DupFinder.o \
V3EmitCMain.o \
V3EmitCMake.o \
@@ -240,6 +250,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3EmitCSyms.o \
V3EmitMk.o \
V3EmitXml.o \
V3ExecGraph.o \
V3Expand.o \
V3Force.o \
V3Fork.o \
@@ -263,8 +274,12 @@ RAW_OBJS_PCH_ASTNOMT = \
V3MergeCond.o \
V3Name.o \
V3Order.o \
V3OrderGraphBuilder.o \
V3OrderMoveGraph.o \
V3OrderParallel.o \
V3OrderProcessDomains.o \
V3OrderSerial.o \
V3Param.o \
V3Partition.o \
V3Premit.o \
V3ProtectLib.o \
V3Randomize.o \
+5 -6
View File
@@ -97,8 +97,8 @@ protected:
result = vertexp->user();
break;
case LatchDetectGraphVertex::VT_BLOCK: // (OR of potentially many siblings)
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
if (latchCheckInternal(castVertexp(edgep->top()))) {
for (V3GraphEdge& edge : vertexp->outEdges()) {
if (latchCheckInternal(castVertexp(edge.top()))) {
result = true;
break;
}
@@ -106,9 +106,8 @@ protected:
break;
case LatchDetectGraphVertex::VT_BRANCH: // (AND of both sibling)
// A BRANCH vertex always has exactly 2 siblings
LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outBeginp()->top());
LatchDetectGraphVertex* const elsp
= castVertexp(vertexp->outBeginp()->outNextp()->top());
LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outEdges().frontp()->top());
LatchDetectGraphVertex* const elsp = castVertexp(vertexp->outEdges().backp()->top());
result = latchCheckInternal(ifp) && latchCheckInternal(elsp);
break;
}
@@ -170,7 +169,7 @@ public:
for (const auto& vrp : m_outputs) {
LatchDetectGraphVertex* const vertp = castVertexp(vrp->varp()->user1p());
vertp->user(true); // Identify the output vertex we are checking paths _to_
if (!latchCheckInternal(castVertexp(verticesBeginp()))) latch_detected = true;
if (!latchCheckInternal(castVertexp(vertices().frontp()))) latch_detected = true;
if (latch_detected && !latch_expected) {
nodep->v3warn(
LATCH,
+17 -2
View File
@@ -68,7 +68,7 @@ class ActiveTopVisitor final : public VNVisitor {
UINFO(4, " ACTIVE " << nodep << endl);
// Remove duplicate clocks and such; sensesp() may change!
V3Const::constifyExpensiveEdit(nodep);
AstSenTree* const sensesp = nodep->sensesp();
AstSenTree* sensesp = nodep->sensesp();
UASSERT_OBJ(sensesp, nodep, "nullptr");
if (sensesp->sensesp() && sensesp->sensesp()->isNever()) {
// Never executing. Kill it.
@@ -77,6 +77,21 @@ class ActiveTopVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
// Delete empty procedures
for (AstNode *stmtp = nodep->stmtsp(), *nextp; stmtp; stmtp = nextp) {
nextp = stmtp->nextp();
if (AstNodeProcedure* const procp = VN_CAST(stmtp, NodeProcedure)) {
if (!procp->stmtsp()) VL_DO_DANGLING(pushDeletep(procp->unlinkFrBack()), stmtp);
}
}
// Delete empty actives
if (!nodep->stmtsp()) {
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
// Move the SENTREE for each active up to the global level.
// This way we'll easily see what clock domains are identical
AstSenTree* const wantp = m_finder.getSenTree(sensesp);
@@ -91,7 +106,7 @@ class ActiveTopVisitor final : public VNVisitor {
// There may be other references to same sense tree,
// we'll be removing all references when we get to them,
// but don't dangle our pointer yet!
pushDeletep(sensesp);
VL_DO_DANGLING(pushDeletep(sensesp), sensesp);
}
nodep->sensesp(wantp);
}
+50 -2
View File
@@ -170,7 +170,7 @@ class AssertVisitor final : public VNVisitor {
if (m_procedurep) {
// To support this need queue of asserts to activate
nodep->v3error("Unsupported: Procedural concurrent assertion with"
" clocking event inside always (IEEE 1800-2017 16.14.6)");
" clocking event inside always (IEEE 1800-2023 16.14.6)");
}
}
//
@@ -287,7 +287,7 @@ class AssertVisitor final : public VNVisitor {
checkifp->isBoundsCheck(true); // To avoid LATCH warning
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
nodep->replaceWith(checkifp);
pushDeletep(nodep);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else {
iterateChildren(nodep);
}
@@ -518,6 +518,54 @@ class AssertVisitor final : public VNVisitor {
iterateChildren(nodep);
newPslAssertion(nodep, nodep->failsp());
}
void visit(AstAssertCtl* nodep) override {
if (VN_IS(m_modp, Class) || VN_IS(m_modp, Iface)) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: assertcontrols in classes or interfaces");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
iterateChildren(nodep);
if (const AstConst* const constp = VN_CAST(nodep->controlTypep(), Const)) {
nodep->ctlType(constp->toSInt());
} else if (nodep->ctlType() == VAssertCtlType::_TO_BE_EVALUATED) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: non-const assert control type expression");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
switch (nodep->ctlType()) {
case VAssertCtlType::ON:
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});
break;
}
case VAssertCtlType::LOCK:
case VAssertCtlType::UNLOCK:
case VAssertCtlType::PASS_ON:
case VAssertCtlType::PASS_OFF:
case VAssertCtlType::FAIL_ON:
case VAssertCtlType::FAIL_OFF:
case VAssertCtlType::NONVACUOUS_ON:
case VAssertCtlType::VACUOUS_OFF: {
nodep->unlinkFrBack();
nodep->v3warn(E_UNSUPPORTED, "Unsupported assertcontrol control_type");
break;
}
default: {
nodep->unlinkFrBack();
nodep->v3warn(EC_ERROR, "Bad assertcontrol control_type (IEEE 1800-2023 Table 20-5)");
}
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstAssertIntrinsic* nodep) override {
iterateChildren(nodep);
newPslAssertion(nodep, nodep->failsp());
+28 -17
View File
@@ -115,8 +115,9 @@ private:
AstArg* const argp = tconnect.second;
AstNode* const pinp = argp->exprp()->unlinkFrBack();
replaceVarRefsWithExprRecurse(propExprp, portp, pinp);
VL_DO_DANGLING(pushDeletep(pinp), pinp);
}
// Handle case with 2 disable iff statement (IEEE 1800-2017 16.12.1)
// Handle case with 2 disable iff statement (IEEE 1800-2023 16.12.1)
if (nodep->disablep() && propExprp->disablep()) {
nodep->v3error("disable iff expression before property call and in its "
"body is not legal");
@@ -157,26 +158,31 @@ private:
iterateChildren(nodep);
}
void visit(AstClockingItem* const nodep) override {
// Get a ref to the sampled/driven variable
AstVar* const varp = nodep->varp();
if (!varp) {
// Unused item
pushDeletep(nodep->unlinkFrBack());
return;
}
FileLine* const flp = nodep->fileline();
V3Const::constifyEdit(nodep->skewp());
if (!VN_IS(nodep->skewp(), Const)) {
nodep->skewp()->v3error("Skew must be constant (IEEE 1800-2017 14.4)");
nodep->skewp()->v3error("Skew must be constant (IEEE 1800-2023 14.4)");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
AstConst* const skewp = VN_AS(nodep->skewp(), Const);
if (skewp->num().isNegative()) skewp->v3error("Skew cannot be negative");
AstNodeExpr* const exprp = nodep->exprp();
// Get a ref to the sampled/driven variable
AstVar* const varp = nodep->varp()->unlinkFrBack();
m_clockingp->addVarsp(varp);
m_clockingp->addVarsp(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};
if (skewp->isZero()) {
// Drive the var in Re-NBA (IEEE 1800-2017 14.16)
// 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});
} else if (skewp->fileline()->timingOn()) {
@@ -206,12 +212,12 @@ private:
AstVarRef* const refp = new AstVarRef{flp, varp, VAccess::WRITE};
refp->user1(true);
if (skewp->num().is1Step()) {
// Assign the sampled expression to the clockvar (IEEE 1800-2017 14.13)
// Assign the sampled expression to the clockvar (IEEE 1800-2023 14.13)
AstSampled* const sampledp = new AstSampled{flp, exprp->cloneTreePure(false)};
sampledp->dtypeFrom(exprp);
m_clockingp->addNextHere(new AstAssignW{flp, refp, sampledp});
} else if (skewp->isZero()) {
// #0 means the var has to be sampled in Observed (IEEE 1800-2017 14.13)
// #0 means the var has to be sampled in Observed (IEEE 1800-2023 14.13)
AstAssign* const assignp = new AstAssign{flp, refp, exprp->cloneTreePure(false)};
m_clockingp->addNextHere(new AstAlwaysObserved{
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, assignp});
@@ -223,6 +229,7 @@ private:
AstVar* const queueVarp = new AstVar{
flp, VVarType::MODULETEMP,
"__Vqueue__" + m_clockingp->name() + "__DOT__" + varp->name(), queueDtp};
queueVarp->lifetime(VLifetime::STATIC);
m_clockingp->addNextHere(queueVarp);
// Create a process like this:
// always queue.push(<sampled var>);
@@ -249,20 +256,20 @@ private:
} else {
nodep->v3fatal("Invalid direction");
}
pushDeletep(nodep->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstDelay* nodep) override {
// Only cycle delays are relevant in this stage; also only process once
if (!nodep->isCycleDelay()) {
if (m_inSynchDrive) {
nodep->v3error("Only cycle delays can be used in synchronous drives"
" (IEEE 1800-2017 14.16)");
" (IEEE 1800-2023 14.16)");
}
return;
}
if (m_inAssign && !m_inSynchDrive) {
nodep->v3error("Cycle delays not allowed as intra-assignment delays"
" (IEEE 1800-2017 14.11)");
" (IEEE 1800-2023 14.11)");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
@@ -273,12 +280,14 @@ private:
if (constp->isZero()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: ##0 delays");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
VL_DO_DANGLING(valuep->deleteTree(), valuep);
return;
}
if (!m_defaultClockingp) {
nodep->v3error("Usage of cycle delays requires default clocking"
" (IEEE 1800-2017 14.11)");
" (IEEE 1800-2023 14.11)");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
VL_DO_DANGLING(valuep->deleteTree(), valuep);
return;
}
AstEventControl* const controlp = new AstEventControl{
@@ -287,6 +296,8 @@ private:
const std::string delayName = m_cycleDlyNames.get(nodep);
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::AUTOMATIC);
cntVarp->funcLocal(true);
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, false, true};
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
beginp->addStmtsp(new AstWhile{
@@ -302,24 +313,24 @@ private:
void visit(AstSenTree* nodep) override {
if (m_inSynchDrive) {
nodep->v3error("Event controls cannot be used in "
"synchronous drives (IEEE 1800-2017 14.16)");
"synchronous drives (IEEE 1800-2023 14.16)");
}
}
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) {
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2017 14.3)");
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-2017 14.16)");
"to clockvars (IEEE 1800-2023 14.16)");
}
if (m_inAssign) m_inSynchDrive = true;
} else if (!nodep->user1() && itemp->direction() == VDirection::INPUT) {
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2017 14.3)");
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
}
}
}
@@ -463,7 +474,7 @@ private:
if (clockingp->isDefault()) {
if (m_defaultClockingp) {
clockingp->v3error("Only one default clocking block allowed per module"
" (IEEE 1800-2017 14.12)");
" (IEEE 1800-2023 14.12)");
}
m_defaultClockingp = clockingp;
}
+54 -21
View File
@@ -17,7 +17,6 @@
#include "V3PchAstMT.h"
#include "V3Broken.h"
#include "V3EmitV.h"
#include "V3File.h"
#include <iomanip>
@@ -1171,8 +1170,50 @@ void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE {
default: this->v3fatalSrc("Bad case"); break;
}
}
if (v3Global.opt.debugWidth() && v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH) {
if (const AstNodeExpr* const exprp = VN_CAST(this, NodeExpr)) {
const char* const whyp = exprp->widthMismatch();
if (whyp) {
auto dtypeStr = [](const AstNodeExpr* exprp) VL_MT_STABLE {
std::ostringstream ss;
exprp->dtypep()->dumpSmall(ss);
return ss.str();
};
if (const AstNodeUniop* const uniopp = VN_CAST(exprp, NodeUniop)) {
UASSERT_OBJ(!whyp, uniopp,
"widthMismatch detected " << whyp << "OUT:" << dtypeStr(uniopp)
<< " LHS:" << dtypeStr(uniopp->lhsp()));
} else if (const AstNodeBiop* const biopp = VN_CAST(exprp, NodeBiop)) {
UASSERT_OBJ(!whyp, biopp,
"widthMismatch detected " << whyp << "OUT:" << dtypeStr(biopp)
<< " LHS:" << dtypeStr(biopp->lhsp())
<< " RHS:" << dtypeStr(biopp->rhsp()));
} else {
UASSERT_OBJ(false, exprp,
"widthMismatch detected " << whyp << " in an unexpected type");
}
}
}
}
}
// cppcheck-suppress unusedFunction // Debug only
char* AstNode::dumpTreeJsonGdb(const AstNode* nodep) {
if (!nodep) return strdup("{\"addr\":\"NULL\"}\n");
std::stringstream nodepStream;
nodep->dumpTreeJson(nodepStream);
const std::string str = nodepStream.rdbuf()->str();
return strdup(str.c_str());
}
// cppcheck-suppress unusedFunction // Debug only
// identity func to allow for passing already done dumps to jtree
char* AstNode::dumpTreeJsonGdb(const char* str) { return strdup(str); }
// cppcheck-suppress unusedFunction // Debug only
// allow for passing pointer literals like 0x42.. without manual cast
char* AstNode::dumpTreeJsonGdb(intptr_t nodep) {
if (!nodep) return strdup("{\"addr\":\"NULL\"}\n");
return dumpTreeJsonGdb((const AstNode*)nodep);
}
// cppcheck-suppress unusedFunction // Debug only
void AstNode::dumpGdb(const AstNode* nodep) { // For GDB only // LCOV_EXCL_LINE
if (!nodep) {
@@ -1276,7 +1317,7 @@ void AstNode::dumpTreeAndNext(std::ostream& os, const string& indent, int maxDep
}
}
void AstNode::dumpTreeFile(const string& filename, bool doDump, bool doCheck) {
void AstNode::dumpTreeFile(const string& filename, bool doDump) {
// Not const function as calls checkTree
if (doDump) {
{ // Write log & close
@@ -1294,14 +1335,6 @@ void AstNode::dumpTreeFile(const string& filename, bool doDump, bool doCheck) {
}
}
}
if (doDump && v3Global.opt.debugEmitV()) V3EmitV::debugEmitV(filename + ".v");
if (doCheck && (v3Global.opt.debugCheck() || ::dumpTreeLevel())) {
// Error check
checkTree();
// Broken isn't part of check tree because it can munge iterp's
// set by other steps if it is called in the middle of other operations
if (AstNetlist* const netp = VN_CAST(this, Netlist)) V3Broken::brokenAll(netp);
}
}
static void drawChildren(std::ostream& os, const AstNode* thisp, const AstNode* childp,
@@ -1340,10 +1373,11 @@ void AstNode::dumpTreeJsonFile(const string& filename, bool doDump) {
*treejsonp << '\n';
}
void AstNode::dumpJsonMetaFileGdb(const char* filename) { dumpJsonMetaFile(filename); }
void AstNode::dumpJsonMetaFile(const string& filename) {
UINFO(2, "Dumping " << filename << endl);
const std::unique_ptr<std::ofstream> treejsonp{V3File::new_ofstream(filename)};
if (treejsonp->fail()) v3fatal("Can't write " << filename);
if (treejsonp->fail()) v3fatalStatic("Can't write " << filename);
*treejsonp << '{';
FileLine::fileNameNumMapDumpJson(*treejsonp);
*treejsonp << ',';
@@ -1530,8 +1564,7 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp
return VCastable::ENUM_IMPLICIT;
if (fromNumericable) return VCastable::ENUM_EXPLICIT;
} else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) {
if (VN_IS(fromConstp, Const) && VN_AS(fromConstp, Const)->num().isNull())
return VCastable::COMPATIBLE;
if (fromConstp->isNull()) return VCastable::COMPATIBLE;
} else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) {
const auto toClassp = VN_AS(toDtp, ClassRefDType)->classp();
const auto fromClassp = VN_AS(fromDtp, ClassRefDType)->classp();
@@ -1557,26 +1590,26 @@ VCastable AstNode::computeCastable(const AstNodeDType* toDtp, const AstNodeDType
return castable;
}
AstNodeDType* AstNode::getCommonClassTypep(AstNode* nodep1, AstNode* nodep2) {
// Return the class type that both nodep1 and nodep2 are castable to.
AstNodeDType* AstNode::getCommonClassTypep(AstNode* node1p, AstNode* node2p) {
// Return the class type that both node1p and node2p are castable to.
// If both are null, return the type of null constant.
// If one is a class and one is null, return AstClassRefDType that points to that class.
// If no common class type exists, return nullptr.
// First handle cases with null values and when one class is a super class of the other.
if (VN_IS(nodep1, Const)) std::swap(nodep1, nodep2);
if (VN_IS(node1p, Const)) std::swap(node1p, node2p);
{
const VCastable castable = computeCastable(nodep1->dtypep(), nodep2->dtypep(), nodep2);
const VCastable castable = computeCastable(node1p->dtypep(), node2p->dtypep(), node2p);
if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) {
return nodep1->dtypep();
return node1p->dtypep();
} else if (castable == VCastable::DYNAMIC_CLASS) {
return nodep2->dtypep();
return node2p->dtypep();
}
}
AstClassRefDType* classDtypep1 = VN_CAST(nodep1->dtypep(), ClassRefDType);
AstClassRefDType* classDtypep1 = VN_CAST(node1p->dtypep(), ClassRefDType);
while (classDtypep1) {
const VCastable castable = computeCastable(classDtypep1, nodep2->dtypep(), nodep2);
const VCastable castable = computeCastable(classDtypep1, node2p->dtypep(), node2p);
if (castable == VCastable::COMPATIBLE) return classDtypep1;
AstClassExtends* const extendsp = classDtypep1->classp()->extendsp();
classDtypep1 = extendsp ? VN_AS(extendsp->dtypep(), ClassRefDType) : nullptr;
+126 -33
View File
@@ -49,9 +49,6 @@ class VFlagLogicPacked {};
class VFlagBitPacked {};
class VFlagChildDType {}; // Used by parser.y to select constructor that sets childDType
// Used as key for another map, needs operator<, hence not an unordered_set
using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
//######################################################################
// For broken() function, return error string if have a match
@@ -245,6 +242,51 @@ inline std::ostream& operator<<(std::ostream& os, const VAccess& rhs) { return o
// ######################################################################
class VBaseOverride final {
bool m_extends : 1;
bool m_final : 1;
bool m_initial : 1;
public:
VBaseOverride()
: m_extends{false}
, m_final{false}
, m_initial{false} {}
class Extends {};
explicit VBaseOverride(Extends)
: m_extends{true}
, m_final{false}
, m_initial{false} {}
class Final {};
explicit VBaseOverride(Final)
: m_extends{false}
, m_final{true}
, m_initial{false} {}
class Initial {};
explicit VBaseOverride(Initial)
: m_extends{false}
, m_final{false}
, m_initial{true} {}
void combine(const VBaseOverride& other) {
m_extends |= other.m_extends;
m_final |= other.m_final;
m_initial |= other.m_initial;
}
bool isAny() const { return m_extends | m_final | m_initial; }
bool isExtends() const { return m_extends; }
bool isFinal() const { return m_final; }
bool isInitial() const { return m_initial; }
string ascii() const {
string out;
if (m_initial) out = VString::dot(out, " ", "initial");
if (m_extends) out = VString::dot(out, " ", "extends");
if (m_final) out = VString::dot(out, " ", "final");
return out;
}
};
// ######################################################################
class VSigning final {
public:
enum en : uint8_t {
@@ -323,7 +365,6 @@ public:
enum en : uint8_t {
// These must be in general -> most specific order, as we sort by it
// in V3Const::visit AstSenTree
ET_ILLEGAL,
// Involving a variable
ET_CHANGED, // Value changed
ET_BOTHEDGE, // POSEDGE | NEGEDGE (i.e.: 'edge' in Verilog)
@@ -343,8 +384,6 @@ public:
enum en m_e;
bool clockedStmt() const {
static const bool clocked[] = {
false, // ET_ILLEGAL
true, // ET_CHANGED
true, // ET_BOTHEDGE
true, // ET_POSEDGE
@@ -368,20 +407,19 @@ public:
case ET_BOTHEDGE: return ET_BOTHEDGE;
case ET_POSEDGE: return ET_NEGEDGE;
case ET_NEGEDGE: return ET_POSEDGE;
default: UASSERT_STATIC(0, "Inverting bad edgeType()");
default: UASSERT_STATIC(0, "Inverting bad edgeType()"); return ET_NEGEDGE;
}
return VEdgeType::ET_ILLEGAL;
}
const char* ascii() const {
static const char* const names[]
= {"%E-edge", "CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE",
"COMBO", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
= {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE",
"COMBO", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
return names[m_e];
}
const char* verilogKwd() const {
static const char* const names[]
= {"%E-edge", "[changed]", "edge", "posedge", "negedge", "[event]", "[true]",
"*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
= {"[changed]", "edge", "posedge", "negedge", "[event]", "[true]",
"*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
return names[m_e];
}
// Return true iff this and the other have mutually exclusive transitions
@@ -397,8 +435,6 @@ public:
}
return false;
}
VEdgeType()
: m_e{ET_ILLEGAL} {}
// cppcheck-suppress noExplicitConstructor
constexpr VEdgeType(en _e)
: m_e{_e} {}
@@ -523,6 +559,7 @@ public:
DYNAMIC_TRIGGER_SCHEDULER,
FORK_SYNC,
PROCESS_REFERENCE,
RANDOM_GENERATOR,
// Unsigned and two state; fundamental types
UINT32,
UINT64,
@@ -556,6 +593,7 @@ public:
"VlDynamicTriggerScheduler",
"VlFork",
"VlProcessRef",
"VlRandomizer",
"IData",
"QData",
"LOGIC_IMPLICIT",
@@ -563,20 +601,13 @@ public:
return names[m_e];
}
const char* dpiType() const {
static const char* const names[] = {"%E-unk", "svBit",
"char", "void*",
"char", "int",
"%E-integer", "svLogic",
"long long", "double",
"short", "%E-time",
"const char*", "%E-untyped",
"dpiScope", "const char*",
"%E-mtaskstate", "%E-triggervec",
"%E-dly-sched", "%E-trig-sched",
"%E-dyn-sched", "%E-fork",
"%E-proc-ref", "IData",
"QData", "%E-logic-implct",
" MAX"};
static const char* const names[]
= {"%E-unk", "svBit", "char", "void*", "char",
"int", "%E-integer", "svLogic", "long long", "double",
"short", "%E-time", "const char*", "%E-untyped", "dpiScope",
"const char*", "%E-mtaskstate", "%E-triggervec", "%E-dly-sched", "%E-trig-sched",
"%E-dyn-sched", "%E-fork", "%E-proc-ref", "%E-rand-gen", "IData",
"QData", "%E-logic-implct", " MAX"};
return names[m_e];
}
static void selfTest() {
@@ -616,6 +647,7 @@ public:
case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque
case FORK_SYNC: return 0; // opaque
case PROCESS_REFERENCE: return 0; // opaque
case RANDOM_GENERATOR: return 0; // opaque
case UINT32: return 32;
case UINT64: return 64;
default: return 0;
@@ -655,7 +687,8 @@ public:
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|| m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER
|| m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC
|| m_e == PROCESS_REFERENCE || m_e == DOUBLE || m_e == UNTYPED);
|| m_e == PROCESS_REFERENCE || m_e == RANDOM_GENERATOR || m_e == DOUBLE
|| m_e == UNTYPED);
}
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
bool isEvent() const { return m_e == EVENT; }
@@ -867,7 +900,7 @@ public:
TRIWIRE,
TRI0,
TRI1,
PORT, // Temp type used in parser only
PORT, // Used in parser and V3Fork to recognize ports
BLOCKTEMP,
MODULETEMP,
STMTTEMP,
@@ -1066,6 +1099,61 @@ constexpr bool operator==(VAlwaysKwd::en lhs, const VAlwaysKwd& rhs) { return lh
// ######################################################################
class VAssertCtlType final {
public:
// IEEE 1800-2023 Table 20-5
enum en : uint8_t {
_TO_BE_EVALUATED = 0,
LOCK = 1,
UNLOCK = 2,
ON = 3,
OFF = 4,
KILL = 5,
PASS_ON = 6,
PASS_OFF = 7,
FAIL_ON = 8,
FAIL_OFF = 9,
NONVACUOUS_ON = 10,
VACUOUS_OFF = 11
};
enum en m_e;
VAssertCtlType()
: m_e{_TO_BE_EVALUATED} {}
// cppcheck-suppress noExplicitConstructor
constexpr VAssertCtlType(en _e)
: m_e{_e} {}
explicit VAssertCtlType(int _e)
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
constexpr operator en() const { return m_e; }
const char* ascii() const {
// IEEE 1800-2023 20.11
static const char* const names[] = {"",
"",
"",
"$asserton",
"$assertoff",
"$assertkill",
"$assertpasson",
"$assertpassoff",
"$assertfailon",
"$assertfailoff",
"$assertnonvacuouson",
"$assertvacuousoff"};
return names[m_e];
}
};
constexpr bool operator==(const VAssertCtlType& lhs, const VAssertCtlType& rhs) {
return lhs.m_e == rhs.m_e;
}
constexpr bool operator==(const VAssertCtlType& lhs, VAssertCtlType::en rhs) {
return lhs.m_e == rhs;
}
constexpr bool operator==(VAssertCtlType::en lhs, const VAssertCtlType& rhs) {
return lhs == rhs.m_e;
}
// ######################################################################
class VCaseType final {
public:
enum en : uint8_t { CT_CASE, CT_CASEX, CT_CASEZ, CT_CASEINSIDE };
@@ -2002,7 +2090,7 @@ public:
// TODO stomp these width functions out, and call via dtypep() instead
inline int width() const VL_MT_STABLE;
inline int widthMin() const;
inline int widthMin() const VL_MT_STABLE;
int widthMinV() const {
return v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH ? widthMin() : width();
}
@@ -2088,6 +2176,7 @@ public:
// ACCESSORS for specific types
// Alas these can't be virtual or they break when passed a nullptr
inline bool isClassHandleValue() const;
inline bool isNull() const;
inline bool isZero() const;
inline bool isOne() const;
inline bool isNeqZero() const;
@@ -2166,6 +2255,9 @@ public:
string warnOther() const VL_REQUIRES(V3Error::s().m_mutex) { return fileline()->warnOther(); }
virtual void dump(std::ostream& str = std::cout) const;
static char* dumpTreeJsonGdb(const AstNode* nodep); // For GDB only, free()d by caller
static char* dumpTreeJsonGdb(const char* str); // For GDB only, free()d by caller
static char* dumpTreeJsonGdb(intptr_t nodep); // For GDB only, free()d by caller
static void dumpGdb(const AstNode* nodep); // For GDB only
void dumpGdbHeader() const;
@@ -2216,7 +2308,7 @@ public:
static void dumpTreeGdb(const AstNode* nodep); // For GDB only
void dumpTreeAndNext(std::ostream& os = std::cout, const string& indent = " ",
int maxDepth = 0) const;
void dumpTreeFile(const string& filename, bool doDump = true, bool doCheck = true);
void dumpTreeFile(const string& filename, bool doDump = true);
static void dumpTreeFileGdb(const AstNode* nodep, const char* filenamep = nullptr);
void dumpTreeDot(std::ostream& os = std::cout) const;
void dumpTreeDotFile(const string& filename, bool doDump = true);
@@ -2227,7 +2319,8 @@ public:
virtual void dumpTreeJsonOpGen(std::ostream& os, const string& indent) const {};
void dumpTreeJson(std::ostream& os, const string& indent = "") const;
void dumpTreeJsonFile(const string& filename, bool doDump = true);
void dumpJsonMetaFile(const string& filename);
static void dumpJsonMetaFileGdb(const char* filename);
static void dumpJsonMetaFile(const string& filename);
// Render node address for dumps. By default this is just the address
// printed as hex, but with --dump-tree-addrids we map addresses to short
+3 -2
View File
@@ -26,7 +26,7 @@
// Inline METHODS
int AstNode::width() const VL_MT_STABLE { return dtypep() ? dtypep()->width() : 0; }
int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
int AstNode::widthMin() const VL_MT_STABLE { return dtypep() ? dtypep()->widthMin() : 0; }
bool AstNode::width1() const { // V3Const uses to know it can optimize
return dtypep() && dtypep()->width() == 1;
}
@@ -47,8 +47,9 @@ bool AstNode::isSigned() const VL_MT_STABLE { return dtypep() && dtypep()->isSig
bool AstNode::isClassHandleValue() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNull())
|| VN_IS(dtypep(), ClassRefDType);
|| (dtypep() && VN_IS(dtypep()->skipRefp(), ClassRefDType));
}
bool AstNode::isNull() const { return VN_IS(this, Const) && VN_AS(this, Const)->num().isNull(); }
bool AstNode::isZero() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
}
+63 -22
View File
@@ -53,7 +53,7 @@ public:
// ACCESSORS
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
virtual void dumpSmall(std::ostream& str) const;
virtual void dumpSmall(std::ostream& str) const VL_MT_STABLE;
bool hasDType() const override { return true; }
/// Require VlUnpacked, instead of [] for POD elements.
/// A non-POD object is always compound, but some POD elements
@@ -89,8 +89,8 @@ public:
virtual AstNodeDType* subDTypep() const VL_MT_SAFE { return nullptr; }
virtual bool isFourstate() const;
// Ideally an IEEE $typename
virtual string prettyDTypeName() const { return prettyTypeName(); }
string prettyDTypeNameQ() const { return "'" + prettyDTypeName() + "'"; }
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
string prettyDTypeNameQ() const { return "'" + prettyDTypeName(false) + "'"; }
//
// Changing the width may confuse the data type resolution, so must clear
// TypeTable cache after use.
@@ -126,13 +126,13 @@ public:
const char* charIQWN() const {
return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I");
}
string cType(const string& name, bool forFunc, bool isRef) const;
string cType(const string& name, bool forFunc, bool isRef, bool packed = false) const;
// Represents a C++ LiteralType? (can be constexpr)
bool isLiteralType() const VL_MT_STABLE;
private:
class CTypeRecursed;
CTypeRecursed cTypeRecurse(bool compound) const;
CTypeRecursed cTypeRecurse(bool compound, bool packed) const;
};
class AstNodeArrayDType VL_NOT_FINAL : public AstNodeDType {
// Array data type, ie "some_dtype var_name [2:0]"
@@ -215,6 +215,7 @@ public:
int uniqueNum() const { return m_uniqueNum; }
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string prettyDTypeName(bool) const override;
bool isCompound() const override { return !packed(); }
// For basicp() we reuse the size to indicate a "fake" basic type of same size
AstBasicDType* basicp() const override {
@@ -313,7 +314,7 @@ public:
return asamep->subDTypep()
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
}
string prettyDTypeName() const override;
string prettyDTypeName(bool full) const override;
void dumpSmall(std::ostream& str) const override;
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* getChild2DTypep() const override { return keyChildDTypep(); }
@@ -387,7 +388,7 @@ public:
return type() == samep->type() && same(samep);
}
string name() const override VL_MT_STABLE { return m.m_keyword.ascii(); }
string prettyDTypeName() const override;
string prettyDTypeName(bool full) const override;
const char* broken() const override {
BROKEN_RTN(dtypep() != this);
return nullptr;
@@ -428,6 +429,9 @@ public:
bool isDynamicTriggerScheduler() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::DYNAMIC_TRIGGER_SCHEDULER;
}
bool isRandomGenerator() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::RANDOM_GENERATOR;
}
bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); }
bool isString() const VL_MT_SAFE { return keyword().isString(); }
bool isZeroInit() const { return keyword().isZeroInit(); }
@@ -469,7 +473,7 @@ public:
this->elementsp(elementsp);
}
ASTGEN_MEMBERS_AstBracketArrayDType;
bool similarDType(const AstNodeDType* samep) const override { V3ERROR_NA_RETURN(false); }
bool similarDType(const AstNodeDType* samep) const override { return same(samep); }
AstNodeDType* subDTypep() const override VL_MT_STABLE { return childDTypep(); }
// METHODS
// Will be removed in V3Width, which relies on this
@@ -501,7 +505,7 @@ public:
}
bool similarDType(const AstNodeDType* samep) const override { return same(samep); }
string name() const override VL_MT_STABLE { return m_name; }
string prettyDTypeName() const override { return m_name; }
string prettyDTypeName(bool) const override { return m_name; }
// METHODS
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
@@ -510,11 +514,17 @@ public:
int widthAlignBytes() const override { return 8; } // Assume
int widthTotalBytes() const override { return 8; } // Assume
bool isCompound() const override { return true; }
static string typeToHold(uint64_t maxItem) {
return (maxItem < (1ULL << 8)) ? "CData"
: (maxItem < (1ULL << 16)) ? "SData"
: (maxItem < (1ULL << 32)) ? "IData"
: "QData";
static string typeToHold(int width) {
if (width <= 8)
return "CData";
else if (width <= 16)
return "SData";
else if (width <= VL_IDATASIZE)
return "IData";
else if (width <= VL_QUADSIZE)
return "QData";
else
return "VlWide<" + std::to_string(VL_WORDS_I(width)) + ">";
}
};
class AstClassRefDType final : public AstNodeDType {
@@ -542,6 +552,7 @@ public:
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
string name() const override VL_MT_STABLE;
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
@@ -710,7 +721,7 @@ public:
return asamep->subDTypep()
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
}
string prettyDTypeName() const override;
string prettyDTypeName(bool full) const override;
void dumpSmall(std::ostream& str) const override;
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* subDTypep() const override VL_MT_STABLE {
@@ -800,6 +811,7 @@ public:
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;
string prettyDTypeName(bool full) const override;
// METHODS
AstBasicDType* basicp() const override VL_MT_STABLE { return subDTypep()->basicp(); }
AstNodeDType* skipRefp() const override VL_MT_STABLE { return subDTypep()->skipRefp(); }
@@ -808,9 +820,9 @@ public:
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
int itemCount() const {
size_t itemCount() const {
size_t count = 0;
for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) count++;
for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) ++count;
return count;
}
bool isCompound() const override { return false; }
@@ -953,6 +965,30 @@ public:
return false;
}
};
class AstNBACommitQueueDType final : public AstNodeDType {
// @astgen ptr := m_subDTypep : AstNodeDType // Type of the corresponding variable
const bool m_partial; // Partial element update required
public:
AstNBACommitQueueDType(FileLine* fl, AstNodeDType* subDTypep, bool partial)
: ASTGEN_SUPER_NBACommitQueueDType(fl)
, m_partial{partial}
, m_subDTypep{subDTypep} {
dtypep(this);
}
ASTGEN_MEMBERS_AstNBACommitQueueDType;
AstNodeDType* subDTypep() const override { return m_subDTypep; }
bool partial() const { return m_partial; }
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
AstBasicDType* basicp() const override { return nullptr; }
AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
int widthAlignBytes() const override { return 1; }
int widthTotalBytes() const override { return 24; }
bool isCompound() const override { return true; }
};
class AstParamTypeDType final : public AstNodeDType {
// Parents: MODULE
// A parameter type statement; much like a var or typedef
@@ -1060,7 +1096,7 @@ public:
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
}
void dumpSmall(std::ostream& str) const override;
string prettyDTypeName() const override;
string prettyDTypeName(bool full) const override;
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
AstNodeDType* subDTypep() const override VL_MT_STABLE {
return m_refDTypep ? m_refDTypep : childDTypep();
@@ -1123,8 +1159,8 @@ public:
void dumpJson(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
string prettyDTypeName() const override {
return subDTypep() ? prettyName(subDTypep()->name()) : prettyName();
string prettyDTypeName(bool full) const override {
return subDTypep() ? prettyName(subDTypep()->prettyDTypeName(full)) : prettyName();
}
AstBasicDType* basicp() const override VL_MT_STABLE {
return subDTypep() ? subDTypep()->basicp() : nullptr;
@@ -1256,6 +1292,11 @@ public:
refDTypep(nullptr);
dtypep(nullptr); // V3Width will resolve
}
AstUnsizedArrayDType(FileLine* fl, AstNodeDType* dtp)
: ASTGEN_SUPER_UnsizedArrayDType(fl) {
refDTypep(dtp);
dtypep(nullptr); // V3Width will resolve
}
ASTGEN_MEMBERS_AstUnsizedArrayDType;
const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep()) || (!m_refDTypep && childDTypep())));
@@ -1351,7 +1392,7 @@ public:
inline AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep);
inline AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep);
ASTGEN_MEMBERS_AstPackArrayDType;
string prettyDTypeName() const override;
string prettyDTypeName(bool full) const override;
bool isCompound() const override { return false; }
};
class AstUnpackArrayDType final : public AstNodeArrayDType {
@@ -1378,7 +1419,7 @@ public:
widthFromSub(subDTypep());
}
ASTGEN_MEMBERS_AstUnpackArrayDType;
string prettyDTypeName() const override;
string prettyDTypeName(bool full) const override;
bool same(const AstNode* samep) const override {
const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType);
return m_isCompound == sp->m_isCompound;
+82 -5
View File
@@ -56,6 +56,7 @@ public:
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
// For documentation on emitC format see EmitCFunc::emitOpName
virtual string emitC() = 0;
virtual string emitSMT() const { V3ERROR_NA_RETURN(""); };
virtual string emitSimpleOperator() { return ""; } // "" means not ok to use
virtual bool emitCheckMaxWords() { return false; } // Check VL_MULS_MAX_WORDS
virtual bool cleanOut() const = 0; // True if output has extra upper bits zero
@@ -65,6 +66,9 @@ public:
// Wrap This expression into an AstStmtExpr to denote it occurs in statement position
inline AstStmtExpr* makeStmt();
// Returns an error message if widthMin() is not correct otherwise returns nullptr like
// broken()
virtual const char* widthMismatch() const VL_MT_STABLE { return nullptr; }
};
class AstNodeBiop VL_NOT_FINAL : public AstNodeExpr {
// Binary expression
@@ -1109,6 +1113,34 @@ public:
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
};
class AstCvtPackedToUnpackArray final : public AstNodeExpr {
// Cast from packed array to dynamic queue data type
// @astgen op1 := fromp : AstNodeExpr
public:
AstCvtPackedToUnpackArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp)
: ASTGEN_SUPER_CvtPackedToUnpackArray(fl) {
this->fromp(fromp);
dtypeFrom(dtp);
}
ASTGEN_MEMBERS_AstCvtPackedToUnpackArray;
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
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; }
};
class AstDot final : public AstNodeExpr {
// A dot separating paths in an AstVarXRef, AstFuncRef or AstTaskRef
// These are eliminated in the link stage
@@ -2228,7 +2260,7 @@ class AstWith final : public AstNodeExpr {
// @astgen op3 := exprp : List[AstNode]
public:
AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
AstNodeExpr* exprp)
AstNode* exprp)
: ASTGEN_SUPER_With(fl) {
this->indexArgRefp(indexArgRefp);
this->valueArgRefp(valueArgRefp);
@@ -2251,13 +2283,13 @@ class AstWithParse final : public AstNodeExpr {
// Replaced with AstWith
// Parents: expr|stmt
// Children: funcref, expr
// @astgen op1 := funcrefp : AstNode
// @astgen op2 := exprp : Optional[AstNodeExpr]
// @astgen op1 := funcrefp : AstNodeExpr
// @astgen op2 := exprsp : List[AstNode]
public:
AstWithParse(FileLine* fl, AstNode* funcrefp, AstNodeExpr* exprp)
AstWithParse(FileLine* fl, AstNodeExpr* funcrefp, AstNode* exprsp)
: ASTGEN_SUPER_WithParse(fl) {
this->funcrefp(funcrefp);
this->exprp(exprp);
this->addExprsp(exprsp);
}
ASTGEN_MEMBERS_AstWithParse;
bool same(const AstNode* /*samep*/) const override { return true; }
@@ -2370,6 +2402,7 @@ public:
out.opConcat(lhs, rhs);
}
string emitC() override { return "VL_CONCAT_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(concat %l %r)"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -2416,6 +2449,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f/ %r)"; }
string emitC() override { return "VL_DIV_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvudiv %l %r)"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -2462,6 +2496,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f/ %r)"; }
string emitC() override { return "VL_DIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvsdiv %l %r)"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -2488,6 +2523,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f==? %r)"; }
string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(= %l %r)"; }
string emitSimpleOperator() override { return "=="; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2610,6 +2646,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f> %r)"; }
string emitC() override { return "VL_GT_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvugt %l %r)"; }
string emitSimpleOperator() override { return ">"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2680,6 +2717,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f> %r)"; }
string emitC() override { return "VL_GTS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvsgt %l %r)"; }
string emitSimpleOperator() override { return ""; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2703,6 +2741,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f>= %r)"; }
string emitC() override { return "VL_GTE_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvuge %l %r)"; }
string emitSimpleOperator() override { return ">="; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2773,6 +2812,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f>= %r)"; }
string emitC() override { return "VL_GTES_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvsge %l %r)"; }
string emitSimpleOperator() override { return ""; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2796,6 +2836,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f&& %r)"; }
string emitC() override { return "VL_LOGAND_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(and %l %r)"; }
string emitSimpleOperator() override { return "&&"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2819,6 +2860,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f-> %r)"; }
string emitC() override { return "VL_LOGIF_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(=> %l %r)"; }
string emitSimpleOperator() override { return "->"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2842,6 +2884,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f|| %r)"; }
string emitC() override { return "VL_LOGOR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(or %l %r)"; }
string emitSimpleOperator() override { return "||"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2865,6 +2908,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f< %r)"; }
string emitC() override { return "VL_LT_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvult %l %r)"; }
string emitSimpleOperator() override { return "<"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2935,6 +2979,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f< %r)"; }
string emitC() override { return "VL_LTS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvslt %l %r)"; }
string emitSimpleOperator() override { return ""; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -2958,6 +3003,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f<= %r)"; }
string emitC() override { return "VL_LTE_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvule %l %r)"; }
string emitSimpleOperator() override { return "<="; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -3028,6 +3074,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f<= %r)"; }
string emitC() override { return "VL_LTES_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvsle %l %r)"; }
string emitSimpleOperator() override { return ""; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -3051,6 +3098,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f%% %r)"; }
string emitC() override { return "VL_MODDIV_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvurem %l %r)"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -3073,6 +3121,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f%% %r)"; }
string emitC() override { return "VL_MODDIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvsmod %l %r)"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -3246,6 +3295,9 @@ public:
}
string emitVerilog() override { return "%f{%r{%k%l}}"; }
string emitC() override { return "VL_REPLICATE_%nq%lq%rq(%lw, %P, %li, %ri)"; }
string emitSMT() const override {
return "((_ repeat " + cvtToStr(width() / lhsp()->width()) + ") %l)";
}
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -3294,6 +3346,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f<< %r)"; }
string emitC() override { return "VL_SHIFTL_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvshl %l %r)"; }
string emitSimpleOperator() override {
return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : "<<";
}
@@ -3342,6 +3395,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f>> %r)"; }
string emitC() override { return "VL_SHIFTR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvlshr %l %r)"; }
string emitSimpleOperator() override {
return (rhsp()->isWide() || rhsp()->isQuad()) ? "" : ">>";
}
@@ -3394,6 +3448,7 @@ public:
out.opShiftRS(lhs, rhs, lhsp()->widthMinV());
}
string emitVerilog() override { return "%k(%l %f>>> %r)"; }
string emitSMT() const override { return "(bvashr %l %r)"; }
string emitC() override { return "VL_SHIFTRS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSimpleOperator() override { return ""; }
bool cleanOut() const override { return false; }
@@ -3445,6 +3500,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f- %r)"; }
string emitC() override { return "VL_SUB_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvsub %l %r)"; }
string emitSimpleOperator() override { return "-"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
@@ -3521,6 +3577,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f== %r)"; }
string emitC() override { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(= %l %r)"; }
string emitSimpleOperator() override { return "=="; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -3637,6 +3694,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f<-> %r)"; }
string emitC() override { return "VL_LOGEQ_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvxnor %l %r)"; }
string emitSimpleOperator() override { return "<->"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -3661,6 +3719,7 @@ public:
string emitVerilog() override { return "%k(%l %f!= %r)"; }
string emitC() override { return "VL_NEQ_%lq(%lW, %P, %li, %ri)"; }
string emitSimpleOperator() override { return "!="; }
string emitSMT() const override { return "(not (= %l %r))"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
@@ -3778,6 +3837,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f+ %r)"; }
string emitC() override { return "VL_ADD_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvadd %l %r)"; }
string emitSimpleOperator() override { return "+"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
@@ -3824,12 +3884,14 @@ public:
}
string emitVerilog() override { return "%k(%l %f& %r)"; }
string emitC() override { return "VL_AND_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvand %l %r)"; }
string emitSimpleOperator() override { return "&"; }
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
bool cleanLhs() const override { return false; }
bool cleanRhs() const override { return false; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
const char* widthMismatch() const override VL_MT_STABLE;
};
class AstMul final : public AstNodeBiComAsv {
public:
@@ -3846,6 +3908,7 @@ public:
}
string emitVerilog() override { return "%k(%l %f* %r)"; }
string emitC() override { return "VL_MUL_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvmul %l %r)"; }
string emitSimpleOperator() override { return "*"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
@@ -3918,12 +3981,14 @@ public:
}
string emitVerilog() override { return "%k(%l %f| %r)"; }
string emitC() override { return "VL_OR_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvor %l %r)"; }
string emitSimpleOperator() override { return "|"; }
bool cleanOut() const override { V3ERROR_NA_RETURN(false); }
bool cleanLhs() const override { return false; }
bool cleanRhs() const override { return false; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
const char* widthMismatch() const override VL_MT_STABLE;
};
class AstXor final : public AstNodeBiComAsv {
public:
@@ -3940,12 +4005,14 @@ public:
}
string emitVerilog() override { return "%k(%l %f^ %r)"; }
string emitC() override { return "VL_XOR_%lq(%lW, %P, %li, %ri)"; }
string emitSMT() const override { return "(bvxor %l %r)"; }
string emitSimpleOperator() override { return "^"; }
bool cleanOut() const override { return false; } // Lclean && Rclean
bool cleanLhs() const override { return false; }
bool cleanRhs() const override { return false; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
const char* widthMismatch() const override VL_MT_STABLE;
};
// === AstNodeDistBiop ===
@@ -4861,6 +4928,9 @@ public:
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opAssign(lhs); }
string emitVerilog() override { return "%l"; }
string emitC() override { return "VL_EXTEND_%nq%lq(%nw,%lw, %P, %li)"; }
string emitSMT() const override {
return "((_ zero_extend " + cvtToStr(width() - lhsp()->width()) + ") %l)";
}
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override {
@@ -4884,6 +4954,9 @@ public:
}
string emitVerilog() override { return "%l"; }
string emitC() override { return "VL_EXTENDS_%nq%lq(%nw,%lw, %P, %li)"; }
string emitSMT() const override {
return "((_ sign_extend " + cvtToStr(width() - lhsp()->width()) + ") %l)";
}
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return true; }
bool sizeMattersLhs() const override {
@@ -5016,6 +5089,7 @@ public:
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opLogNot(lhs); }
string emitVerilog() override { return "%f(! %l)"; }
string emitC() override { return "VL_LOGNOT_%nq%lq(%nw,%lw, %P, %li)"; }
string emitSMT() const override { return "(not %l)"; }
string emitSimpleOperator() override { return "!"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
@@ -5046,6 +5120,7 @@ public:
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNegate(lhs); }
string emitVerilog() override { return "%f(- %l)"; }
string emitC() override { return "VL_NEGATE_%lq(%lW, %P, %li)"; }
string emitSMT() const override { return "(bvneg %l)"; }
string emitSimpleOperator() override { return "-"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
@@ -5078,10 +5153,12 @@ public:
void numberOperate(V3Number& out, const V3Number& lhs) override { out.opNot(lhs); }
string emitVerilog() override { return "%f(~ %l)"; }
string emitC() override { return "VL_NOT_%lq(%lW, %P, %li)"; }
string emitSMT() const override { return "(bvnot %l)"; }
string emitSimpleOperator() override { return "~"; }
bool cleanOut() const override { return false; }
bool cleanLhs() const override { return false; }
bool sizeMattersLhs() const override { return true; }
const char* widthMismatch() const override VL_MT_STABLE;
};
class AstNullCheck final : public AstNodeUniop {
// Return LHS after checking that LHS is non-null
+78 -32
View File
@@ -76,7 +76,6 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
bool m_dpiContext : 1; // DPI import context
bool m_dpiOpenChild : 1; // DPI import open array child wrapper
bool m_dpiTask : 1; // DPI import task (vs. void function)
bool m_dpiTraceInit : 1; // DPI trace_init
bool m_isConstructor : 1; // Class constructor
bool m_isHideLocal : 1; // Verilog local
bool m_isHideProtected : 1; // Verilog protected
@@ -87,6 +86,7 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
bool m_underGenerate : 1; // Under generate (for warning)
bool m_virtual : 1; // Virtual method in class
bool m_needProcess : 1; // Needs access to VlProcess of the caller
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
VLifetime m_lifetime; // Default lifetime of local vars
VIsCached m_purity; // Pure state
@@ -105,7 +105,6 @@ protected:
, m_dpiContext{false}
, m_dpiOpenChild{false}
, m_dpiTask{false}
, m_dpiTraceInit{false}
, m_isConstructor{false}
, m_isHideLocal{false}
, m_isHideProtected{false}
@@ -161,8 +160,6 @@ public:
bool dpiOpenChild() const { return m_dpiOpenChild; }
void dpiTask(bool flag) { m_dpiTask = flag; }
bool dpiTask() const { return m_dpiTask; }
void dpiTraceInit(bool flag) { m_dpiTraceInit = flag; }
bool dpiTraceInit() const { return m_dpiTraceInit; }
void isConstructor(bool flag) { m_isConstructor = flag; }
bool isConstructor() const { return m_isConstructor; }
bool isHideLocal() const { return m_isHideLocal; }
@@ -183,6 +180,8 @@ public:
bool isVirtual() const { return m_virtual; }
void setNeedProcess() { m_needProcess = true; }
bool needProcess() const { return m_needProcess; }
void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; }
VBaseOverride baseOverride() const { return m_baseOverride; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
@@ -627,7 +626,6 @@ class AstCFunc final : public AstNode {
bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
bool m_dpiTraceInit : 1; // DPI trace_init
bool m_needProcess : 1; // Needs access to VlProcess of the caller
bool m_recursive : 1; // Recursive or part of recursion
public:
@@ -657,7 +655,6 @@ public:
m_dpiExportImpl = false;
m_dpiImportPrototype = false;
m_dpiImportWrapper = false;
m_dpiTraceInit = false;
m_recursive = false;
}
ASTGEN_MEMBERS_AstCFunc;
@@ -731,8 +728,6 @@ public:
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
void dpiTraceInit(bool flag) { m_dpiTraceInit = flag; }
bool dpiTraceInit() const { return m_dpiTraceInit; }
bool isCoroutine() const { return m_rtnType == "VlCoroutine"; }
void recursive(bool flag) { m_recursive = flag; }
bool recursive() const { return m_recursive; }
@@ -863,13 +858,42 @@ public:
void dumpJson(std::ostream& str) const override;
// ACCESSORS
string name() const override VL_MT_STABLE { return m_name; } // * = Cell name
bool maybePointedTo() const override { return true; }
string origModName() const { return m_origModName; } // * = modp()->origName() before inlining
void name(const string& name) override { m_name = name; }
void scopep(AstScope* scp) { m_scopep = scp; }
AstScope* scopep() const { return m_scopep; }
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
};
class AstCellInlineScope final : public AstNode {
// A particular scoped usage of a Cell Inline
// Parents: Scope
// Children: none
//
// @astgen ptr := m_scopep : Optional[AstScope] // Scope variable is underneath
// @astgen ptr := m_cellp : Optional[AstCellInline] // Cell ref
const string m_origModName; // Original name of module, ignoring name() changes, for LinkDot
public:
AstCellInlineScope(FileLine* fl, AstScope* scopep, AstCellInline* cellp)
: ASTGEN_SUPER_CellInlineScope(fl)
, m_scopep{scopep}
, m_cellp{cellp} {
UASSERT_OBJ(scopep, fl, "Scope must be non-null");
UASSERT_OBJ(cellp, fl, "CellInline must be non-null");
}
ASTGEN_MEMBERS_AstCellInlineScope;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
// ACCESSORS
string name() const override VL_MT_STABLE { return m_cellp->name(); }
bool maybePointedTo() const override { return true; }
AstScope* scopep() const VL_MT_STABLE { return m_scopep; } // Pointer to scope it's under
string origModName() const {
return m_cellp->origModName();
} // * = modp()->origName() before inlining
void scopep(AstScope* nodep) { m_scopep = nodep; }
};
class AstClassExtends final : public AstNode {
// class extends class name, or class implements class name
// Children: List of AstParseRef for packages/classes
@@ -932,6 +956,7 @@ class AstClockingItem final : public AstNode {
// @astgen op2 := exprp : Optional[AstNodeExpr]
// @astgen op3 := assignp : Optional[AstAssign]
// @astgen op4 := varp : Optional[AstVar]
// @astgen ptr := m_outputp : Optional[AstClockingItem]
VDirection m_direction;
public:
@@ -947,6 +972,9 @@ public:
}
ASTGEN_MEMBERS_AstClockingItem;
VDirection direction() const { return m_direction; }
AstClockingItem* outputp() const { return m_outputp; }
void outputp(AstClockingItem* outputp) { m_outputp = outputp; }
bool maybePointedTo() const override { return true; }
};
class AstConstPool final : public AstNode {
// Container for const static data
@@ -1115,8 +1143,9 @@ class AstExecGraph final : public AstNode {
public:
explicit AstExecGraph(FileLine* fl, const string& name) VL_MT_DISABLED;
ASTGEN_MEMBERS_AstExecGraph;
~AstExecGraph() override;
ASTGEN_MEMBERS_AstExecGraph;
void cloneRelink() override { V3ERROR_NA; }
const char* broken() const override {
BROKEN_RTN(!m_depGraphp);
return nullptr;
@@ -1170,6 +1199,7 @@ public:
explicit AstMTaskBody(FileLine* fl)
: ASTGEN_SUPER_MTaskBody(fl) {}
ASTGEN_MEMBERS_AstMTaskBody;
void cloneRelink() override { V3ERROR_NA; }
const char* broken() const override {
BROKEN_RTN(!m_execMTaskp);
return nullptr;
@@ -1267,9 +1297,6 @@ class AstNetlist final : public AstNode {
VTimescale m_timeunit; // Global time unit
VTimescale m_timeprecision; // Global time precision
bool m_timescaleSpecified = false; // Input HDL specified timescale
uint32_t m_nextFreeMTaskID = 1; // Next unique MTask ID within netlist
// starts at 1 so 0 means no MTask ID
uint32_t m_nextFreeMTaskProfilingID = 0; // Next unique ID to use for PGO
public:
AstNetlist();
ASTGEN_MEMBERS_AstNetlist;
@@ -1310,9 +1337,6 @@ public:
void timeprecisionMerge(FileLine*, const VTimescale& value);
void timescaleSpecified(bool specified) { m_timescaleSpecified = specified; }
bool timescaleSpecified() const { return m_timescaleSpecified; }
uint32_t allocNextMTaskID() { return m_nextFreeMTaskID++; }
uint32_t allocNextMTaskProfilingID() { return m_nextFreeMTaskProfilingID++; }
uint32_t usedMTaskProfilingIDs() const { return m_nextFreeMTaskProfilingID; }
};
class AstPackageExport final : public AstNode {
// A package export declaration
@@ -1469,6 +1493,7 @@ class AstScope final : public AstNode {
// Children: NODEBLOCK
// @astgen op1 := varsp : List[AstVarScope]
// @astgen op2 := blocksp : List[AstNode] // Logic blocks/AstActive/AstCFunc
// @astgen op3 := inlinesp : List[AstCellInlineScope] // Cell Inlines
//
// Below scope and cell are nullptr if top scope
// @astgen ptr := m_aboveScopep : Optional[AstScope] // Scope above this one in the hierarchy
@@ -1514,7 +1539,6 @@ class AstSenItem final : public AstNode {
VEdgeType m_edgeType; // Edge type
public:
class Combo {}; // for constructor type-overload selection
class Illegal {}; // for constructor type-overload selection
class Static {}; // for constructor type-overload selection
class Initial {}; // for constructor type-overload selection
class Final {}; // for constructor type-overload selection
@@ -1528,9 +1552,6 @@ public:
AstSenItem(FileLine* fl, Combo)
: ASTGEN_SUPER_SenItem(fl)
, m_edgeType{VEdgeType::ET_COMBO} {}
AstSenItem(FileLine* fl, Illegal)
: ASTGEN_SUPER_SenItem(fl)
, m_edgeType{VEdgeType::ET_ILLEGAL} {}
AstSenItem(FileLine* fl, Static)
: ASTGEN_SUPER_SenItem(fl)
, m_edgeType{VEdgeType::ET_STATIC} {}
@@ -1562,7 +1583,6 @@ public:
bool isStatic() const { return edgeType() == VEdgeType::ET_STATIC; }
bool isInitial() const { return edgeType() == VEdgeType::ET_INITIAL; }
bool isFinal() const { return edgeType() == VEdgeType::ET_FINAL; }
bool isIllegal() const { return edgeType() == VEdgeType::ET_ILLEGAL; }
bool isNever() const { return edgeType() == VEdgeType::ET_NEVER; }
};
class AstSenTree final : public AstNode {
@@ -1751,7 +1771,6 @@ class AstVar final : public AstNode {
VDirection m_declDirection; // Declared direction input/output etc
VLifetime m_lifetime; // Lifetime
VVarAttrClocker m_attrClocker;
MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var
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,6 +1813,7 @@ class AstVar final : public AstNode {
bool m_trace : 1; // Trace this variable
bool m_isLatched : 1; // Not assigned in all control paths of combo always
bool m_isForceable : 1; // May be forced/released externally from user C code
bool m_isForcedByCode : 1; // May be forced/released from AstAssignForce/AstRelease
bool m_isWrittenByDpi : 1; // This variable can be written by a DPI Export
bool m_isWrittenBySuspendable : 1; // This variable can be written by a suspendable process
@@ -1839,6 +1859,7 @@ class AstVar final : public AstNode {
m_trace = false;
m_isLatched = false;
m_isForceable = false;
m_isForcedByCode = false;
m_isWrittenByDpi = false;
m_isWrittenBySuspendable = false;
m_attrClocker = VVarAttrClocker::CLOCKER_UNKNOWN;
@@ -1994,6 +2015,8 @@ public:
void isLatched(bool flag) { m_isLatched = flag; }
bool isForceable() const { return m_isForceable; }
void setForceable() { m_isForceable = true; }
void setForcedByCode() { m_isForcedByCode = true; }
bool isForced() const { return m_isForceable || m_isForcedByCode; }
bool isWrittenByDpi() const { return m_isWrittenByDpi; }
void setWrittenByDpi() { m_isWrittenByDpi = true; }
bool isWrittenBySuspendable() const { return m_isWrittenBySuspendable; }
@@ -2105,9 +2128,6 @@ public:
m_name = name;
}
static AstVar* scVarRecurse(AstNode* nodep);
void addProducingMTaskId(int id) { m_mtaskIds.insert(id); }
void addConsumingMTaskId(int id) { m_mtaskIds.insert(id); }
const MTaskIdSet& mtaskIds() const { return m_mtaskIds; }
void pinNum(int id) { m_pinNum = id; }
int pinNum() const { return m_pinNum; }
};
@@ -2285,6 +2305,7 @@ class AstClass final : public AstNodeModule {
// @astgen op4 := extendsp : List[AstClassExtends]
// MEMBERS
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
bool m_extended = false; // Is extension or extended by other classes
bool m_interfaceClass = false; // Interface class
bool m_needRNG = false; // Need RNG, uses srandom/randomize
@@ -2320,6 +2341,8 @@ public:
// Return true if this class is an extension of base class (SLOW)
// Accepts nullptrs
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; }
VBaseOverride baseOverride() const { return m_baseOverride; }
// Return the lowest class extended from, or this class
AstClass* baseMostClassp();
static bool isCacheableChild(const AstNode* nodep);
@@ -2419,13 +2442,11 @@ public:
ASTGEN_MEMBERS_AstAlwaysObserved;
};
class AstAlwaysPost final : public AstNodeProcedure {
// Like always but post assignments for memory assignment IFs
// @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list iff clocked
// Like always but 'post' scheduled, e.g. for array NBA commits
public:
AstAlwaysPost(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp)
: ASTGEN_SUPER_AlwaysPost(fl, stmtsp) {
this->sensesp(sensesp);
}
explicit AstAlwaysPost(FileLine* fl)
: ASTGEN_SUPER_AlwaysPost(fl, nullptr) {}
ASTGEN_MEMBERS_AstAlwaysPost;
};
class AstAlwaysPostponed final : public AstNodeProcedure {
@@ -2565,6 +2586,31 @@ public:
bool isJustOneBodyStmt() const { return stmtsp() && !stmtsp()->nextp(); }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
};
class AstAssertCtl final : public AstNodeStmt {
// @astgen op1 := controlTypep : AstNodeExpr
// @astgen op2 := levelp : AstNodeExpr
// @astgen op3 := itemsp : List[AstNodeExpr]
// Type of assertcontrol task; either known from parser or from evaluated
// controlTypep expression.
VAssertCtlType m_ctlType; // $assert keyword type
public:
AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr* levelp = nullptr,
AstNodeExpr* itemsp = nullptr);
AstAssertCtl(FileLine* fl, AstNodeExpr* controlTypep, AstNodeExpr* assertionTypep = nullptr,
AstNodeExpr* directiveTypep = nullptr, AstNodeExpr* levelp = nullptr,
AstNodeExpr* itemsp = nullptr);
ASTGEN_MEMBERS_AstAssertCtl;
string verilogKwd() const override { return m_ctlType.ascii(); }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isPure() override { return false; }
bool isOutputter() override { return true; }
VAssertCtlType ctlType() const { return m_ctlType; }
void ctlType(int32_t type) { m_ctlType = VAssertCtlType{type}; }
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
};
class AstBreak final : public AstNodeStmt {
public:
explicit AstBreak(FileLine* fl)
+171 -38
View File
@@ -17,11 +17,11 @@
#include "V3PchAstMT.h"
#include "V3EmitCBase.h"
#include "V3ExecGraph.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3Graph.h"
#include "V3Hasher.h"
#include "V3PartitionGraph.h" // Just for mtask dumping
#include "V3String.h"
#include "V3Ast__gen_impl.h" // Generated by 'astgen'
@@ -319,7 +319,7 @@ AstNodeExpr* AstInsideRange::newAndFromInside(AstNodeExpr* exprp, AstNodeExpr* l
AstNodeExpr* const bp = new AstLte{fileline(), exprp->cloneTreePure(true), rhsp};
ap->fileline()->modifyWarnOff(V3ErrorCode::UNSIGNED, true);
bp->fileline()->modifyWarnOff(V3ErrorCode::CMPCONST, true);
return new AstAnd{fileline(), ap, bp};
return new AstLogAnd{fileline(), ap, bp};
}
AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
@@ -738,49 +738,82 @@ public:
}
};
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
const CTypeRecursed info = cTypeRecurse(false);
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef, bool packed) const {
const CTypeRecursed info = cTypeRecurse(false, packed);
return info.render(name, isRef);
}
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packed) const {
// Legacy compound argument currently just passed through and unused
CTypeRecursed info;
const AstNodeDType* const dtypep = this->skipRefp();
if (const auto* const adtypep = VN_CAST(dtypep, AssocArrayDType)) {
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true);
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true);
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true, false);
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true, false);
info.m_type = "VlAssocArray<" + key.m_type + ", " + val.m_type + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, CDType)) {
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
info.m_type = adtypep->name();
} else if (const auto* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
info.m_type = "VlAssocArray<std::string, " + sub.m_type + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
info.m_type = "VlQueue<" + sub.m_type + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, QueueDType)) {
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
info.m_type = "VlQueue<" + sub.m_type;
// + 1 below as VlQueue uses 0 to mean unlimited, 1 to mean size() max is 1
if (adtypep->boundp()) info.m_type += ", " + cvtToStr(adtypep->boundConst() + 1);
info.m_type += ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, SampleQueueDType)) {
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true, false);
info.m_type = "VlSampleQueue<" + sub.m_type + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
info.m_type = "VlClassRef<" + EmitCBase::prefixNameProtect(adtypep) + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
info.m_type = EmitCBase::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
} else if (const auto* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
if (adtypep->isCompound()) compound = true;
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound);
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound, false);
info.m_type = "VlUnpacked<" + sub.m_type;
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
info.m_type += ">";
} else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) {
const auto* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
} else if (const auto* const adtypep = VN_CAST(dtypep, NBACommitQueueDType)) {
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
compound = true;
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound, false);
AstNodeDType* eDTypep = adtypep->subDTypep();
unsigned rank = 0;
while (AstUnpackArrayDType* const uaDTypep = VN_CAST(eDTypep, UnpackArrayDType)) {
eDTypep = uaDTypep->subDTypep()->skipRefp();
++rank;
}
info.m_type = "VlNBACommitQueue<";
info.m_type += sub.m_type;
info.m_type += adtypep->partial() ? ", true" : ", false";
info.m_type += ", " + eDTypep->cTypeRecurse(compound, false).m_type;
info.m_type += ", " + std::to_string(rank);
info.m_type += ">";
} else if (packed && (VN_IS(dtypep, PackArrayDType))) {
const AstPackArrayDType* const adtypep = VN_CAST(dtypep, PackArrayDType);
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(false, true);
info.m_type = std::move(sub.m_type);
info.m_dims = "[" + cvtToStr(adtypep->elementsConst()) + "]" + sub.m_dims;
} else if (VN_IS(dtypep, NodeUOrStructDType)
&& (!VN_AS(dtypep, NodeUOrStructDType)->packed() || packed)) {
const AstNodeUOrStructDType* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
UASSERT_OBJ(!packed || sdtypep->packed(), this,
"Unsupported type for packed struct or union");
info.m_type = EmitCBase::prefixNameProtect(sdtypep);
} else if (const AstBasicDType* const bdtypep = dtypep->basicp()) {
// We don't print msb()/lsb() as multidim packed would require recursion,
@@ -810,6 +843,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
info.m_type = "VlForkSync";
} else if (bdtypep->isProcessRef()) {
info.m_type = "VlProcessRef";
} else if (bdtypep->isRandomGenerator()) {
info.m_type = "VlRandomizer";
} else if (bdtypep->isEvent()) {
info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent";
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
@@ -823,6 +858,13 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
} else if (dtypep->isWide()) {
info.m_type = "VlWide<" + cvtToStr(dtypep->widthWords()) + ">" + bitvec;
}
// CData, SData, IData, QData or VlWide are packed type.
const bool packedType = VString::startsWith(info.m_type, "CData")
|| VString::startsWith(info.m_type, "SData")
|| VString::startsWith(info.m_type, "IData")
|| VString::startsWith(info.m_type, "QData")
|| VString::startsWith(info.m_type, "VlWide");
UASSERT_OBJ(!packed || packedType, this, "Unsupported type for packed struct or union");
} else {
v3fatalSrc("Unknown data type in var type emitter: " << dtypep->prettyName());
}
@@ -861,9 +903,10 @@ std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) {
}
dtypep = adtypep->subDTypep();
continue;
} else if (const AstQueueDType* const qdtypep = VN_CAST(dtypep, QueueDType)) {
} else if (VN_IS(dtypep, QueueDType) || VN_IS(dtypep, DynArrayDType)
|| VN_IS(dtypep, AssocArrayDType) || VN_IS(dtypep, WildcardArrayDType)) {
unpacked++;
dtypep = qdtypep->subDTypep();
dtypep = dtypep->subDTypep();
continue;
} else if (const AstBasicDType* const adtypep = VN_CAST(dtypep, BasicDType)) {
if (includeBasic && (adtypep->isRanged() || adtypep->isString())) packed++;
@@ -1418,7 +1461,32 @@ void AstAlways::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "keyword", keyword().ascii());
dumpJsonGen(str);
}
AstAssertCtl::AstAssertCtl(FileLine* fl, VAssertCtlType ctlType, AstNodeExpr* levelp,
AstNodeExpr* itemsp)
: 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)
: ASTGEN_SUPER_AssertCtl(fl)
, m_ctlType{VAssertCtlType::_TO_BE_EVALUATED} {
this->controlTypep(controlTypep);
if (!levelp) levelp = new AstConst{fl, 0};
this->levelp(levelp);
addItemsp(itemsp);
}
void AstAssertCtl::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " [" << ctlType().ascii() << "]";
}
void AstAssertCtl::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "ctlType", ctlType().ascii());
dumpJsonGen(str);
}
void AstAttrOf::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " [" << attrType().ascii() << "]";
@@ -1439,7 +1507,7 @@ void AstBasicDType::dumpJson(std::ostream& str) const {
}
dumpJsonGen(str);
}
string AstBasicDType::prettyDTypeName() const {
string AstBasicDType::prettyDTypeName(bool) const {
std::ostringstream os;
os << keyword().ascii();
if (isRanged() && !rangep() && keyword().width() <= 1) {
@@ -1479,12 +1547,20 @@ void AstCell::dumpJson(std::ostream& str) const {
void AstCellInline::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " -> " << origModName();
str << " [scopep=" << nodeAddr(scopep()) << "]";
}
void AstCellInline::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, origModName);
dumpJsonGen(str);
}
void AstCellInlineScope::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " -> " << origModName();
str << " [scopep=" << nodeAddr(scopep()) << "]";
}
void AstCellInlineScope::dumpJson(std::ostream& str) const {
dumpJsonStrFunc(str, origModName);
dumpJsonGen(str);
}
bool AstClass::isCacheableChild(const AstNode* nodep) {
return (VN_IS(nodep, Var) || VN_IS(nodep, Constraint) || VN_IS(nodep, EnumItemRef)
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
@@ -1515,6 +1591,7 @@ void AstClass::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, isExtended);
dumpJsonBoolFunc(str, isInterfaceClass);
dumpJsonBoolFunc(str, isVirtual);
if (baseOverride().isAny()) dumpJsonStr(str, "baseOverride", baseOverride().ascii());
dumpJsonGen(str);
}
void AstClassExtends::dump(std::ostream& str) const {
@@ -1555,6 +1632,7 @@ void AstClassRefDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "class:" << name();
}
string AstClassRefDType::prettyDTypeName(bool) const { return "class{}"s + prettyName(); }
string AstClassRefDType::name() const { return classp() ? classp()->name() : "<unlinked>"; }
void AstNodeCoverOrAssert::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
@@ -1591,6 +1669,22 @@ void AstEnumDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "enum";
}
string AstEnumDType::prettyDTypeName(bool full) const {
string result = "enum{";
if (full) { // else shorten for error messages
for (AstEnumItem* itemp = itemsp(); itemp; itemp = VN_AS(itemp->nextp(), EnumItem)) {
result += itemp->prettyName() + "=";
if (AstConst* constp = VN_CAST(itemp->valuep(), Const)) {
result += constp->num().ascii(true, true);
} else {
result += "?";
}
result += ";";
}
}
result += "}" + prettyName();
return result;
}
void AstEnumItemRef::dump(std::ostream& str) const {
this->AstNodeExpr::dump(str);
str << " -> ";
@@ -1699,7 +1793,7 @@ void AstJumpGo::dump(std::ostream& str) const {
if (labelp()) {
labelp()->dump(str);
} else {
str << "%Error:UNLINKED";
str << "%E:UNLINKED";
}
}
void AstJumpGo::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
@@ -1714,7 +1808,7 @@ void AstJumpLabel::dump(std::ostream& str) const {
if (blockp()) {
blockp()->dump(str);
} else {
str << "%Error:UNLINKED";
str << "%E:UNLINKED";
}
}
void AstJumpLabel::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
@@ -1728,7 +1822,8 @@ AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
while (AstNodeArrayDType* const asubdtp = VN_CAST(subdtp, NodeArrayDType)) {
subdtp = asubdtp->subDTypep();
}
return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr
// It's possible that `subdtp` is still a ref type, so skip it.
return VN_CAST(subdtp->skipRefp(), NodeUOrStructDType); // Maybe nullptr
}
bool AstMemberSel::same(const AstNode* samep) const {
@@ -1743,7 +1838,7 @@ void AstMemberSel::dump(std::ostream& str) const {
if (varp()) {
varp()->dump(str);
} else {
str << "%Error:UNLINKED";
str << "%E:UNLINKED";
}
}
void AstMemberSel::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
@@ -1904,6 +1999,18 @@ void AstNodeUOrStructDType::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, isFourstate);
dumpJsonGen(str);
}
string AstNodeUOrStructDType::prettyDTypeName(bool full) const {
string result = verilogKwd() + "{";
if (full) { // else shorten for errors
for (AstMemberDType* itemp = membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
result += itemp->subDTypep()->prettyDTypeName(full);
result += " " + itemp->prettyName() + ";";
}
}
result += "}" + prettyName();
return result;
}
void AstNodeDType::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (generic()) str << " [GENERIC]";
@@ -1916,7 +2023,7 @@ void AstNodeDType::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, generic);
dumpJsonGen(str);
}
void AstNodeDType::dumpSmall(std::ostream& str) const {
void AstNodeDType::dumpSmall(std::ostream& str) const VL_MT_STABLE {
str << "(" << (generic() ? "G/" : "") << ((isSigned() && !isDouble()) ? "s" : "")
<< (isNosign() ? "n" : "") << (isDouble() ? "d" : "") << (isString() ? "str" : "");
if (!isDouble() && !isString()) str << "w" << (widthSized() ? "" : "u") << width();
@@ -1943,13 +2050,13 @@ void AstNodeArrayDType::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "declRange", cvtToStr(declRange()));
dumpJsonGen(str);
}
string AstPackArrayDType::prettyDTypeName() const {
string AstPackArrayDType::prettyDTypeName(bool full) const {
std::ostringstream os;
if (const auto subp = subDTypep()) os << subp->prettyDTypeName();
if (const auto subp = subDTypep()) os << subp->prettyDTypeName(full);
os << declRange();
return os.str();
}
string AstUnpackArrayDType::prettyDTypeName() const {
string AstUnpackArrayDType::prettyDTypeName(bool full) const {
std::ostringstream os;
string ranges = cvtToStr(declRange());
// Unfortunately we need a single $ for the first unpacked, and all
@@ -1959,7 +2066,7 @@ string AstUnpackArrayDType::prettyDTypeName() const {
ranges += cvtToStr(adtypep->declRange());
subp = adtypep->subDTypep()->skipRefp();
}
os << subp->prettyDTypeName() << "$" << ranges;
os << subp->prettyDTypeName(full) << "$" << ranges;
return os.str();
}
std::vector<AstUnpackArrayDType*> AstUnpackArrayDType::unpackDimensions() {
@@ -2063,7 +2170,7 @@ void AstSliceSel::dump(std::ostream& str) const {
if (declRange().ranged()) str << " decl" << declRange();
}
void AstSliceSel::dumpJson(std::ostream& str) const {
if (declRange().ranged()) { dumpJsonStr(str, "declRange", cvtToStr(declRange())); }
if (declRange().ranged()) dumpJsonStr(str, "declRange", cvtToStr(declRange()));
dumpJsonGen(str);
}
void AstMTaskBody::dump(std::ostream& str) const {
@@ -2103,20 +2210,22 @@ void AstAssocArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[assoc-" << nodeAddr(keyDTypep()) << "]";
}
string AstAssocArrayDType::prettyDTypeName() const {
return subDTypep()->prettyDTypeName() + "[" + keyDTypep()->prettyDTypeName() + "]";
string AstAssocArrayDType::prettyDTypeName(bool full) const {
return subDTypep()->prettyDTypeName(full) + "$[" + keyDTypep()->prettyDTypeName(full) + "]";
}
void AstDynArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[]";
str << "$[]";
}
string AstDynArrayDType::prettyDTypeName(bool full) const {
return subDTypep()->prettyDTypeName(full) + "$[]";
}
string AstDynArrayDType::prettyDTypeName() const { return subDTypep()->prettyDTypeName() + "[]"; }
void AstQueueDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[queue]";
}
string AstQueueDType::prettyDTypeName() const {
string str = subDTypep()->prettyDTypeName() + "[$";
string AstQueueDType::prettyDTypeName(bool full) const {
string str = subDTypep()->prettyDTypeName(full) + "$[$";
if (boundConst()) str += ":" + cvtToStr(boundConst());
return str + "]";
}
@@ -2424,6 +2533,7 @@ void AstNodeFTask::dump(std::ostream& str) const {
if (dpiOpenChild()) str << " [DPIOPENCHILD]";
if (dpiOpenParent()) str << " [DPIOPENPARENT]";
if (prototype()) str << " [PROTOTYPE]";
if (pureVirtual()) str << " [PUREVIRTUAL]";
if (recursive()) str << " [RECURSIVE]";
if (taskPublic()) str << " [PUBLIC]";
if ((dpiImport() || dpiExport()) && cname() != name()) str << " [c=" << cname() << "]";
@@ -2462,6 +2572,7 @@ void AstNodeFTask::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, prototype);
dumpJsonBoolFunc(str, recursive);
dumpJsonBoolFunc(str, taskPublic);
if (baseOverride().isAny()) dumpJsonStr(str, "baseOverride", baseOverride().ascii());
dumpJsonStrFunc(str, cname);
dumpJsonGen(str);
}
@@ -2517,7 +2628,7 @@ void AstCoverInc::dump(std::ostream& str) const {
if (declp()) {
declp()->dump(str);
} else {
str << "%Error:UNLINKED";
str << "%E:UNLINKED";
}
}
void AstCoverInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
@@ -2543,7 +2654,7 @@ void AstTraceInc::dump(std::ostream& str) const {
if (declp()) {
declp()->dump(str);
} else {
str << "%Error:UNLINKED";
str << "%E:UNLINKED";
}
}
void AstTraceInc::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
@@ -2647,6 +2758,7 @@ void AstCMethodHard::setPurity() {
{"commit", false},
{"delay", false},
{"done", false},
{"enqueue", false},
{"erase", false},
{"evaluate", false},
{"evaluation", false},
@@ -2659,6 +2771,7 @@ void AstCMethodHard::setPurity() {
{"find_last_index", true},
{"fire", false},
{"first", false},
{"hard", false},
{"init", false},
{"insert", false},
{"inside", true},
@@ -2698,7 +2811,8 @@ void AstCMethodHard::setPurity() {
{"trigger", false},
{"unique", true},
{"unique_index", true},
{"word", true}};
{"word", true},
{"write_var", false}};
auto isPureIt = isPureMethod.find(name());
UASSERT_OBJ(isPureIt != isPureMethod.end(), this, "Unknown purity of method " + name());
@@ -2757,3 +2871,22 @@ void AstDelay::dumpJson(std::ostream& str) const {
dumpJsonBoolFunc(str, isCycleDelay);
dumpJsonGen(str);
}
const char* AstAnd::widthMismatch() const VL_MT_STABLE {
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
BROKEN_RTN(lhsp()->widthMin() != widthMin());
return nullptr;
}
const char* AstOr::widthMismatch() const VL_MT_STABLE {
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
BROKEN_RTN(lhsp()->widthMin() != widthMin());
return nullptr;
}
const char* AstXor::widthMismatch() const VL_MT_STABLE {
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
BROKEN_RTN(lhsp()->widthMin() != widthMin());
return nullptr;
}
const char* AstNot::widthMismatch() const VL_MT_STABLE {
BROKEN_RTN(lhsp()->widthMin() != widthMin());
return nullptr;
}
+4
View File
@@ -372,7 +372,11 @@ void V3Broken::selfTest() {
// Exercise addNewed and deleted for coverage, as otherwise only used with VL_LEAK_CHECKS
FileLine* const fl = new FileLine{FileLine::commandLineFilename()};
const AstNode* const newp = new AstBegin{fl, "[EditWrapper]", nullptr};
// Don't actually do it with VL_LEAK_CHECKS, when new/delete calls these.
// Otherwise you call addNewed twice on the same address, which is an error.
#ifndef VL_LEAK_CHECKS
addNewed(newp);
deleted(newp);
#endif
VL_DO_DANGLING(delete newp, newp);
}
+10 -5
View File
@@ -82,7 +82,7 @@ class CastVisitor final : public VNVisitor {
}
void ensureCast(AstNodeExpr* nodep) {
if (castSize(nodep->backp()) != castSize(nodep) || !nodep->user1()) {
insertCast(nodep, castSize(nodep->backp()));
if (!nodep->isNull()) insertCast(nodep, castSize(nodep->backp()));
}
}
void ensureLower32Cast(AstCCast* nodep) {
@@ -121,14 +121,14 @@ class CastVisitor final : public VNVisitor {
// already checked by V3Width and dtypep of a condition operator is a type of their
// common base class, so both classes can be safely casted.
const AstClassRefDType* const thenClassDtypep
= VN_CAST(nodep->thenp()->dtypep(), ClassRefDType);
= VN_CAST(nodep->thenp()->dtypep()->skipRefp(), ClassRefDType);
const AstClassRefDType* const elseClassDtypep
= VN_CAST(nodep->elsep()->dtypep(), ClassRefDType);
= VN_CAST(nodep->elsep()->dtypep()->skipRefp(), ClassRefDType);
const bool castRequired = thenClassDtypep && elseClassDtypep
&& (thenClassDtypep->classp() != elseClassDtypep->classp());
if (castRequired) {
const AstClass* const commonBaseClassp
= VN_AS(nodep->dtypep(), ClassRefDType)->classp();
= VN_AS(nodep->dtypep()->skipRefp(), ClassRefDType)->classp();
if (thenClassDtypep->classp() != commonBaseClassp) {
AstNodeExpr* thenp = nodep->thenp()->unlinkFrBack();
nodep->thenp(new AstCCast{thenp->fileline(), thenp, nodep});
@@ -201,7 +201,7 @@ class CastVisitor final : public VNVisitor {
// Constants are of unknown size if smaller than 33 bits, because
// we're too lazy to wrap every constant in the universe in
// ((IData)#).
nodep->user1(nodep->isQuad() || nodep->isWide());
nodep->user1(nodep->isQuad() || nodep->isWide() || nodep->isNull());
}
// Null dereference protection
@@ -212,6 +212,11 @@ class CastVisitor final : public VNVisitor {
void visit(AstCMethodCall* nodep) override {
iterateChildren(nodep);
ensureNullChecked(nodep->fromp());
nodep->user1(true);
}
void visit(AstCMethodHard* nodep) override {
iterateChildren(nodep);
nodep->user1(true);
}
void visit(AstMemberSel* nodep) override {
iterateChildren(nodep);
+43 -8
View File
@@ -26,6 +26,9 @@
#include "V3UniqueNames.h"
#include <queue>
#include <set>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
@@ -46,6 +49,11 @@ class ClassVisitor final : public VNVisitor {
const AstNodeFTask* m_ftaskp = nullptr; // Current task
std::vector<std::pair<AstNode*, AstScope*>> m_toScopeMoves;
std::vector<std::pair<AstNode*, AstNodeModule*>> m_toPackageMoves;
std::set<AstTypedef*> m_typedefps; // Contains all typedef nodes
std::set<AstNodeUOrStructDType*> m_strDtypeps; // Contains all packed structs and unions
// Contains all public packed structs and unions, using a queue to
// mark embedded struct / union public by BFS
std::queue<AstNodeUOrStructDType*> m_pubStrDtypeps;
// METHODS
@@ -150,7 +158,7 @@ class ClassVisitor final : public VNVisitor {
// have a pointer to it yet
m_toScopeMoves.emplace_back(nodep, m_packageScopep);
}
if (!m_ftaskp && nodep->lifetime().isStatic()) {
if (!m_ftaskp && (nodep->lifetime().isStatic() || nodep->isParam())) {
m_toPackageMoves.emplace_back(nodep, m_classPackagep);
// We're really moving the VarScope but we might not
// have a pointer to it yet
@@ -190,12 +198,12 @@ class ClassVisitor final : public VNVisitor {
void visit(AstInitial* nodep) override {
// But not AstInitialAutomatic, which remains under the class
iterateChildren(nodep);
if (m_packageScopep) { m_toScopeMoves.emplace_back(nodep, m_packageScopep); }
if (m_packageScopep) m_toScopeMoves.emplace_back(nodep, m_packageScopep);
}
void visit(AstInitialStatic* nodep) override {
// But not AstInitialAutomatic, which remains under the class
iterateChildren(nodep);
if (m_packageScopep) { m_toScopeMoves.emplace_back(nodep, m_packageScopep); }
if (m_packageScopep) m_toScopeMoves.emplace_back(nodep, m_packageScopep);
}
void setStructModulep(AstNodeUOrStructDType* const dtypep) {
@@ -204,22 +212,27 @@ class ClassVisitor final : public VNVisitor {
dtypep->classOrPackagep(m_classPackagep ? m_classPackagep : m_modp);
dtypep->name(
m_names.get(dtypep->name() + (VN_IS(dtypep, UnionDType) ? "__union" : "__struct")));
if (dtypep->packed()) m_strDtypeps.insert(dtypep);
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
// Recurse only into anonymous unpacked structs inside this definition,
// other unpacked structs will be reached from another typedefs
if (subp && !subp->packed() && subp->name().empty()) setStructModulep(subp);
// Recurse only into anonymous structs inside this definition,
// other structs will be reached from another typedefs
if (subp && subp->name().empty()) setStructModulep(subp);
}
}
void visit(AstTypedef* nodep) override {
if (nodep->user1SetOnce()) return;
AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType);
if (dtypep && dtypep->packed()) {
m_typedefps.insert(nodep);
if (nodep->attrPublic()) m_pubStrDtypeps.push(dtypep);
}
iterateChildren(nodep);
if (m_classPackagep) m_classPackagep->addStmtsp(nodep->unlinkFrBack());
AstNodeUOrStructDType* const dtypep = VN_CAST(nodep->dtypep(), NodeUOrStructDType);
if (dtypep && !dtypep->packed()) {
if (dtypep) {
dtypep->name(nodep->name());
setStructModulep(dtypep);
}
@@ -258,6 +271,28 @@ public:
nodep->unlinkFrBack();
modp->addStmtsp(nodep);
}
// BFS to mark public typedefs.
std::set<AstNodeUOrStructDType*> pubStrDtypeps;
while (!m_pubStrDtypeps.empty()) {
AstNodeUOrStructDType* const dtypep = m_pubStrDtypeps.front();
m_pubStrDtypeps.pop();
if (pubStrDtypeps.insert(dtypep).second) {
for (const AstMemberDType* itemp = dtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (AstNodeUOrStructDType* const subp = itemp->getChildStructp())
m_pubStrDtypeps.push(subp);
}
}
}
for (AstTypedef* typedefp : m_typedefps) {
AstNodeUOrStructDType* const sdtypep = VN_AS(typedefp->dtypep(), NodeUOrStructDType);
if (pubStrDtypeps.count(sdtypep)) typedefp->attrPublic(true);
}
// Clear package pointer of non-public packed struct / union type, which will never be
// exported.
for (AstNodeUOrStructDType* sdtypep : m_strDtypeps) {
if (!pubStrDtypeps.count(sdtypep)) sdtypep->classOrPackagep(nullptr);
}
}
};
+5
View File
@@ -224,6 +224,11 @@ class CleanVisitor final : public VNVisitor {
iterateChildren(nodep);
setClean(nodep, true);
}
void visit(AstConsPackMember* nodep) override {
iterateChildren(nodep);
ensureClean(nodep->rhsp());
setClean(nodep, true);
}
void visit(AstSel* nodep) override {
operandTriop(nodep);
setClean(nodep, nodep->cleanOut());
+4
View File
@@ -53,6 +53,7 @@ class ConvertWriteRefsToRead final : public VNVisitor {
if (nodep->access().isWriteOnly()) {
nodep->replaceWith(
new AstVarRef{nodep->fileline(), nodep->varScopep(), VAccess::READ});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
}
@@ -66,6 +67,9 @@ public:
// Clock state, as a visitor of each AstNode
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
+3 -1
View File
@@ -113,7 +113,9 @@ static void makeToStringMiddle(AstClass* nodep) {
std::string comma;
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
if (const auto* const varp = VN_CAST(itemp, Var)) {
if (!varp->isParam() && !varp->isInternal()) {
if (!varp->isParam() && !varp->isInternal()
&& !(varp->dtypeSkipRefp()->basicp()
&& varp->dtypeSkipRefp()->basicp()->isRandomGenerator())) {
string stmt = "out += \"";
stmt += comma;
comma = ", ";
+1 -1
View File
@@ -286,7 +286,7 @@ public:
}
void update(const V3ConfigFile& file) {
// Copy in all Attributes
for (const auto& itr : file.m_lineAttrs) { m_lineAttrs[itr.first] |= itr.second; }
for (const auto& itr : file.m_lineAttrs) m_lineAttrs[itr.first] |= itr.second;
// Copy in all ignores
for (const auto& ignLine : file.m_ignLines) m_ignLines.insert(ignLine);
// Update the iterator after the list has changed
+83 -29
View File
@@ -684,7 +684,7 @@ public:
// Reduction ops are transformed in the same way.
// &{v[0], v[1]} => 2'b11 == (2'b11 & v)
static AstNodeExpr* simplify(AstNodeExpr* nodep, int resultWidth, unsigned externalOps,
VDouble0& reduction, VNDeleter& deleterr) {
VDouble0& reduction) {
UASSERT_OBJ(1 <= resultWidth && resultWidth <= 64, nodep, "resultWidth out of range");
// Walk tree, gathering all terms referenced in expression
@@ -727,7 +727,7 @@ public:
}
// Set width and widthMin precisely
resultp->dtypeChgWidth(resultWidth, 1);
for (AstNode* const termp : termps) deleterr.pushDeletep(termp);
for (AstNode* const termp : termps) VL_DO_DANGLING(termp->deleteTree(), termp);
return resultp;
}
const ResultTerm result = v->getResultTerm();
@@ -803,7 +803,7 @@ public:
// Only substitute the result if beneficial as determined by operation count
if (visitor.m_ops <= resultOps) {
for (AstNode* const termp : termps) deleterr.pushDeletep(termp);
for (AstNode* const termp : termps) VL_DO_DANGLING(termp->deleteTree(), termp);
return nullptr;
}
@@ -1187,11 +1187,9 @@ class ConstVisitor final : public VNVisitor {
const AstAnd* const andp = VN_CAST(nodep, And);
const int width = nodep->width();
if (andp && isConst(andp->lhsp(), 1)) { // 1 & BitOpTree
newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), width, 1, m_statBitOpReduction,
deleter());
newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), width, 1, m_statBitOpReduction);
} else { // BitOpTree
newp = ConstBitOpTreeVisitor::simplify(nodep, width, 0, m_statBitOpReduction,
deleter());
newp = ConstBitOpTreeVisitor::simplify(nodep, width, 0, m_statBitOpReduction);
}
if (newp) {
@@ -1345,7 +1343,8 @@ class ConstVisitor final : public VNVisitor {
= new AstSel{nodep->fileline(), ap->unlinkFrBack(), newLsb, nodep->widthConst()};
newp->dtypeFrom(nodep);
if (debug() >= 9) newp->dumpTree("- SEL(SH)-ou: ");
VL_DO_DANGLING(nodep->replaceWith(newp), nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return true;
}
@@ -1557,7 +1556,9 @@ class ConstVisitor final : public VNVisitor {
}
return false;
}
bool operandsSameSize(AstNode* lhsp, AstNode* rhsp) { return lhsp->width() == rhsp->width(); }
static bool operandsSameWidth(const AstNode* lhsp, const AstNode* rhsp) {
return lhsp->width() == rhsp->width();
}
//----------------------------------------
// Constant Replacement functions.
@@ -1822,16 +1823,20 @@ class ConstVisitor final : public VNVisitor {
// {llp OP lrp, rlp OP rrp} => {llp, rlp} OP {lrp, rrp}, where OP = AND/OR/XOR
AstNodeBiop* const lp = VN_AS(nodep->lhsp(), NodeBiop);
AstNodeBiop* const rp = VN_AS(nodep->rhsp(), NodeBiop);
AstNodeExpr* const llp = lp->lhsp()->cloneTreePure(false);
AstNodeExpr* const lrp = lp->rhsp()->cloneTreePure(false);
AstNodeExpr* const rlp = rp->lhsp()->cloneTreePure(false);
AstNodeExpr* const rrp = rp->rhsp()->cloneTreePure(false);
if (concatMergeable(lp, rp, 0)) {
AstConcat* const newlp = new AstConcat{rlp->fileline(), llp, rlp};
AstConcat* const newrp = new AstConcat{rrp->fileline(), lrp, rrp};
AstNodeExpr* const llp = lp->lhsp();
AstNodeExpr* const lrp = lp->rhsp();
AstNodeExpr* const rlp = rp->lhsp();
AstNodeExpr* const rrp = rp->rhsp();
AstConcat* const newlp = new AstConcat{rlp->fileline(), llp->cloneTreePure(false),
rlp->cloneTreePure(false)};
AstConcat* const newrp = new AstConcat{rrp->fileline(), lrp->cloneTreePure(false),
rrp->cloneTreePure(false)};
// use the lhs to replace the parent concat
lp->lhsp()->replaceWith(newlp);
lp->rhsp()->replaceWith(newrp);
llp->replaceWith(newlp);
VL_DO_DANGLING(pushDeletep(llp), llp);
lrp->replaceWith(newrp);
VL_DO_DANGLING(pushDeletep(lrp), lrp);
lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), VSigning::UNSIGNED);
UINFO(5, "merged " << nodep << endl);
VL_DO_DANGLING(pushDeletep(rp->unlinkFrBack()), rp);
@@ -2170,6 +2175,21 @@ class ConstVisitor final : public VNVisitor {
"array to a variable of size greater than 64");
}
srcp = new AstCvtDynArrayToPacked{srcp->fileline(), srcp, srcDTypep};
} 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()};
// 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
= 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};
}
nodep->rhsp(srcp);
VL_DO_DANGLING(pushDeletep(streamp), streamp);
@@ -2190,10 +2210,17 @@ class ConstVisitor final : public VNVisitor {
} else {
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(), VSigning::UNSIGNED);
}
if (dWidth == 0) {
streamp = new AstCvtPackedToDynArray{nodep->fileline(), streamp, dstp->dtypep()};
} else if (sWidth > dWidth) {
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
if (VN_IS(dstp->dtypep(), UnpackArrayDType)) {
streamp
= new AstCvtPackedToUnpackArray{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()};
} else if (sWidth >= dWidth) {
streamp = new AstSel{streamp->fileline(), streamp, sWidth - dWidth, dWidth};
}
}
nodep->lhsp(dstp);
nodep->rhsp(streamp);
@@ -2207,10 +2234,24 @@ class ConstVisitor final : public VNVisitor {
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
const int sWidth = srcp->width();
const int dWidth = dstp->width();
if (dWidth == 0) {
srcp = new AstCvtPackedToDynArray{nodep->fileline(), srcp, dstp->dtypep()};
} else if (sWidth > dWidth) {
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
if (VN_IS(dstp->dtypep(), UnpackArrayDType)) {
const int dstBitWidth
= dWidth * VN_AS(dstp->dtypep(), UnpackArrayDType)->arrayUnpackedElements();
// Handling the case where rhs is wider than lhs. StreamL does not require this
// since the combination of the left streaming operation and the implicit
// truncation in VL_ASSIGN_UNPACK automatically selects the left-most bits.
if (sWidth > dstBitWidth) {
srcp
= new AstSel{streamp->fileline(), srcp, sWidth - dstBitWidth, dstBitWidth};
}
srcp = new AstCvtPackedToUnpackArray{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()};
} else if (sWidth >= dWidth) {
srcp = new AstSel{streamp->fileline(), srcp, sWidth - dWidth, dWidth};
}
}
nodep->lhsp(dstp);
nodep->rhsp(srcp);
@@ -2230,6 +2271,14 @@ class ConstVisitor final : public VNVisitor {
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});
streamp->dtypeFrom(lhsDtypep);
}
} else if (m_doV && replaceAssignMultiSel(nodep)) {
return true;
@@ -3024,8 +3073,9 @@ class ConstVisitor final : public VNVisitor {
&& varrefp // Don't do messes with BITREFs/ARRAYREFs
&& !varrefp->varp()->hasStrengthAssignment() // Strengths are resolved in V3Tristate
&& !varrefp->varp()->valuep() // Not already constified
&& !varrefp->varScopep()) { // Not scoped (or each scope may have different initial
// value)
&& !varrefp->varScopep() // Not scoped (or each scope may have different initial val.)
&& !varrefp->varp()->isForced() // Not forced (not really a constant)
) {
// ASSIGNW (VARREF, const) -> INITIAL ( ASSIGN (VARREF, const) )
UINFO(4, "constAssignW " << nodep << endl);
// Make a initial assignment
@@ -3300,6 +3350,9 @@ class ConstVisitor final : public VNVisitor {
if (nodep->precondsp()) {
nodep->replaceWith(nodep->precondsp());
} else {
nodep->v3warn(UNUSEDLOOP,
"Loop condition is always false; body will never execute");
nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSEDLOOP, true);
nodep->unlinkFrBack();
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
@@ -3492,7 +3545,7 @@ class ConstVisitor final : public VNVisitor {
TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)");
TREEOP ("AstDiv {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
TREEOP ("AstDivS {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)");
TREEOP ("AstMul {operandIsPowTwo($lhsp), operandsSameSize($lhsp,,$rhsp)}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
TREEOP ("AstMul {operandIsPowTwo($lhsp), operandsSameWidth($lhsp,,$rhsp)}", "replaceMulShift(nodep)"); // a*2^n -> a<<n
TREEOP ("AstDiv {$lhsp, operandIsPowTwo($rhsp)}", "replaceDivShift(nodep)"); // a/2^n -> a>>n
TREEOP ("AstModDiv{$lhsp, operandIsPowTwo($rhsp)}", "replaceModAnd(nodep)"); // a % 2^n -> a&(2^n-1)
TREEOP ("AstPow {operandIsTwo($lhsp), !$rhsp.isZero}", "replacePowShift(nodep)"); // 2**a == 1<<a
@@ -3686,7 +3739,8 @@ class ConstVisitor final : public VNVisitor {
TREEOPC("AstXor {matchBitOpTree(nodep)}", "DONE");
// Note can't simplify a extend{extends}, extends{extend}, as the sign
// bits end up in the wrong places
TREEOPV("AstExtend {$lhsp.castExtend}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), Extend)->lhsp())");
TREEOPV("AstExtend{operandsSameWidth(nodep,,$lhsp)}", "replaceWLhs(nodep)");
TREEOPV("AstExtend{$lhsp.castExtend}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), Extend)->lhsp())");
TREEOPV("AstExtendS{$lhsp.castExtendS}", "replaceExtend(nodep, VN_AS(nodep->lhsp(), ExtendS)->lhsp())");
TREEOPV("AstReplicate{$srcp, $countp.isOne, $srcp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
TREEOPV("AstReplicateN{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs
+679 -433
View File
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -223,8 +223,7 @@ class DescopeVisitor final : public VNVisitor {
}
void visit(AstVarScope* nodep) override {
// Delete the varscope when we're finished
nodep->unlinkFrBack();
pushDeletep(nodep);
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstNodeVarRef* nodep) override {
iterateChildren(nodep);
+22 -20
View File
@@ -38,20 +38,21 @@ void DfgGraph::addGraph(DfgGraph& other) {
m_size += other.m_size;
other.m_size = 0;
const auto moveVertexList = [this](V3List<DfgVertex*>& src, V3List<DfgVertex*>& dst) {
if (DfgVertex* vtxp = src.begin()) {
vtxp->m_verticesEnt.moveAppend(src, dst, vtxp);
do {
vtxp->m_userCnt = 0;
vtxp->m_graphp = this;
vtxp = vtxp->verticesNext();
} while (vtxp);
}
};
moveVertexList(other.m_varVertices, m_varVertices);
moveVertexList(other.m_constVertices, m_constVertices);
moveVertexList(other.m_opVertices, m_opVertices);
for (DfgVertexVar& vtx : other.m_varVertices) {
vtx.m_userCnt = 0;
vtx.m_graphp = this;
}
m_varVertices.splice(m_varVertices.end(), other.m_varVertices);
for (DfgConst& vtx : other.m_constVertices) {
vtx.m_userCnt = 0;
vtx.m_graphp = this;
}
m_constVertices.splice(m_constVertices.end(), other.m_constVertices);
for (DfgVertex& vtx : other.m_opVertices) {
vtx.m_userCnt = 0;
vtx.m_graphp = this;
}
m_opVertices.splice(m_opVertices.end(), other.m_opVertices);
}
static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; }
@@ -74,6 +75,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
} else if (varVtxp->hasExtRefs()) {
os << ", shape=box, style=filled, fillcolor=firebrick2"; // Red
} else if (varVtxp->hasModRefs()) {
os << ", shape=box, style=filled, fillcolor=darkorange1"; // Orange
} else if (varVtxp->hasDfgRefs()) {
os << ", shape=box, style=filled, fillcolor=gold2"; // Yellow
} else if (varVtxp->keep()) {
os << ", shape=box, style=filled, fillcolor=grey";
@@ -98,6 +101,8 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
} else if (arrVtxp->hasExtRefs()) {
os << ", shape=box3d, style=filled, fillcolor=firebrick2"; // Red
} else if (arrVtxp->hasModRefs()) {
os << ", shape=box3d, style=filled, fillcolor=darkorange1"; // Orange
} else if (arrVtxp->hasDfgRefs()) {
os << ", shape=box3d, style=filled, fillcolor=gold2"; // Yellow
} else if (arrVtxp->keep()) {
os << ", shape=box3d, style=filled, fillcolor=grey";
@@ -229,7 +234,7 @@ static void dumpDotUpstreamConeFromVertex(std::ostream& os, const DfgVertex& vtx
// Emit all DfgVarPacked vertices that have external references driven by this vertex
vtx.forEachSink([&](const DfgVertex& dst) {
if (const DfgVarPacked* const varVtxp = dst.cast<DfgVarPacked>()) {
if (varVtxp->hasRefs()) dumpDotVertexAndSourceEdges(os, dst);
if (varVtxp->hasNonLocalRefs()) dumpDotVertexAndSourceEdges(os, dst);
}
});
}
@@ -263,7 +268,7 @@ void DfgGraph::dumpDotAllVarConesPrefixed(const string& label) const {
// Check if this vertex drives a variable referenced outside the DFG.
const DfgVarPacked* const sinkp
= vtx.findSink<DfgVarPacked>([](const DfgVarPacked& sink) { //
return sink.hasRefs();
return sink.hasNonLocalRefs();
});
// We only dump cones driving an externally referenced variable
@@ -341,10 +346,7 @@ DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType*
dfg.addVertex(*this);
}
DfgVertex::~DfgVertex() {
// TODO: It would be best to intern these via AstTypeTable to save the effort
if (VN_IS(m_dtypep, UnpackArrayDType)) VL_DO_DANGLING(delete m_dtypep, m_dtypep);
}
DfgVertex::~DfgVertex() {}
bool DfgVertex::selfEquals(const DfgVertex& that) const { return true; }
+333 -354
View File
@@ -55,6 +55,8 @@
#endif
class DfgEdge;
class DfgVertex;
class DfgGraph;
class DfgVisitor;
//------------------------------------------------------------------------------
@@ -89,140 +91,6 @@ constexpr bool operator==(VDfgType lhs, VDfgType::en rhs) { return lhs.m_e == rh
constexpr bool operator==(VDfgType::en lhs, VDfgType rhs) { return lhs == rhs.m_e; }
inline std::ostream& operator<<(std::ostream& os, const VDfgType& t) { return os << t.ascii(); }
//------------------------------------------------------------------------------
// Dataflow graph
//------------------------------------------------------------------------------
class DfgGraph final {
friend class DfgVertex;
// TYPES
// RAII handle for DfgVertex user data
class UserDataInUse final {
DfgGraph* m_graphp; // The referenced graph
public:
// cppcheck-suppress noExplicitConstructor
UserDataInUse(DfgGraph* graphp)
: m_graphp{graphp} {}
// cppcheck-suppress noExplicitConstructor
UserDataInUse(UserDataInUse&& that) {
UASSERT(that.m_graphp, "Moving from empty");
m_graphp = std::exchange(that.m_graphp, nullptr);
}
VL_UNCOPYABLE(UserDataInUse);
UserDataInUse& operator=(UserDataInUse&& that) {
UASSERT(that.m_graphp, "Moving from empty");
m_graphp = std::exchange(that.m_graphp, nullptr);
return *this;
}
~UserDataInUse() {
if (m_graphp) m_graphp->m_userCurrent = 0;
}
};
// MEMBERS
// Variables and constants make up a significant proportion of vertices (40-50% was observed
// in large designs), and they can often be treated specially in algorithms, which in turn
// enables significant Verilation performance gains, so we keep these in separate lists for
// direct access.
V3List<DfgVertex*> m_varVertices; // The variable vertices in the graph
V3List<DfgVertex*> m_constVertices; // The constant vertices in the graph
V3List<DfgVertex*> m_opVertices; // The operation vertices in the graph
size_t m_size = 0; // Number of vertices in the graph
uint32_t m_userCurrent = 0; // Vertex user data generation number currently in use
uint32_t m_userCnt = 0; // Vertex user data generation counter
// Parent of the graph (i.e.: the module containing the logic represented by this graph).
AstModule* const m_modulep;
const string m_name; // Name of graph (for debugging)
public:
// CONSTRUCTOR
explicit DfgGraph(AstModule& module, const string& name = "") VL_MT_DISABLED;
~DfgGraph() VL_MT_DISABLED;
VL_UNCOPYABLE(DfgGraph);
// METHODS
public:
// Add DfgVertex to this graph (assumes not yet contained).
inline void addVertex(DfgVertex& vtx);
// Remove DfgVertex form this graph (assumes it is contained).
inline void removeVertex(DfgVertex& vtx);
// Number of vertices in this graph
size_t size() const { return m_size; }
// Parent module
AstModule* modulep() const { return m_modulep; }
// Name of this graph
const string& name() const { return m_name; }
// Reset Vertex user data
UserDataInUse userDataInUse() {
UASSERT(!m_userCurrent, "Conflicting use of DfgVertex user data");
++m_userCnt;
UASSERT(m_userCnt, "'m_userCnt' overflow");
m_userCurrent = m_userCnt;
return UserDataInUse{this};
}
// Access to vertex lists for faster iteration in important contexts
inline DfgVertexVar* varVerticesBeginp() const;
inline DfgVertexVar* varVerticesRbeginp() const;
inline DfgConst* constVerticesBeginp() const;
inline DfgConst* constVerticesRbeginp() const;
inline DfgVertex* opVerticesBeginp() const;
inline DfgVertex* opVerticesRbeginp() const;
// Calls given function 'f' for each vertex in the graph. It is safe to manipulate any vertices
// in the graph, or to delete/unlink the vertex passed to 'f' during iteration. It is however
// not safe to delete/unlink any vertex in the same graph other than the one passed to 'f'.
inline void forEachVertex(std::function<void(DfgVertex&)> f);
// 'const' variant of 'forEachVertex'. No mutation allowed.
inline void forEachVertex(std::function<void(const DfgVertex&)> f) const;
// Add contents of other graph to this graph. Leaves other graph empty.
void addGraph(DfgGraph& other) VL_MT_DISABLED;
// Split this graph into individual components (unique sub-graphs with no edges between them).
// Also removes any vertices that are not weakly connected to any variable.
// Leaves 'this' graph empty.
std::vector<std::unique_ptr<DfgGraph>> splitIntoComponents(std::string label) VL_MT_DISABLED;
// Extract cyclic sub-graphs from 'this' graph. Cyclic sub-graphs are those that contain at
// least one strongly connected component (SCC) plus any other vertices that feed or sink from
// the SCCs, up to a variable boundary. This means that the returned graphs are guaranteed to
// be cyclic, but they are not guaranteed to be strongly connected (however, they are always
// at least weakly connected). Trivial SCCs that are acyclic (i.e.: vertices that are not part
// of a cycle) are left in 'this' graph. This means that at the end 'this' graph is guaranteed
// to be a DAG (acyclic). 'this' will not necessarily be a connected graph at the end, even if
// it was originally connected.
std::vector<std::unique_ptr<DfgGraph>>
extractCyclicComponents(std::string label) VL_MT_DISABLED;
// 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
// the name of the graph which is included in the output.
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,
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
// included in the file names and the output. This is useful for very large graphs that are
// otherwise difficult to browse visually due to their size.
void dumpDotAllVarConesPrefixed(const string& label = "") const VL_MT_DISABLED;
};
//------------------------------------------------------------------------------
// Dataflow graph edge
//------------------------------------------------------------------------------
@@ -264,7 +132,8 @@ class DfgVertex VL_NOT_FINAL {
using UserDataStorage = void*; // Storage allocated for user data
// STATE
V3ListEnt<DfgVertex*> m_verticesEnt; // V3List handle of this vertex, kept under the DfgGraph
V3ListLinks<DfgVertex> m_links; // V3List links
protected:
DfgEdge* m_sinksp = nullptr; // List of sinks of this vertex
FileLine* const m_filelinep; // Source location
@@ -280,6 +149,14 @@ protected:
public:
virtual ~DfgVertex() VL_MT_DISABLED;
private:
V3ListLinks<DfgVertex>& links() { return m_links; }
public:
// List type that can store Vertex (which must be a DfgVertex) instances via m_links
template <typename Vertex>
using List = V3List<DfgVertex, &DfgVertex::links, Vertex>;
// METHODS
private:
// Visitor accept method
@@ -330,10 +207,11 @@ public:
UDEBUGONLY(UASSERT_OBJ(isSupportedDType(nodep->dtypep()), nodep, "Unsupported dtype"););
// For simplicity, all packed types are represented with a fixed type
if (AstUnpackArrayDType* const typep = VN_CAST(nodep->dtypep(), UnpackArrayDType)) {
// TODO: these need interning via AstTypeTable otherwise they leak
return new AstUnpackArrayDType{typep->fileline(),
dtypeForWidth(typep->subDTypep()->width()),
typep->rangep()->cloneTree(false)};
AstNodeDType* const dtypep = new AstUnpackArrayDType{
typep->fileline(), dtypeForWidth(typep->subDTypep()->width()),
typep->rangep()->cloneTree(false)};
v3Global.rootp()->typeTablep()->addTypesp(dtypep);
return dtypep;
}
return dtypeForWidth(nodep->width());
}
@@ -348,54 +226,15 @@ public:
// Retrieve user data, constructing it fresh on first try.
template <typename T>
T& user() {
static_assert(sizeof(T) <= sizeof(UserDataStorage),
"Size of user data type 'T' is too large for allocated storage");
static_assert(alignof(T) <= alignof(UserDataStorage),
"Alignment of user data type 'T' is larger than allocated storage");
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
const uint32_t userCurrent = m_graphp->m_userCurrent;
UDEBUGONLY(UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"););
if (m_userCnt != userCurrent) {
m_userCnt = userCurrent;
// cppcheck-has-bug-suppress uninitvar
VL_ATTR_UNUSED T* const resultp = new (storagep) T{};
UDEBUGONLY(UASSERT_OBJ(resultp == storagep, this, "Something is odd"););
}
return *storagep;
}
inline T& user();
// Retrieve user data, must be current.
template <typename T>
T& getUser() {
static_assert(sizeof(T) <= sizeof(UserDataStorage),
"Size of user data type 'T' is too large for allocated storage");
static_assert(alignof(T) <= alignof(UserDataStorage),
"Alignment of user data type 'T' is larger than allocated storage");
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
#if VL_DEBUG
const uint32_t userCurrent = m_graphp->m_userCurrent;
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
UASSERT_OBJ(m_userCnt == userCurrent, this, "DfgVertex user data is stale");
#endif
return *storagep;
}
inline T& getUser();
// Set user data, becomes current.
template <typename T>
typename std::enable_if<sizeof(T) <= sizeof(void*), void>::type setUser(T value) {
static_assert(sizeof(T) <= sizeof(UserDataStorage),
"Size of user data type 'T' is too large for allocated storage");
static_assert(alignof(T) <= alignof(UserDataStorage),
"Alignment of user data type 'T' is larger than allocated storage");
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
const uint32_t userCurrent = m_graphp->m_userCurrent;
#if VL_DEBUG
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
#endif
m_userCnt = userCurrent;
*storagep = value;
}
inline void setUser(T value);
// Width of result
uint32_t width() const {
@@ -448,10 +287,6 @@ public:
// Relink all sinks to be driven from the given new source
void replaceWith(DfgVertex* newSourcep) VL_MT_DISABLED;
// Access to vertex list for faster iteration in important contexts
DfgVertex* verticesNext() const { return m_verticesEnt.nextp(); }
DfgVertex* verticesPrev() const { return m_verticesEnt.prevp(); }
// Calls given function 'f' for each source vertex of this vertex
// Unconnected source edges are not iterated.
inline void forEachSource(std::function<void(DfgVertex&)> f) VL_MT_DISABLED;
@@ -504,9 +339,6 @@ public:
// Is this a DfgConst that is all ones
inline bool isOnes() const VL_MT_DISABLED;
// Should this vertex be inlined when rendering to Ast, or be stored to a temporary
inline bool inlined() const VL_MT_DISABLED;
// Methods that allow DfgVertex to participate in error reporting/messaging
void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) {
m_filelinep->v3errorEnd(str);
@@ -569,9 +401,6 @@ public:
virtual const string srcName(size_t idx) const = 0;
};
// Specializations of privateTypeTest
#include "V3Dfg__gen_type_tests.h" // From ./astgen
//------------------------------------------------------------------------------
// Dfg vertex visitor
//------------------------------------------------------------------------------
@@ -585,152 +414,6 @@ public:
#include "V3Dfg__gen_visitor_decls.h" // From ./astgen
};
//------------------------------------------------------------------------------
// Inline method definitions
//------------------------------------------------------------------------------
void DfgGraph::addVertex(DfgVertex& vtx) {
// Note: changes here need to be replicated in DfgGraph::addGraph
++m_size;
if (vtx.is<DfgConst>()) {
vtx.m_verticesEnt.pushBack(m_constVertices, &vtx);
} else if (vtx.is<DfgVertexVar>()) {
vtx.m_verticesEnt.pushBack(m_varVertices, &vtx);
} else {
vtx.m_verticesEnt.pushBack(m_opVertices, &vtx);
}
vtx.m_userCnt = 0;
vtx.m_graphp = this;
}
void DfgGraph::removeVertex(DfgVertex& vtx) {
// Note: changes here need to be replicated in DfgGraph::addGraph
--m_size;
if (vtx.is<DfgConst>()) {
vtx.m_verticesEnt.unlink(m_constVertices, &vtx);
} else if (vtx.is<DfgVertexVar>()) {
vtx.m_verticesEnt.unlink(m_varVertices, &vtx);
} else {
vtx.m_verticesEnt.unlink(m_opVertices, &vtx);
}
vtx.m_userCnt = 0;
vtx.m_graphp = nullptr;
}
void DfgGraph::forEachVertex(std::function<void(DfgVertex&)> f) {
for (DfgVertex *vtxp = m_varVertices.begin(), *nextp; vtxp; vtxp = nextp) {
nextp = vtxp->verticesNext();
f(*vtxp);
}
for (DfgVertex *vtxp = m_constVertices.begin(), *nextp; vtxp; vtxp = nextp) {
nextp = vtxp->verticesNext();
f(*vtxp);
}
for (DfgVertex *vtxp = m_opVertices.begin(), *nextp; vtxp; vtxp = nextp) {
nextp = vtxp->verticesNext();
f(*vtxp);
}
}
void DfgGraph::forEachVertex(std::function<void(const DfgVertex&)> f) const {
for (const DfgVertex* vtxp = m_varVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) {
f(*vtxp);
}
for (const DfgVertex* vtxp = m_constVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) {
f(*vtxp);
}
for (const DfgVertex* vtxp = m_opVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) {
f(*vtxp);
}
}
void DfgVertex::forEachSource(std::function<void(DfgVertex&)> f) {
const auto pair = sourceEdges();
const DfgEdge* const edgesp = pair.first;
const size_t arity = pair.second;
for (size_t i = 0; i < arity; ++i) {
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
}
}
void DfgVertex::forEachSource(std::function<void(const DfgVertex&)> f) const {
const auto pair = sourceEdges();
const DfgEdge* const edgesp = pair.first;
const size_t arity = pair.second;
for (size_t i = 0; i < arity; ++i) {
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
}
}
void DfgVertex::forEachSink(std::function<void(DfgVertex&)> f) {
for (const DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
nextp = edgep->m_nextp;
f(*edgep->m_sinkp);
}
}
void DfgVertex::forEachSink(std::function<void(const DfgVertex&)> f) const {
for (const DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) f(*edgep->m_sinkp);
}
void DfgVertex::forEachSourceEdge(std::function<void(DfgEdge&, size_t)> f) {
const auto pair = sourceEdges();
DfgEdge* const edgesp = pair.first;
const size_t arity = pair.second;
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
}
void DfgVertex::forEachSourceEdge(std::function<void(const DfgEdge&, size_t)> f) const {
const auto pair = sourceEdges();
const DfgEdge* const edgesp = pair.first;
const size_t arity = pair.second;
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
}
void DfgVertex::forEachSinkEdge(std::function<void(DfgEdge&)> f) {
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
nextp = edgep->m_nextp;
f(*edgep);
}
}
void DfgVertex::forEachSinkEdge(std::function<void(const DfgEdge&)> f) const {
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
nextp = edgep->m_nextp;
f(*edgep);
}
}
const DfgEdge* DfgVertex::findSourceEdge(std::function<bool(const DfgEdge&, size_t)> p) const {
const auto pair = sourceEdges();
const DfgEdge* const edgesp = pair.first;
const size_t arity = pair.second;
for (size_t i = 0; i < arity; ++i) {
const DfgEdge& edge = edgesp[i];
if (p(edge, i)) return &edge;
}
return nullptr;
}
template <typename Vertex>
Vertex* DfgVertex::findSink(std::function<bool(const Vertex&)> p) const {
static_assert(std::is_base_of<DfgVertex, Vertex>::value,
"'Vertex' must be subclass of 'DfgVertex'");
for (DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) {
if (Vertex* const sinkp = edgep->m_sinkp->cast<Vertex>()) {
if (p(*sinkp)) return sinkp;
}
}
return nullptr;
}
template <typename Vertex>
Vertex* DfgVertex::findSink() const {
static_assert(!std::is_same<DfgVertex, Vertex>::value,
"'Vertex' must be proper subclass of 'DfgVertex'");
return findSink<Vertex>([](const Vertex&) { return true; });
}
//------------------------------------------------------------------------------
// DfgVertex sub-types follow
//------------------------------------------------------------------------------
@@ -905,20 +588,281 @@ public:
// The rest of the DfgVertex subclasses are generated by 'astgen' from AstNodeExpr nodes
#include "V3Dfg__gen_auto_classes.h"
DfgVertexVar* DfgGraph::varVerticesBeginp() const {
return static_cast<DfgVertexVar*>(m_varVertices.begin());
//------------------------------------------------------------------------------
// Dataflow graph
//------------------------------------------------------------------------------
class DfgGraph final {
friend class DfgVertex;
// TYPES
// RAII handle for DfgVertex user data
class UserDataInUse final {
DfgGraph* m_graphp; // The referenced graph
public:
// cppcheck-suppress noExplicitConstructor
UserDataInUse(DfgGraph* graphp)
: m_graphp{graphp} {}
// cppcheck-suppress noExplicitConstructor
UserDataInUse(UserDataInUse&& that) {
UASSERT(that.m_graphp, "Moving from empty");
m_graphp = std::exchange(that.m_graphp, nullptr);
}
VL_UNCOPYABLE(UserDataInUse);
UserDataInUse& operator=(UserDataInUse&& that) {
UASSERT(that.m_graphp, "Moving from empty");
m_graphp = std::exchange(that.m_graphp, nullptr);
return *this;
}
~UserDataInUse() {
if (m_graphp) m_graphp->m_userCurrent = 0;
}
};
// MEMBERS
// Variables and constants make up a significant proportion of vertices (40-50% was observed
// in large designs), and they can often be treated specially in algorithms, which in turn
// enables significant Verilation performance gains, so we keep these in separate lists for
// direct access.
DfgVertex::List<DfgVertexVar> m_varVertices; // The variable vertices in the graph
DfgVertex::List<DfgConst> m_constVertices; // The constant vertices in the graph
DfgVertex::List<DfgVertex> m_opVertices; // The operation vertices in the graph
size_t m_size = 0; // Number of vertices in the graph
uint32_t m_userCurrent = 0; // Vertex user data generation number currently in use
uint32_t m_userCnt = 0; // Vertex user data generation counter
// Parent of the graph (i.e.: the module containing the logic represented by this graph).
AstModule* const m_modulep;
const string m_name; // Name of graph (for debugging)
public:
// CONSTRUCTOR
explicit DfgGraph(AstModule& module, const string& name = "") VL_MT_DISABLED;
~DfgGraph() VL_MT_DISABLED;
VL_UNCOPYABLE(DfgGraph);
// METHODS
public:
// Add DfgVertex to this graph (assumes not yet contained).
inline void addVertex(DfgVertex& vtx);
// Remove DfgVertex form this graph (assumes it is contained).
inline void removeVertex(DfgVertex& vtx);
// Number of vertices in this graph
size_t size() const { return m_size; }
// Parent module
AstModule* modulep() const { return m_modulep; }
// Name of this graph
const string& name() const { return m_name; }
// Reset Vertex user data
UserDataInUse userDataInUse() {
UASSERT(!m_userCurrent, "Conflicting use of DfgVertex user data");
++m_userCnt;
UASSERT(m_userCnt, "'m_userCnt' overflow");
m_userCurrent = m_userCnt;
return UserDataInUse{this};
}
// Access to vertex lists
DfgVertex::List<DfgVertexVar>& varVertices() { return m_varVertices; }
const DfgVertex::List<DfgVertexVar>& varVertices() const { return m_varVertices; }
DfgVertex::List<DfgConst>& constVertices() { return m_constVertices; }
const DfgVertex::List<DfgConst>& constVertices() const { return m_constVertices; }
DfgVertex::List<DfgVertex>& opVertices() { return m_opVertices; }
const DfgVertex::List<DfgVertex>& opVertices() const { return m_opVertices; }
// Calls given function 'f' for each vertex in the graph. It is safe to manipulate any vertices
// in the graph, or to delete/unlink the vertex passed to 'f' during iteration. It is however
// not safe to delete/unlink any vertex in the same graph other than the one passed to 'f'.
inline void forEachVertex(std::function<void(DfgVertex&)> f);
// 'const' variant of 'forEachVertex'. No mutation allowed.
inline void forEachVertex(std::function<void(const DfgVertex&)> f) const;
// Add contents of other graph to this graph. Leaves other graph empty.
void addGraph(DfgGraph& other) VL_MT_DISABLED;
// Split this graph into individual components (unique sub-graphs with no edges between them).
// Also removes any vertices that are not weakly connected to any variable.
// Leaves 'this' graph empty.
std::vector<std::unique_ptr<DfgGraph>> splitIntoComponents(std::string label) VL_MT_DISABLED;
// Extract cyclic sub-graphs from 'this' graph. Cyclic sub-graphs are those that contain at
// least one strongly connected component (SCC) plus any other vertices that feed or sink from
// the SCCs, up to a variable boundary. This means that the returned graphs are guaranteed to
// be cyclic, but they are not guaranteed to be strongly connected (however, they are always
// at least weakly connected). Trivial SCCs that are acyclic (i.e.: vertices that are not part
// of a cycle) are left in 'this' graph. This means that at the end 'this' graph is guaranteed
// to be a DAG (acyclic). 'this' will not necessarily be a connected graph at the end, even if
// it was originally connected.
std::vector<std::unique_ptr<DfgGraph>>
extractCyclicComponents(std::string label) VL_MT_DISABLED;
// 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
// the name of the graph which is included in the output.
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,
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
// included in the file names and the output. This is useful for very large graphs that are
// otherwise difficult to browse visually due to their size.
void dumpDotAllVarConesPrefixed(const string& label = "") const VL_MT_DISABLED;
};
// Specializations of privateTypeTest
#include "V3Dfg__gen_type_tests.h" // From ./astgen
//------------------------------------------------------------------------------
// Inline method definitions - for DfgVertex
//------------------------------------------------------------------------------
template <typename T>
T& DfgVertex::user() {
static_assert(sizeof(T) <= sizeof(UserDataStorage),
"Size of user data type 'T' is too large for allocated storage");
static_assert(alignof(T) <= alignof(UserDataStorage),
"Alignment of user data type 'T' is larger than allocated storage");
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
const uint32_t userCurrent = m_graphp->m_userCurrent;
UDEBUGONLY(UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"););
if (m_userCnt != userCurrent) {
m_userCnt = userCurrent;
// cppcheck-has-bug-suppress uninitvar
VL_ATTR_UNUSED T* const resultp = new (storagep) T{};
UDEBUGONLY(UASSERT_OBJ(resultp == storagep, this, "Something is odd"););
}
return *storagep;
}
DfgVertexVar* DfgGraph::varVerticesRbeginp() const {
return static_cast<DfgVertexVar*>(m_varVertices.rbegin());
template <typename T>
T& DfgVertex::getUser() {
static_assert(sizeof(T) <= sizeof(UserDataStorage),
"Size of user data type 'T' is too large for allocated storage");
static_assert(alignof(T) <= alignof(UserDataStorage),
"Alignment of user data type 'T' is larger than allocated storage");
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
#if VL_DEBUG
const uint32_t userCurrent = m_graphp->m_userCurrent;
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
UASSERT_OBJ(m_userCnt == userCurrent, this, "DfgVertex user data is stale");
#endif
return *storagep;
}
DfgConst* DfgGraph::constVerticesBeginp() const {
return static_cast<DfgConst*>(m_constVertices.begin());
template <typename T>
void DfgVertex::setUser(T value) {
static_assert(sizeof(T) <= sizeof(UserDataStorage),
"Size of user data type 'T' is too large for allocated storage");
static_assert(alignof(T) <= alignof(UserDataStorage),
"Alignment of user data type 'T' is larger than allocated storage");
T* const storagep = reinterpret_cast<T*>(&m_userDataStorage);
const uint32_t userCurrent = m_graphp->m_userCurrent;
#if VL_DEBUG
UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");
#endif
m_userCnt = userCurrent;
*storagep = value;
}
DfgConst* DfgGraph::constVerticesRbeginp() const {
return static_cast<DfgConst*>(m_constVertices.rbegin());
void DfgVertex::forEachSource(std::function<void(DfgVertex&)> f) {
const auto pair = sourceEdges();
const DfgEdge* const edgesp = pair.first;
const size_t arity = pair.second;
for (size_t i = 0; i < arity; ++i) {
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
}
}
void DfgVertex::forEachSource(std::function<void(const DfgVertex&)> f) const {
const auto pair = sourceEdges();
const DfgEdge* const edgesp = pair.first;
const size_t arity = pair.second;
for (size_t i = 0; i < arity; ++i) {
if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep);
}
}
void DfgVertex::forEachSink(std::function<void(DfgVertex&)> f) {
for (const DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
nextp = edgep->m_nextp;
f(*edgep->m_sinkp);
}
}
void DfgVertex::forEachSink(std::function<void(const DfgVertex&)> f) const {
for (const DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) f(*edgep->m_sinkp);
}
void DfgVertex::forEachSourceEdge(std::function<void(DfgEdge&, size_t)> f) {
const auto pair = sourceEdges();
DfgEdge* const edgesp = pair.first;
const size_t arity = pair.second;
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
}
void DfgVertex::forEachSourceEdge(std::function<void(const DfgEdge&, size_t)> f) const {
const auto pair = sourceEdges();
const DfgEdge* const edgesp = pair.first;
const size_t arity = pair.second;
for (size_t i = 0; i < arity; ++i) f(edgesp[i], i);
}
void DfgVertex::forEachSinkEdge(std::function<void(DfgEdge&)> f) {
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
nextp = edgep->m_nextp;
f(*edgep);
}
}
void DfgVertex::forEachSinkEdge(std::function<void(const DfgEdge&)> f) const {
for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) {
nextp = edgep->m_nextp;
f(*edgep);
}
}
const DfgEdge* DfgVertex::findSourceEdge(std::function<bool(const DfgEdge&, size_t)> p) const {
const auto pair = sourceEdges();
const DfgEdge* const edgesp = pair.first;
const size_t arity = pair.second;
for (size_t i = 0; i < arity; ++i) {
const DfgEdge& edge = edgesp[i];
if (p(edge, i)) return &edge;
}
return nullptr;
}
template <typename Vertex>
Vertex* DfgVertex::findSink(std::function<bool(const Vertex&)> p) const {
static_assert(std::is_base_of<DfgVertex, Vertex>::value,
"'Vertex' must be subclass of 'DfgVertex'");
for (DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) {
if (Vertex* const sinkp = edgep->m_sinkp->cast<Vertex>()) {
if (p(*sinkp)) return sinkp;
}
}
return nullptr;
}
template <typename Vertex>
Vertex* DfgVertex::findSink() const {
static_assert(!std::is_same<DfgVertex, Vertex>::value,
"'Vertex' must be proper subclass of 'DfgVertex'");
return findSink<Vertex>([](const Vertex&) { return true; });
}
DfgVertex* DfgGraph::opVerticesBeginp() const { return m_opVertices.begin(); }
DfgVertex* DfgGraph::opVerticesRbeginp() const { return m_opVertices.rbegin(); }
bool DfgVertex::isZero() const {
if (const DfgConst* const constp = cast<DfgConst>()) return constp->isZero();
@@ -930,13 +874,48 @@ bool DfgVertex::isOnes() const {
return false;
}
bool DfgVertex::inlined() const {
// Inline vertices that drive only a single node, or are special
if (!hasMultipleSinks()) return true;
if (is<DfgConst>()) return true;
if (is<DfgVertexVar>()) return true;
if (const DfgArraySel* const selp = cast<DfgArraySel>()) return selp->bitp()->is<DfgConst>();
return false;
//------------------------------------------------------------------------------
// Inline method definitions - for DfgGraph
//------------------------------------------------------------------------------
void DfgGraph::addVertex(DfgVertex& vtx) {
// Note: changes here need to be replicated in DfgGraph::addGraph
++m_size;
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
m_constVertices.linkBack(cVtxp);
} else if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
m_varVertices.linkBack(vVtxp);
} else {
m_opVertices.linkBack(&vtx);
}
vtx.m_userCnt = 0;
vtx.m_graphp = this;
}
void DfgGraph::removeVertex(DfgVertex& vtx) {
// Note: changes here need to be replicated in DfgGraph::addGraph
--m_size;
if (DfgConst* const cVtxp = vtx.cast<DfgConst>()) {
m_constVertices.unlink(cVtxp);
} else if (DfgVertexVar* const vVtxp = vtx.cast<DfgVertexVar>()) {
m_varVertices.unlink(vVtxp);
} else {
m_opVertices.unlink(&vtx);
}
vtx.m_userCnt = 0;
vtx.m_graphp = nullptr;
}
void DfgGraph::forEachVertex(std::function<void(DfgVertex&)> f) {
for (DfgVertexVar* const vtxp : m_varVertices.unlinkable()) f(*vtxp);
for (DfgConst* const vtxp : m_constVertices.unlinkable()) f(*vtxp);
for (DfgVertex* const vtxp : m_opVertices.unlinkable()) f(*vtxp);
}
void DfgGraph::forEachVertex(std::function<void(const DfgVertex&)> f) const {
for (const DfgVertexVar& vtx : m_varVertices) f(vtx);
for (const DfgConst& vtx : m_constVertices) f(vtx);
for (const DfgVertex& vtx : m_opVertices) f(vtx);
}
#endif

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