Compare commits

..
194 Commits
Author SHA1 Message Date
Wilson Snyder 746c7ea8f7 Version bump 2022-10-01 08:28:27 -04:00
Wilson Snyder 5ed882faf2 Fix unused compiler warning when not VL_THREADED. 2022-09-30 23:41:35 -04:00
Wilson Snyder fa4b10b4d9 Commentary: Changes update 2022-09-30 23:03:26 -04:00
Wilson Snyder f25a9a4c80 examples: Use SC_ZERO_TIME in tracing (#3646) 2022-09-28 19:11:00 -04:00
Geza Lore a999c73ce0 Commentary 2022-09-28 14:43:40 +01:00
Wilson Snyder 75a70bee6d Update to clang-format-14 on Ubuntu22.04 2022-09-27 21:47:45 -04:00
Wilson Snyder 1b1907af49 Tests: Rename some tests 2022-09-27 18:42:03 -04:00
Jake Merdich 1b18eee5dc Tests: Fix CCache test not properly skipped (#3643)
If the skip condition happens, the rest still runs and it fails. Don't do that.
2022-09-27 09:11:49 -04:00
Ryszard Rozak 4931e48016 Support resolving assignments with equal strengths (#3637) 2022-09-26 21:21:37 -04:00
Geza Lore f96454adcc gitignore .gdb_history 2022-09-26 14:43:09 +01:00
Geza Lore 9a20a258f5 Omit AstNode::m_editCount in release build
This is only a debugging aid at this point, so compile out of the
release build. This reduces peak memory consumption by 4-5%. We still
keep the global counters to detect the tree have changed, to avoid
unnecessary dumps.
2022-09-25 08:57:33 +01:00
Geza Lore 10796457d2 V3Life: don't depend on AstNode::editCountGbl()
No functional change intended.
2022-09-24 20:45:30 +01:00
Geza Lore 78e659a142 Reduce size of FileLine
Multiple tricks to reduce the size of class FileLine from 72 to 40
bytes:

- Reduce file name index from 32 to 16 bits. This still allows 64K
  unique input files, which is hopefully enough.
- Intern message/warning enable bitset and use a 16-bit index, again
  allowing 64K unique sets which is hopefully enough.
- Put the m_waive flag into the sign bit of one of the line numbers.
- Use explicit reference counting to avoid overhead of shared_ptr.

Added assertions to ensure interned data fits within it's index space.

This saves ~5-10% peak memory consumption at no measurable run-time cost
on various designs.
2022-09-24 20:16:21 +01:00
Geza Lore 2f50642ecb Exclude VL_DEFINE_DEBUG_FUNCTIONS from code coverage 2022-09-23 17:20:24 +01:00
Geza Lore 050060b139 Make enum constructors and operators constexpr 2022-09-23 11:10:28 +01:00
Geza Lore 63c694f65f Streamline dump control options
- Rename `--dump-treei` option to `--dumpi-tree`, which itself is now a
  special case of `--dumpi-<tag>` where tag can be a magic word, or a
  filename
- Control dumping via static `dump*()` functions, analogous to `debug()`
- Make dumping independent of the value of `debug()` (so dumping always
  works even without the debug flag)
- Add separate `--dumpi-graph` for dumping V3Graphs, which is again a
  special case of `--dumpi-<tag>`
- Alias `--dump-<tag>` to `--dumpi-<tag> 3` as before
2022-09-22 17:24:41 +01:00
github action 12093e6939 Apply 'make format' 2022-09-21 19:22:15 +00:00
Geza Lore 9949a6cd17 Generate AstGen::checkTreeiter to enforce Ast op*p use
Use astgen to generate a more thorough version of AstNode::checkTree,
which checks that operands are or consistent structure and type, as
described in the @astgen op directives. Also change checkTree to always
run when --debug-check is given.

Fix discovered fallout.
2022-09-21 18:12:11 +01:00
Geza Lore ce03293128 Generate AstNode accessors via astgen
Introduce the @astgen directives parsed by astgen, currently used for
the generation child node (operand) accessors. Please see the updated
internal documentation for details.
2022-09-21 13:56:03 +01:00
Geza Lore 0a8cfb8d2c Put dump file in TEST_OBJ_DIR 2022-09-21 11:35:03 +01:00
Kamil Rakoczy 0b07679ff2 v3errorEnd: look for instance only when warning is not ignored (#3632)
This approach reduced total time of V3Undriven stage from 34,2s to 2,5s
in design containing almost 400 000 unused variables.

Signed-off-by: Kamil Rakoczy <[email protected]>
2022-09-21 10:54:23 +01:00
Wilson Snyder 5df14627fd Fix 32-bit build of previous commit 2022-09-20 18:23:44 -04:00
Mariusz Glebocki fc3ce29845 Improve Verilation memory by reducing V3Number size (#3521) 2022-09-20 16:46:47 -04:00
Yu-Sheng Lin bba800f2d6 Fix calling trace() after open() segfault (#3610) (#3627) 2022-09-20 16:45:09 -04:00
Ryszard Rozak fe2a1e1749 Remove assignments with strengths weaker than strongest non-tristate RHS (#3629) 2022-09-19 04:54:20 -04:00
Wilson Snyder fc4ffd454e Rename --bin to --build-dep-bin. 2022-09-18 10:32:43 -04:00
Wilson Snyder 8dae4ad93a Tests: Rename to avoid dash. 2022-09-18 10:19:15 -04:00
Wilson Snyder 8da261f302 Tests: Rename to avoid dash. 2022-09-18 10:19:15 -04:00
Geza Lore 7d88e63bab astgen: generate type specific addNext, remove astNextNull
Generate type specific static overloads of Ast<Node>::addNext, which
return the correct sub-type of the 'this' they were invoked on.

Also remove AstNode::addNextNull, which is now only used in the parser,
implement in verilog.y directly as a template function.
2022-09-17 15:05:22 +01:00
Wilson Snyder 79be097e34 Sort -V env variable output 2022-09-17 08:17:55 -04:00
Wilson Snyder 11b0d36ba2 Merge cleanups from 'develop-v5'. No functional change 2022-09-17 08:17:22 -04:00
Wilson Snyder 80b73859a2 Commentary: Some fixes from 'develop-v5' 2022-09-17 08:00:40 -04:00
Wilson Snyder a548a77f26 Merge branch 'develop-v5' into master: Changes file 2022-09-17 07:58:56 -04:00
Wilson Snyder d6bfb37e6a Docker: Use 22.04 2022-09-17 07:47:06 -04:00
Geza Lore 38a8d7fb2e Remove redundant 'inline' keywords from definitions
Also add checks to t/t_dist_cppstyle
2022-09-16 15:52:25 +01:00
Geza Lore 0c70a0dcbf Remove redundant 'virtual' keywords from overridden methods
'virtual' is redundant when 'override' is present, so keep only
'override'.

Add t/t_dist_cppstyle.pl to check for this.
2022-09-16 15:19:38 +01:00
Geza Lore d16619fe86 astgen: Explicitly generate AstNode members
Generate boilerplate members of AstNode sub-types directly via astgen.
This is in preparation for generating additional members.
2022-09-16 11:18:20 +01:00
Wilson Snyder 2dc85a5acd Internals: enum constructor cleanups. No functional change intended. 2022-09-15 19:58:10 -04:00
Kamil Rakoczy dbe1348b4c Tests: Fix earlier commit, add build jobs to stats (#3623) (#3626) 2022-09-15 11:29:50 -04:00
Wilson Snyder d74536a4dc Internals: Cleanup some constructors. No functional change intended. 2022-09-15 08:54:04 -04:00
Kamil Rakoczy da20da264b Add --build-jobs, and rework arguments for -j (#3623) 2022-09-15 08:28:58 -04:00
Geza Lore 22b9dfb9c9 Split and re-order AstNode definitions (#3622)
- Move DType representations into V3AstNodeDType.h
- Move AstNodeMath and subclasses into V3AstNodeMath.h
- Move any other AstNode subtypes into V3AstNodeOther.h
- Fix up out-of-order definitions via inline methods and implementations
  in V3Inlines.h and V3AstNodes.cpp
- Enforce declaration order of AstNode subtypes via astgen, 
  which will now fail when definitions are mis-ordered.
2022-09-15 13:10:39 +01:00
Wilson Snyder e43c089ab8 Commentary 2022-09-14 23:26:34 -04:00
Wilson Snyder d85b909054 Internals: Use std:: for mem and str functions. 2022-09-14 21:10:19 -04:00
Wilson Snyder 75fd71d7e5 Add --main to generate main() C++ (previously was experimental only) (#3265). 2022-09-14 20:18:40 -04:00
Wilson Snyder 9efd64ab98 Commentary 2022-09-14 20:13:28 -04:00
Wilson Snyder 7aa01625d8 Commentary: Changes update 2022-09-14 08:15:42 -04:00
Ryszard Rozak a3c58d7b70 Support IEEE constant signal strengths (#3601). 2022-09-14 07:39:27 -04:00
Kamil Rakoczy ae466b1703 Internals: Improve Verilation peak memory usage in V3Subst (#3512). 2022-09-14 07:37:51 -04:00
Geza Lore 2564484429 astgen: Rewrite in a more OOP way, in preparation for extensions
Rely less on strings and represent AstNode classes as a 'class Node',
with all associated properties kept together, rather than distributed
over multiple dictionaries or constructed at retrieval time.

No functional change intended.
2022-09-13 21:54:12 +01:00
Kamil Rakoczy 93a044f587 Internals: Rework addFilesp towards parallel emit (#3620). No functional change intended. 2022-09-13 12:15:34 -04:00
Wilson Snyder 81fe35ee2e Fix typedef'ed class conversion to boolean (#3616). 2022-09-12 18:03:56 -04:00
Geza Lore 08b6bdddf9 Update default --mod-prefix when --prefix is repeated
Fixes #3603
2022-09-12 17:25:09 +01:00
Kamil Rakoczy 4d49db48a3 Internals: Remove usage of user1 from EmitCTrace (#3617). No Functional change intended. 2022-09-12 12:00:41 -04:00
Kamil Rakoczy 9b2266f68c Internals: Remove usage of global state in V3EmitCFunc (#3615). No functional change intended. 2022-09-12 11:59:14 -04:00
Wilson Snyder 752f425025 Tests: Process/Semaphore/Mailbox testing (all fail until supported) 2022-09-11 13:05:24 -04:00
Gustav Svensk 47262cd4ec Fix arguments in non-static method call (#3547) (#3582) 2022-09-11 12:33:31 -04:00
Wilson Snyder 47e64535d6 Commentary 2022-09-11 12:25:44 -04:00
Wilson Snyder 45d622e4bc Github actions: Avoid cpan cert expirations. 2022-09-08 11:06:44 -04:00
Wilson Snyder 249feaae7c Tests: Remove lint-py, need ci package. 2022-09-07 22:59:32 -04:00
Wilson Snyder 9a27004ae5 Github actions: Avoid cpan cert expirations. 2022-09-07 22:49:09 -04:00
Wilson Snyder 42283150d7 Actions: Use 22.04 for contrib action 2022-09-07 22:13:02 -04:00
Wilson Snyder 5a1bcf9794 Tests: Add lint-py checker 2022-09-07 22:04:57 -04:00
Wilson Snyder 361cef4633 Fix pylint warnings. 2022-09-07 21:48:52 -04:00
github action e94cdcf29c Apply 'make format' 2022-09-05 22:43:09 +00:00
Mladen Slijepcevic 1af046986d Fix thread saftey in SystemC VL_ASSIGN_SBW/WSB (#3494) (#3513). 2022-09-05 18:42:12 -04:00
Wilson Snyder 1c9263a25b Commentary 2022-09-05 15:20:08 -04:00
Krzysztof Bieganski 6b6790fc50 Preserve return type of AstNode::addNext via templating (#3597)
No functional change intended.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-09-05 16:56:57 +01:00
Krzysztof Bieganski fb931087ab Add stats tracking for V3Undriven. (#3600)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-09-05 16:20:38 +01:00
Geza Lore 937e893b6d Build verilator_bin with -O3 (#3592)
This is consistently a few percent faster.
2022-09-03 22:10:07 +01:00
Geza Lore d42a2d6494 Fix V3Gate crash on circular logic
The recent patch to defer substitutions on V3Gate crashes on circular
logic that has cycle length >= 3 with all inlineable signals (cycle
length 2 is detected correctly and is not inlined). Fix by stopping
recursion at the loop-back edge.

Fixes #3543
2022-09-02 19:58:58 +01:00
Geza Lore 8e8f4b1e5c Remove AstVarScope::valuep() and related code
This is detritus from when V3TraceDecl used to run after V3Gate, today
V3TraceDecl runs before V3Gate and this value has no function at all.

No functional change intended.
2022-09-02 16:44:13 +01:00
Geza Lore 2ba39b25f1 Replace dynamic_casts with static_casts
dynamic_cast is not free. Replace obvious instances (where the result is
unconditionally dereferenced) with static_cast in contexts with
performance implications.
2022-09-02 12:08:34 +01:00
Geza Lore 5c828b7e60 V3Partition: use V3Lists to keep track of SiblingMCs
Replace std::set<SiblingMC> with V3Lists to keep track of SiblingMCs
associated with MTasks, use a std::set<LogicMTask*> for ensuring
uniqueness. This yields a bit more speed in PartContraction.
2022-09-01 19:40:44 +01:00
Wilson Snyder 8d0c06e570 devel release 2022-08-31 19:49:24 -04:00
Wilson Snyder 5b2fbf4f37 Version bump 2022-08-31 19:46:45 -04:00
Wilson Snyder 592dab2bdb Commentary: Changes update 2022-08-31 19:27:43 -04:00
Wilson Snyder 51daa64e9a Fix --hierarchical with order-based pin connections (#3585). 2022-08-31 18:12:21 -04:00
Aleksander Kiryk 2136afde6b Support negated properties (#3572) 2022-08-30 06:33:42 -04:00
Wilson Snyder ea55db7286 Internals: Cleanup some string constructors. No functional change. 2022-08-30 01:02:39 -04:00
Wilson Snyder 6a5f77b278 Internals: Cleanup some string/model constructors. No functional change. 2022-08-29 23:50:32 -04:00
Wilson Snyder 8658a0d7dc Internals: Constructor format update. No functional change. 2022-08-29 23:05:52 -04:00
Wilson Snyder c335aad25f Fix --hierarchical with order-based pin connections (#3583). 2022-08-29 22:49:19 -04:00
Wilson Snyder 9d9d647c1f Fix indentation of --protect import function SV code. 2022-08-29 22:28:02 -04:00
Wilson Snyder d47a37fb76 Internals: Cleanup constructors etc. No functional change. 2022-08-29 22:17:27 -04:00
Arkadiusz Kozdra 0a3a15a66e Support class parameters (#2231) (#3541) 2022-08-28 10:24:55 -04:00
Wilson Snyder 2358ced061 Rename tracing rolloverSize and add test (#3570). 2022-08-28 08:25:02 -04:00
Varun Koyyalagunta 5869fdf7f6 Fix $dump systemtask with --output-split-cfuncs (#3495) (#3497) 2022-08-25 18:29:11 -05:00
Krzysztof Bieganski 1a1d2ecfd9 Enable tracing in generated main (#3578)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-08-25 14:55:37 +01:00
Geza Lore 9ac64d0b92 Improve performance of MTask coarsening
Various optimizations to speed up MTasks coarsening (which is the long
pole in the multi-threaded scheduling of very large designs).

The biggest impact ones:
- Use efficient hand written Pairing Heaps for implementing priority
  queues and the scoreboard, instead of the old SortByValueMap. This
  helps us avoid having to sort a lot of merge candidates that we will
  never actually consider and helps a lot in performance.
- Remove unnecessary associative containers and store data structures
  (the heap nodes in particular) directly in the object they relate to.
  This eliminates a huge amount of lookups and helps a lot in
  performance.
- Distribute storage for SiblingMC instances into the LogicMTask
  instances, and combine with the sibling maps. This again eliminates
  hash table lookups and makes storage structures smaller.
- Remove some now bidirectional edge maps, keep only the forward map.

There are also some other smaller optimizations:
- Replaced more unnecessary dynamic_casts with static_casts
- Templated some functions/classes to reduce the number of static
  branches in loops.
- Improves sorting of edges for sibling candidate creation
- Various micro-optimizations here and there

This speeds up MTask coarsening by 3.8x on a large design, which
translates to a 2.5x speedup of the ordering pass in multi-threaded
mode. (Combined with the earlier optimizations, ordering is now 3x
faster.)

Due to the elimination of a lot of the auxiliary data structures, and
ensuring a minimal size for the necessary ones, memory consumption of
the MTask coarsening is also reduced (measured up to 4.4x reduction
though the accuracy of this is low).

The algorithm is identical except for minor alterations of the order
some candidates are added or removed, this can cause perturbation in the
output due to tied scores being broken based on IDs.
2022-08-20 21:18:50 +01:00
Wilson Snyder c6607724cb Fix clang warning. 2022-08-20 14:19:00 -04:00
Wilson Snyder 90dc04cf93 Add --future0 and --future1 options. 2022-08-20 14:01:13 -04:00
Geza Lore 4d81eb021d Revert "Improve performance of MTask coarsening"
This reverts commit 83475008d9.
2022-08-19 18:03:45 +01:00
Geza Lore 83475008d9 Improve performance of MTask coarsening
Various optimizations to speed up MTasks coarsening (which is the long
pole in the multi-threaded scheduling of very large designs).

The biggest impact ones:
- Use efficient hand written Pairing Heaps for implementing priority
  queues and the scoreboard, instead of the old SortByValueMap. This
  helps us avoid having to sort a lot of merge candidates that we will
  never actually consider and helps a lot in performance.
- Remove unnecessary associative containers and store data structures
  (the heap nodes in particular) directly in the object they relate to.
  This eliminates a huge amount of lookups and helps a lot in
  performance.
- Distribute storage for SiblingMC instances into the LogicMTask
  instances, and combine with the sibling maps. This again eliminates
  hash table lookups and makes storage structures smaller.
- Remove some now bidirectional edge maps, keep only the forward map.

There are also some other smaller optimizations:
- Replaced more unnecessary dynamic_casts with static_casts
- Templated some functions/classes to reduce the number of static
  branches in loops.
- Improves sorting of edges for sibling candidate creation
- Various micro-optimizations here and there

This speeds up MTask coarsening by 3.8x on a large design, which
translates to a 2.5x speedup of the ordering pass in multi-threaded
mode. (Combined with the earlier optimizations, ordering is now 3x
faster.)

Due to the elimination of a lot of the auxiliary data structures, and
ensuring a minimal size for the necessary ones, memory consumption of
the MTask coarsening is also reduced (measured up to 4.4x reduction
though the accuracy of this is low).

The algorithm is identical except for minor alterations of the order
some candidates are added or removed, this can cause perturbation in the
output due to tied scores being broken based on IDs.
2022-08-19 16:59:20 +01:00
Geza Lore 03ac7ad730 Make PartPropagateCp specific to the MTask graph
While keeping the client code abstract in PartPropagateCp is nice for
testing, there is performance to be had removing the abstraction. As
this code dominates in scheduling large designs, we eliminate the
abstraction and re-work the testing to use the actual LogicMTask and
MTaskEdge graph types. No functional change intended.
2022-08-19 14:06:11 +01:00
Geza Lore cd50949a7e Reuse MTaskEdge instances in MT scheduling
Instead of deleting then re-allocating MTaskEdge instances when merging
two MTasks, just redirect the edged of the donor MTask to the recipient
MTask. This is both faster as it avoids an allocation and a deletion,
together with one update of the sibling maps, and also makes the
algorithm more stable due to MergeCandidate IDs being stable and
allocated up front for all MTaskEdges, before any SiblingMCs are
allocated.

Perturbations in output are expected as the IDs used to break ties
between merge candidates with equal costs are not updated when
redirecting an edge (on purpose). The relinking of only one end of the
graph edges also perturbs the order in which they are enumerated, which
does change candidate opportunities when the number of edges is larger
than PART_SIBLING_EDGE_LIMIT. Confirmed output is identical when
IDs are updated and edges are updated to appear in their original order.
2022-08-19 14:06:11 +01:00
Geza Lore f0040c7b9a Remove reliance on pointer comparison in MT scheduling
The critical path propagation used to rely on a pointer comparison to
break equal scoring critical path updates. Use the corresponding mtask
ids instead, which is deterministic across invocations.
2022-08-19 14:06:11 +01:00
Geza Lore f8a0389e73 Do not use stepCost when gathering sibling merge candidates
siblingPairFromRelatives gathers neighbours of a vertex, and sorts them.
It then takes the N best nodes, and creates sibling merge candidates
from them. We now use the unadjusted cost instead of the step cost of
the vertices when sorting. This is both faster as we need not do the
log-space rounding to compute stepCost, and will also make similar but
yet cheaper nodes appear closer to the front as we don't lose precision
in rounding, hence they are more likely to be entered as merge
candidates. Note that when creating the merge candidate, we still use
the stepCost, so it's purpose of reducing the propagation of critical
path updates is maintained in full. In summary, this should make both
Verilator and the generated model very slightly faster, at least in
theory, and I have observed minor improvement in places.
2022-08-19 14:06:11 +01:00
Geza Lore b436794773 Add specialized GraphStreamUnordered
GraphStreamUnordered used to be GraphStream<std::less<const
V3GraphVertex*>>, but a lot of performance improvements can be had by a
specialized implementation, so added a highly optimized one. This helps
a lot with --debug-partition.
2022-08-19 14:06:11 +01:00
Geza Lore 90d22cbec6 Fix AstNode::exists return type 2022-08-19 13:22:06 +01:00
Krzysztof Bieganski 33e2acfe61 Fix AstNode::forall return type (#3559)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-08-19 12:33:17 +01:00
Ryszard Rozak db5fdfb0ee Fix === with some tristate constants (#3551). 2022-08-18 07:03:05 -04:00
Krzysztof Bieganski 951cd73fe0 Handle MemberSel in V3EmitV.cpp (#3555) 2022-08-18 06:33:45 -04:00
Arkadiusz Kozdra 0eeb40b975 Fix converting subclasses to string (#3552) 2022-08-17 18:08:43 -04:00
Wilson Snyder 93272c13fd Tests: Confirm fixed (#181) 2022-08-15 22:17:36 -04:00
Wilson Snyder 43abaeb055 Tests: Confirm fixed (#485) 2022-08-15 22:17:17 -04:00
Wilson Snyder 18b9e661c9 Tests: Confirm fixed (#446) 2022-08-15 22:17:09 -04:00
Wilson Snyder f435d96241 Fix case statement comparing string literal (#3544). 2022-08-15 21:56:09 -04:00
github action d32e3f042f Apply 'make format' 2022-08-12 10:56:12 +00:00
Mostafa Gamal df5f95a5bd Fix nested default assignment for struct pattern (#3511) (#3524) 2022-08-12 06:55:07 -04:00
Drew Ranck b0c475205b Fix void-cast queue pop_front or pop_back (#3542) (#3364)
Fix compile error for queue method usage, if it is the
first statement in a block of code, and the return
value is not used. Example:

>  if (foo)
>    void'(bar.pop_front());
2022-08-12 06:51:25 -04:00
Wilson Snyder 1e2219347e Internals: Cleanup ifdef, move up not under compilver version ifdef 2022-08-11 17:41:43 -04:00
Wilson Snyder cbe1b8e266 Fix segfault exporting non-existant package (#3535). 2022-08-08 17:53:50 -04:00
Mariusz Glebocki 2b12fe5773 Internals: Construct V3Number with correct type instead of changing it manually. (#3529) 2022-08-08 08:17:02 -04:00
Yutetsu TAKATSUKASA d20f22beb1 Fix tristate logic when reading inout port in a module #3399 (#3523)
* Tests: Add a test to reproduce #3399

* Fix #3399. When reading an inout port in a module, it should refer the
original inout port, not the generated MODTEMP.
2022-08-07 21:12:57 +09:00
Wilson Snyder f4fe10844b Tests: Fix t_flag_help.pl (#3532). 2022-08-07 04:57:59 -04:00
Mariusz Glebocki 122e89ffde Fix V3Number::isMsbXZ(). (#3530) 2022-08-05 19:12:52 +01:00
Geza Lore 96a4b3e5a5 Update clang-format config and apply
- Regroup and sort #include directives (like we used to, but automatic)
- Set AlwaysBreakTemplateDeclarations to true
2022-08-05 12:00:24 +01:00
Geza Lore fac8e76923 Rework SortByValueMap for better performance
Keep a single std::set of key/value pairs, and a single unordered_map
from key to iterators into the set. Also improve some of the accessing
mechanisms using modern C++. This speeds up multi-threaded ordering by
about 10%.
2022-08-03 21:17:02 +01:00
Geza Lore b864f5f5ba V3Partition: use static_cast with LogicMTaskVertex
dynamic_cast is not free, and the mtask graph contains only
LogicMTaskVertex vertices, use static_cast instead for some speedup.
2022-08-03 17:05:01 +01:00
Geza Lore f9f66d787e Fix integer overflow in V3Unroll (#3451) 2022-08-03 09:41:30 +01:00
Geza Lore bd211c87aa astgen: split 'visit' method declarations from definitions
Add definitions to V3Ast.cpp, and use static_cast.
This fixes a lot of clang-tidy noise.
2022-08-02 17:53:19 +01:00
Geza Lore 6c33e6e889 Tell clang-tidy .h files are C++ (not C) headers 2022-08-02 17:53:19 +01:00
Kamil Rakoczy cfb6fd8b34 Reduce max RSS usage (#3483)
By constant folding nodes earlier in V3Expand, we can save some max RSS on large designs.
2022-08-02 13:36:14 +01:00
Geza Lore cb60663d49 V3Gate: Defer substitutions until required as well
Similarly to the earlier patch that defers constant folding on optimized
logic, now we also defer the variable substitutions as well. This again
eliminates a lot of traversals, and yields another ~10x speedup of V3Gate
on a design where V3Gate used to dominate while producing identical
results.
2022-08-01 12:54:41 +01:00
Geza Lore 0d2bf23d82 V3Gate: Defer constant folding until required
Rather than constant folding each logic block after every substitution,
only constant fold updated blocks when re-analysed, or at the end. This
removes a lot of invocations of V3Const on large blocks that can be
optimized well, and should yield the same result.

This speeds up V3Gate by ~4x on a design where V3Gate dominates.
2022-07-31 20:42:04 +01:00
Geza Lore 682a60e325 Cleanup V3Gate, no functional change 2022-07-31 20:07:54 +01:00
Geza Lore 2ab6272cc7 Use AstNode::foreach in V3Gate
This yields a little speedup.
2022-07-31 20:05:25 +01:00
Geza Lore 152a6cd886 Improve AstNode::foreach (also exists and forall)
Speed improvements:
- Use a direct, recursion-free implementation
- Improve pre-fetching

Functionality:
- Support remove/replace of currently iterated node
2022-07-31 19:07:32 +01:00
Wilson Snyder f91793e931 Revert - SC overrides cause non-override clang error. 2022-07-30 13:53:54 -04:00
Wilson Snyder a2d26b45bb Internals: Fix some clang-tidy issues. No functional change intended. 2022-07-30 11:54:28 -04:00
Wilson Snyder dce8f3d25d Internals: Spacing from develop-v5. No functional change. 2022-07-30 11:54:28 -04:00
Geza Lore 38e5b6c1ad Replace __gcov_flush with __gcov_dump
__gcov_flush was a private function and was removed from later GCC
versions (at least from 11.2.0, possibly earlier). Replace with the
documented public __gcov_dump.
2022-07-30 16:02:03 +01:00
Wilson Snyder b9d7819faa Internals: Fix some cppcheck issues. Some dump functions fixed. 2022-07-30 10:01:39 -04:00
Yutetsu TAKATSUKASA 1f9323d086 Set correct dtype in replaceShiftSame() (#3520)
* Tests: Add a test to reproduce bug3399

* Fix3399. Set the correct dtype in replaceShiftSame().

* Tests: update stats.

* Update Changes
2022-07-29 07:05:04 +09:00
Geza Lore 574dbfded1 V3MergeCond: Fix incorrect merge of assignments to the condition 2022-07-28 15:50:02 +01:00
Wilson Snyder 2a87387eb3 Documentation fixes (#3514) 2022-07-28 08:41:01 -04:00
Geza Lore a5ddd10e31 Tests: compare VCD files both ways
vcddiff is a bit broken, and sometimes 'vcddiff a b' fails while the
files are indeed equivalent. There is a chance however that 'vcddif b a'
will succeed in this case, so compare trace files both ways when
checking test results and claim success if vcddiff succeeds in at least
one direction.
2022-07-27 10:48:02 +01:00
github action e871cd8a44 Apply 'make format' 2022-07-25 21:47:29 +00:00
Mostafa Gamal 7b431b37c7 Fix struct pattern assignment (#2328) (#3517). 2022-07-25 17:46:22 -04:00
Gustav Svensk eeef5ab4de Fix sformat string incorrectly cleared (#3515) (#3519). 2022-07-25 17:36:34 +02:00
Geza Lore ac4ec87942 Respect clang's default -fbracket-depth by default
Set default value of --comp-limit-parens to 240, to respect default
 maximum nesting of parentheses in clang (which is controlled by
 -fbracket-depth and defaults to 256). For code generation consistency,
 also use the same default with gcc.
2022-07-25 12:59:26 +01:00
Geza Lore 290c2e0388 Mark FileLine::v3errorEndFatal as noreturn 2022-07-25 12:51:02 +01:00
Geza Lore 89924bda51 Always type '$clog2' as signed 32 2022-07-25 12:48:13 +01:00
Yutetsu TAKATSUKASA 60eab3eb8c Fix wrong result of bit op tree optimization #3509 (#3516)
* Tests: Add a test to reproduce #3509

* Tests: Compile without tautological-compare check because bit op tree optimization is disabled in the test.

* Internals: Dedup code. No functional change is intended.

* Fix #3509.

"2'b10 == (2'b11 & {1'b0, val[0]})"  and "2'b10 != (2'b11 & {1'b0, val[0]})" were
wrongly optimized to "!val[0]" and "val[0]" respectively.
Now properly optimize them to 1'b0 and 1'b1.

* Commentary

* Commentary: Update Changes
2022-07-24 19:54:37 +09:00
Geza Lore e0b61ceabd Remove legacy #ifdef SYSTEMC_64BIT_PATCHES
These days this is always false, see #3505
2022-07-21 15:01:17 +01:00
Geza Lore 30e3edb81d Remove deprecated and unused timescale override defines
These have been 'deprecated' for 2 years and are otherwise unused except
for using a temporary placeholder value, which I have inlined with the
default value.

Also remove the now VL_TIME_STR_CONVERT utility function (and
corresponding unit tests), which have no references in any project on
GitHub.
2022-07-20 14:06:09 +01:00
Arkadiusz Kozdra 542e324869 Wildcard index type support for associative arrays (#3501).
Associative arrays that specify a wildcard index type may be indexed by
integral expressions of any size, with leading zeros removed
automatically.  A natural representation for such expressions is a
string, especially that the standard explicitly specifies automatic
casts from string indices to bit vectors of equivalent size.
The automatic cast part is done implicitly by the existing type system.

A simpler way to just make this work would be to convert wildcard index
type to a string type directly in the parser code, but several new AST
classes are needed to make sure illegal method calls are detected.
The verilated data structure implementation is reused, because there is
no need for differentiating the behavior on C++ side.
2022-07-20 15:01:36 +02:00
Geza Lore 1c5e5704f5 Fix iteration fixup in AstNode::addHereThisAsNext
Previous version broke verialor_ext_tests due to iteration order
mismatch after 3fc8249429
2022-07-20 13:08:51 +01:00
Geza Lore 1d400dd98c Configure tracing at run-time, instead of compile time (#3504)
All remaining use of conditional compilation in the tracing
implementation of the run-time library are replaced with the use of
VerilatedModel::traceConfig, and is now done at run-time.
2022-07-20 11:27:10 +01:00
Geza Lore a4ed3c2086 Make parallel tracing switchable at run-time 2022-07-19 17:13:13 +01:00
Geza Lore efb5caad22 Improve robustness of trace configuration
Always fail if adding a model to a trace file that has already executed
a dump. We used to do this before as well, though in a less robust way.
We will be relying on this property more in the future, so improve the
check.
2022-07-19 14:16:08 +01:00
Geza Lore 3a002b6cf2 Remove VerilatedVcd::m_evcd and related dead code.
The legacy code that was using this was removed earlier, and m_evcd was
constant false, so removed.
2022-07-19 13:58:18 +01:00
Geza Lore f8b7981be4 Make use of FST writer thread switchable at run-time.
Always build the FST libray with -DFST_WRITER_PARALLEL, iff VL_THREADED.
This supports run-time enablement of the FST writer thread, and has no
measurable performance impact on single threaded tracing but simplifies
the library build.

Note: the actual choice of using the fst writer thread is still compile
time, but can now be made run-time easily.
2022-07-19 13:48:03 +01:00
Geza Lore b55ee79d86 Fix typo 2022-07-19 12:36:21 +01:00
Geza Lore af70db88db Remove unused method 2022-07-19 11:32:16 +01:00
Geza Lore 7ef033f876 Ensure generated Makefile for hierarchical build is stable.
Avoid iterating unordered_map. Iterate sorted blocks instead.
2022-07-19 11:32:01 +01:00
Geza Lore db59c07f27 Implement trace offloading with fewer ifdefs
Step towards a proper run-time library. Reduce the amount of ifdefs in
the implementation of offloaded tracing. There are still a very small
number of ifdefs left, which will need more careful changes in order to
keep user API compatibility.
2022-07-19 11:31:35 +01:00
Geza Lore 9085e34d70 Pass VerilatedModel at trace registration time 2022-07-19 11:00:09 +01:00
Arkadiusz Kozdra 0dfa7d3af5 Internals: const-qualify findDType function. No functional change. (#3502) 2022-07-18 18:58:55 +02:00
Todd Strader b0e796ca83 Public combo propagation issues (#2905) 2022-07-15 11:44:32 -04:00
Geza Lore 3bd830eacf Minor clean up of initialization 2022-07-13 18:24:48 +01:00
Geza Lore f4efcbde5c Remove simple use of static data from V3OutFormatter::indentSpaces 2022-07-13 16:15:21 +01:00
Geza Lore 658819bb71 Trivial static const -> constexpr 2022-07-13 16:01:03 +01:00
Geza Lore 3fc8249429 Use AstNode::addHereThisAsNext in a few places 2022-07-13 13:57:00 +01:00
Geza Lore e0a38ce2c2 Remove unnecessary AstNode::clearIter() 2022-07-13 13:57:00 +01:00
Geza Lore 178e1789b5 Make AstNode::addHereThisAsNext always O(1)
Using unlinkFrBackWithNext is O(n) in the size of the list if unlinking
from the middle, so addHereThisAsNext also had this complexity. This
patch implements addHereThisAsNext directly, which is always O(1).
2022-07-13 12:13:40 +01:00
William D. Jones 108c900387 Fix unique_ptr memory header for MinGW64 (#3493). 2022-07-13 06:38:03 -04:00
Wilson Snyder 63507e8e29 Internals: Favor UASSERT_OBJ when have object. 2022-07-12 18:02:57 -04:00
Geza Lore 87f1e06c41 Small algorithmic improvement of PartContraction::siblingPairFromRelatives
Use std::partial_sort for the non-exhaustive case. This is O(n) instead
of O(n*log(n)) in the size of the candidate list being sorted. (It
actually is O(n*log(k)), but k is constant 6 in the non-exhaustive
case).
2022-07-12 19:10:01 +01:00
Geza Lore 7e8bafd217 Remove static data use from PartContraction::siblingPairFromRelatives
Use std::sort with lambda rather than qsort with static function and
static data. Verilation performance neutral.
2022-07-12 19:09:40 +01:00
Geza Lore 457ad07ade Remove unnecessary static state from V3EmitCFunc 2022-07-12 17:51:17 +01:00
Geza Lore 79c901c220 Tighten signatures/implementaion of VerilatedModel abstract methods. 2022-07-12 16:06:08 +01:00
Geza Lore b61d819fcb Move contextp() under VerilatedModel 2022-07-12 16:06:08 +01:00
Geza Lore f4038e3674 Move thread pool and execution profiler into the context. (#3477)
Fixes #3454
2022-07-12 11:41:15 +01:00
Arkadiusz Kozdra 8377514127 Add support for $test$plusargs(expr) (#3489) 2022-07-11 06:21:35 -04:00
Wilson Snyder d8ea989eda Tests/examples: Remove some legacy Verilator:: calls. 2022-07-09 09:50:50 -04:00
Wilson Snyder 5f3316d3dc * Fix empty string arguments to display (#3484). 2022-07-09 08:30:57 -04:00
Wilson Snyder a4fddb3fbe Fix table misoptimizing away display (#3488). 2022-07-09 07:55:46 -04:00
Wilson Snyder 3d71716a8a Internals: Constructor style cleanup. No functional change. 2022-07-09 07:40:07 -04:00
Yutetsu TAKATSUKASA 9f37cef1bb Fix #3470 of incorrect bit op tree optimization (#3476)
* Tests: Add a test to reproduce #3470

* Update LSB during return path of traversal. No functional change is intended.

* Introduce LeafInfo::m_msb

* Update LeafInfo::m_msb when visitin AstCCast

* Internals: Add comment, reorder. No functional change is intended.

* Delete explicit from copy constructor to fix build error.

* Update Changes

* Internals: Remove unused parameter. No functional change is intended.

* Tests: Add explanation to t_const_opt.
2022-07-06 08:33:37 +09:00
Geza Lore 0de1bbc85b Add and use VL_CONSTEXPR_CXX17 2022-07-05 14:21:28 +01:00
Geza Lore 3aa8624658 Set 'threads' in tests via parameter to compile
This is in preparation to #3454.
2022-07-05 12:33:41 +01:00
Geza Lore 42b711b862 Don't use 'assert' in profiler initialization 2022-07-05 12:18:54 +01:00
Mariusz Glebocki 2873dbe154 Optimize file writing by using a memory buffer. (#3461) 2022-07-04 10:23:31 -04:00
Felix Yan 31a83cb0d8 Fix use of C++17 on Arch Linux (#3479) 2022-06-29 11:24:30 -04:00
Geza Lore 383e384739 Remove always true cfg_with_threaded from test driver 2022-06-27 15:23:32 +01:00
Geza Lore 1bb6433649 Improve worker thread shutdown.
Always ensure worker thread task queue is drained before shutting down.
2022-06-27 15:03:36 +01:00
Yutetsu TAKATSUKASA ced39d0982 Internals: preparation for fixing #3470 (#3475)
* Internals: Let LeafInfo class. No functional change is intended.

* Internals: Rename LeafInfo::width -> LeafInfo::varWidth(). No functional change is intende.
2022-06-27 22:41:33 +09:00
Wilson Snyder fc4d6a62af Remove VL_PROFILER ifdef. Partial (#3454). 2022-06-22 20:06:23 -04:00
Wilson Snyder fa99cbbc73 Commentary: Fix mis-sorted option names. No functional change. 2022-06-21 19:28:26 -04:00
Wilson Snyder 49455721a3 Commentary 2022-06-21 19:28:23 -04:00
Unai Martinez-Corral 11032b1936 Fix bisonpre for MSYS2 (#3471) 2022-06-20 11:59:27 -04:00
Wilson Snyder f2fba51fe2 devel release 2022-06-19 15:13:29 -04:00
527 changed files with 30408 additions and 19760 deletions
+21 -10
View File
@@ -17,7 +17,7 @@ AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
@@ -59,15 +59,27 @@ ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
# Include grouping/sorting
SortIncludes: true
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
- Regex: '"(config_build|verilated_config|verilatedos)\.h"'
Priority: -1 # Sepecials before main header
- Regex: '(<|")verilated.*'
Priority: 1 # Runtime headers
- Regex: '"V3.*__gen.*\.h"'
Priority: 3 # Generated internal headers separately
- Regex: '"V3.*"'
Priority: 2 # Internal header
- Regex: '".*"'
Priority: 4 # Other non-system headers
- Regex: '<[[:alnum:]_.]+>'
Priority: 5 # Simple system headers next
- Regex: '<.*>'
Priority: 6 # Other system headers next
IncludeIsMainRegex: '$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 4
@@ -91,7 +103,6 @@ PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
+1 -1
View File
@@ -11,7 +11,7 @@ on:
jobs:
Test:
name: "'docs/CONTRIBUTORS' was signed"
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- run: test_regress/t/t_dist_contributors.pl
+5 -5
View File
@@ -10,11 +10,11 @@ on:
jobs:
format:
runs-on: ubuntu-20.04
name: Ubuntu 20.04 | format
runs-on: ubuntu-22.04
name: Ubuntu 22.04 | format
env:
CI_OS_NAME: linux
CI_RUNS_ON: ubuntu-20.04
CI_RUNS_ON: ubuntu-22.04
CI_COMMIT: ${{ github.sha }}
CI_M32: 0
steps:
@@ -27,14 +27,14 @@ jobs:
CI_BUILD_STAGE_NAME: build
run: |
bash ci/ci-install.bash &&
sudo apt-get install clang-format-11 yapf3 &&
sudo apt-get install clang-format-14 yapf3 &&
git config --global user.email "[email protected]" &&
git config --global user.name "github action"
- name: Format code
run: |
autoconf &&
./configure &&
make -j 2 format CLANGFORMAT=clang-format-11 &&
make -j 2 format CLANGFORMAT=clang-format-14 &&
git status
- name: Push
run: |
+1
View File
@@ -1,5 +1,6 @@
\#*
.#*
.gdb_history
.nfs*
*~
*.tidy
+64 -5
View File
@@ -8,6 +8,65 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 4.228 2022-10-01
==========================
**Announcement:**
* The next release is anticipated primere Verilator Version 5. Please
consider beta-testing the github 'develop-v5' branch, which will soon
merge into the github 'master' branch (#3383).
**Minor:**
* Support some IEEE signal strengths (#3601) (#3629). [Ryszard Rozak/Antmicro]
* Add --main to generate main() C++ (previously was experimental only).
* Add --build-jobs, and rework arguments for -j (#3623). [Kamil Rakoczy]
* Rename --bin to --build-dep-bin.
* Rename debug flags --dumpi-tree, --dumpi-graph, etc. [Geza Lore]
* Fix thread saftey in SystemC VL_ASSIGN_SBW/WSB (#3494) (#3513). [Mladen Slijepcevic]
* Fix crash in gate optimization of circular logic (#3543). [Bill Flynn]
* Fix arguments in non-static method call (#3547) (#3582). [Gustav Svensk]
* Fix default --mod-prefix when --prefix is repeated (#3603). [Geza Lore]
* Fix calling trace() after open() segfault (#3610) (#3627). [Yu-Sheng Lin]
* Fix typedef'ed class conversion to boolean (#3616). [Aleksander Kiryk]
* Fix Verilation speed when disabled warnings (#3632). [Kamil Rakoczy/Antmicro]
Verilator 4.226 2022-08-31
==========================
**Minor:**
* Add --future0 and --future1 options.
* Support class parameters (#2231) (#3541). [Arkadiusz Kozdra/Antmicro]
* Support wildcard index associative arrays (#3501). [Arkadiusz Kozdra/Antmicro]
* Support negated properties (#3572). [Aleksander Kiryk]
* Support $test$plusargs(expr) (#3489).
* Rename trace rolloverSize() (#3570).
* Improve Verilation speed with --threads on large designs. [Geza Lore]
* Improve Verilation memory by reducing V3Number (#3521). [Mariusz Glebocki/Antmicro]
* Fix struct pattern assignment (#2328) (#3517). [Mostafa Gamal]
* Fix public combo propagation issues (#2905). [Todd Strader]
* Fix incorrect tristate logic (#3399) [shareefj, Vighnesh Iyer]
* Fix incorrect bit op tree optimization (#3470). [algrobman]
* Fix bisonpre for MSYS2 (#3471).
* Fix max memory usage (#3483). [Kamil Rakoczy/Antmicro]
* Fix empty string arguments to display (#3484). [Grulfen]
* Fix table misoptimizing away display (#3488). [Stefan Post]
* Fix unique_ptr memory header for MinGW64 (#3493).
* Fix $dump systemtask with --output-split-cfuncs (#3495) (#3497). [Varun Koyyalagunta]
* Fix wrong bit op tree optimization (#3509). [Nathan Graybeal]
* Fix nested default assignment for struct pattern (#3511) (#3524). [Mostafa Gamal]
* Fix sformat string incorrectly cleared (#3515) (#3519). [Gustav Svensk]
* Fix segfault exporting non-existant package (#3535).
* Fix void-cast queue pop_front or pop_back (#3542) (#3364). [Drew Ranck]
* Fix case statement comparing string literal (#3544). [Gustav Svensk]
* Fix === with some tristate constants (#3551). [Ryszard Rozak/Antmicro]
* Fix converting subclasses to string (#3552). [Arkadiusz Kozdra/Antmicro]
* Fix --hierarchical with order-based pin connections (#3583) (#3585). [Kelin9298]
Verilator 4.224 2022-06-19
==========================
@@ -26,7 +85,7 @@ Verilator 4.224 2022-06-19
* Improve conditional merging optimization (#3125). [Geza Lore, Shunyao CAD]
* Define VM_TRACE_VCD when tracing in VCD format. [Geza Lore, Shunyao CAD]
* Add assert when VerilatedContext is mis-deleted (#3121). [Rupert Swarbrick]
* Internal prep work towards timing control. [Krzysztof Bieganski]
* Internal prep work towards timing control. [Krzysztof Bieganski/Antmicro]
* Fix hang with large case statement optimization (#3405). [Mike Urbach]
* Fix UNOPTFLAT warning from initial static var (#3406). [Kamil Rakoczy]
* Fix compile error when enable VL_LEAK_CHECKS (#3411). [HungMingWu]
@@ -193,7 +252,7 @@ Verilator 4.212 2021-09-01
* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore]
* Support unpacked array localparams in tasks/functions (#3078). [Geza Lore]
* Support timeunit/timeprecision in $unit.
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski]
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski/Antmicro]
* Add --instr-count-dpi to tune assumed DPI import cost for multithreaded
model scheduling. Default value changed to 200 (#3068). [Yinan Xu]
* Output files are split based on the set of headers required
@@ -258,7 +317,7 @@ Verilator 4.204 2021-06-12
* Prep work towards better ccache hashing/performance. [Geza Lore]
* Fix assertion failure in bitOpTree optimization (#2891) (#2899). [Raynard Qiao]
* Fix DPI functions not seen as vpiModule (#2893). [Todd Strader]
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski]
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski/Antmicro]
* Fix slowdown in elaboration (#2911). [Nathan Graybeal]
* Fix initialization of assoc in assoc array (#2914). [myftptoyman]
* Fix make support for gmake 3.x (#2920) (#2921). [Philipp Wagner]
@@ -373,7 +432,7 @@ Verilator 4.108 2021-01-10
**Major:**
* Many VPI changes for IEEE compatibility, which may alter behavior from previous releases.
* Support randomize() class method and rand (#2607). [Krzysztof Bieganski]
* Support randomize() class method and rand (#2607). [Krzysztof Bieganski/Antmicro]
**Minor:**
@@ -433,7 +492,7 @@ Verilator 4.104 2020-11-14
* Support queue and associative array 'with' statements (#2616).
* Support queue slicing (#2326).
* Support associative array pattern assignments and defaults.
* Support static methods and typedefs in classes (#2615). [Krzysztof Bieganski]
* Support static methods and typedefs in classes (#2615). [Krzysztof Bieganski/Antmicro]
* Add error on typedef referencing self (#2539). [Cody Piersall]
* With --debug, turn off address space layout randomization.
* Fix iteration over mutating list bug in VPI (#2588). [Kaleb Barrett]
+10 -7
View File
@@ -322,7 +322,10 @@ cppcheck: $(CPPCHECK_DEP)
$(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 -DVL_THREADED=1 $(CPPCHECK_INC) $<
CLANGTIDY = clang-tidy
CLANGTIDY_FLAGS = -config='' -checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables'
CLANGTIDY_FLAGS = -config='' \
-header-filter='.*' \
-checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables,-cppcoreguidelines-avoid-goto,-modernize-avoid-c-arrays,-readability-magic-numbers,-readability-simplify-boolean-expr,-cppcoreguidelines-macro-usage' \
CLANGTIDY_DEP = $(subst .h,.h.tidy,$(CPPCHECK_H)) \
$(subst .cpp,.cpp.tidy,$(CPPCHECK_CPP))
CLANGTIDY_DEFS = -DVL_DEBUG=1 -DVL_THREADED=1 -DVL_CPPCHECK=1
@@ -331,7 +334,7 @@ clang-tidy: $(CLANGTIDY_DEP)
%.cpp.tidy: %.cpp
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) | 2>&1 tee $@
%.h.tidy: %.h
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) | 2>&1 tee $@
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) -x c++-header | 2>&1 tee $@
analyzer-src:
-rm -rf src/obj_dbg
@@ -343,13 +346,13 @@ analyzer-include:
format: clang-format yapf format-pl-exec
CLANGFORMAT = clang-format-11
CLANGFORMAT = clang-format-14
CLANGFORMAT_FLAGS = -i
CLANGFORMAT_FILES = $(CPPCHECK_CPP) $(CPPCHECK_H) $(CPPCHECK_YL) test_regress/t/*.c* test_regress/t/*.h
clang-format:
@$(CLANGFORMAT) --version | egrep 11.0 > /dev/null \
|| echo "*** You are not using clang-format 11.0, indents may differ from master's ***"
@$(CLANGFORMAT) --version | egrep 14.0 > /dev/null \
|| echo "*** You are not using clang-format-14, indents may differ from master's ***"
$(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CLANGFORMAT_FILES)
PY_PROGRAMS = \
@@ -388,10 +391,10 @@ yapf:
FLAKE8 = flake8
FLAKE8_FLAGS = \
--extend-exclude=fastcov.py \
--ignore=E123,E129,E251,E501,W503,W504,E701
--ignore=E123,E129,E251,E402,E501,W503,W504,E701
PYLINT = pylint
PYLINT_FLAGS = --disable=R0801
PYLINT_FLAGS = --score=n --disable=R0801
lint-py:
-$(FLAKE8) $(FLAKE8_FLAGS) $(PY_PROGRAMS)
+17 -16
View File
@@ -28,7 +28,7 @@ Welcome to Verilator
- |Logo|
* - |verilator multithreaded performance|
- **Fast**
* Outperforms many commercial simulators
* Outperforms many closed-source commercial simulators
* Single- and multi-threaded output models
* - **Widely Used**
* Wide industry and academic deployment
@@ -55,20 +55,19 @@ performing lint checks, and optionally inserting assertion checks and
coverage-analysis points. It outputs single- or multi-threaded .cpp and .h
files, the "Verilated" code.
The user writes a little C++/SystemC wrapper file, which instantiates the
"Verilated" model of the user's top level module. These C++/SystemC files
are then compiled by a C++ compiler (gcc/clang/MSVC++). Executing the
resulting executable performs the design simulation. Verilator also
supports linking Verilated generated libraries, optionally encrypted, into
other simulators.
These Verilated C++/SystemC files are then compiled by a C++ compiler
(gcc/clang/MSVC++), optionally along with a user's own C++/SystemC wrapper
file to instantiate the Verilated model. Executing the resulting executable
performs the design simulation. Verilator also supports linking Verilated
generated libraries, optionally encrypted, into other simulators.
Verilator may not be the best choice if you are expecting a full featured
replacement for Incisive, ModelSim/Questa, VCS or another commercial
Verilog simulator, or if you are looking for a behavioral Verilog simulator
e.g. for a quick class project (we recommend `Icarus Verilog`_ for this.)
However, if you are looking for a path to migrate SystemVerilog to C++ or
SystemC, or your team is comfortable writing just a touch of C++ code,
Verilator is the tool for you.
replacement for a closed-source Verilog simulator, need SDF annotation,
mixed-signal simulation, or are doing a quick class project (we recommend
`Icarus Verilog`_ for classwork.) However, if you are looking for a path
to migrate SystemVerilog to C++/SystemC, or want high speed simulation of
synthesizable designs containing limited verification constructs, Verilator
is the tool for you.
Performance
@@ -85,9 +84,11 @@ multithreading (yielding 200-1000x total over interpreted simulators).
Verilator has typically similar or better performance versus the
closed-source Verilog simulators (Carbon Design Systems Carbonator,
Modelsim, 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 cycles/dollar.
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
cycles/dollar.
Installation & Documentation
============================
+29 -20
View File
@@ -282,35 +282,40 @@ detailed descriptions of these arguments.
--autoflush Flush streams after all $displays
--bbox-sys Blackbox unknown $system calls
--bbox-unsup Blackbox unsupported language features
--bin <filename> Override Verilator binary
--build Build model executable/library after Verilation
-CFLAGS <flags> C++ compiler arguments for makefile
--build-dep-bin <filename> Override build dependency Verilator binary
--build-jobs <jobs> Parallelism for --build
--cc Create C++ output
--cdc Clock domain crossing analysis
-CFLAGS <flags> C++ compiler arguments for makefile
--clk <signal-name> Mark specified signal as clock
--make <build-tool> Generate scripts for specified build tool
--no-clk <signal-name> Prevent marking specified signal as clock
--compiler <compiler-name> Tune for specified C++ compiler
--converge-limit <loops> Tune convergence settle time
--coverage Enable all coverage
--coverage-line Enable line coverage
--coverage-max-width <width> Maximum array depth for coverage
--coverage-toggle Enable toggle coverage
--coverage-user Enable SVL user coverage
--coverage-underscore Enable coverage of _signals
--coverage-user Enable SVL user coverage
-D<var>[=<value>] Set preprocessor define
--debug Enable debugging
--debug-check Enable debugging assertions
--no-debug-leak Disable leaking memory in --debug mode
--debugi <level> Enable debugging at a specified level
--debugi-<srcfile> <level> Enable debugging a source file at a level
--no-decoration Disable comments and symbol decorations
--default-language <lang> Default language to parse
+define+<var>=<value> Set preprocessor define
--dpi-hdr-only Only produce the DPI header file
--dump-defines Show preprocessor defines with -E
--dump-tree Enable dumping .tree files
--dump-treei <level> Enable dumping .tree files at a level
--dump-treei-<srcfile> <level> Enable dumping .tree file at a source file at a level
--dump-graph Enable dumping V3Graphs to .dot
--dump-tree Enable dumping Ast .tree files
--dump-tree-addrids Use short identifiers instead of addresses
--dump-<srcfile> Enable dumping everything in source file
--dumpi-graph <level> Enable dumping V3Graphs to .dot files at level
--dumpi-tree <level> Enable dumping Ast .tree 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
--exe Link to create executable
@@ -321,6 +326,7 @@ detailed descriptions of these arguments.
--flatten Force inlining of all modules, tasks and functions
-fno-<optimization> Disable internal optimization stage
-G<name>=<value> Overwrite top-level parameter
--gate-stmts <value> Tune gate optimizer depth
--gdb Run Verilator under GDB interactively
--gdbbt Run Verilator under GDB for backtrace
--generate-key Create random key for --protect-key
@@ -328,54 +334,52 @@ detailed descriptions of these arguments.
--help Display this help
--hierarchical Enable hierarchical Verilation
-I<dir> Directory to search for includes
-j <jobs> Parallelism for --build
--gate-stmts <value> Tune gate optimizer depth
--if-depth <value> Tune IFDEPTH warning
+incdir+<dir> Directory to search for includes
--inline-mult <value> Tune module inlining
--instr-count-dpi <value> Assumed dynamic instruction count of DPI imports
-LDFLAGS <flags> Linker pre-object arguments for makefile
-j <jobs> Parallelism for --build (alias to --build-jobs)
--l2-name <value> Verilog scope name of the top module
--language <lang> Default language standard to parse
-LDFLAGS <flags> Linker pre-object arguments for makefile
--lib-create <name> Create a DPI library
+libext+<ext>+[ext]... Extensions for finding modules
--lint-only Lint, but do not make output
--make <build-tool> Generate scripts for specified build tool
-MAKEFLAGS <flags> Arguments to pass to make during --build
--main Generate C++ main() file
--max-num-width <value> Maximum number width (default: 64K)
--MMD Create .d dependency files
--MP Create phony dependency targets
--Mdir <directory> Name of output object directory
--MMD Create .d dependency files
--mod-prefix <topname> Name to prepend to lower classes
--no-clk <signal-name> Prevent marking specified signal as clock
--no-decoration Disable comments and symbol decorations
--no-pins64 Don't use uint64_t's for 33-64 bit sigs
--no-skip-identical Disable skipping identical output
--MP Create phony dependency targets
+notimingchecks Ignored
-O0 Disable optimizations
-O3 High performance optimizations
-O<optimization-letter> Selectable optimizations
-o <executable> Name of final executable
--no-order-clock-delay Disable ordering clock enable assignments
--no-verilate Skip verilation and just compile previously Verilated code.
--no-verilate Skip Verilation, only compile previously Verilated code
--output-split <statements> Split .cpp files into pieces
--output-split-cfuncs <statements> Split model functions
--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-sc-uint Specify types for top level ports
--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
--no-pins64 Don't use uint64_t's for 33-64 bit sigs
--pipe-filter <command> Filter all input through a script
--pp-comments Show preprocessor comments with -E
--prefix <topname> Name of top level class
--private Debugging; see docs
--prof-c Compile C++ code with profiling
--prof-cfuncs Name functions for profiling
--prof-exec Enable generating execution profile for gantt chart
--prof-pgo Enable generating profiling data for PGO
--protect-key <key> Key for symbol protection
--protect-ids Hash identifier names for obscurity
--protect-key <key> Key for symbol protection
--protect-lib <name> Create a DPI protected library
--private Debugging; see docs
--public Debugging; see docs
--public-flat-rw Mark all variables, etc as public_flat_rw
-pvalue+<name>=<value> Overwrite toplevel parameter
@@ -386,6 +390,7 @@ detailed descriptions of these arguments.
--rr Run Verilator and record with rr
--savable Enable model save-restore
--sc Create SystemC output
--no-skip-identical Disable skipping identical output
--stats Create statistics file
--stats-vars Provide statistics on variables
-sv Enable SystemVerilog parsing
@@ -413,6 +418,7 @@ detailed descriptions of these arguments.
--unused-regexp <regexp> Tune UNUSED lint signals
-V Verbose version and config
-v <filename> Verilog library
--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
@@ -427,6 +433,9 @@ detailed descriptions of these arguments.
-Wno-lint Disable all lint warnings
-Wno-style Disable all style warnings
-Wpedantic Warn on compliance-test issues
-Wwarn-<message> Enable specified warning message
-Wwarn-lint Enable lint warning message
-Wwarn-style Enable style warning message
--x-assign <mode> Assign non-initial Xs to this value
--x-initial <mode> Assign initial Xs to this value
--x-initial-edge Enable initial X->0 and X->1 edge triggers
+1 -1
View File
@@ -38,7 +38,7 @@ def diff_dir(a, b):
anyfile = False
for base in sorted(files.keys()):
if (not 'a' in files[base]) or (not 'b' in files[base]):
if ('a' not in files[base]) or ('b' not in files[base]):
continue
a = files[base]['a']
b = files[base]['b']
+2 -1
View File
@@ -149,7 +149,8 @@ def report():
for thread in Threads:
# Make potentially multiple characters per column
for start in Threads[thread]:
if not Threads[thread][start]: continue
if not Threads[thread][start]:
continue
cpu = Threads[thread][start]['cpu']
elapsed = Threads[thread][start]['end'] - start
if cpu not in Global['cpus']:
+4
View File
@@ -19,6 +19,10 @@ set -x
cd $(dirname "$0")/..
# Avoid occasional cpan failures "Issued certificate has expired."
export PERL_LWP_SSL_VERIFY_HOSTNAME=0
echo "check_certificate = off" >> ~/.wgetrc
fatal() {
echo "ERROR: $(basename "$0"): $1" >&2; exit 1;
}
+1 -1
View File
@@ -6,7 +6,7 @@
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
FROM ubuntu:20.04
FROM ubuntu:22.04
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive \
+1 -1
View File
@@ -8,7 +8,7 @@ Verilator build. It uses the following parameters:
- Source revision (default: master)
- Compiler (GCC 9.3.0, clang 10.0.0, default: 9.3.0)
- Compiler (GCC 10.3.0, clang 10.0.0, default: 10.3.0)
The container is published as ``verilator/verilator-buildenv`` on `docker
hub
+1 -1
View File
@@ -6,7 +6,7 @@
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
FROM ubuntu:20.04
FROM ubuntu:22.04
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive \
+6 -6
View File
@@ -5,14 +5,14 @@
# General Public License Version 3 or the Perl Artistic License Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.224 2022-06-19],
[https://verilator.org],
[verilator],[https://verilator.org])
# When releasing, also update header of Changes file
# 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],[4.228 2022-10-01],
[https://verilator.org],
[verilator],[https://verilator.org])
AC_CONFIG_HEADERS(src/config_build.h)
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc verilator-config.cmake verilator-config-version.cmake)
@@ -355,7 +355,7 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
case "$(which lsb_release 2>&1 > /dev/null && lsb_release -d)" in
*Ubuntu*22.04*)
*Arch*Linux* | *Ubuntu*22.04*)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
;;
+12
View File
@@ -5,11 +5,13 @@ Please see the Verilator manual for 200+ additional contributors. Thanks to all.
Adrien Le Masle
Ahmed El-Mahmoudy
Aleksander Kiryk
Alex Chadwick
Àlex Torregrosa
Aliaksei Chapyzhenka
Ameya Vikram Singh
Andreas Kuster
Arkadiusz Kozdra
Chris Randall
Chuxuan Wang
Conor McCullough
@@ -32,6 +34,7 @@ Gianfranco Costamagna
Glen Gibb
Graham Rushton
Guokai Chen
Gustav Svensk
Harald Heckmann
Howard Su
Huang Rui
@@ -41,6 +44,7 @@ HyungKi Jeong
Iru Cai
Ivan Vnučec
Iztok Jeras
Jake Merdich
James Hanlon
James Hutchinson
James Pallister
@@ -68,6 +72,7 @@ Lukasz Dalek
Maarten De Braekeleer
Maciej Sobkowski
Marco Widmer
Mariusz Glebocki
Markus Krause
Marlon James
Marshal Qiao
@@ -78,12 +83,15 @@ Michael Killough
Michaël Lefebvre
Mike Popoloski
Miodrag Milanović
Mladen Slijepcevic
Morten Borup Petersen
Mostafa Gamal
Nandu Raj
Nathan Kohagen
Nathan Myers
Patrick Stewart
Paul Wright
Pawel Sagan
Peter Horvath
Peter Monsson
Philipp Wagner
@@ -95,6 +103,7 @@ Rafal Kapuscik
Raynard Qiao
Richard Myers
Rupert Swarbrick
Ryszard Rozak
Samuel Riedel
Sean Cross
Sebastien Van Cauwenberghe
@@ -111,14 +120,17 @@ Tomasz Gorochowik
Tymoteusz Blazejczyk
Udi Finkelstein
Unai Martinez-Corral
Varun Koyyalagunta
Vassilis Papaefstathiou
Veripool API Bot
Victor Besyakov
William D. Jones
Wilson Snyder
Xi Zhang
Yoda Lee
Yossi Nivin
Yuri Victorovich
Yutetsu TAKATSUKASA
Yu-Sheng Lin
Yves Mathieu
Zhanglei Wang
+1
View File
@@ -13,6 +13,7 @@ from datetime import datetime
import os
import re
import sys
sys.path.insert(0, os.path.abspath('./_ext'))
import sphinx_rtd_theme # pylint: disable=wrong-import-position,
+13 -56
View File
@@ -86,61 +86,17 @@ Connecting to C++
In C++ output mode (:vlopt:`--cc`), the Verilator generated model class is a
simple C++ class. The user must write a C++ wrapper and main loop for the
simulation, which instantiates the model class, and link with the Verilated
model. Here is a simple example:
model.
.. code-block:: C++
Refer to ``examples/make_tracing_c`` in the distribution for a detailed
commented example.
#include <verilated.h> // Defines common routines
#include <iostream> // Need std::cout
#include "Vtop.h" // From Verilating "top.v"
Top level IO signals are read and written as members of the model. You
call the model's :code:`eval()` method to evaluate the model. When the
simulation is complete call the model's :code:`final()` method to execute
any SystemVerilog final blocks, and complete any assertions. See
:ref:`Evaluation Loop`.
Vtop *top; // Instantiation of model
uint64_t main_time = 0; // Current simulation time
// This is a 64-bit integer to reduce wrap over issues and
// allow modulus. This is in units of the timeprecision
// used in Verilog (or from --timescale-override)
double sc_time_stamp() { // Called by $time in Verilog
return main_time; // converts to double, to match
// what SystemC does
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv); // Remember args
top = new Vtop; // Create model
// Do not instead make Vtop as a file-scope static
// variable, as the "C++ static initialization order fiasco"
// may cause a crash
top->reset_l = 0; // Set some inputs
while (!Verilated::gotFinish()) {
if (main_time > 10) {
top->reset_l = 1; // Deassert reset
}
if ((main_time % 10) == 1) {
top->clk = 1; // Toggle clock
}
if ((main_time % 10) == 6) {
top->clk = 0;
}
top->eval(); // Evaluate model
cout << top->out << endl; // Read a output
main_time++; // Time passes...
}
top->final(); // Done simulating
// // (Though this example doesn't get here)
delete top;
}
Note top level IO signals are read and written as members of the model. You
call the :code:`eval()` method to evaluate the model. When the simulation is
complete call the :code:`final()` method to execute any SystemVerilog final
blocks, and complete any assertions. See :ref:`Evaluation Loop`.
Connecting to SystemC
@@ -449,14 +405,15 @@ accesses the above signal "readme" would be:
int main(int argc, char** argv, char** env) {
Verilated::commandArgs(argc, argv);
Vour* top = new Vour;
Verilated::internalsDump(); // See scopes to help debug
while (!Verilated::gotFinish()) {
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
const std::unique_ptr<Vour> top{new Vour{contextp.get()}};
contextp->internalsDump(); // See scopes to help debug
while (!contextp->gotFinish()) {
top->eval();
VerilatedVpi::callValueCbs(); // For signal callbacks
read_and_check();
}
delete top;
return 0;
}
EOF
+2 -2
View File
@@ -8,7 +8,7 @@ Simulation Runtime Arguments
The following are the arguments that may be passed to a Verilated
executable, provided that executable calls
:code:`Verilated::commandArgs()`.
:code:`VerilatedContext*->commandArgs(argc, argv)`.
All simulation runtime arguments begin with "+verilator", so that the
user's executable may skip over all "+verilator" arguments when parsing its
@@ -96,7 +96,7 @@ Summary:
.. option:: +verilator+noassert
Disable assert checking per runtime argument. This is the same as
calling :code:`Verilated::assertOn(false)` in the model.
calling :code:`VerilatedContext*->assertOn(false)` in the model.
.. option:: +verilator+V
+176 -112
View File
@@ -115,13 +115,6 @@ Summary:
Using this argument will likely cause incorrect simulation.
.. option:: --bin <filename>
Rarely needed. Override the default filename for Verilator itself.
When a dependency (.d) file is created, this filename will become a
source dependency, such that a change in this binary will have make
rebuild the output files.
.. option:: --build
After generating the SystemC/C++ code, Verilator will invoke the
@@ -129,15 +122,23 @@ Summary:
is also used). Verilator manages the build itself, and for this --build
requires GNU Make to be available on the platform.
.. option:: -CFLAGS <flags>
.. option:: --build-dep-bin <filename>
Add specified C compiler argument to the generated makefiles. For
multiple flags either pass them as a single argument with space
separators quoted in the shell (:command:`-CFLAGS "-a -b"`), or use
multiple -CFLAGS options (:command:`-CFLAGS -a -CFLAGS -b`).
Rarely needed. When a dependency (.d) file is created, this filename
will become a source dependency, such that a change in this binary will
have make rebuild the output files. Defaults to the full path to the
Verilator binary.
When make is run on the generated makefile these will be passed to the
C++ compiler (g++/clang++/msvc++).
This option was named `--bin` prior to version 4.228.
.. option:: --build-jobs [<value>]
Specify the level of parallelism for :vlopt:`--build`. If zero, uses the
number of threads in the current hardware. Otherwise, the <value> must
be a positive integer specifying the maximum number of parallel build
jobs.
See also :vlopt:`-j`.
.. option:: --cc
@@ -156,6 +157,16 @@ Summary:
have interest in adding more traditional CDC checks, please contact the
authors.
.. option:: -CFLAGS <flags>
Add specified C compiler argument to the generated makefiles. For
multiple flags either pass them as a single argument with space
separators quoted in the shell (:command:`-CFLAGS "-a -b"`), or use
multiple -CFLAGS options (:command:`-CFLAGS -a -CFLAGS -b`).
When make is run on the generated makefile these will be passed to the
C++ compiler (g++/clang++/msvc++).
.. option:: --clk <signal-name>
With :vlopt:`--clk`, the specified signal-name is taken as a root clock
@@ -179,6 +190,11 @@ Summary:
remove it from the combinatorial logic reevaluation checking code. This
may greatly improve performance.
.. option:: --no-clk <signal-name>
Prevent the specified signal from being marked as clock. See
:vlopt:`--clk`.
.. option:: --compiler <compiler-name>
Enables workarounds for the specified C++ compiler (list below).
@@ -250,8 +266,8 @@ Summary:
generally is a less-optimized binary with symbols present (so GDB can be used on it).
* Enable debugging messages (equivalent to :vlopt:`--debugi 3 <--debugi>`).
* Enable internal assertions (equivalent to :vlopt:`--debug-check`).
* Enable intermediate form dump files (equivalent to :vlopt:`--dump-treei 3
<--dump-treei>`).
* Enable intermediate form dump files (equivalent to :vlopt:`--dumpi-tree 3
<--dumpi-tree>`).
* Leak to make node numbers unique (equivalent to :vlopt:`--debug-leak
<--no-debug-leak>`.
* Call abort() instead of exit() if there are any errors (so GDB can see
@@ -288,6 +304,13 @@ Summary:
<--debugi>`). Higher levels produce more detailed messages. See
:vlopt:`--debug` for other implications of enabling debug.
.. option:: --no-decoration
When creating output Verilated code, minimize comments, white space,
symbol names and other decorative items, at the cost of greatly reduced
readability. This may assist C++ compile times. This will not typically
change the ultimate model's performance, but may in some cases.
.. option:: --default-language <value>
Select the language to be used by default when first processing each
@@ -335,26 +358,21 @@ Summary:
touch foo.v ; verilator -E --dump-defines foo.v
.. option:: --dump-graph
Rarely needed. Enable dumping V3Graph .dot debug files with dumping
level 3. Before Verilator 4.228, :vlopt:`--dump-tree` used
to include this option.
.. option:: --dump-tree
Rarely needed. Enable writing .tree debug files with dumping level 3,
Rarely needed. Enable dumping Ast .tree debug files with dumping level 3,
which dumps the standard critical stages. For details on the format see
the Verilator Internals manual. :vlopt:`--dump-tree` is enabled
automatically with :vlopt:`--debug`, so :vlopt:`--debug --no-dump-tree
<--dump-tree>` may be useful if the dump files are large and not
desired.
.. option:: --dump-treei <level>
.. option:: --dump-treei-<srcfile> <level>
Rarely needed - for developer use. Set internal tree dumping level
globally to a specific dumping level or set the specified Verilator
source file to the specified tree dumping level (e.g.
:vlopt:`--dump-treei-V3Order 9 <--dump-treei>`). Level 0 disables dumps
and is equivalent to :vlopt:`--no-dump-tree <--dump-tree>`. Level 9
enables dumping of every stage.
.. option:: --dump-tree-addrids
Rarely needed - for developer use. Replace AST node addresses with
@@ -364,6 +382,28 @@ Summary:
by a newly allocated node after a node with the same address has been
dumped then freed.
.. option:: --dump-<srcfile>
Rarely needed - for developer use. Enable all dumping in the given
source file at level 3.
.. option:: --dumpi-graph <level>
Rarely needed - for developer use. Set internal V3Graph dumping level
globally to the specified value.
.. option:: --dumpi-tree <level>
Rarely needed - for developer use. Set internal Ast dumping level
globally to the specified value.
.. option:: --dumpi-<srcfile> <level>
Rarely needed - for developer use. Set the dumping level in the
specified Verilator source file to the specified value (e.g.
:vlopt:`--dumpi-V3Order 9`). Level 0 disables dumps and is equivalent
to :vlopt:`--no-dump-<srcfile>`. Level 9 enables dumping of everything.
.. option:: -E
Preprocess the source code, but do not compile, similar to C++
@@ -479,6 +519,30 @@ Summary:
are typically used only when recommended by a maintainer to help debug
or work around an issue.
.. option:: -future0 <option>
Rarely needed. Suppress an unknown Verilator option for an option that
takes no additional arguments. This is used to allow scripts written
with pragmas for a later version of Verilator to run under a older
version. e.g. :code:`-future0 option --option` would on older versions
that do not understand :code:`--option` or :code:`+option` suppress what
would otherwise be an invalid option error, and on newer versions that
implement :code:`--option`, :code:`-future0 option --option` would have
the :code:`-future0 option` ignored and the :code:`--option` would
function appropriately.
.. option:: -future1 <option>
Rarely needed. Suppress an unknown Verilator option for an option that
takes an additional argument. This is used to allow scripts written
with pragmas for a later version of Verilator to run under a older
version. e.g. :code:`-future1 option --option arg` would on older
versions that do not understand :code:`--option arg` or :code:`+option
arg` suppress what would otherwise be an invalid option error, and on
newer versions that implement :code:`--option arg`, :code:`-future1
option --option arg` would have the :code:`-future1 option` ignored and
the :code:`--option arg` would function appropriately.
.. option:: -G<name>=<value>
Overwrites the given parameter of the toplevel module. The value is
@@ -579,26 +643,10 @@ Summary:
.. option:: -j [<value>]
Specify the level of parallelism for :vlopt:`--build`. The <value> must
be a positive integer specifying the maximum number of parallel build
jobs, or can be omitted. When <value> is omitted, the build will not try
to limit the number of parallel build jobs but attempt to execute all
independent build steps in parallel.
.. option:: -LDFLAGS <flags>
Add specified C linker arguments to the generated makefiles. For multiple
flags either pass them as a single argument with space separators quoted
in the shell (``-LDFLAGS "-a -b"``), or use multiple -LDFLAGS arguments
(``-LDFLAGS -a -LDFLAGS -b``).
When make is run on the generated makefile these will be passed to the
C++ linker (ld) **after** the primary file being linked. This flag is
called :vlopt:`-LDFLAGS` as that's the traditional name in simulators;
it's would have been better called LDLIBS as that's the Makefile
variable it controls. (In Make, LDFLAGS is before the first object,
LDLIBS after. -L libraries need to be in the Make variable LDLIBS, not
LDFLAGS.)
Specify the level of parallelism for :vlopt:`--build` if
:vlopt:`--build-jobs` isn't provided. If zero, uses the number of threads
in the current hardware. Otherwise, the <value> must be a positive
integer specifying the maximum number of parallel build jobs.
.. option:: --l2-name <value>
@@ -616,12 +664,20 @@ Summary:
A synonym for :vlopt:`--default-language`, for compatibility with other
tools and earlier versions of Verilator.
.. option:: +libext+<ext>[+<ext>][...]
.. option:: -LDFLAGS <flags>
Specify the extensions that should be used for finding modules. If for
example module "my" is referenced, look in :file:`my.<ext>`. Note
"+libext+" is fairly standard across Verilog tools. Defaults to
".v+.sv".
Add specified C linker arguments to the generated makefiles. For multiple
flags either pass them as a single argument with space separators quoted
in the shell (``-LDFLAGS "-a -b"``), or use multiple -LDFLAGS arguments
(``-LDFLAGS -a -LDFLAGS -b``).
When make is run on the generated makefile these will be passed to the
C++ linker (ld) **after** the primary file being linked. This flag is
called :vlopt:`-LDFLAGS` as that's the traditional name in simulators;
it's would have been better called LDLIBS as that's the Makefile
variable it controls. (In Make, LDFLAGS is before the first object,
LDLIBS after. -L libraries need to be in the Make variable LDLIBS, not
LDFLAGS.)
.. option:: --lib-create <name>
@@ -640,6 +696,13 @@ Summary:
See also :vlopt:`--protect-lib`.
.. option:: +libext+<ext>[+<ext>][...]
Specify the extensions that should be used for finding modules. If for
example module "my" is referenced, look in :file:`my.<ext>`. Note
"+libext+" is fairly standard across Verilog tools. Defaults to
".v+.sv".
.. option:: --lint-only
Check the files for lint violations only, do not create any other
@@ -673,22 +736,20 @@ Summary:
(e.g. ``-MAKEFLAGS -l -MAKEFLAGS -k``). Use of this option should not be
required for simple builds using the host toolchain.
.. option:: --main
Generates a top-level C++ main() file that supports parsing arguments,
but does not drive any inputs. This is sufficient to use for top-level
SystemVerilog designs that has no inputs, and does not need the C++ to
do any time advancement.
Implies :vlopt:`--cc` if no other output mode was provided.
.. option:: --max-num-width <value>
Set the maximum number literal width (e.g. in 1024'd22 this it the
1024). Defaults to 64K.
.. option:: --MMD =item --no-MMD
Enable/disable creation of .d dependency files, used for make dependency
detection, similar to gcc -MMD option. By default this option is
enabled for :vlopt:`--cc` or :vlopt:`--sc` modes.
.. option:: --MP
When creating .d dependency files with :vlopt:`--MMD` option, make phony
targets. Similar to :command:`gcc -MP` option.
.. option:: --Mdir <directory>
Specifies the name of the Make object directory. All generated files
@@ -696,33 +757,23 @@ Summary:
The directory is created if it does not exist and the parent directories
exist; otherwise manually create the Mdir before calling Verilator.
.. option:: --MMD
.. option:: --no-MMD
Enable/disable creation of .d dependency files, used for make dependency
detection, similar to gcc -MMD option. By default this option is
enabled for :vlopt:`--cc` or :vlopt:`--sc` modes.
.. option:: --mod-prefix <topname>
Specifies the name to prepend to all lower level classes. Defaults to
the same as :vlopt:`--prefix`.
.. option:: --no-clk <signal-name>
.. option:: --MP
Prevent the specified signal from being marked as clock. See
:vlopt:`--clk`.
.. option:: --no-decoration
When creating output Verilated code, minimize comments, white space,
symbol names and other decorative items, at the cost of greatly reduced
readability. This may assist C++ compile times. This will not typically
change the ultimate model's performance, but may in some cases.
.. option:: --no-pins64
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
.. option:: --no-skip-identical =item --skip-identical
Rarely needed. Disables or enables skipping execution of Verilator if
all source files are identical, and all output files exist with newer
dates. By default this option is enabled for :vlopt:`--cc` or
:vlopt:`--sc` modes only.
When creating .d dependency files with :vlopt:`--MMD` option, make phony
targets. Similar to :command:`gcc -MP` option.
.. option:: +notimingchecks
@@ -801,11 +852,6 @@ Summary:
With :vlopt:`-E`, disable generation of :code:`&96;line` markers and
blank lines, similar to :command:`gcc -P`.
.. option:: --pins64
Backward compatible alias for :vlopt:`--pins-bv 65 <--pins-bv>`. Note
that's a 65, not a 64.
.. option:: --pins-bv <width>
Specifies SystemC inputs/outputs of greater than or equal to <width>
@@ -838,6 +884,15 @@ Summary:
of uint32_t. Likewise pins of width 9-16 will use uint16_t instead of
uint32_t.
.. option:: --pins64
Backward compatible alias for :vlopt:`--pins-bv 65 <--pins-bv>`. Note
that's a 65, not a 64.
.. option:: --no-pins64
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
.. option:: --pipe-filter <command>
Rarely needed. Verilator will spawn the specified command as a
@@ -867,6 +922,11 @@ Summary:
prepended to the name of the :vlopt:`--top` option, or V prepended to
the first Verilog filename passed on the command line.
.. option:: --private
Opposite of :vlopt:`--public`. Is the default; this option exists for
backwards compatibility.
.. option:: --prof-c
When compiling the C++ code, enable the compiler's profiling flag
@@ -900,23 +960,6 @@ Summary:
Deprecated. Same as --prof-exec and --prof-pgo together.
.. option:: --protect-key <key>
Specifies the private key for :vlopt:`--protect-ids`. For best security
this key should be 16 or more random bytes, a reasonable secure choice
is the output of :command:`verilator --generate-key` . Typically, a key
would be created by the user once for a given protected design library,
then every Verilator run for subsequent versions of that library would
be passed the same :vlopt:`--protect-key`. Thus, if the input Verilog is
similar between library versions (Verilator runs), the Verilated code
will likewise be mostly similar.
If :vlopt:`--protect-key` is not specified and a key is needed,
Verilator will generate a new key for every Verilator run. As the key is
not saved, this is best for security, but means every Verilator run will
give vastly different output even for identical input, perhaps harming
compile times (and certainly thrashing any "ccache").
.. option:: --protect-ids
Hash any private identifiers (variable, module, and assertion block
@@ -937,6 +980,23 @@ Summary:
prototypes. Use of the VPI is not recommended as many design details
may be exposed, and an INSECURE warning will be issued.
.. option:: --protect-key <key>
Specifies the private key for :vlopt:`--protect-ids`. For best security
this key should be 16 or more random bytes, a reasonable secure choice
is the output of :command:`verilator --generate-key` . Typically, a key
would be created by the user once for a given protected design library,
then every Verilator run for subsequent versions of that library would
be passed the same :vlopt:`--protect-key`. Thus, if the input Verilog is
similar between library versions (Verilator runs), the Verilated code
will likewise be mostly similar.
If :vlopt:`--protect-key` is not specified and a key is needed,
Verilator will generate a new key for every Verilator run. As the key is
not saved, this is best for security, but means every Verilator run will
give vastly different output even for identical input, perhaps harming
compile times (and certainly thrashing any "ccache").
.. option:: --protect-lib <name>
Produces a DPI library similar to :vlopt:`--lib-create`, but hides
@@ -948,11 +1008,6 @@ Summary:
in the distribution for a demonstration of how to build and use the DPI
library.
.. option:: --private
Opposite of :vlopt:`--public`. Is the default; this option exists for
backwards compatibility.
.. option:: --public
This is only for historical debug use. Using it may result in
@@ -1045,6 +1100,15 @@ Summary:
Specifies SystemC output mode; see also :vlopt:`--cc` option.
.. option:: --skip-identical
.. option:: --no-skip-identical
Rarely needed. Disables or enables skipping execution of Verilator if
all source files are identical, and all output files exist with newer
dates. By default this option is enabled for :vlopt:`--cc` or
:vlopt:`--sc` modes only.
.. option:: --stats
Creates a dump file with statistics on the design in
+12 -6
View File
@@ -50,7 +50,7 @@ For --cc/--sc, it creates:
- Make include file with class names (from --make gmake)
* - *{prefix}*\ _hier.mk
- Make file for hierarchy blocks (from --make gmake)
* - *{prefix|*\ _hierMkArgs.f
* - *{prefix}*\ _hierMkArgs.f
- Arguments for hierarchical Verilation (from --make gmake)
* - *{prefix}*\ _hierCMakeArgs.f
- Arguments for hierarchical Verilation (from --make cmake)
@@ -62,13 +62,17 @@ For --cc/--sc, it creates:
- Top level (SystemVerilog $root) internal header file
* - *{prefix}*\ ___024root.cpp
- Top level (SystemVerilog $root) internal C++ file
* - *{prefix}*___024root*{__n}*\ .cpp
- Additional top level internal C++ files (from --output-split)
* - *{prefix}*\ ___024root\ *{__n}*\ .cpp
- Additional top level internal C++ files
* - *{prefix}*\ ___024root\ *{__DepSet_hash__n}*\ .cpp
- Additional top level internal C++ files (hashed to reduce build times)
* - *{prefix}*\ ___024root__Slow\ *{__n}*\ .cpp
- Infrequent cold routines
* - *{prefix}*\ ___024root__Trace{__n}*\ .cpp
* - *{prefix}*\ ___024root\ *{__DepSet_hash__n}*\ .cpp
- Infrequent cold routines (hashed to reduce build times)
* - *{prefix}*\ ___024root__Trace\ *{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}*\ ___024root__Trace__Slow{__n}*\ .cpp
* - *{prefix}*\ ___024root__Trace__Slow\ *{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}*\ __Dpi.h
- DPI import and export declarations (from --dpi)
@@ -87,7 +91,9 @@ For --cc/--sc, it creates:
* - *{prefix}{each_verilog_module}*\ .cpp
- Lower level internal C++ files
* - *{prefix}{each_verilog_module}{__n}*\ .cpp
- Additional lower C++ files (from --output-split)
- Additional lower C++ files
* - *{prefix}{each_verilog_module}{__DepSet_hash__n}*\ .cpp
- Additional lower C++ files (hased to reduce build times)
For --hierarchy mode, it creates:
+1 -1
View File
@@ -124,7 +124,7 @@ Those developing Verilator itself may also want these (see internals.rst):
::
sudo apt-get install gdb graphviz cmake clang clang-format-11 gprof lcov
sudo apt-get install gdb graphviz cmake clang clang-format-14 gprof lcov
sudo apt-get install yapf3
sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe
cpan install Pod::Perldoc
+1 -1
View File
@@ -489,7 +489,7 @@ $test$plusargs, $value$plusargs
.. code-block:: C++
Verilated::commandArgs(argc, argv);
{VerilatedContext*} ->commandArgs(argc, argv);
to register the command line before calling $test$plusargs or
$value$plusargs.
+3 -3
View File
@@ -7,9 +7,9 @@
Simulating (Verilated-Model Runtime)
************************************
This section describes items related to simulating, that using a Verilated
model's executable. For the runtime arguments to a simulated model, see
:ref:`Simulation Runtime Arguments`.
This section describes items related to simulating, that is, the use of a
Verilated model's executable. For the runtime arguments to a simulated
model, see :ref:`Simulation Runtime Arguments`.
.. _Benchmarking & Optimization:
+92 -13
View File
@@ -96,10 +96,11 @@ this.
Each ``AstNode`` has pointers to up to four children, accessed by the
``op1p`` through ``op4p`` methods. These methods are then abstracted in a
specific Ast\* node class to a more specific name. For example with the
``AstIf`` node (for ``if`` statements), ``ifsp`` calls ``op2p`` to give the
``AstIf`` node (for ``if`` statements), ``thensp`` calls ``op2p`` to give the
pointer to the AST for the "then" block, while ``elsesp`` calls ``op3p`` to
give the pointer to the AST for the "else" block, or NULL if there is not
one.
one. These accessors are automatically generated by ``astgen`` after
parsing the ``@astgen`` directives in the specific ``AstNode`` subclasses.
``AstNode`` has the concept of a next and previous AST - for example the
next and previous statements in a block. Pointers to the AST for these
@@ -501,22 +502,99 @@ code:
The ``astgen`` Script
---------------------
Some of the code implementing passes is extremely repetitive, and must be
implemented for each sub-class of ``AstNode``. However, while repetitive,
there is more variability than can be handled in C++ macros.
The ``astgen`` script is used to generate some of the repetitive C++ code
related to the ``AstNode`` type hierarchy. An example is the abstract ``visit``
methods in ``VNVisitor``. There are other uses, please see the ``*__gen*``
files in the bulid directories and the ``astgen`` script itself for details. A
description of the more advanced features of ``astgen`` are provided here.
In Verilator this is implemented by using a script, ``astgen`` to
pre-process the C++ code. For example in ``V3Const.cpp`` this is used to
implement the ``visit()`` functions for each binary operation using the
``TREEOP`` macro.
The original C source code is transformed into C code in the ``obj_opt``
Generating ``AstNode`` members
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Some of the member s of ``AstNode`` sub-classes are generated by ``astgen``.
These are emitted as pre-processor macro definitions, which then need to be
added to the ``AstNode`` sub-classes they correspond to. Specifically ``class
AstFoo`` should contain an instance of ``ASTGEN_MEMBERS_Foo;`` at class scope.
The ``astgen`` script checks and errors if this is not present. The method
generated depends on whether the class is a concrete final class, or an
abstract ``AstNode*`` base-class, and on ``@astgen`` directives present in
comment sections in the body of the ``AstNode`` sub-class definitions.
List of ``@astgen`` directives
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``@astgen`` directives in comments contained in the body of ``AstNode``
sub-class definitions are parsed and contribute to the code generated by
``astgen``. The general syntax is ``@astgen <keywords> := <description>``,
where ``<keywords>`` determines what is being defined, and ``<description>`` is
a ``<keywords>`` dependent description of the definition. The list of
``@astgen`` directives is as follows:
``op<N>`` operand directives
"""""""""""""""""""""""""""""
The ``op1``, ``op2``, ``op3`` and ``op4`` directives are used to describe the
name and type of the up to 4 child operands of a node. The syntax of the
``<description>`` field is ``<identifier> : <type>``, where ``<identifier>``
will be used as the base name of the generated operand accessors, and
``<type>`` is one of:
1. An ``AstNode`` sub-class, defining the operand to be of that type, always
no-null, and with an always null ``nextp()``. That is, the child node is
always present, and is a single ``AstNode`` (as opposed to a list).
2. ``Optional[<AstNode sub-class>]``. This is just like in point 1 above, but
defines the child node to be optional, meaning it may be null.
3. ``List[AstNode sub-class]`` describes a list operand, which means the child
node may have a non-null ``nextp()`` and in addition the child itself may be
null, representing an empty list.
An example of the full syntax of the directive is
``@astgen op1 := lhsp : AstNodeMath``.
``astnode`` generates accessors for the child nodes based on these directives.
For non-list children, the names of the getter and setter both are that of the
given ``<identifier>``. For list type children, the getter is ``<identifier>``,
and instead of the setter, there an ``add<Identifier>`` method is generated
that appends new nodes (or lists of nodes) to the child list.
``alias op<N>`` operand alias directives
""""""""""""""""""""""""""""""""""""""""
If a super-class already defined a name and type for a child node using the
``op<N>`` directive, but a more appropriate name exists in the context of a
sub-class, then the alias directive can be used to introduce an additional name
for the child node. The is ``alias op<N> := <identifier>`` where
``<identifier>`` is the new name. ``op<N>`` must have been defined in some
super-class of the current node.
Example: ``@astgen alias op1 := condp``
Additional features of ``astgen``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition to generating ``AstNode`` members as described above,
``astgen`` is also use to handle some of the repetitive implementation code
that is still variable enough not to be handled in C++ macros.
In particular, ``astgen`` is used to pre-process some of the C++ source
files. For example in ``V3Const.cpp``, it is used to implement the
``visit()`` functions for each binary operation using the ``TREEOP`` macro.
The original C++ source code is transformed into C++ code in the ``obj_opt``
and ``obj_dbg`` sub-directories (the former for the optimized version of
Verilator, the latter for the debug version). So for example
``V3Const.cpp`` into ``V3Const__gen.cpp``.
Visitor Functions -----------------
Visitor Functions
-----------------
Verilator uses the "Visitor" design pattern to implement its refinement and
optimization passes. This allows separation of the pass algorithm from the
@@ -1153,8 +1231,9 @@ Generally what would you do to add a new feature?
language and has a lot of back-and-forth with Verilator's grammar. Copy
the appropriate rules to src/verilog.y and modify the productions.
4. If a new Ast type is needed, add it to V3AstNodes.h. Follow the
convention described above about the AstNode type hierarchy.
4. If a new Ast type is needed, add it to the appropriate V3AstNode*.h.
Follow the convention described above about the AstNode type hierarchy.
Ordering of definitions is enforced by ``astgen``.
5. Now you can run "test_regress/t/t_<newtestcase>.pl --debug" and it'll
probably fail but you'll see a
+5 -2
View File
@@ -21,11 +21,14 @@ int main(int argc, char** argv, char** env) {
// Prevent unused variable warnings
if (false && argc && argv && env) {}
// Construct a VerilatedContext to hold simulation time, etc.
VerilatedContext* contextp = new VerilatedContext;
// Construct the Verilated model, from Vtop.h generated from Verilating "top.v"
Vtop* top = new Vtop;
Vtop* top = new Vtop{contextp};
// Simulate until $finish
while (!Verilated::gotFinish()) {
while (!contextp->gotFinish()) {
// Evaluate model
top->eval();
+1 -1
View File
@@ -32,7 +32,7 @@ int main(int argc, char** argv, char** env) {
// When tracing, the contents of the secret module will not be seen
VerilatedVcdC* tfp = nullptr;
const char* flag = contextp->commandArgsPlusMatch("trace");
if (flag && 0 == strcmp(flag, "+trace")) {
if (flag && 0 == std::strcmp(flag, "+trace")) {
contextp->traceEverOn(true);
VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n");
tfp = new VerilatedVcdC;
+2 -2
View File
@@ -82,14 +82,14 @@ int sc_main(int argc, char* argv[]) {
// You must do one evaluation before enabling waves, in order to allow
// SystemC to interconnect everything for testing.
sc_start(1, SC_NS);
sc_start(SC_ZERO_TIME);
#if VM_TRACE
// If verilator was invoked with --trace argument,
// and if at run time passed the +trace argument, turn on tracing
VerilatedVcdSc* tfp = nullptr;
const char* flag = Verilated::commandArgsPlusMatch("trace");
if (flag && 0 == strcmp(flag, "+trace")) {
if (flag && 0 == std::strcmp(flag, "+trace")) {
cout << "Enabling waves into logs/vlt_dump.vcd...\n";
tfp = new VerilatedVcdSc;
top->trace(tfp, 99); // Trace 99 levels of hierarchy
+5 -4
View File
@@ -16,12 +16,13 @@ from shutil import copy2
class VlFileCopy:
def __init__(
self,
verilator_args, # presently all verilator options are passed-thru
# ideally this script would check against options mentioned in help
self,
verilator_args, # presently all verilator options are passed-thru
# ideally this script would check against options mentioned in help
debug=0,
output_dir='copied'): # directory name we output file uses
output_dir='copied'): # directory name we output file uses
self.debug = debug
+5 -4
View File
@@ -14,12 +14,13 @@ import xml.etree.ElementTree as ET
class VlHierGraph:
def __init__(
self,
verilator_args, # presently all verilator options are passed-thru
# ideally this script would check against options mentioned in help
self,
verilator_args, # presently all verilator options are passed-thru
# ideally this script would check against options mentioned in help
debug=0,
output_filename='graph.dot'): # output filename
output_filename='graph.dot'): # output filename
self.debug = debug
self.next_vertex_number = 0
self.name_to_number = {}
+106 -59
View File
@@ -47,27 +47,34 @@
#define VERILATOR_VERILATED_CPP_
#include "verilatedos.h"
#include "verilated_imp.h"
#include "verilated_config.h"
#include "verilatedos.h"
#include "verilated_imp.h"
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <cstdlib>
#include <sstream>
#include <sys/stat.h> // mkdir
#include <list>
#include <limits>
#include <list>
#include <sstream>
#include <utility>
#include <sys/stat.h> // mkdir
// clang-format off
#if defined(_WIN32) || defined(__MINGW32__)
# include <direct.h> // mkdir
#endif
#ifdef VL_THREADED
# include "verilated_threads.h"
#endif
// clang-format on
#include "verilated_trace.h"
// Max characters in static char string for VL_VALUE_STRING
constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192;
@@ -673,7 +680,10 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
if (std::numeric_limits<T>::is_integer) {
constexpr int b = 128;
constexpr int w = VL_WORDS_I(b);
VlWide<w> tmp0, tmp1, tmp2, tmp3;
VlWide<w> tmp0;
VlWide<w> tmp1;
VlWide<w> tmp2;
VlWide<w> tmp3;
WDataInP shifted = VL_EXTEND_WQ(b, 0, tmp0, static_cast<QData>(ld));
if (shift < 0) {
@@ -691,7 +701,8 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
= VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits<uint64_t>::max()); // breaks shifted
if (VL_GT_W(w, integer, max64Bit)) {
WDataOutP v = VL_ASSIGN_W(b, tmp3, integer); // breaks fracDigitsPow10
VlWide<w> zero, ten;
VlWide<w> zero;
VlWide<w> ten;
VL_ZERO_W(b, zero);
VL_EXTEND_WI(b, 0, ten, 10);
char buf[128]; // 128B is obviously long enough to represent 128bit integer in decimal
@@ -992,22 +1003,22 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
}
}
static inline bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE {
static bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE {
if (VL_LIKELY(fp)) {
return std::feof(fp) ? true : false; // true : false to prevent MSVC++ warning
} else {
return floc < 0;
}
}
static inline void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
static void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
if (VL_LIKELY(fp)) {
std::fgetc(fp);
} else {
floc -= 8;
}
}
static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr) VL_MT_SAFE {
static int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr) VL_MT_SAFE {
// Get a character without advancing
if (VL_LIKELY(fp)) {
const int data = std::fgetc(fp);
@@ -1024,17 +1035,16 @@ static inline int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
}
}
}
static inline void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr) VL_MT_SAFE {
static void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr) VL_MT_SAFE {
while (true) {
const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
if (c == EOF || !std::isspace(c)) return;
_vl_vsss_advance(fp, floc);
}
}
static inline void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr, char* tmpp,
const char* acceptp) VL_MT_SAFE {
static void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr,
char* tmpp, const char* acceptp) VL_MT_SAFE {
// Read into tmp, consisting of characters from acceptp list
char* cp = tmpp;
while (true) {
@@ -1048,9 +1058,8 @@ static inline void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp,
*cp++ = '\0';
// VL_DBG_MSGF(" _read got='"<<tmpp<<"'\n");
}
static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp,
const std::string& fstr, char* beginp, std::size_t n,
const bool inhibit = false) {
static char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr,
char* beginp, std::size_t n, const bool inhibit = false) {
// Variant of _vl_vsss_read_str using the same underlying I/O functions but optimized
// specifically for block reads of N bytes (read operations are not demarcated by
// whitespace). In the fp case, except descriptor to have been opened in binary mode.
@@ -1062,12 +1071,11 @@ static inline char* _vl_vsss_read_bin(FILE* fp, int& floc, const WDataInP fromp,
}
return beginp;
}
static inline void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits,
IData ld) VL_MT_SAFE {
static void _vl_vsss_setbit(WDataOutP owp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE {
for (; nbits && lsb < obits; nbits--, lsb++, ld >>= 1) { VL_ASSIGNBIT_WI(lsb, owp, ld & 1); }
}
static inline void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
size_t posstart, size_t posend) VL_MT_SAFE {
static 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.
int lsb = 0;
for (int i = 0, pos = static_cast<int>(posend) - 1;
@@ -1453,11 +1461,12 @@ void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) VL_MT_SAFE {
void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, ...) VL_MT_SAFE {
if (obits_ignored) {}
output = "";
std::string temp_output;
va_list ap;
va_start(ap, formatp);
_vl_vsformat(output, formatp, ap);
_vl_vsformat(temp_output, formatp, ap);
va_end(ap);
output = temp_output;
}
std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE {
@@ -1606,8 +1615,8 @@ IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
return code >> 8; // Want exit status
}
IData VL_TESTPLUSARGS_I(const char* formatp) VL_MT_SAFE {
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(formatp);
IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE {
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(format.c_str());
return match.empty() ? 0 : 1;
}
@@ -1840,8 +1849,7 @@ VlReadMem::VlReadMem(bool hex, int bits, const std::string& filename, QData star
, m_bits{bits}
, m_filename(filename) // Need () or GCC 4.8 false warning
, m_end{end}
, m_addr{start}
, m_linenum{0} {
, m_addr{start} {
m_fp = std::fopen(filename.c_str(), "r");
if (VL_UNLIKELY(!m_fp)) {
// We don't report the Verilog source filename as it slow to have to pass it down
@@ -1975,8 +1983,7 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
VlWriteMem::VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end)
: m_hex{hex}
, m_bits{bits}
, m_addr{0} {
, m_bits{bits} {
if (VL_UNLIKELY(start > end)) {
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem invalid address range");
return;
@@ -2158,29 +2165,6 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
//===========================================================================
// Timescale conversion
// Helper function for conversion of timescale strings
// Converts (1|10|100)(s|ms|us|ns|ps|fs) to power of then
int VL_TIME_STR_CONVERT(const char* strp) VL_PURE {
int scale = 0;
if (!strp) return 0;
if (*strp++ != '1') return 0;
while (*strp == '0') {
++scale;
++strp;
}
switch (*strp++) {
case 's': break;
case 'm': scale -= 3; break;
case 'u': scale -= 6; break;
case 'n': scale -= 9; break;
case 'p': scale -= 12; break;
case 'f': scale -= 15; break;
default: return 0;
}
if ((scale < 0) && (*strp++ != 's')) return 0;
if (*strp) return 0;
return scale;
}
static const char* vl_time_str(int scale) VL_PURE {
static const char* const names[]
= {"100s", "10s", "1s", "100ms", "10ms", "1ms", "100us", "10us", "1us",
@@ -2302,8 +2286,9 @@ void VerilatedContext::checkMagic(const VerilatedContext* contextp) {
}
VerilatedContext::Serialized::Serialized() {
m_timeunit = VL_TIME_UNIT; // Initial value until overriden by _Vconfigure
m_timeprecision = VL_TIME_PRECISION; // Initial value until overriden by _Vconfigure
constexpr int8_t picosecond = -12;
m_timeunit = picosecond; // Initial value until overriden by _Vconfigure
m_timeprecision = picosecond; // Initial value until overriden by _Vconfigure
}
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
@@ -2323,7 +2308,7 @@ std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMut
return m_dumpfile;
}
std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const std::string out = dumpfile();
std::string out = dumpfile();
if (VL_UNLIKELY(out.empty())) {
VL_PRINTF_MT("%%Warning: $dumpvar ignored as not proceeded by $dumpfile\n");
return "";
@@ -2428,6 +2413,33 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
return vl_time_str(timeprecision());
}
void VerilatedContext::threads(unsigned n) {
if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: Simulation threads must be >= 1");
if (m_threadPool) {
VL_FATAL_MT(
__FILE__, __LINE__, "",
"%Error: Cannot set simulation threads after the thread pool has been created.");
}
#if VL_THREADED
if (m_threads == n) return; // To avoid unnecessary warnings
m_threads = n;
const unsigned hardwareThreadsAvailable = std::thread::hardware_concurrency();
if (m_threads > hardwareThreadsAvailable) {
VL_PRINTF_MT("%%Warning: System has %u hardware threads but simulation thread count set "
"to %u. This will likely cause significant slowdown.\n",
hardwareThreadsAvailable, m_threads);
}
#else
if (n > 1) {
VL_PRINTF_MT("%%Warning: Verilator run-time library built without VL_THREADS. Ignoring "
"call to 'VerilatedContext::threads' with argument %u.\n",
n);
}
#endif
}
void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock{m_argMutex};
m_args.m_argVec.clear(); // Empty first, then add
@@ -2458,6 +2470,33 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE {
VerilatedImp::userDump();
}
void VerilatedContext::addModel(VerilatedModel* modelp) {
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
if (modelp->threads() > m_threads) {
std::ostringstream msg;
msg << "VerilatedContext has " << m_threads << " threads but model '"
<< modelp->modelName() << "' (instantiated as '" << modelp->hierName()
<< "') was Verilated with --threads " << modelp->threads() << ".\n";
const std::string str = msg.str();
VL_FATAL_MT(__FILE__, __LINE__, modelp->hierName(), str.c_str());
}
}
VerilatedVirtualBase* VerilatedContext::threadPoolp() {
if (m_threads == 1) return nullptr;
#if VL_THREADED
if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
#endif
return m_threadPool.get();
}
VerilatedVirtualBase*
VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) {
if (!m_executionProfiler) m_executionProfiler.reset(construct(*this));
return m_executionProfiler.get();
}
//======================================================================
// VerilatedContextImp:: Methods - command line
@@ -2767,7 +2806,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
// When running internal code coverage (gcc --coverage, as opposed to
// verilator --coverage), dump coverage data to properly cover failing
// tests.
VL_GCOV_FLUSH();
VL_GCOV_DUMP();
}
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE {
@@ -2850,6 +2889,14 @@ void VerilatedImp::versionDump() VL_MT_SAFE {
VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
}
//===========================================================================
// VerilatedModel:: Methods
VerilatedModel::VerilatedModel(VerilatedContext& context)
: m_context{context} {}
std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { return nullptr; }
//===========================================================================
// VerilatedModule:: Methods
+70 -14
View File
@@ -81,6 +81,7 @@
#endif
// clang-format on
class VerilatedContext;
class VerilatedContextImp;
class VerilatedContextImpData;
class VerilatedCovContext;
@@ -90,6 +91,9 @@ class VerilatedFstC;
class VerilatedFstSc;
class VerilatedScope;
class VerilatedScopeNameMap;
template <class, class>
class VerilatedTrace;
class VerilatedTraceConfig;
class VerilatedVar;
class VerilatedVarNameMap;
class VerilatedVcd;
@@ -252,6 +256,40 @@ public:
#endif
};
//=========================================================================
/// Base class of a Verilator generated (Verilated) model.
///
/// VerilatedModel is a base class of the user facing primary class generated
/// by Verilator.
class VerilatedModel VL_NOT_FINAL {
VL_UNCOPYABLE(VerilatedModel);
VerilatedContext& m_context; // The VerilatedContext this model is instantiated under
protected:
explicit VerilatedModel(VerilatedContext& context);
virtual ~VerilatedModel() = default;
public:
/// Returns the VerilatedContext this model is instantiated under
/// Used to get to e.g. simulation time via contextp()->time()
VerilatedContext* contextp() const { return &m_context; }
/// Returns the hierarchical name of this module instance.
virtual const char* hierName() const = 0;
/// Returns the name of this model (the name of the generated model class).
virtual const char* modelName() const = 0;
/// Returns the thread level parallelism, this model was Verilated with. Always 1 or higher.
virtual unsigned threads() const = 0;
private:
// The following are for use by Verilator internals only
template <class, class>
friend class VerilatedTrace;
// Run-time trace configuration requested by this model
virtual std::unique_ptr<VerilatedTraceConfig> traceConfig() const;
};
//=========================================================================
/// Base class for all Verilated module classes.
@@ -266,10 +304,6 @@ public:
const char* name() const { return m_namep; } ///< Return name of module
};
/// Declare a module, ala SC_MODULE
#define VL_MODULE(modname) class modname VL_NOT_FINAL : public VerilatedModule
// Not class final in VL_MODULE, as users might be abstracting our models (--hierarchical)
//=========================================================================
// Functions overridable by user defines
// (Internals however must use VL_PRINTF_MT, which calls these.)
@@ -362,6 +396,16 @@ protected:
// Implementation details
const std::unique_ptr<VerilatedContextImpData> m_impdatap;
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
#ifdef VL_THREADED
unsigned m_threads = std::thread::hardware_concurrency();
#else
const unsigned m_threads = 1;
#endif
// 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
std::unique_ptr<VerilatedVirtualBase> m_executionProfiler;
// Coverage access
std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep()
@@ -413,9 +457,9 @@ public:
VerilatedCovContext* coveragep() VL_MT_SAFE;
/// Set debug level
/// Debug is currently global, but for forward compatibility have a per-context method
static void debug(int val) VL_MT_SAFE;
static inline void debug(int val) VL_MT_SAFE;
/// Return debug level
static int debug() VL_MT_SAFE;
static inline int debug() VL_MT_SAFE;
/// Set current number of errors/assertions
void errorCount(int val) VL_MT_SAFE;
/// Increment current number of errors/assertions
@@ -477,7 +521,7 @@ public:
///
/// * Else, time comes from the legacy 'double sc_time_stamp()' which
/// must be a function defined by the user's wrapper.
uint64_t time() const VL_MT_SAFE;
inline uint64_t time() const VL_MT_SAFE;
/// Set current simulation time. See time() for side effect details
void time(uint64_t value) VL_MT_SAFE { m_s.m_time = value; }
/// Advance current simulation time. See time() for side effect details
@@ -495,6 +539,12 @@ public:
/// Get time precision as IEEE-standard text
const char* timeprecisionString() const VL_MT_SAFE;
/// Get number of threads used for simulation (including the main thread)
unsigned threads() const { return m_threads; }
/// 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);
/// Allow traces to at some point be enabled (disables some optimizations)
void traceEverOn(bool flag) VL_MT_SAFE {
if (flag) calcUnusedSigs(true);
@@ -510,13 +560,20 @@ public:
/// releases - contact the authors before production use.
void scopesDump() const VL_MT_SAFE;
public: // But for internal use only
// METHODS - public but for internal use only
// Internal: access to implementation class
VerilatedContextImp* impp() { return reinterpret_cast<VerilatedContextImp*>(this); }
const VerilatedContextImp* impp() const {
return reinterpret_cast<const VerilatedContextImp*>(this);
}
void addModel(VerilatedModel*);
VerilatedVirtualBase* threadPoolp();
VerilatedVirtualBase*
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
// Internal: $dumpfile
void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
@@ -595,13 +652,13 @@ public: // But internals only - called from VerilatedModule's
const char* name() const { return m_namep; }
const char* identifier() const { return m_identifierp; }
int8_t timeunit() const { return m_timeunit; }
inline VerilatedSyms* symsp() const { return m_symsp; }
VerilatedSyms* symsp() const { return m_symsp; }
VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT;
VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; }
void scopeDump() const;
void* exportFindError(int funcnum) const;
static void* exportFindNullError(int funcnum) VL_MT_SAFE;
static inline void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
static void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
if (VL_UNLIKELY(!scopep)) return exportFindNullError(funcnum);
if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
// m_callbacksp must be declared, as Max'es are > 0
@@ -670,7 +727,7 @@ public:
/// Return debug level
/// When multithreaded this may not immediately react to another thread
/// changing the level (no mutex)
static inline int debug() VL_MT_SAFE { return s_debug; }
static int debug() VL_MT_SAFE { return s_debug; }
#else
/// Return constant 0 debug level, so C++'s optimizer rips up
static constexpr int debug() VL_PURE { return 0; }
@@ -830,7 +887,6 @@ public:
}
#endif
public:
// METHODS - INTERNAL USE ONLY (but public due to what uses it)
// Internal: Create a new module name by concatenating two strings
// Returns pointer to thread-local static data (overwritten on next call)
@@ -877,8 +933,8 @@ private:
#endif
};
inline void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); }
inline int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); }
void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); }
int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); }
//=========================================================================
// Data Types
+1 -21
View File
@@ -142,26 +142,6 @@ ifneq ($(VM_THREADS),0)
endif
endif
ifneq ($(VM_TRACE_THREADS),0)
ifneq ($(VM_TRACE_THREADS),)
ifeq ($(findstring -DVL_THREADED,$(CPPFLAGS)),)
$(error VM_TRACE_THREADS requires VM_THREADS)
endif
CPPFLAGS += -DVL_TRACE_THREADED
VK_C11=1
VK_LIBS_THREADED=1
endif
endif
ifneq ($(VM_TRACE_FST_WRITER_THREAD),0)
ifneq ($(VM_TRACE_FST_WRITER_THREAD),)
CPPFLAGS += -DVL_TRACE_FST_WRITER_THREAD
VK_C11=1
VK_LIBS_THREADED=1
endif
endif
ifneq ($(VK_C11),0)
ifneq ($(VK_C11),)
# Need C++11 at least, so always default to newest
@@ -187,7 +167,7 @@ VM_SLOW += $(VM_CLASSES_SLOW) $(VM_SUPPORT_SLOW)
VK_FAST_OBJS = $(addsuffix .o, $(VM_FAST))
VK_SLOW_OBJS = $(addsuffix .o, $(VM_SLOW))
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
# Note VM_GLOBAL_FAST and VM_GLOBAL_SLOW holds the files required from the
# run-time library. In practice everything is actually in VM_GLOBAL_FAST,
+12 -7
View File
@@ -22,8 +22,10 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_cov.h"
#include "verilated.h"
#include "verilated_cov_key.h"
#include <deque>
@@ -69,22 +71,23 @@ public: // But only local to this file
// This isn't in the header file for auto-magic conversion because it
// inlines to too much code and makes compilation too slow.
template <class T> class VerilatedCoverItemSpec final : public VerilatedCovImpItem {
template <class T>
class VerilatedCoverItemSpec final : public VerilatedCovImpItem {
private:
// MEMBERS
T* m_countp; // Count value
public:
// METHODS
// cppcheck-suppress truncLongCastReturn
virtual uint64_t count() const override { return *m_countp; }
virtual void zero() const override { *m_countp = 0; }
uint64_t count() const override { return *m_countp; }
void zero() const override { *m_countp = 0; }
// CONSTRUCTORS
// cppcheck-suppress noExplicitConstructor
explicit VerilatedCoverItemSpec(T* countp)
: m_countp{countp} {
*m_countp = 0;
}
virtual ~VerilatedCoverItemSpec() override = default;
~VerilatedCoverItemSpec() override = default;
};
//=============================================================================
@@ -122,7 +125,7 @@ public:
protected:
friend class VerilatedCovContext;
virtual ~VerilatedCovImp() override { clearGuts(); }
~VerilatedCovImp() override { clearGuts(); }
private:
// PRIVATE METHODS
@@ -205,7 +208,7 @@ private:
// Forward to . so we have a whole word
const std::string suffix = *bpost ? std::string{bpost + 1} : "";
const std::string out = prefix + "*" + suffix;
std::string out = prefix + "*" + suffix;
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
// <<"\ncho="<<out<<endl;
@@ -511,8 +514,10 @@ VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
static VerilatedMutex s_mutex;
// cppcheck-suppress identicalInnerCondition
if (VL_UNLIKELY(!m_coveragep)) {
const VerilatedLockGuard lock{s_mutex};
// cppcheck-suppress identicalInnerCondition
if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race
m_coveragep.reset(new VerilatedCovImp);
}
+6 -3
View File
@@ -26,6 +26,7 @@
#define VERILATOR_VERILATED_COV_H_
#include "verilatedos.h"
#include "verilated.h"
#include <iostream>
@@ -88,7 +89,8 @@ class VerilatedCovImp;
//=============================================================================
// Convert VL_COVER_INSERT value arguments to strings, is \internal
template <class T> std::string vlCovCvtToStr(const T& t) VL_PURE {
template <class T>
std::string vlCovCvtToStr(const T& t) VL_PURE {
std::ostringstream os;
os << t;
return os.str();
@@ -120,7 +122,8 @@ public:
/// Zero coverage points
void zero() VL_MT_SAFE;
public: // But Internal use only
// METHODS - public but Internal use only
// Insert a coverage item
// We accept from 1-30 key/value pairs, all as strings.
// Call _insert1, followed by _insert2 and _insert3
@@ -158,7 +161,7 @@ protected:
// CONSTRUCTORS
// Internal: Only made as part of VerilatedCovImp
VerilatedCovContext() = default;
virtual ~VerilatedCovContext() = default;
~VerilatedCovContext() override = default;
// METHODS
// Internal: access to implementation class
+3 -1
View File
@@ -28,7 +28,9 @@
#define VERILATOR_VERILATED_DPI_CPP_
#include "verilatedos.h"
#include "verilated_dpi.h"
#include "verilated_imp.h"
// On MSVC++ we need svdpi.h to declare exports, not imports
@@ -179,7 +181,7 @@ void svPutPartselLogic(svLogicVecVal* dp, const svLogicVecVal s, int lbit, int w
//======================================================================
// Open array internals
static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHandle h) {
static const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHandle h) {
if (VL_UNLIKELY(!h)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"%%Error: DPI svOpenArrayHandle function called with nullptr handle");
+1
View File
@@ -27,6 +27,7 @@
#define VERILATOR_VERILATED_DPI_H_
#include "verilatedos.h"
#include "verilated.h" // Also presumably included by caller
#include "verilated_sym_props.h"
+25 -15
View File
@@ -28,7 +28,7 @@
#include "verilated_fst_c.h"
// GTKWave configuration
#ifdef VL_TRACE_FST_WRITER_THREAD
#ifdef VL_THREADED
# define HAVE_LIBPTHREAD
# define FST_WRITER_PARALLEL
#endif
@@ -92,8 +92,7 @@ static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE
//=============================================================================
// VerilatedFst
VerilatedFst::VerilatedFst(void* fst)
: m_fst{fst} {}
VerilatedFst::VerilatedFst(void* /*fst*/) {}
VerilatedFst::~VerilatedFst() {
if (m_fst) fstWriterClose(m_fst);
@@ -106,9 +105,7 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
m_fst = fstWriterCreate(filename, 1);
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
#ifdef VL_TRACE_FST_WRITER_THREAD
fstWriterSetParallelMode(m_fst, 1);
#endif
if (m_useFstWriterThread) fstWriterSetParallelMode(m_fst, 1);
fullDump(true); // First dump must be full for fst
m_curScope.clear();
@@ -250,23 +247,36 @@ void VerilatedFst::declDouble(uint32_t code, const char* name, int dtypenum, fst
//=============================================================================
// Get/commit trace buffer
VerilatedFstBuffer* VerilatedFst::getTraceBuffer() { return new VerilatedFstBuffer{*this}; }
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer() {
#ifdef VL_THREADED
if (offload()) return new OffloadBuffer{*this};
#endif
return new Buffer{*this};
}
void VerilatedFst::commitTraceBuffer(VerilatedFstBuffer* bufp) {
#ifdef VL_TRACE_OFFLOAD
if (bufp->m_offloadBufferWritep) {
m_offloadBufferWritep = bufp->m_offloadBufferWritep;
return; // Buffer will be deleted by the offload thread
void VerilatedFst::commitTraceBuffer(VerilatedFst::Buffer* bufp) {
#ifdef VL_THREADED
if (offload()) {
OffloadBuffer* const offloadBufferp = static_cast<OffloadBuffer*>(bufp);
if (offloadBufferp->m_offloadBufferWritep) {
m_offloadBufferWritep = offloadBufferp->m_offloadBufferWritep;
return; // Buffer will be deleted by the offload thread
}
}
#endif
delete bufp;
}
//=============================================================================
// VerilatedFstBuffer implementation
// Configure
VerilatedFstBuffer::VerilatedFstBuffer(VerilatedFst& owner)
: VerilatedTraceBuffer<VerilatedFst, VerilatedFstBuffer>{owner} {}
void VerilatedFst::configure(const VerilatedTraceConfig& config) {
// If at least one model requests the FST writer thread, then use it
m_useFstWriterThread |= config.m_useFstWriterThread;
}
//=============================================================================
// VerilatedFstBuffer implementation
//=============================================================================
// Trace rendering primitives
+40 -25
View File
@@ -43,18 +43,20 @@ public:
using Super = VerilatedTrace<VerilatedFst, VerilatedFstBuffer>;
private:
friend Buffer; // Give the buffer access to the private bits
friend VerilatedFstBuffer; // Give the buffer access to the private bits
//=========================================================================
// FST specific internals
void* m_fst;
void* m_fst = nullptr;
std::map<uint32_t, fstHandle> m_code2symbol;
std::map<int, fstEnumHandle> m_local2fstdtype;
std::list<std::string> m_curScope;
fstHandle* m_symbolp = nullptr; // same as m_code2symbol, but as an array
char* m_strbuf = nullptr; // String buffer long enough to hold maxBits() chars
bool m_useFstWriterThread = false; // Whether to use the separate FST writer thread
// CONSTRUCTORS
VL_UNCOPYABLE(VerilatedFst);
void declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
@@ -65,15 +67,18 @@ protected:
// Implementation of VerilatedTrace interface
// Called when the trace moves forward to a new time point
virtual void emitTimeChange(uint64_t timeui) override;
void emitTimeChange(uint64_t timeui) override;
// Hooks called from VerilatedTrace
virtual bool preFullDump() override { return isOpen(); }
virtual bool preChangeDump() override { return isOpen(); }
bool preFullDump() override { return isOpen(); }
bool preChangeDump() override { return isOpen(); }
// Trace buffer management
virtual VerilatedFstBuffer* getTraceBuffer() override;
virtual void commitTraceBuffer(VerilatedFstBuffer*) override;
Buffer* getTraceBuffer() override;
void commitTraceBuffer(Buffer*) override;
// Configure sub-class
void configure(const VerilatedTraceConfig&) override;
public:
//=========================================================================
@@ -113,21 +118,31 @@ public:
#ifndef DOXYGEN
// Declare specialization here as it's used in VerilatedFstC just below
template <> void VerilatedFst::Super::dump(uint64_t time);
template <> void VerilatedFst::Super::set_time_unit(const char* unitp);
template <> void VerilatedFst::Super::set_time_unit(const std::string& unit);
template <> void VerilatedFst::Super::set_time_resolution(const char* unitp);
template <> void VerilatedFst::Super::set_time_resolution(const std::string& unit);
template <> void VerilatedFst::Super::dumpvars(int level, const std::string& hier);
template <>
void VerilatedFst::Super::dump(uint64_t time);
template <>
void VerilatedFst::Super::set_time_unit(const char* unitp);
template <>
void VerilatedFst::Super::set_time_unit(const std::string& unit);
template <>
void VerilatedFst::Super::set_time_resolution(const char* unitp);
template <>
void VerilatedFst::Super::set_time_resolution(const std::string& unit);
template <>
void VerilatedFst::Super::dumpvars(int level, const std::string& hier);
#endif
//=============================================================================
// VerilatedFstBuffer
class VerilatedFstBuffer final : public VerilatedTraceBuffer<VerilatedFst, VerilatedFstBuffer> {
class VerilatedFstBuffer VL_NOT_FINAL {
// Give the trace file access to the private bits
friend VerilatedFst;
friend VerilatedFst::Super;
friend VerilatedFst::Buffer;
friend VerilatedFst::OffloadBuffer;
VerilatedFst& m_owner; // Trace file owning this buffer. Required by subclasses.
// The FST file handle
void* const m_fst = m_owner.m_fst;
@@ -136,23 +151,23 @@ class VerilatedFstBuffer final : public VerilatedTraceBuffer<VerilatedFst, Veril
// String buffer long enough to hold maxBits() chars
char* const m_strbuf = m_owner.m_strbuf;
public:
// CONSTRUCTOR
explicit VerilatedFstBuffer(VerilatedFst& owner);
~VerilatedFstBuffer() = default;
explicit VerilatedFstBuffer(VerilatedFst& owner)
: m_owner{owner} {}
virtual ~VerilatedFstBuffer() = default;
//=========================================================================
// Implementation of VerilatedTraceBuffer interface
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
// called from only one place (the full* methods), so always inline them.
VL_ATTR_ALWINLINE inline void emitBit(uint32_t code, CData newval);
VL_ATTR_ALWINLINE inline void emitCData(uint32_t code, CData newval, int bits);
VL_ATTR_ALWINLINE inline void emitSData(uint32_t code, SData newval, int bits);
VL_ATTR_ALWINLINE inline void emitIData(uint32_t code, IData newval, int bits);
VL_ATTR_ALWINLINE inline void emitQData(uint32_t code, QData newval, int bits);
VL_ATTR_ALWINLINE inline void emitWData(uint32_t code, const WData* newvalp, int bits);
VL_ATTR_ALWINLINE inline void emitDouble(uint32_t code, double newval);
VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
};
//=============================================================================
@@ -217,7 +232,7 @@ public:
}
// Internal class access
inline VerilatedFst* spTrace() { return &m_sptrace; }
VerilatedFst* spTrace() { return &m_sptrace; }
};
#endif // guard
+2 -4
View File
@@ -22,6 +22,7 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_fst_sc.h"
//======================================================================
@@ -30,7 +31,7 @@
void VerilatedFstSc::open(const char* filename) {
if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) {
vl_fatal(__FILE__, __LINE__, "VerilatedFstSc",
("%Error: VerilatedFstSc::open(\"" + std::string(filename)
("%Error: VerilatedFstSc::open(\"" + std::string{filename}
+ "\") is called before sc_core::sc_start(). "
"Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file.")
.c_str());
@@ -63,9 +64,6 @@ void VerilatedFstSc::trace(const unsigned int&, const std::string&, const char**
DECL_TRACE_METHOD_B( unsigned short )
DECL_TRACE_METHOD_B( unsigned int )
DECL_TRACE_METHOD_B( unsigned long )
#ifdef SYSTEMC_64BIT_PATCHES
DECL_TRACE_METHOD_B( unsigned long long)
#endif
DECL_TRACE_METHOD_B( char )
DECL_TRACE_METHOD_B( short )
DECL_TRACE_METHOD_B( int )
+12 -13
View File
@@ -23,8 +23,9 @@
#define _VERILATED_FST_SC_H_ 1
#include "verilatedos.h"
#include "verilated_sc.h"
#include "verilated_fst_c.h"
#include "verilated_sc.h"
#include <string>
@@ -51,34 +52,35 @@ public:
}
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
}
virtual ~VerilatedFstSc() { close(); }
~VerilatedFstSc() override { close(); }
// METHODS
/// Called by SystemC simulate()
virtual void cycle(bool delta_cycle) {
void cycle(bool delta_cycle) override {
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
}
// Override VerilatedFstC. Must be called after starting simulation.
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
// Note: this is not a virtual function in the base class, so no 'override'
virtual void open(const char* filename) VL_MT_SAFE;
private:
/// Fake outs for linker
#ifdef NC_SYSTEMC
// Cadence Incisive has these as abstract functions so we must create them
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
void set_time_unit(int exponent10_seconds) override {} // deprecated
#endif
virtual void set_time_unit(double v, sc_time_unit tu) {} // LCOV_EXCL_LINE
void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE
//--------------------------------------------------
// SystemC 2.1.v1
#define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const std::string& name);
#define DECL_TRACE_METHOD_A(tp) void trace(const tp& object, const std::string& name) override;
#define DECL_TRACE_METHOD_B(tp) \
virtual void trace(const tp& object, const std::string& name, int width);
void trace(const tp& object, const std::string& name, int width) override;
virtual void write_comment(const std::string&);
virtual void trace(const unsigned int&, const std::string&, const char**);
void write_comment(const std::string&) override;
void trace(const unsigned int&, const std::string&, const char**) override;
// clang-format off
// Formatting matches that of sc_trace.h
@@ -95,9 +97,6 @@ private:
DECL_TRACE_METHOD_B( unsigned short )
DECL_TRACE_METHOD_B( unsigned int )
DECL_TRACE_METHOD_B( unsigned long )
#ifdef SYSTEMC_64BIT_PATCHES
DECL_TRACE_METHOD_B( unsigned long long)
#endif
DECL_TRACE_METHOD_B( char )
DECL_TRACE_METHOD_B( short )
DECL_TRACE_METHOD_B( int )
+27 -28
View File
@@ -146,7 +146,7 @@ extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp);
extern IData VL_SYSTEM_IQ(QData lhs);
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
extern IData VL_TESTPLUSARGS_I(const char* formatp);
extern IData VL_TESTPLUSARGS_I(const std::string& format);
extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish
//=========================================================================
@@ -256,25 +256,7 @@ extern void _vl_debug_print_w(int lbits, WDataInP const iwp);
//=========================================================================
// Pli macros
extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE;
// These are deprecated and used only to establish the default precision/units.
// Use Verilator timescale-override for better control.
// clang-format off
#ifndef VL_TIME_PRECISION
# ifdef VL_TIME_PRECISION_STR
# define VL_TIME_PRECISION VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR))
# else
# define VL_TIME_PRECISION (-12) ///< Timescale default units if not in Verilog - picoseconds
# endif
#endif
#ifndef VL_TIME_UNIT
# ifdef VL_TIME_UNIT_STR
# define VL_TIME_UNIT VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR))
# else
# define VL_TIME_UNIT (-12) ///< Timescale default units if not in Verilog - picoseconds
# endif
#endif
#if defined(SYSTEMC_VERSION)
/// Return current simulation time
@@ -300,7 +282,7 @@ inline uint64_t vl_time_stamp64() {
# endif
#endif
inline uint64_t VerilatedContext::time() const VL_MT_SAFE {
uint64_t VerilatedContext::time() const VL_MT_SAFE {
// When using non-default context, fastest path is return time
if (VL_LIKELY(m_s.m_time)) return m_s.m_time;
#if defined(SYSTEMC_VERSION) || (!defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY))
@@ -466,10 +448,17 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
{ \
const int words = VL_WORDS_I(obits); \
sc_biguint<(obits)> _butemp = (svar).read(); \
for (int i = 0; i < words; ++i) { \
int msb = ((i + 1) * VL_IDATASIZE) - 1; \
msb = (msb >= (obits)) ? ((obits)-1) : msb; \
(owp)[i] = _butemp.range(msb, i * VL_IDATASIZE).to_uint(); \
uint32_t* chunk = _butemp.get_raw(); \
int32_t lsb = 0; \
while (lsb < obits - BITS_PER_DIGIT) { \
const uint32_t data = *chunk; \
++chunk; \
_vl_insert_WI(owp.data(), data, lsb + BITS_PER_DIGIT - 1, lsb); \
lsb += BITS_PER_DIGIT; \
} \
if (lsb < obits) { \
const uint32_t msb_data = *chunk; \
_vl_insert_WI(owp.data(), msb_data, obits - 1, lsb); \
} \
(owp)[words - 1] &= VL_MASK_E(obits); \
}
@@ -510,13 +499,23 @@ 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_biguint<(obits)> _butemp; \
for (int i = 0; i < VL_WORDS_I(obits); ++i) { \
int msb = ((i + 1) * VL_IDATASIZE) - 1; \
msb = (msb >= (obits)) ? ((obits)-1) : msb; \
_butemp.range(msb, i* VL_IDATASIZE) = (rwp)[i]; \
int32_t lsb = 0; \
uint32_t* chunk = _butemp.get_raw(); \
while (lsb + VL_SC_BITS_PER_DIGIT < (obits)) { \
static_assert(std::is_same<IData, EData>::value, "IData and EData missmatch"); \
const uint32_t data = VL_SEL_IWII(lsb + VL_SC_BITS_PER_DIGIT + 1, (rwp).data(), lsb, \
VL_SC_BITS_PER_DIGIT); \
*chunk = data & VL_MASK_E(VL_SC_BITS_PER_DIGIT); \
++chunk; \
lsb += VL_SC_BITS_PER_DIGIT; \
} \
if (lsb < (obits)) { \
const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits)-lsb); \
*chunk = msb_data & VL_MASK_E((obits)-lsb); \
} \
(svar).write(_butemp); \
}
+19 -24
View File
@@ -143,11 +143,10 @@ class VerilatedThreadMsgQueue final {
public:
// CONSTRUCTORS
VerilatedThreadMsgQueue() {}
~VerilatedThreadMsgQueue() {
// The only call of this with a non-empty queue is a fatal error.
// So this does not flush the queue, as the destination queue is not known to this class.
}
VerilatedThreadMsgQueue() = default;
~VerilatedThreadMsgQueue() = default;
// The only call of destructor with a non-empty queue is a fatal error.
// So this does not flush the queue, as the destination queue is not known to this class.
private:
VL_UNCOPYABLE(VerilatedThreadMsgQueue);
@@ -188,11 +187,11 @@ class VerilatedFpList final {
public:
using const_iterator = FILE* const*;
explicit VerilatedFpList() {}
explicit VerilatedFpList() = default;
const_iterator begin() const { return m_fp; }
const_iterator end() const { return m_fp + m_sz; }
std::size_t size() const { return m_sz; }
std::size_t capacity() const { return 31; }
static std::size_t capacity() { return 31; }
void push_back(FILE* fd) {
if (VL_LIKELY(size() < capacity())) m_fp[m_sz++] = fd;
}
@@ -236,12 +235,11 @@ class VerilatedContextImp final : VerilatedContext {
return s_s;
}
private:
public: // But only for verilated*.cpp
// CONSTRUCTORS - no data can live here, use only VerilatedContext
VerilatedContextImp() = delete;
~VerilatedContextImp() = delete;
public: // But only for verilated*.cpp
// METHODS - extending into VerilatedContext, call via impp()->
// Random seed handling
@@ -272,13 +270,12 @@ public: // But only for verilated*.cpp
std::string argPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex);
std::pair<int, char**> argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex);
public: // But only for verilated*.cpp
// METHODS - scope name
// METHODS - scope name - INTERNAL only for verilated*.cpp
void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE;
void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE;
public: // But only for verilated*.cpp
// METHODS - file IO
// METHODS - file IO - INTERNAL only for verilated*.cpp
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock{m_fdMutex};
if (m_fdFreeMct.empty()) return 0;
@@ -298,7 +295,7 @@ public: // But only for verilated*.cpp
const std::size_t start = std::max<std::size_t>(31UL + 1UL + 3UL, m_fdps.size());
const std::size_t excess = 10;
m_fdps.resize(start + excess);
std::fill(m_fdps.begin() + start, m_fdps.end(), (FILE*)0);
std::fill(m_fdps.begin() + start, m_fdps.end(), static_cast<FILE*>(nullptr));
m_fdFree.resize(excess);
for (std::size_t i = 0, id = start; i < m_fdFree.size(); ++i, ++id) {
m_fdFree[i] = id;
@@ -344,7 +341,7 @@ public: // But only for verilated*.cpp
if (VL_UNLIKELY(idx <= 2)) return; // stdout/stdin/stderr
if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free
std::fclose(m_fdps[idx]);
m_fdps[idx] = (FILE*)0;
m_fdps[idx] = nullptr;
m_fdFree.push_back(idx);
} else {
// MCD case
@@ -359,7 +356,7 @@ public: // But only for verilated*.cpp
}
}
}
inline FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock{m_fdMutex};
const VerilatedFpList fdlist = fdToFpList(fdi);
if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
@@ -464,13 +461,12 @@ public:
// METHODS - debug
static void versionDump() VL_MT_SAFE;
public:
// METHODS - user scope tracking
// We implement this as a single large map instead of one map per scope.
// There's often many more scopes than userdata's and thus having a ~48byte
// per map overhead * N scopes would take much more space and cache thrashing.
// As scopep's are pointers, this implicitly handles multiple Context's
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
static void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
const VerilatedLockGuard lock{s().m_userMapMutex};
const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
if (it != s().m_userMap.end()) {
@@ -479,14 +475,15 @@ public:
s().m_userMap.emplace(std::make_pair(scopep, userKey), userData);
}
}
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
static void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
const VerilatedLockGuard lock{s().m_userMapMutex};
const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
return it->second;
}
public: // But only for verilated.cpp
// METHODS - But only for verilated.cpp
// Symbol table destruction cleans up the entries for each scope.
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope on destruction, so we only iterate.
@@ -512,8 +509,7 @@ public: // But only for verilated.cpp
}
}
public: // But only for verilated*.cpp
// METHODS - hierarchy
// METHODS - hierarchy - only for verilated*.cpp
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at construction for VPI accessible elements
const VerilatedLockGuard lock{s().m_hierMapMutex};
@@ -534,8 +530,7 @@ public: // But only for verilated*.cpp
return &s().m_hierMap;
}
public: // But only for verilated*.cpp
// METHODS - export names
// METHODS - export names - only for verilated*.cpp
// Each function prototype is converted to a function number which we
// then use to index a 2D table also indexed by scope number, because we
+44 -10
View File
@@ -17,6 +17,7 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_profiler.h"
#if VL_THREADED
@@ -60,54 +61,87 @@ uint16_t VlExecutionRecord::getcpu() {
//=============================================================================
// VlExecutionProfiler implementation
template <size_t N> static size_t roundUptoMultipleOf(size_t value) {
template <size_t N>
static size_t roundUptoMultipleOf(size_t value) {
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
size_t mask = N - 1;
return (value + mask) & ~mask;
}
VlExecutionProfiler::VlExecutionProfiler() {
VlExecutionProfiler::VlExecutionProfiler(VerilatedContext& context)
: m_context{context} {
// Setup profiling on main thread
setupThread(0);
}
void VlExecutionProfiler::configure(const VerilatedContext& context) {
void VlExecutionProfiler::configure() {
if (VL_UNLIKELY(m_enabled)) {
--m_windowCount;
if (VL_UNLIKELY(m_windowCount == context.profExecWindow())) {
if (VL_UNLIKELY(m_windowCount == m_context.profExecWindow())) {
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start collection\n"););
clear(); // Clear the profile after the cache warm-up cycles.
m_tickBegin = VL_CPU_TICK();
} else if (VL_UNLIKELY(m_windowCount == 0)) {
const uint64_t tickEnd = VL_CPU_TICK();
VL_DEBUG_IF(VL_DBG_MSGF("+ profile end\n"););
const std::string& fileName = context.profExecFilename();
const std::string& fileName = m_context.profExecFilename();
dump(fileName.c_str(), tickEnd);
m_enabled = false;
}
return;
}
const uint64_t startReq = context.profExecStart() + 1; // + 1, so we can start at time 0
const uint64_t startReq = m_context.profExecStart() + 1; // + 1, so we can start at time 0
if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= context.profExecStart())) {
if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= m_context.profExecStart())) {
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start warmup\n"););
VL_DEBUG_IF(assert(m_windowCount == 0););
m_enabled = true;
m_windowCount = context.profExecWindow() * 2;
m_windowCount = m_context.profExecWindow() * 2;
m_lastStartReq = startReq;
}
}
VerilatedVirtualBase* VlExecutionProfiler::construct(VerilatedContext& context) {
VlExecutionProfiler* const selfp = new VlExecutionProfiler{context};
#if VL_THREADED
if (VlThreadPool* const threadPoolp = static_cast<VlThreadPool*>(context.threadPoolp())) {
for (int i = 0; i < threadPoolp->numThreads(); ++i) {
// Data to pass to worker thread initialization
struct Data {
VlExecutionProfiler* const selfp;
const uint32_t threadId;
} data{selfp, static_cast<uint32_t>(i + 1)};
// Initialize worker thread
threadPoolp->workerp(i)->addTask(
[](void* userp, bool) {
Data* const datap = static_cast<Data*>(userp);
datap->selfp->setupThread(datap->threadId);
},
&data);
// Wait until initializationis complete
threadPoolp->workerp(i)->wait();
}
}
#endif
return selfp;
}
void VlExecutionProfiler::setupThread(uint32_t threadId) {
// Reserve some space in the thread-local profiling buffer, in order to try to avoid malloc
// while profiling.
t_trace.reserve(RESERVED_TRACE_CAPACITY);
// Register thread-local buffer in list of all buffers
bool exists;
{
const VerilatedLockGuard lock{m_mutex};
bool exists = !m_traceps.emplace(threadId, &t_trace).second;
assert(!exists);
exists = !m_traceps.emplace(threadId, &t_trace).second;
}
if (VL_UNLIKELY(exists)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "multiple initialization of profiler on some thread");
}
}
+16 -14
View File
@@ -24,10 +24,6 @@
#include "verilatedos.h"
#ifndef VL_PROFILER
#error "verilated_profiler.h/cpp expects VL_PROFILER (from --prof-{exec, pgo}"
#endif
#include "verilated.h"
#include <array>
@@ -38,18 +34,18 @@
#include <vector>
class VlExecutionProfiler;
class VlThreadPool;
//=============================================================================
// Macros to simplify generated code
#define VL_EXEC_TRACE_ADD_RECORD(vlSymsp) \
if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfiler.enabled())) \
(vlSymsp)->__Vm_executionProfiler.addRecord()
if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfilerp->enabled())) \
(vlSymsp)->__Vm_executionProfilerp->addRecord()
//=============================================================================
// Return high-precision counter for profiling, or 0x0 if not available
VL_ATTR_ALWINLINE
inline QData VL_CPU_TICK() {
VL_ATTR_ALWINLINE QData VL_CPU_TICK() {
uint64_t val;
VL_GET_CPU_TICK(val);
return val;
@@ -136,7 +132,7 @@ static_assert(std::is_trivially_destructible<VlExecutionRecord>::value,
//=============================================================================
// VlExecutionProfiler is for collecting profiling data about model execution
class VlExecutionProfiler final {
class VlExecutionProfiler final : public VerilatedVirtualBase {
// CONSTANTS
// In order to try to avoid dynamic memory allocations during the actual profiling phase,
@@ -154,6 +150,7 @@ class VlExecutionProfiler final {
using ExecutionTrace = std::vector<VlExecutionRecord>;
// STATE
VerilatedContext& m_context; // The context this profiler is under
static VL_THREAD_LOCAL ExecutionTrace t_trace; // thread-local trace buffers
mutable VerilatedMutex m_mutex;
// Map from thread id to &t_trace of given thread
@@ -167,31 +164,36 @@ class VlExecutionProfiler final {
public:
// CONSTRUCTOR
VlExecutionProfiler();
explicit VlExecutionProfiler(VerilatedContext& context);
~VlExecutionProfiler() override = default;
// METHODS
// Is profiling enabled
inline bool enabled() const { return m_enabled; }
bool enabled() const { return m_enabled; }
// Append a trace record to the trace buffer of the current thread
inline VlExecutionRecord& addRecord() {
static VlExecutionRecord& addRecord() {
t_trace.emplace_back();
return t_trace.back();
}
// Configure profiler (called in beginning of 'eval')
void configure(const VerilatedContext&);
void configure();
// Setup profiling on a particular thread;
void setupThread(uint32_t threadId);
// Clear all profiling data
void clear() VL_MT_SAFE_EXCLUDES(m_mutex);
// Write profiling data into file
void dump(const char* filenamep, uint64_t tickEnd) VL_MT_SAFE_EXCLUDES(m_mutex);
// Passed to VerilatedContext to create the VlExecutionProfiler profiler instance
static VerilatedVirtualBase* construct(VerilatedContext& context);
};
//=============================================================================
// VlPgoProfiler is for collecting profiling data for PGO
template <std::size_t T_Entries> class VlPgoProfiler final {
template <std::size_t T_Entries>
class VlPgoProfiler final {
// TYPES
struct Record final {
const std::string m_name; // Hashed name of mtask/etc
+6 -4
View File
@@ -24,8 +24,10 @@
#define VERILATOR_VERILATED_SAVE_CPP_
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_save.h"
#include "verilated.h"
#include "verilated_imp.h"
#include <cerrno>
@@ -38,13 +40,13 @@
# include <unistd.h>
#endif
#ifndef O_LARGEFILE // For example on WIN32
#ifndef O_LARGEFILE // WIN32 headers omit this
# define O_LARGEFILE 0
#endif
#ifndef O_NONBLOCK
#ifndef O_NONBLOCK // WIN32 headers omit this
# define O_NONBLOCK 0
#endif
#ifndef O_CLOEXEC
#ifndef O_CLOEXEC // WIN32 headers omit this
# define O_CLOEXEC 0
#endif
// clang-format on
+11 -9
View File
@@ -23,6 +23,7 @@
#define VERILATOR_VERILATED_SAVE_C_H_
#include "verilatedos.h"
#include "verilated.h"
#include <string>
@@ -77,7 +78,7 @@ public:
virtual void flush() VL_MT_UNSAFE_ONE {}
/// Write data to stream
VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
const uint8_t* __restrict dp = (const uint8_t* __restrict)datap;
const uint8_t* __restrict dp = static_cast<const uint8_t* __restrict>(datap);
while (size) {
bufferCheck();
size_t blk = size;
@@ -194,16 +195,16 @@ public:
/// Construct new object
VerilatedSave() = default;
/// Flush, close and destruct
virtual ~VerilatedSave() override { close(); }
~VerilatedSave() override { close(); }
// METHODS
/// Open the file; call isOpen() to see if errors
void open(const char* filenamep) VL_MT_UNSAFE_ONE;
/// Open the file; call isOpen() to see if errors
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
/// Flush and close the file
virtual void close() override VL_MT_UNSAFE_ONE;
void close() override VL_MT_UNSAFE_ONE;
/// Flush data to file
virtual void flush() override VL_MT_UNSAFE_ONE;
void flush() override VL_MT_UNSAFE_ONE;
};
//=============================================================================
@@ -221,7 +222,7 @@ public:
/// Construct new object
VerilatedRestore() = default;
/// Flush, close and destruct
virtual ~VerilatedRestore() override { close(); }
~VerilatedRestore() override { close(); }
// METHODS
/// Open the file; call isOpen() to see if errors
@@ -229,9 +230,9 @@ public:
/// Open the file; call isOpen() to see if errors
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
/// Close the file
virtual void close() override VL_MT_UNSAFE_ONE;
virtual void flush() override VL_MT_UNSAFE_ONE {}
virtual void fill() override VL_MT_UNSAFE_ONE;
void close() override VL_MT_UNSAFE_ONE;
void flush() override VL_MT_UNSAFE_ONE {}
void fill() override VL_MT_UNSAFE_ONE;
};
//=============================================================================
@@ -287,7 +288,8 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& r
uint32_t len = 0;
os >> len;
rhs.resize(len);
return os.read((void*)rhs.data(), len);
// C cast is required below
return os.read((void*)(rhs.data()), len);
}
VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp);
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp);
-1
View File
@@ -134,7 +134,6 @@ public:
initUnpacked(ulims);
}
public:
~VerilatedVarProps() = default;
// METHODS
bool magicOk() const { return m_magic == MAGIC; }
+1
View File
@@ -30,6 +30,7 @@
#define VERILATOR_VERILATED_SYMS_H_
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_sym_props.h"
+34 -46
View File
@@ -22,11 +22,8 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_threads.h"
#ifdef VL_PROFILER
#include "verilated_profiler.h"
#endif
#include "verilated_threads.h"
#include <cstdio>
#include <memory>
@@ -51,68 +48,59 @@ VlMTaskVertex::VlMTaskVertex(uint32_t upstreamDepCount)
//=============================================================================
// VlWorkerThread
VlWorkerThread::VlWorkerThread(uint32_t threadId, VerilatedContext* contextp,
VlExecutionProfiler* profilerp)
VlWorkerThread::VlWorkerThread(VerilatedContext* contextp)
: m_ready_size{0}
, m_exiting{false}
, m_cthread{startWorker, this, threadId, profilerp}
, m_contextp{contextp} {}
, m_cthread{startWorker, this, contextp} {}
VlWorkerThread::~VlWorkerThread() {
m_exiting.store(true, std::memory_order_release);
wakeUp();
shutdown();
// The thread should exit; join it.
m_cthread.join();
}
static void shutdownTask(void*, bool) {
// Deliberately empty, we use the address of this function as a magic number
}
void VlWorkerThread::shutdown() { addTask(shutdownTask, nullptr); }
void VlWorkerThread::wait() {
// Enqueue a task that sets this flag. Execution is in-order so this ensures completion.
std::atomic<bool> flag{false};
addTask([](void* flagp, bool) { static_cast<std::atomic<bool>*>(flagp)->store(true); }, &flag);
// Spin wait
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
if (flag.load()) return;
VL_CPU_RELAX();
}
// Yield wait
while (!flag.load()) std::this_thread::yield();
}
void VlWorkerThread::workerLoop() {
ExecRec work;
work.m_fnp = nullptr;
// Wait for the first task without spinning, in case the thread is never actually used.
dequeWork</* SpinWait: */ false>(&work);
while (true) {
if (VL_LIKELY(!work.m_fnp)) dequeWork(&work);
// Do this here, not above, to avoid a race with the destructor.
if (VL_UNLIKELY(m_exiting.load(std::memory_order_acquire))) break;
if (VL_LIKELY(work.m_fnp)) {
work.m_fnp(work.m_selfp, work.m_evenCycle);
work.m_fnp = nullptr;
}
if (VL_UNLIKELY(work.m_fnp == shutdownTask)) break;
work.m_fnp(work.m_selfp, work.m_evenCycle);
// Wait for next task with spinning.
dequeWork</* SpinWait: */ true>(&work);
}
}
void VlWorkerThread::startWorker(VlWorkerThread* workerp, uint32_t threadId,
VlExecutionProfiler* profilerp) {
Verilated::threadContextp(workerp->m_contextp);
#ifdef VL_PROFILER
// Note: setupThread is not defined without VL_PROFILER, hence the #ifdef. Still, we might
// not be profiling execution (e.g.: PGO only), so profilerp might still be nullptr.
if (profilerp) profilerp->setupThread(threadId);
#endif
void VlWorkerThread::startWorker(VlWorkerThread* workerp, VerilatedContext* contextp) {
Verilated::threadContextp(contextp);
workerp->workerLoop();
}
//=============================================================================
// VlThreadPool
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads,
VlExecutionProfiler* profiler) {
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
++nThreads;
const unsigned cpus = std::thread::hardware_concurrency();
if (cpus < nThreads) {
static int warnedOnce = 0;
if (!warnedOnce++) {
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
" --threads %d; may run slow.\n",
cpus, nThreads);
}
}
// Create worker threads
for (uint32_t threadId = 1; threadId < nThreads; ++threadId) {
m_workers.push_back(new VlWorkerThread{threadId, contextp, profiler});
}
VlThreadPool::VlThreadPool(VerilatedContext* contextp, unsigned nThreads) {
for (unsigned i = 0; i < nThreads; ++i) m_workers.push_back(new VlWorkerThread{contextp});
}
VlThreadPool::~VlThreadPool() {
+29 -31
View File
@@ -24,6 +24,7 @@
#define VERILATOR_VERILATED_THREADS_H_
#include "verilatedos.h"
#include "verilated.h" // for VerilatedMutex and clang annotations
#ifndef VL_THREADED
@@ -50,6 +51,9 @@
#endif
// clang-format on
class VlExecutionProfiler;
class VlThreadPool;
// VlMTaskVertex and VlThreadpool will work with multiple model class types.
// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
// as a void* here.
@@ -99,7 +103,7 @@ public:
// Upstream mtasks must call this when they complete.
// Returns true when the current MTaskVertex becomes ready to execute,
// false while it's still waiting on more dependencies.
inline bool signalUpstreamDone(bool evenCycle) {
bool signalUpstreamDone(bool evenCycle) {
if (evenCycle) {
const uint32_t upstreamDepsDone
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
@@ -112,11 +116,11 @@ public:
return (upstreamDepsDone_prev == 1);
}
}
inline bool areUpstreamDepsDone(bool evenCycle) const {
bool areUpstreamDepsDone(bool evenCycle) const {
const uint32_t target = evenCycle ? m_upstreamDepCount : 0;
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
}
inline void waitUntilUpstreamDone(bool evenCycle) const {
void waitUntilUpstreamDone(bool evenCycle) const {
unsigned ct = 0;
while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
VL_CPU_RELAX();
@@ -129,20 +133,14 @@ public:
}
};
class VlExecutionProfiler;
class VlThreadPool;
class VlWorkerThread final {
private:
// TYPES
struct ExecRec {
VlExecFnp m_fnp; // Function to execute
VlSelfP m_selfp; // Symbol table to execute
bool m_evenCycle; // Even/odd for flag alternation
ExecRec()
: m_fnp{nullptr}
, m_selfp{nullptr}
, m_evenCycle{false} {}
VlExecFnp m_fnp = nullptr; // Function to execute
VlSelfP m_selfp = nullptr; // Symbol table to execute
bool m_evenCycle = false; // Even/odd for flag alternation
ExecRec() = default;
ExecRec(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
: m_fnp{fnp}
, m_selfp{selfp}
@@ -162,26 +160,24 @@ private:
// Store the size atomically, so we can spin wait
std::atomic<size_t> m_ready_size;
std::atomic<bool> m_exiting; // Worker thread should exit
std::thread m_cthread; // Underlying C++ thread record
VerilatedContext* const m_contextp; // Context for spawned thread
VL_UNCOPYABLE(VlWorkerThread);
public:
// CONSTRUCTORS
explicit VlWorkerThread(uint32_t threadId, VerilatedContext* contextp,
VlExecutionProfiler* profilerp);
explicit VlWorkerThread(VerilatedContext* contextp);
~VlWorkerThread();
// METHODS
inline void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
template <bool SpinWait>
void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Spin for a while, waiting for new data
for (int i = 0; i < VL_LOCK_SPINS; ++i) {
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) { //
break;
if VL_CONSTEXPR_CXX17 (SpinWait) {
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) break;
VL_CPU_RELAX();
}
VL_CPU_RELAX();
}
VerilatedLockGuard lock{m_mutex};
while (m_ready.empty()) {
@@ -195,8 +191,7 @@ public:
m_ready.erase(m_ready.begin());
m_ready_size.fetch_sub(1, std::memory_order_relaxed);
}
inline void wakeUp() { addTask(nullptr, nullptr, false); }
inline void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle = false)
VL_MT_SAFE_EXCLUDES(m_mutex) {
bool notify;
{
@@ -207,12 +202,15 @@ public:
}
if (notify) m_cv.notify_one();
}
void shutdown(); // Finish current tasks, then terminate thread
void wait(); // Blocks calling thread until all tasks complete in this thread
void workerLoop();
static void startWorker(VlWorkerThread* workerp, uint32_t threadId,
VlExecutionProfiler* profilerp);
static void startWorker(VlWorkerThread* workerp, VerilatedContext* contextp);
};
class VlThreadPool final {
class VlThreadPool final : public VerilatedVirtualBase {
// MEMBERS
std::vector<VlWorkerThread*> m_workers; // our workers
@@ -221,12 +219,12 @@ public:
// Construct a thread pool with 'nThreads' dedicated threads. The thread
// pool will create these threads and make them available to execute tasks
// via this->workerp(index)->addTask(...)
VlThreadPool(VerilatedContext* contextp, int nThreads, VlExecutionProfiler* profilerp);
~VlThreadPool();
VlThreadPool(VerilatedContext* contextp, unsigned nThreads);
~VlThreadPool() override;
// METHODS
inline int numThreads() const { return m_workers.size(); }
inline VlWorkerThread* workerp(int index) {
int numThreads() const { return m_workers.size(); }
VlWorkerThread* workerp(int index) {
assert(index >= 0);
assert(index < m_workers.size());
return m_workers[index];
+169 -141
View File
@@ -24,21 +24,6 @@
// clang-format off
// In FST mode, VL_TRACE_THREADED enables offloading, but only if we also have
// the FST writer thread. This means with --trace-threads 1, we get the FST
// writer thread only, and with --trace-threads 2 we get offloading as well
#if defined(VL_TRACE_FST_WRITER_THREAD) && defined(VL_TRACE_THREADED)
# define VL_TRACE_OFFLOAD
#endif
// VCD tracing can happen fully in parallel
#if defined(VM_TRACE_VCD) && VM_TRACE_VCD && defined(VL_TRACE_THREADED)
# define VL_TRACE_PARALLEL
#endif
#if defined(VL_TRACE_PARALLEL) && defined(VL_TRACE_OFFLOAD)
# error "Cannot have VL_TRACE_PARALLEL and VL_TRACE_OFFLOAD together"
#endif
#include "verilated.h"
#include "verilated_trace_defs.h"
@@ -47,9 +32,10 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#ifdef VL_TRACE_OFFLOAD
#ifdef VL_THREADED
# include <deque>
# include <thread>
#endif
@@ -57,14 +43,18 @@
// clang-format on
class VlThreadPool;
template <class T_Trace, class T_Buffer> class VerilatedTraceBuffer;
template <class T_Buffer>
class VerilatedTraceBuffer;
template <class T_Buffer>
class VerilatedTraceOffloadBuffer;
#ifdef VL_TRACE_OFFLOAD
#ifdef VL_THREADED
//=============================================================================
// Offloaded tracing
// A simple synchronized first in first out queue
template <class T> class VerilatedThreadQueue final { // LCOV_EXCL_LINE // lcov bug
template <class T>
class VerilatedThreadQueue final { // LCOV_EXCL_LINE // lcov bug
private:
mutable VerilatedMutex m_mutex; // Protects m_queue
std::condition_variable_any m_cv;
@@ -129,27 +119,47 @@ public:
};
#endif
//=============================================================================
// VerilatedTraceConfig
// Simple data representing trace configuration required by generated models.
class VerilatedTraceConfig final {
public:
const bool m_useParallel; // Use parallel tracing
const bool m_useOffloading; // Offloading trace rendering
const bool m_useFstWriterThread; // Use the separate FST writer thread
VerilatedTraceConfig(bool useParallel, bool useOffloading, bool useFstWriterThread)
: m_useParallel{useParallel}
, m_useOffloading{useOffloading}
, m_useFstWriterThread{useFstWriterThread} {}
};
//=============================================================================
// VerilatedTrace
// T_Trace is the format specific subclass of VerilatedTrace.
// T_Buffer is the format specific subclass of VerilatedTraceBuffer.
template <class T_Trace, class T_Buffer> class VerilatedTrace VL_NOT_FINAL {
// Give the buffer (both base and derived) access to the private bits
friend VerilatedTraceBuffer<T_Trace, T_Buffer>;
friend T_Buffer;
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
template <class T_Trace, class T_Buffer>
class VerilatedTrace VL_NOT_FINAL {
public:
using Buffer = T_Buffer;
using Buffer = VerilatedTraceBuffer<T_Buffer>;
using OffloadBuffer = VerilatedTraceOffloadBuffer<T_Buffer>;
//=========================================================================
// Generic tracing internals
using initCb_t = void (*)(void*, T_Trace*, uint32_t); // Type of init callbacks
using dumpCb_t = void (*)(void*, Buffer*); // Type of dump callbacks
using dumpOffloadCb_t = void (*)(void*, OffloadBuffer*); // Type of offload dump callbacks
using cleanupCb_t = void (*)(void*, T_Trace*); // Type of cleanup callbacks
private:
// Give the buffer (both base and derived) access to the private bits
friend T_Buffer;
friend Buffer;
friend OffloadBuffer;
struct CallbackRecord {
// 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
@@ -158,6 +168,7 @@ private:
union { // The callback
initCb_t m_initCb;
dumpCb_t m_dumpCb;
dumpOffloadCb_t m_dumpOffloadCb;
cleanupCb_t m_cleanupCb;
};
void* m_userp; // The user pointer to pass to the callback (the symbol table)
@@ -167,12 +178,18 @@ private:
CallbackRecord(dumpCb_t cb, void* userp)
: m_dumpCb{cb}
, m_userp{userp} {}
CallbackRecord(dumpOffloadCb_t cb, void* userp)
: m_dumpOffloadCb{cb}
, m_userp{userp} {}
CallbackRecord(cleanupCb_t cb, void* userp)
: m_cleanupCb{cb}
, m_userp{userp} {}
};
#ifdef VL_TRACE_PARALLEL
bool m_offload = false; // Use the offload thread (ignored if !VL_THREADED)
bool m_parallel = false; // Use parallel tracing (ignored if !VL_THREADED)
#ifdef VL_THREADED
struct ParallelWorkerData {
const dumpCb_t m_cb; // The callback
void* const m_userp; // The use pointer to pass to the callback
@@ -194,19 +211,17 @@ private:
static void parallelWorkerTask(void*, bool);
#endif
using ParallelCallbackMap = std::unordered_map<VlThreadPool*, std::vector<CallbackRecord>>;
protected:
uint32_t* m_sigs_oldvalp = nullptr; // Previous value store
EData* m_sigs_enabledp = nullptr; // Bit vector of enabled codes (nullptr = all on)
private:
uint64_t m_timeLastDump = 0; // Last time we did a dump
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
std::vector<CallbackRecord> m_initCbs; // Routines to initialize tracing
ParallelCallbackMap m_fullCbs; // Routines to perform full dump
ParallelCallbackMap m_chgCbs; // Routines to perform incremental dump
std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump
std::vector<CallbackRecord> m_fullOffloadCbs; // Routines to perform offloaded full dump
std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump
std::vector<CallbackRecord> m_chgOffloadCbs; // Routines to perform offloaded incremental dump
std::vector<CallbackRecord> m_cleanupCbs; // Routines to call at the end of dump
std::vector<VlThreadPool*> m_threadPoolps; // All thread pools, in insertion order
bool m_fullDump = true; // Whether a full dump is required on the next call to 'dump'
uint32_t m_nextCode = 0; // Next code number to assign
uint32_t m_numSignals = 0; // Number of distinct signals
@@ -216,24 +231,27 @@ private:
char m_scopeEscape = '.';
double m_timeRes = 1e-9; // Time resolution (ns/ms etc)
double m_timeUnit = 1e-0; // Time units (ns/ms etc)
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
void addThreadPool(VlThreadPool* threadPoolp) VL_MT_SAFE_EXCLUDES(m_mutex);
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord& cbRec)
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex);
// Equivalent to 'this' but is of the sub-type 'T_Trace*'. Use 'self()->'
// to access duck-typed functions to avoid a virtual function call.
T_Trace* self() { return static_cast<T_Trace*>(this); }
void runParallelCallbacks(const ParallelCallbackMap& cbMap);
void runCallbacks(const std::vector<CallbackRecord>& cbVec);
void runOffloadedCallbacks(const std::vector<CallbackRecord>& cbVec);
// Flush any remaining data for this file
static void onFlush(void* selfp) VL_MT_UNSAFE_ONE;
// Close the file on termination
static void onExit(void* selfp) VL_MT_UNSAFE_ONE;
#ifdef VL_TRACE_OFFLOAD
#ifdef VL_THREADED
// Number of total offload buffers that have been allocated
uint32_t m_numOffloadBuffers = 0;
// Size of offload buffers
@@ -279,7 +297,6 @@ protected:
uint32_t numSignals() const { return m_numSignals; }
uint32_t maxBits() const { return m_maxBits; }
void fullDump(bool value) { m_fullDump = value; }
uint64_t timeLastDump() { return m_timeLastDump; }
double timeRes() const { return m_timeRes; }
double timeUnit() const { return m_timeUnit; }
@@ -300,6 +317,14 @@ protected:
void closeBase();
void flushBase();
#ifdef VL_THREADED
bool offload() const { return m_offload; }
bool parallel() const { return m_parallel; }
#else
static constexpr bool offload() { return false; }
static constexpr bool parallel() { return false; }
#endif
//=========================================================================
// Virtual functions to be provided by the format specific implementation
@@ -315,6 +340,9 @@ protected:
virtual Buffer* getTraceBuffer() = 0;
virtual void commitTraceBuffer(Buffer*) = 0;
// Configure sub-class
virtual void configure(const VerilatedTraceConfig&) = 0;
public:
//=========================================================================
// External interface to client code
@@ -341,9 +369,12 @@ public:
//=========================================================================
// Non-hot path internal interface to Verilator generated code
void addModel(VerilatedModel*) VL_MT_SAFE_EXCLUDES(m_mutex);
void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE;
void addFullCb(dumpCb_t cb, void* userp, VlThreadPool* = nullptr) VL_MT_SAFE;
void addChgCb(dumpCb_t cb, void* userp, VlThreadPool* = nullptr) VL_MT_SAFE;
void addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
void addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE;
void scopeEscape(char flag) { m_scopeEscape = flag; }
@@ -355,33 +386,26 @@ public:
//=============================================================================
// VerilatedTraceBuffer
// T_Trace is the format specific subclass of VerilatedTrace.
// T_Buffer is the format specific subclass of VerilatedTraceBuffer.
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
template <class T_Trace, class T_Buffer> class VerilatedTraceBuffer VL_NOT_FINAL {
friend T_Trace; // Give the trace file access to the private bits
template <class T_Buffer>
class VerilatedTraceBuffer VL_NOT_FINAL : public T_Buffer {
protected:
T_Trace& m_owner; // The VerilatedTrace subclass that owns this buffer
// Type of the owner trace file
using Trace = typename std::remove_cv<
typename std::remove_reference<decltype(T_Buffer::m_owner)>::type>::type;
// Previous value store
uint32_t* const m_sigs_oldvalp = m_owner.m_sigs_oldvalp;
// Bit vector of enabled codes (nullptr = all on)
EData* const m_sigs_enabledp = m_owner.m_sigs_enabledp;
static_assert(std::has_virtual_destructor<T_Buffer>::value, "");
static_assert(std::is_base_of<VerilatedTrace<Trace, T_Buffer>, Trace>::value, "");
#ifdef VL_TRACE_OFFLOAD
// Write pointer into current buffer
uint32_t* m_offloadBufferWritep = m_owner.m_offloadBufferWritep;
// End of offload buffer
uint32_t* const m_offloadBufferEndp = m_owner.m_offloadBufferEndp;
#endif
friend Trace; // Give the trace file access to the private bits
friend std::default_delete<VerilatedTraceBuffer<T_Buffer>>;
// Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->'
// to access duck-typed functions to avoid a virtual function call.
inline T_Buffer* self() { return static_cast<T_Buffer*>(this); }
uint32_t* const m_sigs_oldvalp; // Previous value store
EData* const m_sigs_enabledp; // Bit vector of enabled codes (nullptr = all on)
explicit VerilatedTraceBuffer(T_Trace& owner);
virtual ~VerilatedTraceBuffer() = default;
explicit VerilatedTraceBuffer(Trace& owner);
~VerilatedTraceBuffer() override = default;
public:
//=========================================================================
@@ -401,7 +425,7 @@ public:
// duck-typed void emitWData(uint32_t code, const WData* newvalp, int bits) = 0;
// duck-typed void emitDouble(uint32_t code, double newval) = 0;
VL_ATTR_ALWINLINE inline uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
VL_ATTR_ALWINLINE uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
// Write to previous value buffer value and emit trace entry.
void fullBit(uint32_t* oldp, CData newval);
@@ -412,93 +436,32 @@ public:
void fullWData(uint32_t* oldp, const WData* newvalp, int bits);
void fullDouble(uint32_t* oldp, double newval);
#ifdef VL_TRACE_OFFLOAD
// Offloaded tracing. Just dump everything in the offload buffer
inline void chgBit(uint32_t code, CData newval) {
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep += 2;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgCData(uint32_t code, CData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_CDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgSData(uint32_t code, SData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_SDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgIData(uint32_t code, IData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_IDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgQData(uint32_t code, QData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_QDATA;
m_offloadBufferWritep[1] = code;
*reinterpret_cast<QData*>(m_offloadBufferWritep + 2) = newval;
m_offloadBufferWritep += 4;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgWData(uint32_t code, const WData* newvalp, int bits) {
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]; }
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgDouble(uint32_t code, double newval) {
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_DOUBLE;
m_offloadBufferWritep[1] = code;
// cppcheck-suppress invalidPointerCast
*reinterpret_cast<double*>(m_offloadBufferWritep + 2) = newval;
m_offloadBufferWritep += 4;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
#define chgBit chgBitImpl
#define chgCData chgCDataImpl
#define chgSData chgSDataImpl
#define chgIData chgIDataImpl
#define chgQData chgQDataImpl
#define chgWData chgWDataImpl
#define chgDouble chgDoubleImpl
#endif
// In non-offload mode, these are called directly by the trace callbacks,
// and are called chg*. In offload mode, they are called by the worker
// thread and are called chg*Impl
// Check previous dumped value of signal. If changed, then emit trace entry
VL_ATTR_ALWINLINE inline void chgBit(uint32_t* oldp, CData newval) {
VL_ATTR_ALWINLINE void chgBit(uint32_t* oldp, CData newval) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
}
VL_ATTR_ALWINLINE inline void chgCData(uint32_t* oldp, CData newval, int bits) {
VL_ATTR_ALWINLINE void chgCData(uint32_t* oldp, CData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgSData(uint32_t* oldp, SData newval, int bits) {
VL_ATTR_ALWINLINE void chgSData(uint32_t* oldp, SData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgIData(uint32_t* oldp, IData newval, int bits) {
VL_ATTR_ALWINLINE void chgIData(uint32_t* oldp, IData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgQData(uint32_t* oldp, QData newval, int bits) {
VL_ATTR_ALWINLINE void chgQData(uint32_t* oldp, QData newval, int bits) {
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
for (int i = 0; i < (bits + 31) / 32; ++i) {
if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
fullWData(oldp, newvalp, bits);
@@ -506,20 +469,85 @@ public:
}
}
}
VL_ATTR_ALWINLINE inline void chgDouble(uint32_t* oldp, double newval) {
VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) {
// cppcheck-suppress invalidPointerCast
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
}
#ifdef VL_TRACE_OFFLOAD
#undef chgBit
#undef chgCData
#undef chgSData
#undef chgIData
#undef chgQData
#undef chgWData
#undef chgDouble
#endif
};
#ifdef VL_THREADED
//=============================================================================
// VerilatedTraceOffloadBuffer
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
template <class T_Buffer>
class VerilatedTraceOffloadBuffer final : public VerilatedTraceBuffer<T_Buffer> {
using typename VerilatedTraceBuffer<T_Buffer>::Trace;
friend Trace; // Give the trace file access to the private bits
uint32_t* m_offloadBufferWritep; // Write pointer into current buffer
uint32_t* const m_offloadBufferEndp; // End of offload buffer
explicit VerilatedTraceOffloadBuffer(Trace& owner);
~VerilatedTraceOffloadBuffer() override = default;
public:
//=========================================================================
// Hot path internal interface to Verilator generated code
// Offloaded tracing. Just dump everything in the offload buffer
void chgBit(uint32_t code, CData newval) {
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep += 2;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgCData(uint32_t code, CData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_CDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgSData(uint32_t code, SData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_SDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgIData(uint32_t code, IData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_IDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgQData(uint32_t code, QData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_QDATA;
m_offloadBufferWritep[1] = code;
*reinterpret_cast<QData*>(m_offloadBufferWritep + 2) = newval;
m_offloadBufferWritep += 4;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgWData(uint32_t code, const WData* newvalp, int bits) {
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]; }
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
void chgDouble(uint32_t code, double newval) {
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_DOUBLE;
m_offloadBufferWritep[1] = code;
// cppcheck-suppress invalidPointerCast
*reinterpret_cast<double*>(m_offloadBufferWritep + 2) = newval;
m_offloadBufferWritep += 4;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
};
#endif
#endif // guard
+275 -178
View File
@@ -26,7 +26,7 @@
#include "verilated_intrinsics.h"
#include "verilated_trace.h"
#ifdef VL_TRACE_PARALLEL
#ifdef VL_THREADED
# include "verilated_threads.h"
# include <list>
#endif
@@ -78,11 +78,12 @@ static std::string doubleToTimescale(double value) {
return valuestr; // Gets converted to string, so no ref to stack
}
#ifdef VL_TRACE_OFFLOAD
#ifdef VL_THREADED
//=========================================================================
// Buffer management
template <> uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
template <>
uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
uint32_t* bufferp;
// Some jitter is expected, so some number of alternative offlaod buffers are
// required, but don't allocate more than 8 buffers.
@@ -101,7 +102,8 @@ template <> uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
return bufferp;
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const uint32_t* buffp) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const uint32_t* buffp) {
// Slow path code only called on flush/shutdown, so use a simple algorithm.
// Collect buffers from worker and stash them until we get the one we want.
std::deque<uint32_t*> stash;
@@ -116,7 +118,8 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const
//=========================================================================
// Worker thread
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
bool shutdown = false;
do {
@@ -127,7 +130,7 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
const uint32_t* readp = bufferp;
std::unique_ptr<VL_BUF_T> traceBufp; // We own the passed tracebuffer
std::unique_ptr<Buffer> traceBufp; // We own the passed tracebuffer
while (true) {
const uint32_t cmd = readp[0];
@@ -143,44 +146,44 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
// CHG_* commands
case VerilatedTraceOffloadCommand::CHG_BIT_0:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_0 " << top);
traceBufp->chgBitImpl(oldp, 0);
traceBufp->chgBit(oldp, 0);
continue;
case VerilatedTraceOffloadCommand::CHG_BIT_1:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_1 " << top);
traceBufp->chgBitImpl(oldp, 1);
traceBufp->chgBit(oldp, 1);
continue;
case VerilatedTraceOffloadCommand::CHG_CDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_CDATA " << top);
// Bits stored in bottom byte of command
traceBufp->chgCDataImpl(oldp, *readp, top);
traceBufp->chgCData(oldp, *readp, top);
readp += 1;
continue;
case VerilatedTraceOffloadCommand::CHG_SDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_SDATA " << top);
// Bits stored in bottom byte of command
traceBufp->chgSDataImpl(oldp, *readp, top);
traceBufp->chgSData(oldp, *readp, top);
readp += 1;
continue;
case VerilatedTraceOffloadCommand::CHG_IDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_IDATA " << top);
// Bits stored in bottom byte of command
traceBufp->chgIDataImpl(oldp, *readp, top);
traceBufp->chgIData(oldp, *readp, top);
readp += 1;
continue;
case VerilatedTraceOffloadCommand::CHG_QDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_QDATA " << top);
// Bits stored in bottom byte of command
traceBufp->chgQDataImpl(oldp, *reinterpret_cast<const QData*>(readp), top);
traceBufp->chgQData(oldp, *reinterpret_cast<const QData*>(readp), top);
readp += 2;
continue;
case VerilatedTraceOffloadCommand::CHG_WDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_WDATA " << top);
traceBufp->chgWDataImpl(oldp, readp, top);
traceBufp->chgWData(oldp, readp, top);
readp += VL_WORDS_I(top);
continue;
case VerilatedTraceOffloadCommand::CHG_DOUBLE:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_DOUBLE " << top);
traceBufp->chgDoubleImpl(oldp, *reinterpret_cast<const double*>(readp));
traceBufp->chgDouble(oldp, *reinterpret_cast<const double*>(readp));
readp += 2;
continue;
@@ -196,7 +199,7 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
case VerilatedTraceOffloadCommand::TRACE_BUFFER:
VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
readp -= 1; // No code in this command, undo increment
traceBufp.reset(*reinterpret_cast<VL_BUF_T* const*>(readp));
traceBufp.reset(*reinterpret_cast<Buffer* const*>(readp));
readp += 2;
continue;
@@ -231,7 +234,8 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
} while (VL_LIKELY(!shutdown));
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
// If the worker thread is not running, done..
if (!m_workerThread) return;
@@ -251,37 +255,45 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
//=============================================================================
// Life cycle
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {
#ifdef VL_TRACE_OFFLOAD
shutdownOffloadWorker();
while (m_numOffloadBuffers) {
delete[] m_offloadBuffersFromWorker.get();
--m_numOffloadBuffers;
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {
#ifdef VL_THREADED
if (offload()) {
shutdownOffloadWorker();
while (m_numOffloadBuffers) {
delete[] m_offloadBuffersFromWorker.get();
--m_numOffloadBuffers;
}
}
#endif
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {
#ifdef VL_TRACE_OFFLOAD
// Hand an empty buffer to the worker thread
uint32_t* const bufferp = getOffloadBuffer();
*bufferp = VerilatedTraceOffloadCommand::END;
m_offloadBuffersToWorker.put(bufferp);
// Wait for it to be returned. As the processing is in-order,
// this ensures all previous buffers have been processed.
waitForOffloadBuffer(bufferp);
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {
#ifdef VL_THREADED
if (offload()) {
// Hand an empty buffer to the worker thread
uint32_t* const bufferp = getOffloadBuffer();
*bufferp = VerilatedTraceOffloadCommand::END;
m_offloadBuffersToWorker.put(bufferp);
// Wait for it to be returned. As the processing is in-order,
// this ensures all previous buffers have been processed.
waitForOffloadBuffer(bufferp);
}
#endif
}
//=============================================================================
// Callbacks to run on global events
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush(void* selfp) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush(void* selfp) {
// This calls 'flush' on the derived class (which must then get any mutex)
reinterpret_cast<VL_SUB_T*>(selfp)->flush();
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
// This calls 'close' on the derived class (which must then get any mutex)
reinterpret_cast<VL_SUB_T*>(selfp)->close();
}
@@ -289,25 +301,26 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
//=============================================================================
// VerilatedTrace
template <> VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace() {
template <>
VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace() {
set_time_unit(Verilated::threadContextp()->timeunitString());
set_time_resolution(Verilated::threadContextp()->timeprecisionString());
}
template <> VerilatedTrace<VL_SUB_T, VL_BUF_T>::~VerilatedTrace() {
template <>
VerilatedTrace<VL_SUB_T, VL_BUF_T>::~VerilatedTrace() {
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = nullptr);
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
Verilated::removeFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
Verilated::removeExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
#ifdef VL_TRACE_OFFLOAD
closeBase();
#endif
if (offload()) closeBase();
}
//=========================================================================
// Internals available to format specific implementations
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
// Note: It is possible to re-open a trace file (VCD in particular),
// so we must reset the next code here, but it must have the same number
// of codes on re-open
@@ -355,17 +368,19 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
Verilated::addFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
Verilated::addExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
#ifdef VL_TRACE_OFFLOAD
// Compute offload buffer size. we need to be able to store a new value for
// each signal, which is 'nextCode()' entries after the init callbacks
// above have been run, plus up to 2 more words of metadata per signal,
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
// update.
m_offloadBufferSize = nextCode() + numSignals() * 2 + 4;
#ifdef VL_THREADED
if (offload()) {
// Compute offload buffer size. we need to be able to store a new value for
// each signal, which is 'nextCode()' entries after the init callbacks
// above have been run, plus up to 2 more words of metadata per signal,
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
// update.
m_offloadBufferSize = nextCode() + numSignals() * 2 + 4;
// Start the worker thread
m_workerThread.reset(
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
// Start the worker thread
m_workerThread.reset(
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
}
#endif
}
@@ -415,14 +430,16 @@ bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const char* nam
//=========================================================================
// Internals available to format specific implementations
template <> std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
template <>
std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
return doubleToTimescale(m_timeRes);
}
//=========================================================================
// External interface to client code
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
m_timeUnit = timescaleToDouble(unitp);
}
template <>
@@ -451,8 +468,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dumpvars(int level, const std::string&
}
}
#ifdef VL_TRACE_PARALLEL
template <> //
#ifdef VL_THREADED
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::parallelWorkerTask(void* datap, bool) {
ParallelWorkerData* const wdp = reinterpret_cast<ParallelWorkerData*>(datap);
// Run the task
@@ -463,7 +480,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::parallelWorkerTask(void* datap, bool) {
if (wdp->m_waiting) wdp->m_cv.notify_one();
}
template <> VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wait() {
template <>
VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wait() {
// Spin for a while, waiting for the buffer to become ready
for (int i = 0; i < VL_LOCK_SPINS; ++i) {
if (VL_LIKELY(m_ready.load(std::memory_order_relaxed))) return;
@@ -478,56 +496,70 @@ template <> VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWo
#endif
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runParallelCallbacks(const ParallelCallbackMap& cbMap) {
for (VlThreadPool* threadPoolp : m_threadPoolps) {
#ifdef VL_TRACE_PARALLEL
// If tracing in parallel, dispatch to the thread pool (if exists)
if (threadPoolp && threadPoolp->numThreads()) {
// List of work items for thread (std::list, as ParallelWorkerData is not movable)
std::list<ParallelWorkerData> workerData;
// We use the whole pool + the main thread
const unsigned threads = threadPoolp->numThreads() + 1;
// Main thread executes all jobs with index % threads == 0
std::vector<ParallelWorkerData*> mainThreadWorkerData;
// The tracing callbacks to execute on this thread-pool
const auto& cbVec = cbMap.at(threadPoolp);
// Enuque all the jobs
for (unsigned i = 0; i < cbVec.size(); ++i) {
const CallbackRecord& cbr = cbVec[i];
// Always get the trace buffer on the main thread
Buffer* const bufp = getTraceBuffer();
// Create new work item
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
// Grab the new work item
ParallelWorkerData* const itemp = &workerData.back();
// Enqueue task to thread pool, or main thread
if (unsigned rem = i % threads) {
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp, false);
} else {
mainThreadWorkerData.push_back(itemp);
}
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runCallbacks(const std::vector<CallbackRecord>& cbVec) {
#ifdef VL_THREADED
if (parallel()) {
// If tracing in parallel, dispatch to the thread pool
VlThreadPool* threadPoolp = static_cast<VlThreadPool*>(m_contextp->threadPoolp());
// List of work items for thread (std::list, as ParallelWorkerData is not movable)
std::list<ParallelWorkerData> workerData;
// We use the whole pool + the main thread
const unsigned threads = threadPoolp->numThreads() + 1;
// Main thread executes all jobs with index % threads == 0
std::vector<ParallelWorkerData*> mainThreadWorkerData;
// Enuque all the jobs
for (unsigned i = 0; i < cbVec.size(); ++i) {
const CallbackRecord& cbr = cbVec[i];
// Always get the trace buffer on the main thread
Buffer* const bufp = getTraceBuffer();
// Create new work item
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
// Grab the new work item
ParallelWorkerData* const itemp = &workerData.back();
// Enqueue task to thread pool, or main thread
if (unsigned rem = i % threads) {
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp);
} else {
mainThreadWorkerData.push_back(itemp);
}
// Execute main thead jobs
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
parallelWorkerTask(itemp, false);
}
// Commit all trace buffers in order
for (ParallelWorkerData& item : workerData) {
// Wait until ready
item.wait();
// Commit the buffer
commitTraceBuffer(item.m_bufp);
}
continue;
}
#endif
// Fall back on sequential execution
for (const CallbackRecord& cbr : cbMap.at(threadPoolp)) {
Buffer* const traceBufferp = getTraceBuffer();
cbr.m_dumpCb(cbr.m_userp, traceBufferp);
commitTraceBuffer(traceBufferp);
// Execute main thead jobs
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
parallelWorkerTask(itemp, false);
}
// Commit all trace buffers in order
for (ParallelWorkerData& item : workerData) {
// Wait until ready
item.wait();
// Commit the buffer
commitTraceBuffer(item.m_bufp);
}
// Done
return;
}
#endif
// Fall back on sequential execution
for (const CallbackRecord& cbr : cbVec) {
Buffer* const traceBufferp = getTraceBuffer();
cbr.m_dumpCb(cbr.m_userp, traceBufferp);
commitTraceBuffer(traceBufferp);
}
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runOffloadedCallbacks(
const std::vector<CallbackRecord>& cbVec) {
// Fall back on sequential execution
#ifdef VL_THREADED
for (const CallbackRecord& cbr : cbVec) {
Buffer* traceBufferp = getTraceBuffer();
cbr.m_dumpOffloadCb(cbr.m_userp, static_cast<OffloadBuffer*>(traceBufferp));
commitTraceBuffer(traceBufferp);
}
#else
if (!cbVec.empty()) VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#endif
}
template <>
@@ -536,13 +568,14 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
// This does get the mutex, but if multiple threads are trying to dump
// chances are the data being dumped will have other problems
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
if (VL_UNCOVERABLE(m_didSomeDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
VL_PRINTF_MT("%%Warning: previous dump at t=%" PRIu64 ", requesting t=%" PRIu64
", dump call ignored\n",
m_timeLastDump, timeui);
return;
} // LCOV_EXCL_STOP
m_timeLastDump = timeui;
m_didSomeDump = true;
Verilated::quiesce();
@@ -553,35 +586,49 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
if (!preChangeDump()) return;
}
#ifdef VL_TRACE_OFFLOAD
// Currently only incremental dumps run on the worker thread
#ifdef VL_THREADED
uint32_t* bufferp = nullptr;
if (VL_LIKELY(!m_fullDump)) {
// Get the offload buffer we are about to fill
bufferp = getOffloadBuffer();
m_offloadBufferWritep = bufferp;
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
#endif
if (offload()) {
#ifdef VL_THREADED
// Currently only incremental dumps run on the worker thread
if (VL_LIKELY(!m_fullDump)) {
// Get the offload buffer we are about to fill
bufferp = getOffloadBuffer();
m_offloadBufferWritep = bufferp;
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
// Tell worker to update time point
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
m_offloadBufferWritep += 3;
// Tell worker to update time point
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
m_offloadBufferWritep += 3;
} else {
// Update time point
flushBase();
emitTimeChange(timeui);
}
#else
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#endif
} else {
// Update time point
flushBase();
emitTimeChange(timeui);
}
#else
// Update time point
emitTimeChange(timeui);
#endif
// Run the callbacks
if (VL_UNLIKELY(m_fullDump)) {
m_fullDump = false; // No more need for next dump to be full
runParallelCallbacks(m_fullCbs);
if (offload()) {
runOffloadedCallbacks(m_fullOffloadCbs);
} else {
runCallbacks(m_fullCbs);
}
} else {
runParallelCallbacks(m_chgCbs);
if (offload()) {
runOffloadedCallbacks(m_chgOffloadCbs);
} else {
runCallbacks(m_chgCbs);
}
}
for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
@@ -589,8 +636,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
cbr.m_cleanupCb(cbr.m_userp, self());
}
#ifdef VL_TRACE_OFFLOAD
if (VL_LIKELY(bufferp)) {
#ifdef VL_THREADED
if (offload() && VL_LIKELY(bufferp)) {
// Mark end of the offload buffer we just filled
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::END;
@@ -607,58 +654,98 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
// Non-hot path internal interface to Verilator generated code
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addThreadPool(VlThreadPool* threadPoolp)
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
for (VlThreadPool* const poolp : m_threadPoolps) {
if (poolp == threadPoolp) return;
const bool firstModel = m_models.empty();
const bool newModel = m_models.insert(modelp).second;
VerilatedContext* const contextp = modelp->contextp();
// Validate
if (!newModel) { // LCOV_EXCL_START
VL_FATAL_MT(__FILE__, __LINE__, "",
"The same model has already been added to this trace file");
}
m_threadPoolps.push_back(threadPoolp);
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");
}
if (VL_UNCOVERABLE(m_didSomeDump)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"Cannot add models to a trace file if 'dump' has already been called");
} // LCOV_EXCL_STOP
// Keep hold of the context
m_contextp = contextp;
// Get the desired trace config from the model
const std::unique_ptr<VerilatedTraceConfig> configp = modelp->traceConfig();
#ifndef VL_THREADED
if (configp->m_useOffloading) {
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use trace offloading without VL_THREADED");
}
#endif
// Configure trace base class
if (!firstModel) {
if (m_offload != configp->m_useOffloading) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"Either all or no models using the same trace file must use offloading");
}
}
m_offload = configp->m_useOffloading;
// If at least one model requests parallel tracing, then use it
m_parallel |= configp->m_useParallel;
if (VL_UNCOVERABLE(m_parallel && m_offload)) { // LCOV_EXCL_START
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use parallel tracing with offloading");
} // LCOV_EXCL_STOP
// Configure format specific sub class
configure(*(configp.get()));
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
CallbackRecord& cbRec)
CallbackRecord&& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
const std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__
+ " called with already open file");
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
} // LCOV_EXCL_STOP
cbVec.push_back(cbRec);
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_initCbs, cbr);
addCallbackRecord(m_initCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp,
VlThreadPool* threadPoolp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addThreadPool(threadPoolp);
addCallbackRecord(m_fullCbs[threadPoolp], cbr);
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_fullCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp,
VlThreadPool* threadPoolp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addThreadPool(threadPoolp);
addCallbackRecord(m_chgCbs[threadPoolp], cbr);
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_fullOffloadCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_chgCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_chgOffloadCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_cleanupCbs, cbr);
addCallbackRecord(m_cleanupCbs, CallbackRecord{cb, userp});
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) {
m_namePrefixStack.push_back(m_namePrefixStack.back() + prefix);
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::popNamePrefix(unsigned count) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::popNamePrefix(unsigned count) {
while (count--) m_namePrefixStack.pop_back();
assert(!m_namePrefixStack.empty());
}
@@ -756,83 +843,93 @@ static inline void cvtQDataToStr(char* dstp, QData value) {
//=========================================================================
// VerilatedTraceBuffer
template <> //
VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::VerilatedTraceBuffer(VL_SUB_T& owner)
: m_owner{owner} {
#ifdef VL_TRACE_OFFLOAD
if (m_offloadBufferWritep) {
using This = VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>*;
// Tack on the buffer address
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
"This should be enough on all plafrorms");
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
*reinterpret_cast<This*>(m_offloadBufferWritep) = this;
m_offloadBufferWritep += 2;
}
#endif
}
template <>
VerilatedTraceBuffer<VL_BUF_T>::VerilatedTraceBuffer(Trace& owner)
: VL_BUF_T{owner}
, m_sigs_oldvalp{owner.m_sigs_oldvalp}
, m_sigs_enabledp{owner.m_sigs_enabledp} {}
// These functions must write the new value back into the old value store,
// and subsequently call the format specific emit* implementations. Note
// that this file must be included in the format specific implementation, so
// the emit* functions can be inlined for performance.
template <> //
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
template <>
void VerilatedTraceBuffer<VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitBit(code, newval);
emitBit(code, newval);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitCData(code, newval, bits);
emitCData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitSData(code, newval, bits);
emitSData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitIData(code, newval, bits);
emitIData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<QData*>(oldp) = newval;
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitQData(code, newval, bits);
emitQData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp,
int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitWData(code, newvalp, bits);
emitWData(code, newvalp, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
void VerilatedTraceBuffer<VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<double*>(oldp) = newval;
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
// cppcheck-suppress invalidPointerCast
self()->emitDouble(code, newval);
emitDouble(code, newval);
}
#ifdef VL_THREADED
//=========================================================================
// VerilatedTraceOffloadBuffer
template <>
VerilatedTraceOffloadBuffer<VL_BUF_T>::VerilatedTraceOffloadBuffer(VL_SUB_T& owner)
: VerilatedTraceBuffer<VL_BUF_T>{owner}
, m_offloadBufferWritep{owner.m_offloadBufferWritep}
, m_offloadBufferEndp{owner.m_offloadBufferEndp} {
if (m_offloadBufferWritep) {
using This = VerilatedTraceBuffer<VL_BUF_T>*;
// Tack on the buffer address
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
"This should be enough on all plafrorms");
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
*reinterpret_cast<This*>(m_offloadBufferWritep) = static_cast<This>(this);
m_offloadBufferWritep += 2;
}
}
#endif
#endif // VL_CPPCHECK
+67 -36
View File
@@ -91,9 +91,9 @@ class VlReadMem final {
const int m_bits; // Bit width of values
const std::string& m_filename; // Filename
const QData m_end; // End address (as specified by user)
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to read
int m_linenum; // Line number last read from file
FILE* m_fp = nullptr; // File handle for filename
QData m_addr = 0; // Next address to read
int m_linenum = 0; // Line number last read from file
bool m_anyAddr = false; // Had address directive in the file
public:
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
@@ -107,8 +107,8 @@ public:
class VlWriteMem final {
const bool m_hex; // Hex format
const int m_bits; // Bit width of values
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to write
FILE* m_fp = nullptr; // File handle for filename
QData m_addr = 0; // Next address to write
public:
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlWriteMem();
@@ -131,7 +131,8 @@ public:
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE;
template <std::size_t T_Words> struct VlWide final {
template <std::size_t T_Words>
struct VlWide final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
EData m_storage[T_Words]; // Contents of the packed array
@@ -163,7 +164,8 @@ VlWide<T_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<T_Words>&) {
return *((VlWide<T_Words>*)inp);
}
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
template <std::size_t T_Words>
std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
return VL_TO_STRING_W(T_Words, obj.data());
}
@@ -174,7 +176,8 @@ template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& o
//
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
// For dynamic arrays it is always zero
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
template <class T_Value, size_t T_MaxSize = 0>
class VlQueue final {
private:
// TYPES
using Deque = std::deque<T_Value>;
@@ -199,7 +202,8 @@ public:
// Standard copy constructor works. Verilog: assoca = assocb
// Also must allow conversion from a different T_MaxSize queue
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
template <size_t U_MaxSize = 0>
VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
m_deque = rhs.privateDeque();
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
return *this;
@@ -330,7 +334,8 @@ public:
// Methods
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
template <typename Func> void sort(Func with_func) {
template <typename Func>
void sort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
@@ -338,7 +343,8 @@ public:
});
}
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
template <typename Func> void rsort(Func with_func) {
template <typename Func>
void rsort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
@@ -373,7 +379,8 @@ public:
}
return out;
}
template <typename Func> VlQueue find(Func with_func) const {
template <typename Func>
VlQueue find(Func with_func) const {
VlQueue out;
IData index = 0;
for (const auto& i : m_deque) {
@@ -382,7 +389,8 @@ public:
}
return out;
}
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
template <typename Func>
VlQueue<IData> find_index(Func with_func) const {
VlQueue<IData> out;
IData index = 0;
for (const auto& i : m_deque) {
@@ -391,7 +399,8 @@ public:
}
return out;
}
template <typename Func> VlQueue find_first(Func with_func) const {
template <typename Func>
VlQueue find_first(Func with_func) const {
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_deque) {
@@ -400,7 +409,8 @@ public:
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
template <typename Func>
VlQueue<IData> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
@@ -408,7 +418,8 @@ public:
}
return VlQueue<IData>{};
}
template <typename Func> VlQueue find_last(Func with_func) const {
template <typename Func>
VlQueue find_last(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto& item : vlstd::reverse_view(m_deque)) {
if (with_func(index, item)) return VlQueue::cons(item);
@@ -416,7 +427,8 @@ public:
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
template <typename Func>
VlQueue<IData> find_last_index(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto& item : vlstd::reverse_view(m_deque)) {
if (with_func(index, item)) return VlQueue<IData>::cons(index);
@@ -442,7 +454,8 @@ public:
for (const auto& i : m_deque) out += i;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
template <typename Func>
T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out += with_func(index++, i);
@@ -456,7 +469,8 @@ public:
for (; it != m_deque.end(); ++it) out *= *it;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
template <typename Func>
T_Value r_product(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
@@ -474,7 +488,8 @@ public:
for (; it != m_deque.end(); ++it) out &= *it;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
template <typename Func>
T_Value r_and(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
@@ -489,7 +504,8 @@ public:
for (const auto& i : m_deque) out |= i;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
template <typename Func>
T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out |= with_func(index++, i);
@@ -500,7 +516,8 @@ public:
for (const auto& i : m_deque) out ^= i;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
template <typename Func>
T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out ^= with_func(index++, i);
@@ -520,7 +537,8 @@ public:
}
};
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
template <class T_Value>
std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
return obj.to_string();
}
@@ -529,7 +547,8 @@ template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
template <class T_Key, class T_Value> class VlAssocArray final {
template <class T_Key, class T_Value>
class VlAssocArray final {
private:
// TYPES
using Map = std::map<T_Key, T_Value>;
@@ -654,19 +673,22 @@ public:
}
return out;
}
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
template <typename Func>
VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.second);
return out;
}
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
template <typename Func>
VlQueue<T_Key> find_index(Func with_func) const {
VlQueue<T_Key> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.first);
return out;
}
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
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) {
return with_func(i.first, i.second);
@@ -674,7 +696,8 @@ public:
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
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) {
return with_func(i.first, i.second);
@@ -682,7 +705,8 @@ public:
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
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);
@@ -690,7 +714,8 @@ public:
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 {
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);
@@ -724,7 +749,8 @@ public:
for (const auto& i : m_map) out += i.second;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
template <typename Func>
T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += with_func(i.first, i.second);
return out;
@@ -737,7 +763,8 @@ public:
for (; it != m_map.end(); ++it) out *= it->second;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
template <typename Func>
T_Value r_product(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
@@ -753,7 +780,8 @@ public:
for (; it != m_map.end(); ++it) out &= it->second;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
template <typename Func>
T_Value r_and(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
@@ -766,7 +794,8 @@ public:
for (const auto& i : m_map) out |= i.second;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
template <typename Func>
T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= with_func(i.first, i.second);
return out;
@@ -776,7 +805,8 @@ public:
for (const auto& i : m_map) out ^= i.second;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
template <typename Func>
T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
return out;
@@ -839,7 +869,8 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
/// This class may get exposed to a Verilated Model's top I/O, if the top
/// IO has an unpacked array.
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
template <class T_Value, std::size_t T_Depth>
struct VlUnpacked final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
T_Value m_storage[T_Depth]; // Contents of the unpacked array
+110 -121
View File
@@ -38,13 +38,13 @@
# include <unistd.h>
#endif
#ifndef O_LARGEFILE // For example on WIN32
#ifndef O_LARGEFILE // WIN32 headers omit this
# define O_LARGEFILE 0
#endif
#ifndef O_NONBLOCK
#ifndef O_NONBLOCK // WIN32 headers omit this
# define O_NONBLOCK 0
#endif
#ifndef O_CLOEXEC
#ifndef O_CLOEXEC // WIN32 headers omit this
# define O_CLOEXEC 0
#endif
@@ -65,7 +65,8 @@ constexpr unsigned VL_TRACE_SUFFIX_ENTRY_SIZE = 8; // Size of a suffix entry
//=============================================================================
// Utility functions: TODO: put these in a common place and share them.
template <size_t N> static size_t roundUpToMultipleOf(size_t value) {
template <size_t N>
static size_t roundUpToMultipleOf(size_t value) {
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
size_t mask = N - 1;
return (value + mask) & ~mask;
@@ -119,13 +120,13 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Set member variables
m_filename = filename; // "" is ok, as someone may overload open
openNextImp(m_rolloverMB != 0);
openNextImp(m_rolloverSize != 0);
if (!isOpen()) return;
dumpHeader();
// When using rollover, the first chunk contains the header only.
if (m_rolloverMB) openNextImp(true);
if (m_rolloverSize) openNextImp(true);
}
void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
@@ -179,7 +180,7 @@ void VerilatedVcd::openNextImp(bool incFilename) {
}
bool VerilatedVcd::preChangeDump() {
if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > m_rolloverMB)) openNextImp(true);
if (VL_UNLIKELY(m_rolloverSize && m_wroteBytes > m_rolloverSize)) openNextImp(true);
return isOpen();
}
@@ -229,9 +230,11 @@ VerilatedVcd::~VerilatedVcd() {
if (m_wrBufp) VL_DO_CLEAR(delete[] m_wrBufp, m_wrBufp = nullptr);
deleteNameMap();
if (m_filep && m_fileNewed) VL_DO_CLEAR(delete m_filep, m_filep = nullptr);
#ifdef VL_TRACE_PARALLEL
assert(m_numBuffers == m_freeBuffers.size());
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
#ifdef VL_THREADED
if (parallel()) {
assert(m_numBuffers == m_freeBuffers.size());
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
}
#endif
}
@@ -260,11 +263,6 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
// This function is on the flush() call path
const VerilatedLockGuard lock{m_mutex};
if (!isOpen()) return;
if (m_evcd) {
printStr("$vcdclose ");
printQuad(timeLastDump());
printStr(" $end\n");
}
closePrev();
// closePrev() called Super::flush(), so we just
// need to shut down the tracing thread here.
@@ -514,38 +512,29 @@ void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, b
// Print reference
std::string decl = "$var ";
if (m_evcd) {
decl += "port";
} else {
decl += wirep; // usually "wire"
}
decl += wirep; // usually "wire"
constexpr size_t bufsize = 1000;
char buf[bufsize];
VL_SNPRINTF(buf, bufsize, " %2d ", bits);
decl += buf;
if (m_evcd) {
VL_SNPRINTF(buf, bufsize, "<%u", code);
decl += buf;
} else {
// Add string code to decl
char* const endp = writeCode(buf, code);
*endp = '\0';
decl += buf;
// Build suffix array entry
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
const size_t length = endp - buf;
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
// 1 bit values don't have a ' ' separator between value and string code
const bool isBit = bits == 1;
entryp[0] = ' '; // Separator
// Use memcpy as we checked size above, and strcpy is flagged unsafe
std::memcpy(entryp + !isBit, buf,
std::strlen(buf)); // Code (overwrite separator if isBit)
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
// Set length of suffix (used to increment write pointer)
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1;
}
// Add string code to decl
char* const endp = writeCode(buf, code);
*endp = '\0';
decl += buf;
// Build suffix array entry
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
const size_t length = endp - buf;
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
// 1 bit values don't have a ' ' separator between value and string code
const bool isBit = bits == 1;
entryp[0] = ' '; // Separator
// Use memcpy as we checked size above, and strcpy is flagged unsafe
std::memcpy(entryp + !isBit, buf,
std::strlen(buf)); // Code (overwrite separator if isBit)
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
// Set length of suffix (used to increment write pointer)
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1;
decl += " ";
decl += basename;
if (array) {
@@ -583,74 +572,70 @@ void VerilatedVcd::declDouble(uint32_t code, const char* name, bool array, int a
//=============================================================================
// Get/commit trace buffer
VerilatedVcdBuffer* VerilatedVcd::getTraceBuffer() {
#ifdef VL_TRACE_PARALLEL
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// If no buffer available, allocate a new one
if (m_freeBuffers.empty()) {
constexpr size_t pageSize = 4096;
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
size_t startingSize = roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
++m_numBuffers;
VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer() {
VerilatedVcd::Buffer* const bufp = new Buffer{*this};
#ifdef VL_THREADED
if (parallel()) {
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// If no buffer available, allocate a new one
if (m_freeBuffers.empty()) {
constexpr size_t pageSize = 4096;
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
size_t startingSize = roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
++m_numBuffers;
}
// Grab a buffer
const auto pair = m_freeBuffers.back();
m_freeBuffers.pop_back();
// Initialize
bufp->m_writep = bufp->m_bufp = pair.first;
bufp->m_size = pair.second;
bufp->adjustGrowp();
}
// Grab a buffer
const auto pair = m_freeBuffers.back();
m_freeBuffers.pop_back();
// Return the buffer
return new VerilatedVcdBuffer{*this, pair.first, pair.second};
#else
return new VerilatedVcdBuffer{*this};
#endif
// Return the buffer
return bufp;
}
void VerilatedVcd::commitTraceBuffer(VerilatedVcdBuffer* bufp) {
#ifdef VL_TRACE_PARALLEL
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// Resize output buffer. Note, we use the full size of the trace buffer, as
// this is a lot more stable than the actual occupancy of the trace buffer.
// This helps us to avoid re-allocations due to small size changes.
bufferResize(bufp->m_size);
// Compute occupancy of buffer
const size_t usedSize = bufp->m_writep - bufp->m_bufp;
// Copy to output buffer
std::memcpy(m_writep, bufp->m_bufp, usedSize);
// Adjust write pointer
m_writep += usedSize;
// Flush if necessary
bufferCheck();
// Put buffer back on free list
m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
void VerilatedVcd::commitTraceBuffer(VerilatedVcd::Buffer* bufp) {
if (parallel()) {
#if VL_THREADED
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// Resize output buffer. Note, we use the full size of the trace buffer, as
// this is a lot more stable than the actual occupancy of the trace buffer.
// This helps us to avoid re-allocations due to small size changes.
bufferResize(bufp->m_size);
// Compute occupancy of buffer
const size_t usedSize = bufp->m_writep - bufp->m_bufp;
// Copy to output buffer
std::memcpy(m_writep, bufp->m_bufp, usedSize);
// Adjust write pointer
m_writep += usedSize;
// Flush if necessary
bufferCheck();
// Put buffer back on free list
m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
#else
// Needs adjusting for emitTimeChange
m_writep = bufp->m_writep;
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#endif
} else {
// Needs adjusting for emitTimeChange
m_writep = bufp->m_writep;
}
delete bufp;
}
//=============================================================================
// VerilatedVcdBuffer implementation
#ifdef VL_TRACE_PARALLEL
VerilatedVcdBuffer::VerilatedVcdBuffer(VerilatedVcd& owner, char* bufp, size_t size)
: VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer>{owner}
, m_writep{bufp}
, m_bufp{bufp}
, m_size{size} {
adjustGrowp();
}
#else
VerilatedVcdBuffer::VerilatedVcdBuffer(VerilatedVcd& owner)
: VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer>{owner} {}
#endif
//=============================================================================
// Trace rendering primitives
static inline void
VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
static void VerilatedVcdCCopyAndAppendNewLine(char* writep,
const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
static void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
// Copy the whole suffix (this avoid having hard to predict branches which
// helps a lot). Note: The maximum length of the suffix is
// VL_TRACE_MAX_VCD_CODE_SIZE + 2 == 7, but we unroll this here for speed.
@@ -679,35 +664,39 @@ void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) {
// suffix, which was stored in the last byte of the suffix buffer entry.
m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];
#ifdef VL_TRACE_PARALLEL
// Double the size of the buffer if necessary
if (VL_UNLIKELY(m_writep >= m_growp)) {
// Compute occupied size of current buffer
const size_t usedSize = m_writep - m_bufp;
// We are always doubling the size
m_size *= 2;
// Allocate the new buffer
char* const newBufp = new char[m_size];
// Copy from current buffer to new buffer
std::memcpy(newBufp, m_bufp, usedSize);
// Delete current buffer
delete[] m_bufp;
// Make new buffer the current buffer
m_bufp = newBufp;
// Adjust write pointer
m_writep = m_bufp + usedSize;
// Adjust resize limit
adjustGrowp();
}
if (m_owner.parallel()) {
#ifdef VL_THREADED
// Double the size of the buffer if necessary
if (VL_UNLIKELY(m_writep >= m_growp)) {
// Compute occupied size of current buffer
const size_t usedSize = m_writep - m_bufp;
// We are always doubling the size
m_size *= 2;
// Allocate the new buffer
char* const newBufp = new char[m_size];
// Copy from current buffer to new buffer
std::memcpy(newBufp, m_bufp, usedSize);
// Delete current buffer
delete[] m_bufp;
// Make new buffer the current buffer
m_bufp = newBufp;
// Adjust write pointer
m_writep = m_bufp + usedSize;
// Adjust resize limit
adjustGrowp();
}
#else
// Flush the write buffer if there's not enough space left for new information
// We only call this once per vector, so we need enough slop for a very wide "b###" line
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
m_owner.m_writep = m_writep;
m_owner.bufferFlush();
m_writep = m_owner.m_writep;
}
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#endif
} else {
// Flush the write buffer if there's not enough space left for new information
// We only call this once per vector, so we need enough slop for a very wide "b###" line
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
m_owner.m_writep = m_writep;
m_owner.bufferFlush();
m_writep = m_owner.m_writep;
}
}
}
//=============================================================================
+60 -50
View File
@@ -41,7 +41,7 @@ public:
using Super = VerilatedTrace<VerilatedVcd, VerilatedVcdBuffer>;
private:
friend Buffer; // Give the buffer access to the private bits
friend VerilatedVcdBuffer; // Give the buffer access to the private bits
//=========================================================================
// VCD specific internals
@@ -49,9 +49,8 @@ private:
VerilatedVcdFile* m_filep; // File we're writing to
bool m_fileNewed; // m_filep needs destruction
bool m_isOpen = false; // True indicates open file
bool m_evcd = false; // True for evcd format
std::string m_filename; // Filename we're writing to (if open)
uint64_t m_rolloverMB = 0; // MB of file size to rollover at
uint64_t m_rolloverSize = 0; // File size to rollover at
int m_modDepth = 0; // Depth of module hierarchy
char* m_wrBufp; // Output buffer
@@ -66,7 +65,7 @@ private:
using NameMap = std::map<const std::string, const std::string>;
NameMap* m_namemapp = nullptr; // List of names for the header
#ifdef VL_TRACE_PARALLEL
#ifdef VL_THREADED
// Vector of free trace buffers as (pointer, size) pairs.
std::vector<std::pair<char*, size_t>> m_freeBuffers;
size_t m_numBuffers = 0; // Number of trace buffers allocated
@@ -74,7 +73,7 @@ private:
void bufferResize(size_t minsize);
void bufferFlush() VL_MT_UNSAFE_ONE;
inline void bufferCheck() {
void bufferCheck() {
// Flush the write buffer if there's not enough space left for new information
// We only call this once per vector, so we need enough slop for a very wide "b###" line
if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush();
@@ -103,15 +102,18 @@ protected:
// Implementation of VerilatedTrace interface
// Called when the trace moves forward to a new time point
virtual void emitTimeChange(uint64_t timeui) override;
void emitTimeChange(uint64_t timeui) override;
// Hooks called from VerilatedTrace
virtual bool preFullDump() override { return isOpen(); }
virtual bool preChangeDump() override;
bool preFullDump() override { return isOpen(); }
bool preChangeDump() override;
// Trace buffer management
virtual VerilatedVcdBuffer* getTraceBuffer() override;
virtual void commitTraceBuffer(VerilatedVcdBuffer*) override;
Buffer* getTraceBuffer() override;
void commitTraceBuffer(Buffer*) override;
// Configure sub-class
void configure(const VerilatedTraceConfig&) override{};
public:
//=========================================================================
@@ -122,8 +124,8 @@ public:
~VerilatedVcd();
// ACCESSORS
// Set size in megabytes after which new file should be created
void rolloverMB(uint64_t rolloverMB) { m_rolloverMB = rolloverMB; }
// Set size in bytes after which new file should be created.
void rolloverSize(uint64_t size) { m_rolloverSize = size; }
// METHODS - All must be thread safe
// Open the file; call isOpen() to see if errors
@@ -149,67 +151,72 @@ public:
#ifndef DOXYGEN
// Declare specialization here as it's used in VerilatedFstC just below
template <> void VerilatedVcd::Super::dump(uint64_t time);
template <> void VerilatedVcd::Super::set_time_unit(const char* unitp);
template <> void VerilatedVcd::Super::set_time_unit(const std::string& unit);
template <> void VerilatedVcd::Super::set_time_resolution(const char* unitp);
template <> void VerilatedVcd::Super::set_time_resolution(const std::string& unit);
template <> void VerilatedVcd::Super::dumpvars(int level, const std::string& hier);
template <>
void VerilatedVcd::Super::dump(uint64_t time);
template <>
void VerilatedVcd::Super::set_time_unit(const char* unitp);
template <>
void VerilatedVcd::Super::set_time_unit(const std::string& unit);
template <>
void VerilatedVcd::Super::set_time_resolution(const char* unitp);
template <>
void VerilatedVcd::Super::set_time_resolution(const std::string& unit);
template <>
void VerilatedVcd::Super::dumpvars(int level, const std::string& hier);
#endif // DOXYGEN
//=============================================================================
// VerilatedVcdBuffer
class VerilatedVcdBuffer final : public VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer> {
// Give the trace file access to the private bits
class VerilatedVcdBuffer VL_NOT_FINAL {
// Give the trace file ans sub-classes access to the private bits
friend VerilatedVcd;
friend VerilatedVcd::Super;
friend VerilatedVcd::Buffer;
friend VerilatedVcd::OffloadBuffer;
#ifdef VL_TRACE_PARALLEL
char* m_writep; // Write pointer into m_bufp
char* m_bufp; // The beginning of the trace buffer
size_t m_size; // The size of the buffer at m_bufp
char* m_growp; // Resize limit pointer
#else
char* m_writep = m_owner.m_writep; // Write pointer into output buffer
char* const m_wrFlushp = m_owner.m_wrFlushp; // Output buffer flush trigger location
#endif
VerilatedVcd& m_owner; // Trace file owning this buffer. Required by subclasses.
// Write pointer into output buffer (in parallel mode, this is set up in 'getTraceBuffer')
char* m_writep = m_owner.parallel() ? nullptr : m_owner.m_writep;
// Output buffer flush trigger location (only used when not parallel)
char* const m_wrFlushp = m_owner.parallel() ? nullptr : m_owner.m_wrFlushp;
// VCD line end string codes + metadata
const char* const m_suffixes = m_owner.m_suffixes.data();
// The maximum number of bytes a single signal can emit
const size_t m_maxSignalBytes = m_owner.m_maxSignalBytes;
void finishLine(uint32_t code, char* writep);
#ifdef VL_THREADED
// Additional data for parallel tracing only
char* m_bufp = nullptr; // The beginning of the trace buffer
size_t m_size = 0; // The size of the buffer at m_bufp
char* m_growp = nullptr; // Resize limit pointer
#ifdef VL_TRACE_PARALLEL
void adjustGrowp() {
m_growp = (m_bufp + m_size) - (2 * m_maxSignalBytes);
assert(m_growp >= m_bufp + m_maxSignalBytes);
}
#endif
public:
void finishLine(uint32_t code, char* writep);
// CONSTRUCTOR
#ifdef VL_TRACE_PARALLEL
explicit VerilatedVcdBuffer(VerilatedVcd& owner, char* bufp, size_t size);
#else
explicit VerilatedVcdBuffer(VerilatedVcd& owner);
#endif
~VerilatedVcdBuffer() = default;
explicit VerilatedVcdBuffer(VerilatedVcd& owner)
: m_owner{owner} {}
virtual ~VerilatedVcdBuffer() = default;
//=========================================================================
// Implementation of VerilatedTraceBuffer interface
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
// called from only one place (the full* methods), so always inline them.
VL_ATTR_ALWINLINE inline void emitBit(uint32_t code, CData newval);
VL_ATTR_ALWINLINE inline void emitCData(uint32_t code, CData newval, int bits);
VL_ATTR_ALWINLINE inline void emitSData(uint32_t code, SData newval, int bits);
VL_ATTR_ALWINLINE inline void emitIData(uint32_t code, IData newval, int bits);
VL_ATTR_ALWINLINE inline void emitQData(uint32_t code, QData newval, int bits);
VL_ATTR_ALWINLINE inline void emitWData(uint32_t code, const WData* newvalp, int bits);
VL_ATTR_ALWINLINE inline void emitDouble(uint32_t code, double newval);
VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
};
//=============================================================================
@@ -252,7 +259,6 @@ public:
/// Destruct, flush, and close the dump
virtual ~VerilatedVcdC() { close(); }
public:
// METHODS - User called
/// Return if file is open
@@ -265,8 +271,12 @@ public:
/// The header is only in the first file created, this allows
/// "cat" to be used to combine the header plus any number of data files.
void openNext(bool incFilename = true) VL_MT_SAFE { m_sptrace.openNext(incFilename); }
/// Set size in megabytes after which new file should be created
void rolloverMB(size_t rolloverMB) VL_MT_SAFE { m_sptrace.rolloverMB(rolloverMB); }
/// Set size in bytes after which new file should be created
/// This will create a header file, followed by each separate file
/// which might be larger than the given size (due to chunking and
/// alignment to a start of a given time's dump). Any file but the
/// 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(); }
/// Flush dump
@@ -303,7 +313,7 @@ public:
}
// Internal class access
inline VerilatedVcd* spTrace() { return &m_sptrace; }
VerilatedVcd* spTrace() { return &m_sptrace; }
};
#endif // guard
+2 -4
View File
@@ -22,6 +22,7 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_vcd_sc.h"
//======================================================================
@@ -30,7 +31,7 @@
void VerilatedVcdSc::open(const char* filename) {
if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) {
vl_fatal(__FILE__, __LINE__, "VerilatedVcdSc",
("%Error: VerilatedVcdSc::open(\"" + std::string(filename)
("%Error: VerilatedVcdSc::open(\"" + std::string{filename}
+ "\") is called before sc_core::sc_start(). "
"Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file.")
.c_str());
@@ -63,9 +64,6 @@ void VerilatedVcdSc::trace(const unsigned int&, const std::string&, const char**
DECL_TRACE_METHOD_B( unsigned short )
DECL_TRACE_METHOD_B( unsigned int )
DECL_TRACE_METHOD_B( unsigned long )
#ifdef SYSTEMC_64BIT_PATCHES
DECL_TRACE_METHOD_B( unsigned long long)
#endif
DECL_TRACE_METHOD_B( char )
DECL_TRACE_METHOD_B( short )
DECL_TRACE_METHOD_B( int )
+10 -12
View File
@@ -23,6 +23,7 @@
#define VERILATOR_VERILATED_VCD_SC_H_
#include "verilatedos.h"
#include "verilated_sc.h"
#include "verilated_vcd_c.h"
@@ -54,34 +55,34 @@ public:
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
}
/// Destruct, flush, and close the dump
virtual ~VerilatedVcdSc() { close(); }
~VerilatedVcdSc() override { close(); }
// METHODS - for SC kernel
// Called by SystemC simulate()
virtual void cycle(bool delta_cycle) {
void cycle(bool delta_cycle) override {
if (!delta_cycle) this->dump(sc_time_stamp().to_double());
}
// Override VerilatedVcdC. Must be called after starting simulation.
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
void open(const char* filename) override VL_MT_SAFE;
private:
// METHODS - Fake outs for linker
#ifdef NC_SYSTEMC
// Cadence Incisive has these as abstract functions so we must create them
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
void set_time_unit(int exponent10_seconds) override {} // deprecated
#endif
virtual void set_time_unit(double v, sc_time_unit tu) {} // LCOV_EXCL_LINE
void set_time_unit(double v, sc_time_unit tu) override {} // LCOV_EXCL_LINE
//--------------------------------------------------
// SystemC 2.1.v1
#define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const std::string& name);
#define DECL_TRACE_METHOD_A(tp) void trace(const tp& object, const std::string& name) override;
#define DECL_TRACE_METHOD_B(tp) \
virtual void trace(const tp& object, const std::string& name, int width);
void trace(const tp& object, const std::string& name, int width) override;
virtual void write_comment(const std::string&);
virtual void trace(const unsigned int&, const std::string&, const char**);
void write_comment(const std::string&) override;
void trace(const unsigned int&, const std::string&, const char**) override;
// clang-format off
// Formatting matches that of sc_trace.h
@@ -98,9 +99,6 @@ private:
DECL_TRACE_METHOD_B( unsigned short )
DECL_TRACE_METHOD_B( unsigned int )
DECL_TRACE_METHOD_B( unsigned long )
#ifdef SYSTEMC_64BIT_PATCHES
DECL_TRACE_METHOD_B( unsigned long long)
#endif
DECL_TRACE_METHOD_B( char )
DECL_TRACE_METHOD_B( short )
DECL_TRACE_METHOD_B( int )
+48 -47
View File
@@ -26,8 +26,9 @@
#define VERILATOR_VERILATED_VPI_CPP_
#include "verilated.h"
#include "verilated_vpi.h"
#include "verilated.h"
#include "verilated_imp.h"
#include <list>
@@ -95,7 +96,7 @@ public:
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
return newp + 8;
}
static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE {
static void operator delete(void* obj, size_t /*size*/) VL_MT_SAFE {
uint8_t* const oldp = (static_cast<uint8_t*>(obj)) - 8;
if (VL_UNLIKELY(*(reinterpret_cast<uint32_t*>(oldp)) != activeMagic())) {
VL_FATAL_MT(__FILE__, __LINE__, "",
@@ -113,7 +114,7 @@ public:
static VerilatedVpio* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpio*>(reinterpret_cast<VerilatedVpio*>(h));
}
inline vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
vpiHandle castVpiHandle() { return reinterpret_cast<vpiHandle>(this); }
// ACCESSORS
virtual const char* name() const { return "<null>"; }
virtual const char* fullname() const { return "<null>"; }
@@ -135,12 +136,12 @@ public:
VerilatedVpioTimedCb(uint64_t id, QData time)
: m_id{id}
, m_time{time} {}
virtual ~VerilatedVpioTimedCb() override = default;
~VerilatedVpioTimedCb() override = default;
static VerilatedVpioTimedCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioTimedCb*>(reinterpret_cast<VerilatedVpioTimedCb*>(h));
}
virtual uint32_t type() const override { return vpiCallback; }
virtual PLI_INT32 dovpi_remove_cb() override;
uint32_t type() const override { return vpiCallback; }
PLI_INT32 dovpi_remove_cb() override;
};
class VerilatedVpioReasonCb final : public VerilatedVpio {
@@ -154,12 +155,12 @@ public:
VerilatedVpioReasonCb(uint64_t id, PLI_INT32 reason)
: m_id{id}
, m_reason{reason} {}
virtual ~VerilatedVpioReasonCb() override = default;
~VerilatedVpioReasonCb() override = default;
static VerilatedVpioReasonCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioReasonCb*>(reinterpret_cast<VerilatedVpioReasonCb*>(h));
}
virtual uint32_t type() const override { return vpiCallback; }
virtual PLI_INT32 dovpi_remove_cb() override;
uint32_t type() const override { return vpiCallback; }
PLI_INT32 dovpi_remove_cb() override;
};
class VerilatedVpioConst final : public VerilatedVpio {
@@ -168,11 +169,11 @@ class VerilatedVpioConst final : public VerilatedVpio {
public:
explicit VerilatedVpioConst(int32_t num)
: m_num{num} {}
virtual ~VerilatedVpioConst() override = default;
~VerilatedVpioConst() override = default;
static VerilatedVpioConst* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioConst*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiConstant; }
uint32_t type() const override { return vpiConstant; }
int32_t num() const { return m_num; }
};
@@ -200,10 +201,10 @@ public:
}
const VerilatedVar* varp() const { return m_varp; }
const VerilatedScope* scopep() const { return m_scopep; }
virtual uint32_t size() const override { return get_range().elements(); }
virtual const VerilatedRange* rangep() const override { return &get_range(); }
virtual const char* name() const override { return m_varp->name(); }
virtual const char* fullname() const override {
uint32_t size() const override { return get_range().elements(); }
const VerilatedRange* rangep() const override { return &get_range(); }
const char* name() const override { return m_varp->name(); }
const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
t_out = std::string{m_scopep->name()} + "." + name();
return t_out.c_str();
@@ -214,12 +215,12 @@ class VerilatedVpioParam final : public VerilatedVpioVarBase {
public:
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
: VerilatedVpioVarBase{varp, scopep} {}
virtual ~VerilatedVpioParam() override = default;
~VerilatedVpioParam() override = default;
static VerilatedVpioParam* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioParam*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiParameter; }
uint32_t type() const override { return vpiParameter; }
void* varDatap() const { return m_varp->datap(); }
};
@@ -229,13 +230,13 @@ class VerilatedVpioRange final : public VerilatedVpio {
public:
explicit VerilatedVpioRange(const VerilatedRange* range)
: m_range{range} {}
virtual ~VerilatedVpioRange() override = default;
~VerilatedVpioRange() override = default;
static VerilatedVpioRange* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioRange*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiRange; }
virtual uint32_t size() const override { return m_range->elements(); }
virtual const VerilatedRange* rangep() const override { return m_range; }
uint32_t type() const override { return vpiRange; }
uint32_t size() const override { return m_range->elements(); }
const VerilatedRange* rangep() const override { return m_range; }
};
class VerilatedVpioRangeIter final : public VerilatedVpio {
@@ -246,12 +247,12 @@ class VerilatedVpioRangeIter final : public VerilatedVpio {
public:
explicit VerilatedVpioRangeIter(const VerilatedRange* range)
: m_range{range} {}
virtual ~VerilatedVpioRangeIter() override = default;
~VerilatedVpioRangeIter() override = default;
static VerilatedVpioRangeIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioRangeIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
if (VL_UNLIKELY(m_done)) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
@@ -268,14 +269,14 @@ protected:
public:
explicit VerilatedVpioScope(const VerilatedScope* scopep)
: m_scopep{scopep} {}
virtual ~VerilatedVpioScope() override = default;
~VerilatedVpioScope() override = default;
static VerilatedVpioScope* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioScope*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiScope; }
uint32_t type() const override { return vpiScope; }
const VerilatedScope* scopep() const { return m_scopep; }
virtual const char* name() const override { return m_scopep->name(); }
virtual const char* fullname() const override { return m_scopep->name(); }
const char* name() const override { return m_scopep->name(); }
const char* fullname() const override { return m_scopep->name(); }
};
class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase {
@@ -308,7 +309,7 @@ public:
m_mask.u32 = 0;
}
}
virtual ~VerilatedVpioVar() override {
~VerilatedVpioVar() override {
if (m_prevDatap) VL_DO_CLEAR(delete[] m_prevDatap, m_prevDatap = nullptr);
}
static VerilatedVpioVar* castp(vpiHandle h) {
@@ -318,7 +319,7 @@ public:
uint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
uint32_t entSize() const { return m_entSize; }
uint32_t index() const { return m_index; }
virtual uint32_t type() const override {
uint32_t type() const override {
return (varp()->dims() > 1) ? vpiMemory : vpiReg; // but might be wire, logic
}
void* prevDatap() const { return m_prevDatap; }
@@ -339,14 +340,14 @@ public:
m_index = index;
m_varDatap = (static_cast<uint8_t*>(varp->datap())) + entSize() * offset;
}
virtual ~VerilatedVpioMemoryWord() override = default;
~VerilatedVpioMemoryWord() override = default;
static VerilatedVpioMemoryWord* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioMemoryWord*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiMemoryWord; }
virtual uint32_t size() const override { return varp()->packed().elements(); }
virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); }
virtual const char* fullname() const override {
uint32_t type() const override { return vpiMemoryWord; }
uint32_t size() const override { return varp()->packed().elements(); }
const VerilatedRange* rangep() const override { return &(varp()->packed()); }
const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
constexpr size_t LEN_MAX_INDEX = 25;
char num[LEN_MAX_INDEX];
@@ -364,12 +365,12 @@ class VerilatedVpioVarIter final : public VerilatedVpio {
public:
explicit VerilatedVpioVarIter(const VerilatedScope* scopep)
: m_scopep{scopep} {}
virtual ~VerilatedVpioVarIter() override = default;
~VerilatedVpioVarIter() override = default;
static VerilatedVpioVarIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioVarIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
if (VL_LIKELY(m_scopep->varsp())) {
const VerilatedVarNameMap* const varsp = m_scopep->varsp();
if (VL_UNLIKELY(!m_started)) {
@@ -405,15 +406,15 @@ public:
, m_varp{varp}
, m_iteration{varp->unpacked().right()}
, m_direction{VL_LIKELY(varp->unpacked().left() > varp->unpacked().right()) ? 1 : -1} {}
virtual ~VerilatedVpioMemoryWordIter() override = default;
~VerilatedVpioMemoryWordIter() override = default;
static VerilatedVpioMemoryWordIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioMemoryWordIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiIterator; }
uint32_t type() const override { return vpiIterator; }
void iterationInc() {
if (!(m_done = (m_iteration == m_varp->unpacked().left()))) m_iteration += m_direction;
}
virtual vpiHandle dovpi_scan() override {
vpiHandle dovpi_scan() override {
if (VL_UNLIKELY(m_done)) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
@@ -438,9 +439,9 @@ public:
static VerilatedVpioModule* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModule*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiModule; }
virtual const char* name() const override { return m_name; }
virtual const char* fullname() const override { return m_fullname; }
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 {
@@ -452,12 +453,12 @@ public:
: m_vec{&vec} {
m_it = m_vec->begin();
}
virtual ~VerilatedVpioModuleIter() override = default;
~VerilatedVpioModuleIter() override = default;
static VerilatedVpioModuleIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModuleIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
if (m_it == m_vec->end()) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
+2 -1
View File
@@ -25,6 +25,7 @@
#define VERILATOR_VERILATED_VPI_H_
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_syms.h"
@@ -47,7 +48,7 @@ public:
static bool callValueCbs() VL_MT_UNSAFE_ONE;
/// Call callbacks of arbitrary types.
/// User wrapper code should call this from their main loops.
static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE;
static bool callCbs(uint32_t reason) VL_MT_UNSAFE_ONE;
/// Returns time of the next registered VPI callback, or
/// ~(0ULL) if none are registered
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
+50 -26
View File
@@ -39,7 +39,7 @@
#ifdef __GNUC__
# define VL_ATTR_ALIGNED(alignment) __attribute__((aligned(alignment)))
# define VL_ATTR_ALWINLINE __attribute__((always_inline))
# define VL_ATTR_ALWINLINE __attribute__((always_inline)) inline
# define VL_ATTR_NOINLINE __attribute__((noinline))
# define VL_ATTR_COLD __attribute__((cold))
# define VL_ATTR_HOT __attribute__((hot))
@@ -218,22 +218,33 @@
// C++-2011
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(VL_CPPCHECK)
# ifndef VL_NO_LEGACY
// These are deprecated historical defines. We leave them in case users referenced them.
# define VL_EQ_DELETE = delete
# define vl_unique_ptr std::unique_ptr
# define vl_unordered_map std::unordered_map
# define vl_unordered_set std::unordered_set
# define VL_INCLUDE_UNORDERED_MAP <unordered_map>
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
# define VL_FINAL final
# define VL_MUTABLE mutable
# define VL_OVERRIDE override
# endif
#else
# error "Verilator requires a C++11 or newer compiler"
#endif
#ifndef VL_NO_LEGACY
// These are deprecated historical defines. We leave them in case users referenced them.
# define VL_EQ_DELETE = delete
# define vl_unique_ptr std::unique_ptr
# define vl_unordered_map std::unordered_map
# define vl_unordered_set std::unordered_set
# define VL_INCLUDE_UNORDERED_MAP <unordered_map>
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
# define VL_FINAL final
# define VL_MUTABLE mutable
# define VL_OVERRIDE override
#endif
//=========================================================================
// C++-2017
#if __cplusplus >= 201703L
# define VL_CONSTEXPR_CXX17 constexpr
#else
# define VL_CONSTEXPR_CXX17
#endif
//=========================================================================
// Optimization
@@ -245,14 +256,11 @@
// Internal coverage
#ifdef VL_GCOV
extern "C" {
void __gcov_flush(); // gcc sources gcc/gcov-io.h has the prototype
}
// Flush internal code coverage data before e.g. std::abort()
# define VL_GCOV_FLUSH() \
__gcov_flush()
extern "C" void __gcov_dump();
// Dump internal code coverage data before e.g. std::abort()
# define VL_GCOV_DUMP() __gcov_dump()
#else
# define VL_GCOV_FLUSH()
# define VL_GCOV_DUMP()
#endif
//=========================================================================
@@ -446,7 +454,8 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// or 0x0 if not implemented on this platform
#define VL_GET_CPU_TICK(val) \
{ \
uint32_t hi, lo; \
uint32_t hi; \
uint32_t lo; \
asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); \
(val) = ((uint64_t)lo) | (((uint64_t)hi) << 32); \
}
@@ -521,25 +530,40 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#define VL_STRINGIFY(x) VL_STRINGIFY2(x)
#define VL_STRINGIFY2(x) #x
//=========================================================================
// Offset of field in type
// Address zero can cause compiler problems
#define VL_OFFSETOF(type, field) \
(reinterpret_cast<size_t>(&(reinterpret_cast<type*>(0x10000000)->field)) - 0x10000000)
//=========================================================================
// Conversions
namespace vlstd {
template <typename T> struct reverse_wrapper {
template <typename T>
struct reverse_wrapper {
const T& m_v;
explicit reverse_wrapper(const T& a_v)
: m_v(a_v) {}
inline auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); }
inline auto end() -> decltype(m_v.rend()) { return m_v.rend(); }
auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); }
auto end() -> decltype(m_v.rend()) { return m_v.rend(); }
};
// C++20's std::ranges::reverse_view
template <typename T> reverse_wrapper<T> reverse_view(const T& v) { return reverse_wrapper<T>(v); }
template <typename T>
reverse_wrapper<T> reverse_view(const T& v) {
return reverse_wrapper<T>(v);
}
// C++17's std::as_const
template <class T> T const& as_const(T& v) { return v; }
template <class T>
T const& as_const(T& v) {
return v;
}
}; // namespace vlstd
//=========================================================================
+1
View File
@@ -48,6 +48,7 @@ exclude_line_regexp(r'\bNUM_ASSERT')
exclude_line_regexp(r'\bERROR_RSVD_WORD')
exclude_line_regexp(r'\bV3ERROR_NA')
exclude_line_regexp(r'\bUINFO\b')
exclude_line_regexp(r'\bVL_DEFINE_DEBUG_FUNCTIONS\b')
# Exclude for branch coverage only
exclude_branch_regexp(r'\bdebug\(\)')
+13 -6
View File
@@ -79,7 +79,7 @@ ifeq ($(VL_NOOPT),1)
CPPFLAGS += -O0
else ifeq ($(VL_DEBUG),)
# Optimize
CPPFLAGS += -O2
CPPFLAGS += -O3
else
# Debug
CPPFLAGS += @CFG_CXXFLAGS_DEBUG@ -DVL_DEBUG -D_GLIBCXX_DEBUG
@@ -276,9 +276,16 @@ VLCOV_OBJS = \
NON_STANDALONE_HEADERS = \
V3AstInlines.h \
V3AstNodes.h \
V3AstNodeDType.h \
V3AstNodeMath.h \
V3AstNodeOther.h \
V3WidthCommit.h \
AST_DEFS := \
V3AstNodeDType.h \
V3AstNodeMath.h \
V3AstNodeOther.h \
#### Linking
ifeq ($(VL_VLCOV),)
@@ -301,8 +308,8 @@ V3Number_test: V3Number_test.o
#### Modules
%__gen.cpp: %.cpp $(ASTGEN) V3Ast.h V3AstNodes.h
$(PYTHON3) $(ASTGEN) -I $(srcdir) $*.cpp
%__gen.cpp: %.cpp $(ASTGEN) $(AST_DEFS)
$(PYTHON3) $(ASTGEN) -I $(srcdir) $(foreach f,$(AST_DEFS),--astdef $f) $*.cpp
%.o: %.cpp
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $< -o $@
@@ -332,8 +339,8 @@ vlcovgen.d: $(VLCOVGEN) $(srcdir)/../include/verilated_cov_key.h
$(PYTHON3) $(VLCOVGEN) --srcdir $(srcdir)
touch $@
V3Ast__gen_classes.h : $(ASTGEN) V3Ast.h V3AstNodes.h
$(PYTHON3) $(ASTGEN) -I $(srcdir) --classes
V3Ast__gen_classes.h : $(ASTGEN) $(AST_DEFS)
$(PYTHON3) $(ASTGEN) -I $(srcdir) $(foreach f,$(AST_DEFS),--astdef $f) --classes
V3ParseBison.h: V3ParseBison.c
+51 -55
View File
@@ -29,14 +29,17 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Active.h"
#include "V3Ast.h"
#include "V3Const.h"
#include "V3Global.h"
#include "V3Graph.h"
#include <unordered_map>
VL_DEFINE_DEBUG_FUNCTIONS;
//***** See below for main transformation engine
//######################################################################
@@ -65,8 +68,8 @@ public:
: V3GraphVertex{graphp}
, m_name{name}
, m_type{type} {}
virtual string name() const override { return m_name + " " + typestr(); }
virtual string dotColor() const override { return user() ? "green" : "black"; }
string name() const override { return m_name + " " + typestr(); }
string dotColor() const override { return user() ? "green" : "black"; }
virtual int type() const { return m_type; }
};
@@ -75,11 +78,9 @@ public:
class LatchDetectGraph final : public V3Graph {
protected:
LatchDetectGraphVertex* m_curVertexp; // Current latch detection graph vertex
LatchDetectGraphVertex* m_curVertexp = nullptr; // Current latch detection graph vertex
std::vector<AstVarRef*> m_outputs; // Vector of lvalues encountered on this pass
VL_DEBUG_FUNC; // Declare debug()
static LatchDetectGraphVertex* castVertexp(void* vertexp) {
return reinterpret_cast<LatchDetectGraphVertex*>(vertexp);
}
@@ -119,7 +120,7 @@ protected:
public:
LatchDetectGraph() { clear(); }
virtual ~LatchDetectGraph() override { clear(); }
~LatchDetectGraph() override { clear(); }
// ACCESSORS
LatchDetectGraphVertex* currentp() { return m_curVertexp; }
void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; }
@@ -182,7 +183,7 @@ public:
<< " (not all control paths of combinational always assign a value)\n"
<< nodep->warnMore()
<< "... Suggest use of always_latch for intentional latches");
if (debug() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
if (dumpGraph() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
}
vertp->user(false); // Clear again (see above)
vrp->varp()->isLatched(latch_detected);
@@ -197,12 +198,7 @@ public:
//######################################################################
// Collect existing active names
class ActiveBaseVisitor VL_NOT_FINAL : public VNVisitor {
protected:
VL_DEBUG_FUNC; // Declare debug()
};
class ActiveNamer final : public ActiveBaseVisitor {
class ActiveNamer final : public VNVisitor {
private:
// STATE
AstScope* m_scopep = nullptr; // Current scope to add statement to
@@ -215,10 +211,10 @@ private:
// METHODS
void addActive(AstActive* nodep) {
UASSERT_OBJ(m_scopep, nodep, "nullptr scope");
m_scopep->addActivep(nodep);
m_scopep->addBlocksp(nodep);
}
// VISITORS
virtual void visit(AstScope* nodep) override {
void visit(AstScope* nodep) override {
m_scopep = nodep;
m_iActivep = nullptr;
m_cActivep = nullptr;
@@ -226,13 +222,13 @@ private:
iterateChildren(nodep);
// Don't clear scopep, the namer persists beyond this visit
}
virtual void visit(AstSenTree* nodep) override {
void visit(AstSenTree* nodep) override {
// Simplify sensitivity list
VL_DO_DANGLING(V3Const::constifyExpensiveEdit(nodep), nodep);
}
//--------------------
virtual void visit(AstNodeStmt*) override {} // Accelerate
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNodeStmt*) override {} // Accelerate
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// METHODS
@@ -274,14 +270,14 @@ public:
// CONSTRUCTORS
ActiveNamer() = default;
virtual ~ActiveNamer() override = default;
~ActiveNamer() override = default;
void main(AstScope* nodep) { iterate(nodep); }
};
//######################################################################
// Latch checking visitor
class ActiveLatchCheckVisitor final : public ActiveBaseVisitor {
class ActiveLatchCheckVisitor final : public VNVisitor {
private:
// NODE STATE
// Input:
@@ -290,25 +286,25 @@ private:
// STATE
LatchDetectGraph m_graph; // Graph used to detect latches in combo always
// VISITORS
virtual void visit(AstVarRef* nodep) {
void visit(AstVarRef* nodep) override {
const AstVar* const varp = nodep->varp();
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) {
m_graph.addAssignment(nodep);
}
}
virtual void visit(AstNodeIf* nodep) {
void visit(AstNodeIf* nodep) override {
if (!nodep->isBoundsCheck()) {
LatchDetectGraphVertex* const parentp = m_graph.currentp();
LatchDetectGraphVertex* const branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
m_graph.addPathVertex(branchp, "IF");
iterateAndNextNull(nodep->ifsp());
iterateAndNextNull(nodep->thensp());
m_graph.addPathVertex(branchp, "ELSE");
iterateAndNextNull(nodep->elsesp());
m_graph.currentp(parentp);
}
}
//--------------------
virtual void visit(AstNode* nodep) { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
@@ -317,13 +313,13 @@ public:
iterate(nodep);
m_graph.latchCheck(nodep, kwd == VAlwaysKwd::ALWAYS_LATCH);
}
virtual ~ActiveLatchCheckVisitor() = default;
~ActiveLatchCheckVisitor() override = default;
};
//######################################################################
// Replace unsupported non-blocking assignments with blocking assignments
class ActiveDlyVisitor final : public ActiveBaseVisitor {
class ActiveDlyVisitor final : public VNVisitor {
public:
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL };
@@ -332,7 +328,7 @@ private:
const CheckType m_check; // Process type we are checking
// VISITORS
virtual void visit(AstAssignDly* nodep) override {
void visit(AstAssignDly* nodep) override {
// Non-blocking assignments are OK in sequential processes
if (m_check == CT_SEQ) return;
@@ -356,7 +352,7 @@ private:
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
virtual void visit(AstAssign* nodep) override {
void visit(AstAssign* nodep) override {
// Blocking assignments are always OK in combinational (and initial/final) processes
if (m_check != CT_SEQ) return;
@@ -379,7 +375,7 @@ private:
}
//--------------------
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
@@ -387,13 +383,13 @@ public:
: m_check{check} {
iterate(nodep);
}
virtual ~ActiveDlyVisitor() override = default;
~ActiveDlyVisitor() override = default;
};
//######################################################################
// Active class functions
class ActiveVisitor final : public ActiveBaseVisitor {
class ActiveVisitor final : public VNVisitor {
private:
// NODE STATE
// Each call to V3Const::constify
@@ -406,7 +402,7 @@ private:
bool m_itemSequent = false; // Found a SenItem sequential
// VISITORS
virtual void visit(AstScope* nodep) override {
void visit(AstScope* nodep) override {
// Create required actives and add to scope
UINFO(4, " SCOPE " << nodep << endl);
// Clear last scope's names, and collect this scope's existing names
@@ -414,10 +410,10 @@ private:
m_scopeFinalp = nullptr;
iterateChildren(nodep);
}
virtual void visit(AstActive* nodep) override {
void visit(AstActive* nodep) override {
// Actives are being formed, so we can ignore any already made
}
virtual void visit(AstInitialStatic* nodep) override {
void visit(AstInitialStatic* nodep) override {
// Relink to IACTIVE, unless already under it
UINFO(4, " INITIAL " << nodep << endl);
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
@@ -425,7 +421,7 @@ private:
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstInitial* nodep) override {
void visit(AstInitial* nodep) override {
// Relink to IACTIVE, unless already under it
UINFO(4, " INITIAL " << nodep << endl);
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
@@ -433,31 +429,31 @@ private:
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstAssignAlias* nodep) override {
void visit(AstAssignAlias* nodep) override {
// Relink to CACTIVE, unless already under it
UINFO(4, " ASSIGNW " << nodep << endl);
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstAssignW* nodep) override {
void visit(AstAssignW* nodep) override {
// Relink to CACTIVE, unless already under it
UINFO(4, " ASSIGNW " << nodep << endl);
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstCoverToggle* nodep) override {
void visit(AstCoverToggle* nodep) override {
// Relink to CACTIVE, unless already under it
UINFO(4, " COVERTOGGLE " << nodep << endl);
AstActive* const wantactivep = m_namer.getCActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstFinal* nodep) override {
void visit(AstFinal* nodep) override {
// Relink to CFUNC for the final
UINFO(4, " FINAL " << nodep << endl);
if (!nodep->bodysp()) { // Empty, Kill it.
if (!nodep->stmtsp()) { // Empty, Kill it.
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
@@ -470,10 +466,10 @@ private:
m_scopeFinalp->isStatic(false);
m_scopeFinalp->isLoose(true);
m_scopeFinalp->slow(true);
m_namer.scopep()->addActivep(m_scopeFinalp);
m_namer.scopep()->addBlocksp(m_scopeFinalp);
}
nodep->unlinkFrBack();
m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext());
m_scopeFinalp->addStmtsp(nodep->stmtsp()->unlinkFrBackWithNext());
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
@@ -530,39 +526,39 @@ private:
// Warn and/or convert any delayed assignments
if (combo && !sequent) {
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB};
{ ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB}; }
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd};
} else if (!combo && sequent) {
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_SEQ};
}
}
virtual void visit(AstAlways* nodep) override {
void visit(AstAlways* nodep) override {
// Move always to appropriate ACTIVE based on its sense list
UINFO(4, " ALW " << nodep << endl);
// if (debug() >= 9) nodep->dumpTree(cout, " Alw: ");
if (!nodep->bodysp()) {
if (!nodep->stmtsp()) {
// Empty always. Kill it.
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
visitAlways(nodep, nodep->sensesp(), nodep->keyword());
}
virtual void visit(AstAlwaysPostponed* nodep) override {
void visit(AstAlwaysPostponed* nodep) override {
UINFO(4, " ALW " << nodep << endl);
if (!nodep->bodysp()) {
if (!nodep->stmtsp()) {
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
visitAlways(nodep, nullptr, VAlwaysKwd::ALWAYS);
}
virtual void visit(AstAlwaysPublic* nodep) override {
void visit(AstAlwaysPublic* nodep) override {
// Move always to appropriate ACTIVE based on its sense list
UINFO(4, " ALWPub " << nodep << endl);
// if (debug() >= 9) nodep->dumpTree(cout, " Alw: ");
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
}
virtual void visit(AstSenItem* nodep) override {
void visit(AstSenItem* nodep) override {
if (nodep->varrefp()) {
if (const AstBasicDType* const basicp = nodep->varrefp()->dtypep()->basicp()) {
if (basicp->isEventValue()) {
@@ -591,14 +587,14 @@ private:
}
//--------------------
virtual void visit(AstNodeMath*) override {} // Accelerate
virtual void visit(AstVarScope*) override {} // Accelerate
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNodeMath*) override {} // Accelerate
void visit(AstVarScope*) override {} // Accelerate
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit ActiveVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~ActiveVisitor() override = default;
~ActiveVisitor() override = default;
};
//######################################################################
@@ -607,5 +603,5 @@ public:
void V3Active::activeAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("active", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("active", 0, dumpTree() >= 3);
}
+16 -14
View File
@@ -26,11 +26,14 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3ActiveTop.h"
#include "V3Ast.h"
#include "V3SenTree.h"
#include "V3Const.h"
#include "V3Global.h"
#include "V3SenTree.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Active class functions
@@ -48,16 +51,15 @@ private:
SenTreeFinder m_finder; // Find global sentree's / add them under the AstTopScope
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
// Create required actives and add to module
// We can start ordering at a module, or a scope
UINFO(4, " MOD " << nodep << endl);
iterateChildren(nodep);
}
virtual void visit(AstActive* nodep) override {
void visit(AstActive* nodep) override {
UINFO(4, " ACTIVE " << nodep << endl);
// Remove duplicate clocks and such; sensesp() may change!
V3Const::constifyExpensiveEdit(nodep);
@@ -100,27 +102,27 @@ private:
// No need to do statements under it, they're already moved.
// iterateChildren(nodep);
}
virtual void visit(AstNodeProcedure* nodep) override { // LCOV_EXCL_LINE
void visit(AstNodeProcedure* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAssignAlias* nodep) override { // LCOV_EXCL_LINE
void visit(AstAssignAlias* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAssignW* nodep) override { // LCOV_EXCL_LINE
void visit(AstAssignW* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
virtual void visit(AstAlwaysPublic* nodep) override { // LCOV_EXCL_LINE
void visit(AstAlwaysPublic* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc("Node should have been under ACTIVE");
}
//--------------------
virtual void visit(AstNodeMath*) override {} // Accelerate
virtual void visit(AstVarScope*) override {} // Accelerate
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNodeMath*) override {} // Accelerate
void visit(AstVarScope*) override {} // Accelerate
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit ActiveTopVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~ActiveTopVisitor() override = default;
~ActiveTopVisitor() override = default;
};
//######################################################################
@@ -129,5 +131,5 @@ public:
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ActiveTopVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("activetop", 0, dumpTree() >= 3);
}
+29 -26
View File
@@ -17,11 +17,14 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Assert.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3Stats.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Assert class functions
@@ -69,7 +72,7 @@ private:
if (!m_monitorNumVarp) {
m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum",
nodep->findUInt64DType()};
v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(m_monitorNumVarp);
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorNumVarp);
}
const auto varrefp = new AstVarRef(nodep->fileline(), m_monitorNumVarp, access);
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
@@ -79,7 +82,7 @@ private:
if (!m_monitorOffVarp) {
m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff",
nodep->findBitDType()};
v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(m_monitorOffVarp);
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorOffVarp);
}
const auto varrefp = new AstVarRef(nodep->fileline(), m_monitorOffVarp, access);
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
@@ -146,7 +149,7 @@ private:
selfDestruct = true;
} else {
// V3Coverage assigned us a bucket to increment.
AstCoverInc* const covincp = VN_AS(snodep->coverincp(), CoverInc);
AstCoverInc* const covincp = VN_AS(snodep->coverincsp(), CoverInc);
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
if (message != "") covincp->declp()->comment(message);
@@ -195,7 +198,7 @@ private:
}
// VISITORS
virtual void visit(AstIf* nodep) override {
void visit(AstIf* nodep) override {
if (nodep->user1SetOnce()) return;
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
const AstNodeIf* ifp = nodep;
@@ -208,7 +211,7 @@ private:
iterateAndNextNull(ifp->condp());
// Recurse into the true case.
iterateAndNextNull(ifp->ifsp());
iterateAndNextNull(ifp->thensp());
// If the last else is not an else if, recurse into that too.
if (ifp->elsesp() && !nextifp) { //
@@ -253,7 +256,7 @@ private:
}
//========== Case assertions
virtual void visit(AstCase* nodep) override {
void visit(AstCase* nodep) override {
iterateChildren(nodep);
if (!nodep->user1SetOnce()) {
bool has_default = false;
@@ -322,7 +325,7 @@ private:
}
//========== Past
virtual void visit(AstPast* nodep) override {
void visit(AstPast* nodep) override {
iterateChildren(nodep);
uint32_t ticks = 1;
if (nodep->ticksp()) {
@@ -337,28 +340,28 @@ private:
sentreep->unlinkFrBack();
AstAlways* const alwaysp
= new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, nullptr);
m_modp->addStmtp(alwaysp);
m_modp->addStmtsp(alwaysp);
for (uint32_t i = 0; i < ticks; ++i) {
AstVar* const outvarp = new AstVar(
nodep->fileline(), VVarType::MODULETEMP,
"_Vpast_" + cvtToStr(m_modPastNum++) + "_" + cvtToStr(i), inp->dtypep());
m_modp->addStmtp(outvarp);
m_modp->addStmtsp(outvarp);
AstNode* const assp = new AstAssignDly(
nodep->fileline(), new AstVarRef(nodep->fileline(), outvarp, VAccess::WRITE), inp);
alwaysp->addStmtp(assp);
alwaysp->addStmtsp(assp);
// if (debug() >= 9) assp->dumpTree(cout, "-ass: ");
invarp = outvarp;
inp = new AstVarRef(nodep->fileline(), invarp, VAccess::READ);
}
nodep->replaceWith(inp);
}
virtual void visit(AstSampled* nodep) override {
void visit(AstSampled* nodep) override {
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
//========== Statements
virtual void visit(AstDisplay* nodep) override {
void visit(AstDisplay* nodep) override {
iterateChildren(nodep);
// Replace the special types with standard text
if (nodep->displayType() == VDisplayType::DT_INFO) {
@@ -386,7 +389,7 @@ private:
stmtsp};
ifp->branchPred(VBranchPred::BP_UNLIKELY);
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
m_modp->addStmtp(newp);
m_modp->addStmtsp(newp);
} else if (nodep->displayType() == VDisplayType::DT_STROBE) {
nodep->displayType(VDisplayType::DT_DISPLAY);
// Need one-shot
@@ -394,7 +397,7 @@ private:
const auto varp
= new AstVar{fl, VVarType::MODULETEMP, "__Vstrobe" + cvtToStr(m_modStrobeNum++),
nodep->findBitDType()};
m_modp->addStmtp(varp);
m_modp->addStmtsp(varp);
// Where $strobe was we do "__Vstrobe = '1;"
const auto newsetp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
new AstConst{fl, AstConst::BitTrue{}}};
@@ -406,35 +409,35 @@ private:
AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
new AstConst{fl, AstConst::BitFalse{}}});
m_modp->addStmtp(newp);
m_modp->addStmtsp(newp);
}
}
virtual void visit(AstMonitorOff* nodep) override {
void visit(AstMonitorOff* nodep) override {
const auto newp
= new AstAssign(nodep->fileline(), newMonitorOffVarRefp(nodep, VAccess::WRITE),
new AstConst(nodep->fileline(), AstConst::BitTrue{}, nodep->off()));
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstAssert* nodep) override {
void visit(AstAssert* nodep) override {
iterateChildren(nodep);
newPslAssertion(nodep, nodep->failsp());
}
virtual void visit(AstAssertIntrinsic* nodep) override {
void visit(AstAssertIntrinsic* nodep) override {
iterateChildren(nodep);
newPslAssertion(nodep, nodep->failsp());
}
virtual void visit(AstCover* nodep) override {
void visit(AstCover* nodep) override {
iterateChildren(nodep);
newPslAssertion(nodep, nullptr);
}
virtual void visit(AstRestrict* nodep) override {
void visit(AstRestrict* nodep) override {
iterateChildren(nodep);
// IEEE says simulator ignores these
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
VL_RESTORER(m_modPastNum);
VL_RESTORER(m_modStrobeNum);
@@ -445,7 +448,7 @@ private:
iterateChildren(nodep);
}
}
virtual void visit(AstBegin* nodep) override {
void visit(AstBegin* nodep) override {
// This code is needed rather than a visitor in V3Begin,
// because V3Assert is called before V3Begin
VL_RESTORER(m_beginp);
@@ -455,12 +458,12 @@ private:
}
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit AssertVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~AssertVisitor() override {
~AssertVisitor() override {
V3Stats::addStat("Assertions, assert non-immediate statements", m_statAsNotImm);
V3Stats::addStat("Assertions, assert immediate statements", m_statAsImm);
V3Stats::addStat("Assertions, cover statements", m_statCover);
@@ -474,5 +477,5 @@ public:
void V3Assert::assertAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assert", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("assert", 0, dumpTree() >= 3);
}
+1 -1
View File
@@ -20,8 +20,8 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
#include "V3Error.h"
//============================================================================
+18 -16
View File
@@ -20,9 +20,12 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3AssertPre.h"
#include "V3Ast.h"
#include "V3Global.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Assert class functions
@@ -44,7 +47,6 @@ private:
AstNode* m_disablep = nullptr; // Last disable
// METHODS
VL_DEBUG_FUNC; // Declare debug()
AstSenTree* newSenTree(AstNode* nodep) {
// Create sentree based on clocked or default clock
@@ -68,7 +70,7 @@ private:
// VISITORS
//========== Statements
virtual void visit(AstClocking* nodep) override {
void visit(AstClocking* nodep) override {
UINFO(8, " CLOCKING" << nodep << endl);
// Store the new default clock, reset on new module
m_seniDefaultp = nodep->sensesp();
@@ -80,14 +82,14 @@ private:
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstAlways* nodep) override {
void visit(AstAlways* nodep) override {
iterateAndNextNull(nodep->sensesp());
if (nodep->sensesp()) m_seniAlwaysp = nodep->sensesp()->sensesp();
iterateAndNextNull(nodep->bodysp());
iterateAndNextNull(nodep->stmtsp());
m_seniAlwaysp = nullptr;
}
virtual void visit(AstNodeCoverOrAssert* nodep) override {
void visit(AstNodeCoverOrAssert* nodep) override {
if (nodep->sentreep()) return; // Already processed
clearAssertInfo();
// Find Clocking's buried under nodep->exprsp
@@ -95,7 +97,7 @@ private:
if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep));
clearAssertInfo();
}
virtual void visit(AstFell* nodep) override {
void visit(AstFell* nodep) override {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
FileLine* const fl = nodep->fileline();
@@ -109,12 +111,12 @@ private:
nodep->sentreep(newSenTree(nodep));
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstPast* nodep) override {
void visit(AstPast* nodep) override {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
nodep->sentreep(newSenTree(nodep));
}
virtual void visit(AstRose* nodep) override {
void visit(AstRose* nodep) override {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
FileLine* const fl = nodep->fileline();
@@ -128,7 +130,7 @@ private:
nodep->sentreep(newSenTree(nodep));
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstStable* nodep) override {
void visit(AstStable* nodep) override {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
FileLine* const fl = nodep->fileline();
@@ -142,7 +144,7 @@ private:
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstImplication* nodep) override {
void visit(AstImplication* nodep) override {
if (nodep->sentreep()) return; // Already processed
FileLine* const fl = nodep->fileline();
@@ -160,7 +162,7 @@ private:
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstPropClocked* nodep) override {
void visit(AstPropClocked* nodep) override {
// No need to iterate the body, once replace will get iterated
iterateAndNextNull(nodep->sensesp());
if (m_senip)
@@ -182,12 +184,12 @@ private:
nodep->replaceWith(blockp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
iterateChildren(nodep);
// Reset defaults
m_seniDefaultp = nullptr;
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
@@ -196,7 +198,7 @@ public:
// Process
iterate(nodep);
}
virtual ~AssertPreVisitor() override = default;
~AssertPreVisitor() override = default;
};
//######################################################################
@@ -205,5 +207,5 @@ public:
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ AssertPreVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("assertpre", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("assertpre", 0, dumpTree() >= 3);
}
+85 -64
View File
@@ -18,15 +18,18 @@
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3Broken.h"
#include "V3EmitV.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3String.h"
#include <iomanip>
#include <memory>
VL_DEFINE_DEBUG_FUNCTIONS;
//======================================================================
// Statics
@@ -164,22 +167,22 @@ string AstNode::prettyName(const string& namein) {
continue;
}
if (pos[0] == '_' && pos[1] == '_') { // Short-circuit
if (0 == strncmp(pos, "__BRA__", 7)) {
if (0 == std::strncmp(pos, "__BRA__", 7)) {
pretty += "[";
pos += 7;
continue;
}
if (0 == strncmp(pos, "__KET__", 7)) {
if (0 == std::strncmp(pos, "__KET__", 7)) {
pretty += "]";
pos += 7;
continue;
}
if (0 == strncmp(pos, "__DOT__", 7)) {
if (0 == std::strncmp(pos, "__DOT__", 7)) {
pretty += ".";
pos += 7;
continue;
}
if (0 == strncmp(pos, "__PVT__", 7)) {
if (0 == std::strncmp(pos, "__PVT__", 7)) {
pretty += "";
pos += 7;
continue;
@@ -205,14 +208,13 @@ string AstNode::prettyName(const string& namein) {
string AstNode::prettyTypeName() const {
if (name() == "") return typeName();
return string(typeName()) + " '" + prettyName() + "'";
return std::string{typeName()} + " '" + prettyName() + "'";
}
//######################################################################
// Insertion
inline void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int lineno,
bool next){
void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, int lineno, bool next) {
#ifdef VL_DEBUG
// Called on all major tree changers.
// Only for use for those really nasty bugs relating to internals
@@ -233,7 +235,8 @@ inline void AstNode::debugTreeChange(const AstNode* nodep, const char* prefix, i
#endif
}
AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
template <>
AstNode* AstNode::addNext<AstNode, AstNode>(AstNode* nodep, AstNode* newp) {
// Add to m_nextp, returns this
UDEBUGONLY(UASSERT_OBJ(newp, nodep, "Null item passed to addNext"););
debugTreeChange(nodep, "-addNextThs: ", __LINE__, false);
@@ -271,23 +274,18 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
return nodep;
}
AstNode* AstNode::addNextNull(AstNode* nodep, AstNode* newp) {
if (!newp) return nodep;
return addNext(nodep, newp);
}
void AstNode::addNextHere(AstNode* newp) {
// Add to m_nextp on exact node passed, not at the end.
// This could be at head, tail, or both (single)
// New could be head of single node, or list
UASSERT(newp, "Null item passed to addNext");
UASSERT(!newp->backp(), "New node (back) already assigned?");
UASSERT_OBJ(!newp->backp(), newp, "New node (back) already assigned?");
debugTreeChange(this, "-addHereThs: ", __LINE__, false);
debugTreeChange(newp, "-addHereNew: ", __LINE__, true);
newp->editCountInc();
AstNode* const addlastp = newp->m_headtailp; // Last node in list to be added
UASSERT(!addlastp->m_nextp, "Headtailp tail isn't at the tail");
UASSERT_OBJ(!addlastp->m_nextp, addlastp, "Headtailp tail isn't at the tail");
// Forward links
AstNode* const oldnextp = this->m_nextp;
@@ -437,7 +435,7 @@ void VNRelinker::dump(std::ostream& str) const {
AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) {
debugTreeChange(this, "-unlinkWNextThs: ", __LINE__, true);
AstNode* const oldp = this;
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
UASSERT_OBJ(oldp->m_backp, oldp, "Node has no back, already unlinked?");
oldp->editCountInc();
AstNode* const backp = oldp->m_backp;
if (linkerp) {
@@ -497,7 +495,7 @@ AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) {
AstNode* AstNode::unlinkFrBack(VNRelinker* linkerp) {
debugTreeChange(this, "-unlinkFrBkThs: ", __LINE__, true);
AstNode* const oldp = this;
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
UASSERT_OBJ(oldp->m_backp, oldp, "Node has no back, already unlinked?");
oldp->editCountInc();
AstNode* const backp = oldp->m_backp;
if (linkerp) {
@@ -565,7 +563,7 @@ void AstNode::relink(VNRelinker* linkerp) {
}
AstNode* const newp = this;
UASSERT(linkerp && linkerp->m_backp, "Need non-empty linker");
UASSERT(!newp->backp(), "New node already linked?");
UASSERT_OBJ(!newp->m_backp, newp, "New node already linked?");
newp->editCountInc();
if (debug() > 8) {
@@ -631,11 +629,56 @@ void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set
}
void AstNode::addHereThisAsNext(AstNode* newp) {
// {old}->this->{next} becomes {old}->new->this->{next}
VNRelinker handle;
this->unlinkFrBackWithNext(&handle);
newp->addNext(this);
handle.relink(newp);
// {back}->this->{next} becomes {back}->new->this->{next}
UASSERT_OBJ(!newp->backp(), newp, "New node already linked?");
UASSERT_OBJ(this->m_backp, this, "'this' node has no back, already unlinked?");
UASSERT_OBJ(newp->m_headtailp, newp, "m_headtailp not set on new node");
//
AstNode* const backp = this->m_backp;
AstNode* const newLastp = newp->m_headtailp;
//
this->editCountInc();
// Common linkage
newLastp->m_nextp = this;
this->m_backp = newLastp;
newp->m_backp = backp;
// newLastp will not be the last node in the list as 'this' will follow it.
// If newLastp == newp, then below handles newp becoming head
newLastp->m_headtailp = nullptr;
// Linkage dependent on position
if (backp && backp->m_nextp == this) {
// If 'this' is not at the head of a list, then the new node will also not be at the head
// of a list, so we can just link in the new node in the middle.
backp->m_nextp = newp;
newp->m_headtailp = nullptr;
} else {
// If 'this' is at the head of a list, then the new node becomes the head of that list.
if (backp) {
if (backp->m_op1p == this) {
backp->m_op1p = newp;
} else if (backp->m_op2p == this) {
backp->m_op2p = newp;
} else if (backp->m_op3p == this) {
backp->m_op3p = newp;
} else {
UASSERT_OBJ(backp->m_op4p == this, this, "Don't know where newp should go");
backp->m_op4p = newp;
}
}
// We also need to update m_headtailp.
AstNode* const tailp = this->m_headtailp;
this->m_headtailp = nullptr;
newp->m_headtailp = tailp;
tailp->m_headtailp = newp;
}
// Iterator fixup
if (newLastp->m_iterpp) *(newLastp->m_iterpp) = this;
if (this->m_iterpp) {
*(this->m_iterpp) = newp;
this->m_iterpp = nullptr;
}
//
debugTreeChange(this, "-addHereThisAsNext: ", __LINE__, true);
}
void AstNode::swapWith(AstNode* bp) {
@@ -879,7 +922,7 @@ AstNode* AstNode::iterateSubtreeReturnEdits(VNVisitor& v) {
} else if (!nodep->backp()) {
// Calling on standalone tree; insert a shim node so we can keep
// track, then delete it on completion
AstBegin* const tempp = new AstBegin(nodep->fileline(), "[EditWrapper]", nodep);
AstBegin* const tempp = new AstBegin{nodep->fileline(), "[EditWrapper]", nodep};
{
VL_DO_DANGLING(tempp->stmtsp()->accept(v),
nodep); // nodep to null as may be replaced
@@ -958,43 +1001,12 @@ bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ig
//======================================================================
// Debugging
void AstNode::checkTreeIter(AstNode* backp) {
void AstNode::checkTreeIter(const AstNode* backp) const {
// private: Check a tree and children
UASSERT_OBJ(backp == this->backp(), this, "Back node inconsistent");
if (VN_IS(this, NodeTermop) || VN_IS(this, NodeVarRef)) {
// Termops have a short-circuited iterateChildren, so check usage
UASSERT_OBJ(!(op1p() || op2p() || op3p() || op4p()), this,
"Terminal operation with non-terminals");
}
if (m_op1p) m_op1p->checkTreeIterList(this);
if (m_op2p) m_op2p->checkTreeIterList(this);
if (m_op3p) m_op3p->checkTreeIterList(this);
if (m_op4p) m_op4p->checkTreeIterList(this);
}
void AstNode::checkTreeIterList(AstNode* backp) {
// private: Check a (possible) list of nodes, this is always the head of the list
// Audited to make sure this is never nullptr
AstNode* const headp = this;
const AstNode* tailp = this;
for (AstNode* nodep = headp; nodep; nodep = nodep->nextp()) {
nodep->checkTreeIter(backp);
UASSERT_OBJ(headp == this || !nextp(), this,
"Headtailp should be null in middle of lists");
tailp = nodep;
backp = nodep;
}
UASSERT_OBJ(headp->m_headtailp == tailp, headp, "Tail in headtailp is inconsistent");
UASSERT_OBJ(tailp->m_headtailp == headp, tailp, "Head in headtailp is inconsistent");
}
void AstNode::checkTree() {
if (!debug()) return;
if (this->backp()) {
// Linked tree- check only the passed node
this->checkTreeIter(this->backp());
} else {
this->checkTreeIterList(this->backp());
switch (this->type()) {
#include "V3Ast__gen_op_checks.h"
default: VL_UNREACHABLE; // LCOV_EXCL_LINE
}
}
@@ -1110,7 +1122,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
if (logsp->fail()) v3fatal("Can't write " << filename);
*logsp << "Verilator Tree Dump (format 0x3900) from <e" << std::dec << editCountLast();
*logsp << "> to <e" << std::dec << editCountGbl() << ">\n";
if (editCountGbl() == editCountLast() && !(v3Global.opt.dumpTree() >= 9)) {
if (editCountGbl() == editCountLast() && ::dumpTree() < 9) {
*logsp << '\n';
*logsp << "No changes since last dump!\n";
} else {
@@ -1120,7 +1132,7 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo
}
}
if (doDump && v3Global.opt.debugEmitV()) V3EmitV::debugEmitV(filename + ".v");
if (doCheck && (v3Global.opt.debugCheck() || v3Global.opt.dumpTree())) {
if (doCheck && (v3Global.opt.debugCheck() || ::dumpTree())) {
// Error check
checkTree();
// Broken isn't part of check tree because it can munge iterp's
@@ -1171,7 +1183,11 @@ void AstNode::v3errorEnd(std::ostringstream& str) const {
const_cast<AstNode*>(this)->dump(nsstr);
nsstr << endl;
}
m_fileline->v3errorEnd(nsstr, instanceStr());
// Don't look for instance name when warning is disabled.
// In case of large number of warnings, this can
// take significant amount of time
m_fileline->v3errorEnd(nsstr,
m_fileline->warnIsOff(V3Error::errorCode()) ? "" : instanceStr());
}
}
@@ -1248,3 +1264,8 @@ void VNDeleter::doDeletes() {
for (AstNode* const nodep : m_deleteps) nodep->deleteTree();
m_deleteps.clear();
}
//######################################################################
// VNVisitor
#include "V3Ast__gen_visitor_defns.h" // From ./astgen
+627 -1471
View File
File diff suppressed because it is too large Load Diff
+141 -37
View File
@@ -17,71 +17,175 @@
#ifndef VERILATOR_V3ASTINLINES_H_
#define VERILATOR_V3ASTINLINES_H_
#ifndef VERILATOR_V3ASTNODES_H_
#ifndef VERILATOR_V3AST_H_
#error "Use V3Ast.h as the include"
#include "V3AstNodes.h" // This helps code analysis tools pick up symbols in V3Ast.h and V3AstNodes.h
#include "V3Ast.h" // This helps code analysis tools pick up symbols in V3Ast.h and relaed
#endif
//######################################################################
// Inline ACCESSORS
// Inline METHODS
inline int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
inline int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
inline bool AstNode::width1() const { // V3Const uses to know it can optimize
int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; }
int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; }
bool AstNode::width1() const { // V3Const uses to know it can optimize
return dtypep() && dtypep()->width() == 1;
}
inline int AstNode::widthInstrs() const {
int AstNode::widthInstrs() const {
return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1));
}
inline bool AstNode::isDouble() const {
bool AstNode::isDouble() const {
return dtypep() && VN_IS(dtypep(), BasicDType) && VN_AS(dtypep(), BasicDType)->isDouble();
}
inline bool AstNode::isString() const {
bool AstNode::isString() const {
return dtypep() && dtypep()->basicp() && dtypep()->basicp()->isString();
}
inline bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
bool AstNode::isSigned() const { return dtypep() && dtypep()->isSigned(); }
inline bool AstNode::isZero() const {
bool AstNode::isZero() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqZero());
}
inline bool AstNode::isNeqZero() const {
bool AstNode::isNeqZero() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isNeqZero());
}
inline bool AstNode::isOne() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqOne());
}
inline bool AstNode::isAllOnes() const {
bool AstNode::isOne() const { return (VN_IS(this, Const) && VN_AS(this, Const)->num().isEqOne()); }
bool AstNode::isAllOnes() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->isEqAllOnes());
}
inline bool AstNode::isAllOnesV() const {
bool AstNode::isAllOnesV() const {
return (VN_IS(this, Const) && VN_AS(this, Const)->isEqAllOnesV());
}
inline bool AstNode::sameTree(const AstNode* node2p) const {
bool AstNode::sameTree(const AstNode* node2p) const {
return sameTreeIter(this, node2p, true, false);
}
inline bool AstNode::sameGateTree(const AstNode* node2p) const {
bool AstNode::sameGateTree(const AstNode* node2p) const {
return sameTreeIter(this, node2p, true, true);
}
inline void AstNodeVarRef::varp(AstVar* varp) {
m_varp = varp;
int AstNodeArrayDType::left() const { return rangep()->leftConst(); }
int AstNodeArrayDType::right() const { return rangep()->rightConst(); }
int AstNodeArrayDType::hi() const { return rangep()->hiConst(); }
int AstNodeArrayDType::lo() const { return rangep()->loConst(); }
int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
VNumRange AstNodeArrayDType::declRange() const { return VNumRange{left(), right()}; }
AstRange::AstRange(FileLine* fl, int left, int right)
: ASTGEN_SUPER_Range(fl) {
leftp(new AstConst{fl, static_cast<uint32_t>(left)});
rightp(new AstConst{fl, static_cast<uint32_t>(right)});
}
AstRange::AstRange(FileLine* fl, const VNumRange& range)
: ASTGEN_SUPER_Range(fl) {
leftp(new AstConst{fl, static_cast<uint32_t>(range.left())});
rightp(new AstConst{fl, static_cast<uint32_t>(range.right())});
}
int AstRange::leftConst() const {
AstConst* const constp = VN_CAST(leftp(), Const);
return (constp ? constp->toSInt() : 0);
}
int AstRange::rightConst() const {
AstConst* const constp = VN_CAST(rightp(), Const);
return (constp ? constp->toSInt() : 0);
}
int AstQueueDType::boundConst() const {
AstConst* const constp = VN_CAST(boundp(), Const);
return (constp ? constp->toSInt() : 0);
}
AstPin::AstPin(FileLine* fl, int pinNum, AstVarRef* varname, AstNode* exprp)
: ASTGEN_SUPER_Pin(fl)
, m_pinNum{pinNum}
, m_name{varname->name()} {
this->exprp(exprp);
}
AstDpiExportUpdated::AstDpiExportUpdated(FileLine* fl, AstVarScope* varScopep)
: ASTGEN_SUPER_DpiExportUpdated(fl) {
this->varRefp(new AstVarRef{fl, varScopep, VAccess::WRITE});
}
AstVarScope* AstDpiExportUpdated::varScopep() const { return varRefp()->varScopep(); }
AstPackArrayDType::AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp,
AstRange* rangep)
: ASTGEN_SUPER_PackArrayDType(fl) {
childDTypep(dtp); // Only for parser
refDTypep(nullptr);
this->rangep(rangep);
dtypep(nullptr); // V3Width will resolve
const int width = subDTypep()->width() * rangep->elementsConst();
widthForce(width, width);
}
AstPackArrayDType::AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep)
: ASTGEN_SUPER_PackArrayDType(fl) {
refDTypep(dtp);
this->rangep(rangep);
dtypep(this);
const int width = subDTypep()->width() * rangep->elementsConst();
widthForce(width, width);
}
int AstBasicDType::hi() const { return (rangep() ? rangep()->hiConst() : m.m_nrange.hi()); }
int AstBasicDType::lo() const { return (rangep() ? rangep()->loConst() : m.m_nrange.lo()); }
int AstBasicDType::elements() const {
return (rangep() ? rangep()->elementsConst() : m.m_nrange.elements());
}
bool AstBasicDType::littleEndian() const {
return (rangep() ? rangep()->littleEndian() : m.m_nrange.littleEndian());
}
bool AstActive::hasInitial() const { return m_sensesp->hasInitial(); }
bool AstActive::hasSettle() const { return m_sensesp->hasSettle(); }
bool AstActive::hasClocked() const { return m_sensesp->hasClocked(); }
AstElabDisplay::AstElabDisplay(FileLine* fl, VDisplayType dispType, AstNode* exprsp)
: ASTGEN_SUPER_ElabDisplay(fl) {
addFmtp(new AstSFormatF{fl, AstSFormatF::NoFormat(), exprsp});
m_displayType = dispType;
}
AstCStmt::AstCStmt(FileLine* fl, const string& textStmt)
: ASTGEN_SUPER_CStmt(fl) {
addExprsp(new AstText{fl, textStmt, true});
}
AstCMath::AstCMath(FileLine* fl, const string& textStmt, int setwidth, bool cleanOut)
: ASTGEN_SUPER_CMath(fl)
, m_cleanOut{cleanOut}
, m_pure{true} {
addExprsp(new AstText{fl, textStmt, true});
if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED);
}
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
: ASTGEN_SUPER_VarRef(fl, varp->name(), varp, access) {}
// This form only allowed post-link (see above)
AstVarRef::AstVarRef(FileLine* fl, AstVarScope* varscp, const VAccess& access)
: ASTGEN_SUPER_VarRef(fl, varscp->varp()->name(), varscp->varp(), access) {
varScopep(varscp);
}
bool AstVarRef::same(const AstVarRef* samep) const {
if (varScopep()) {
return (varScopep() == samep->varScopep() && access() == samep->access());
} else {
return (selfPointer() == samep->selfPointer() && varp()->name() == samep->varp()->name()
&& access() == samep->access());
}
}
bool AstVarRef::sameNoLvalue(AstVarRef* samep) const {
if (varScopep()) {
return (varScopep() == samep->varScopep());
} else {
return (selfPointer() == samep->selfPointer()
&& (!selfPointer().empty() || !samep->selfPointer().empty())
&& varp()->name() == samep->varp()->name());
}
}
AstVarXRef::AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access)
: ASTGEN_SUPER_VarXRef(fl, varp->name(), varp, access)
, m_dotted{dotted} {
dtypeFrom(varp);
}
inline bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
inline void AstNodeArrayDType::rangep(AstRange* nodep) { setOp2p(nodep); }
inline int AstNodeArrayDType::left() const { return rangep()->leftConst(); }
inline int AstNodeArrayDType::right() const { return rangep()->rightConst(); }
inline int AstNodeArrayDType::hi() const { return rangep()->hiConst(); }
inline int AstNodeArrayDType::lo() const { return rangep()->loConst(); }
inline int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
inline VNumRange AstNodeArrayDType::declRange() const { return VNumRange{left(), right()}; }
inline void AstIfaceRefDType::cloneRelink() {
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep();
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep();
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep();
}
#endif // Guard
+1256
View File
File diff suppressed because it is too large Load Diff
+4287
View File
File diff suppressed because it is too large Load Diff
+4133
View File
File diff suppressed because it is too large Load Diff
+324 -49
View File
@@ -26,7 +26,7 @@
#include "V3PartitionGraph.h" // Just for mtask dumping
#include "V3String.h"
#include "V3AstNodes__gen_macros.h" // Generated by 'astgen'
#include "V3Ast__gen_macros.h" // Generated by 'astgen'
#include <iomanip>
#include <iterator>
@@ -155,6 +155,51 @@ void AstNodeCond::numberOperate(V3Number& out, const V3Number& lhs, const V3Numb
}
}
void AstBasicDType::init(VBasicDTypeKwd kwd, VSigning numer, int wantwidth, int wantwidthmin,
AstRange* rangep) {
// wantwidth=0 means figure it out, but if a widthmin is >=0
// we allow width 0 so that {{0{x}},y} works properly
// wantwidthmin=-1: default, use wantwidth if it is non zero
m.m_keyword = kwd;
// Implicitness: // "parameter X" is implicit and sized from initial
// value, "parameter reg x" not
if (keyword() == VBasicDTypeKwd::LOGIC_IMPLICIT) {
if (rangep || wantwidth) m.m_keyword = VBasicDTypeKwd::LOGIC;
}
if (numer == VSigning::NOSIGN) {
if (keyword().isSigned()) {
numer = VSigning::SIGNED;
} else if (keyword().isUnsigned()) {
numer = VSigning::UNSIGNED;
}
}
numeric(numer);
if (!rangep && (wantwidth || wantwidthmin >= 0)) { // Constant width
if (wantwidth > 1) m.m_nrange.init(wantwidth - 1, 0, false);
const int wmin = wantwidthmin >= 0 ? wantwidthmin : wantwidth;
widthForce(wantwidth, wmin);
} else if (!rangep) { // Set based on keyword properties
// V3Width will pull from this width
if (keyword().width() > 1 && !isOpaque()) {
m.m_nrange.init(keyword().width() - 1, 0, false);
}
widthForce(keyword().width(), keyword().width());
} else {
widthForce(rangep->elementsConst(),
rangep->elementsConst()); // Maybe unknown if parameters underneath it
}
this->rangep(rangep);
this->dtypep(this);
}
void AstBasicDType::cvtRangeConst() {
if (rangep() && VN_IS(rangep()->leftp(), Const) && VN_IS(rangep()->rightp(), Const)) {
m.m_nrange = VNumRange{rangep()->leftConst(), rangep()->rightConst()};
rangep()->unlinkFrBackWithNext()->deleteTree();
rangep(nullptr);
}
}
int AstBasicDType::widthAlignBytes() const {
if (width() <= 8) {
return 1;
@@ -481,7 +526,7 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
}
} else {
// Newer internal-compatible types
arg += dtypep()->cType((named ? name() : string{}), true, isRef);
arg += dtypep()->cType((named ? name() : std::string{}), true, isRef);
}
return arg;
}
@@ -489,7 +534,7 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
class dpiTypesToStringConverter VL_NOT_FINAL {
public:
virtual string openArray(const AstVar*) const { return "const svOpenArrayHandle"; }
virtual string bitLogicVector(const AstVar* varp, bool isBit) const {
virtual string bitLogicVector(const AstVar* /*varp*/, bool isBit) const {
return isBit ? "svBitVecVal" : "svLogicVecVal";
}
virtual string primitive(const AstVar* varp) const {
@@ -519,11 +564,11 @@ string AstVar::dpiArgType(bool named, bool forReturn) const {
return dpiTypesToStringConverter{}.convert(this);
} else {
class converter final : public dpiTypesToStringConverter {
virtual string bitLogicVector(const AstVar* varp, bool isBit) const override {
string bitLogicVector(const AstVar* varp, bool isBit) const override {
return string(varp->isReadOnly() ? "const " : "")
+ dpiTypesToStringConverter::bitLogicVector(varp, isBit) + '*';
}
virtual string primitive(const AstVar* varp) const override {
string primitive(const AstVar* varp) const override {
string type = dpiTypesToStringConverter::primitive(varp);
if (varp->isWritable() || VN_IS(varp->dtypep()->skipRefp(), UnpackArrayDType)) {
if (!varp->isWritable() && varp->basicp()->keyword() != VBasicDTypeKwd::STRING)
@@ -555,16 +600,16 @@ string AstVar::dpiTmpVarType(const string& varName) const {
return "";
}
}
virtual string openArray(const AstVar* varp) const override {
string openArray(const AstVar* varp) const override {
return dpiTypesToStringConverter::openArray(varp) + ' ' + m_name
+ arraySuffix(varp, 0);
}
virtual string bitLogicVector(const AstVar* varp, bool isBit) const override {
string bitLogicVector(const AstVar* varp, bool isBit) const override {
string type = dpiTypesToStringConverter::bitLogicVector(varp, isBit);
type += ' ' + m_name + arraySuffix(varp, varp->widthWords());
return type;
}
virtual string primitive(const AstVar* varp) const override {
string primitive(const AstVar* varp) const override {
string type = dpiTypesToStringConverter::primitive(varp);
if (varp->isWritable() || VN_IS(varp->dtypep()->skipRefp(), UnpackArrayDType)) {
if (!varp->isWritable() && varp->basicp()->keyword() == VBasicDTypeKwd::CHANDLE)
@@ -615,29 +660,21 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
} else {
return nullptr;
}
} else if (VN_IS(nodep, VarRef)) {
if (VN_AS(nodep, VarRef)->varp()->isSc()) {
return VN_AS(nodep, VarRef)->varp();
} else if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) {
if (vrefp->varp()->isSc()) {
return vrefp->varp();
} else {
return nullptr;
}
} else if (VN_IS(nodep, ArraySel)) {
if (nodep->op1p()) {
if (AstVar* p = scVarRecurse(nodep->op1p())) return p;
}
if (nodep->op2p()) {
if (AstVar* p = scVarRecurse(nodep->op2p())) return p;
}
if (nodep->op3p()) {
if (AstVar* p = scVarRecurse(nodep->op3p())) return p;
}
if (nodep->op4p()) {
if (AstVar* p = scVarRecurse(nodep->op4p())) return p;
}
} else if (AstArraySel* const arraySelp = VN_CAST(nodep, ArraySel)) {
if (AstVar* const p = scVarRecurse(arraySelp->fromp())) return p;
if (AstVar* const p = scVarRecurse(arraySelp->bitp())) return p;
}
return nullptr;
}
bool AstNodeDType::isFourstate() const { return basicp()->isFourstate(); }
class AstNodeDType::CTypeRecursed final {
public:
string m_type; // The base type, e.g.: "Foo_t"s
@@ -659,7 +696,7 @@ public:
}
};
string AstNodeDType::cType(const string& name, bool forFunc, bool isRef) const {
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
const CTypeRecursed info = cTypeRecurse(false);
return info.render(name, isRef);
}
@@ -673,6 +710,9 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true);
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true);
info.m_type = "VlAssocArray<" + key.m_type + ", " + val.m_type + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
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);
info.m_type = "VlQueue<" + sub.m_type + ">";
@@ -1020,8 +1060,13 @@ AstConstPool::AstConstPool(FileLine* fl)
: ASTGEN_SUPER_ConstPool(fl)
, m_modp{new AstModule(fl, "@CONST-POOL@")}
, m_scopep{new AstScope(fl, m_modp, "@CONST-POOL@", nullptr, nullptr)} {
addOp1p(m_modp);
m_modp->addStmtp(m_scopep);
this->modulep(m_modp);
m_modp->addStmtsp(m_scopep);
}
const char* AstConstPool::broken() const {
BROKEN_RTN(m_modp && !m_modp->brokeExists());
BROKEN_RTN(m_scopep && !m_scopep->brokeExists());
return nullptr;
}
AstVarScope* AstConstPool::createNewEntry(const string& name, AstNode* initp) {
@@ -1030,9 +1075,9 @@ AstVarScope* AstConstPool::createNewEntry(const string& name, AstNode* initp) {
varp->isConst(true);
varp->isStatic(true);
varp->valuep(initp->cloneTree(false));
m_modp->addStmtp(varp);
m_modp->addStmtsp(varp);
AstVarScope* const varScopep = new AstVarScope(fl, m_scopep, varp);
m_scopep->addVarp(varScopep);
m_scopep->addVarsp(varScopep);
return varScopep;
}
@@ -1178,7 +1223,7 @@ void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) {
} else if (belowp == condp()) {
// Goes before condition, IE in preconditions
addPrecondsp(newp);
} else if (belowp == bodysp()) {
} else if (belowp == stmtsp()) {
// Was first statement in body, so new front
belowp->addHereThisAsNext(newp);
} else {
@@ -1195,12 +1240,12 @@ void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) {
belowp->addNextHere(newp);
} else if (belowp == condp()) {
// Becomes first statement in body, body may have been empty
if (bodysp()) {
bodysp()->addHereThisAsNext(newp);
if (stmtsp()) {
stmtsp()->addHereThisAsNext(newp);
} else {
addBodysp(newp);
addStmtsp(newp);
}
} else if (belowp == bodysp()) {
} else if (belowp == stmtsp()) {
// Next statement in body
belowp->addNextHere(newp);
} else {
@@ -1221,10 +1266,10 @@ static std::string nodeAddr(const AstNode* nodep) {
}
void AstNode::dump(std::ostream& str) const {
str << typeName() << " "
<< nodeAddr(this)
//<< " " << nodeAddr(m_backp)
str << typeName() << " " << nodeAddr(this)
#ifdef VL_DEBUG
<< " <e" << std::dec << editCount() << ((editCount() >= editCountLast()) ? "#>" : ">")
#endif
<< " {" << fileline()->filenameLetters() << std::dec << fileline()->lastLineno()
<< fileline()->firstColumnLetters() << "}";
if (user1p()) str << " u1=" << nodeAddr(user1p());
@@ -1294,11 +1339,19 @@ void AstCell::dump(std::ostream& str) const {
str << " ->UNLINKED:" << modName();
}
}
const char* AstCell::broken() const {
BROKEN_RTN(m_modp && !m_modp->brokeExists());
return nullptr;
}
void AstCellInline::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " -> " << origModName();
str << " [scopep=" << reinterpret_cast<const void*>(scopep()) << "]";
}
const char* AstCellInline::broken() const {
BROKEN_RTN(m_scopep && !m_scopep->brokeExists());
return nullptr;
}
const char* AstClassPackage::broken() const {
BROKEN_BASE_RTN(AstNodeModule::broken());
BROKEN_RTN(m_classp && !m_classp->brokeExists());
@@ -1323,8 +1376,9 @@ void AstClass::repairCache() {
clearCache();
for (auto* itemp = membersp(); itemp; itemp = itemp->nextp()) {
if (const auto* const scopep = VN_CAST(itemp, Scope)) {
for (auto* itemp = scopep->blocksp(); itemp; itemp = itemp->nextp())
insertCache(itemp);
for (auto* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
insertCache(blockp);
}
} else {
insertCache(itemp);
}
@@ -1342,6 +1396,17 @@ void AstClass::dump(std::ostream& str) const {
if (isExtended()) str << " [EXT]";
if (isVirtual()) str << " [VIRT]";
}
const char* AstClass::broken() const {
BROKEN_BASE_RTN(AstNodeModule::broken());
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
return nullptr;
}
void AstClass::cloneRelink() {
AstNodeModule::cloneRelink();
if (m_classOrPackagep && m_classOrPackagep->clonep()) {
m_classOrPackagep = m_classOrPackagep->clonep();
}
}
AstClass* AstClassExtends::classp() const {
const AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType);
if (VL_UNLIKELY(!refp)) { // LinkDot uses this for 'super.'
@@ -1351,7 +1416,7 @@ AstClass* AstClassExtends::classp() const {
return refp->classp();
}
void AstClassRefDType::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeDType::dump(str);
if (classOrPackagep()) str << " cpkg=" << nodeAddr(classOrPackagep());
if (classp()) {
str << " -> ";
@@ -1364,6 +1429,18 @@ void AstClassRefDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "class:" << name();
}
const char* AstClassRefDType::broken() const {
BROKEN_RTN(m_classp && !m_classp->brokeExists());
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
return nullptr;
}
void AstClassRefDType::cloneRelink() {
if (m_classp && m_classp->clonep()) m_classp = m_classp->clonep();
if (m_classOrPackagep && m_classOrPackagep->clonep()) {
m_classOrPackagep = m_classOrPackagep->clonep();
}
}
string AstClassRefDType::name() const { return classp() ? classp()->name() : "<unlinked>"; }
void AstNodeCoverOrAssert::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (immediate()) str << " [IMMEDIATE]";
@@ -1381,8 +1458,13 @@ void AstEnumItemRef::dump(std::ostream& str) const {
str << "UNLINKED";
}
}
const char* AstEnumItemRef::broken() const {
BROKEN_RTN(m_itemp && !m_itemp->brokeExists());
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
return nullptr;
}
void AstIfaceRefDType::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeDType::dump(str);
if (cellName() != "") str << " cell=" << cellName();
if (ifaceName() != "") str << " if=" << ifaceName();
if (modportName() != "") str << " mp=" << modportName();
@@ -1400,6 +1482,11 @@ void AstIfaceRefDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "iface";
}
void AstIfaceRefDType::cloneRelink() {
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep();
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep();
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep();
}
void AstInitArray::dump(std::ostream& str) const {
this->AstNode::dump(str);
int n = 0;
@@ -1412,6 +1499,42 @@ void AstInitArray::dump(std::ostream& str) const {
str << " [" << itr.first << "]=" << reinterpret_cast<const void*>(itr.second);
}
}
const char* AstInitArray::broken() const {
for (KeyItemMap::const_iterator it = m_map.begin(); it != m_map.end(); ++it) {
BROKEN_RTN(!it->second);
BROKEN_RTN(!it->second->brokeExists());
}
return nullptr;
}
void AstInitArray::cloneRelink() {
for (KeyItemMap::iterator it = m_map.begin(); it != m_map.end(); ++it) {
if (it->second->clonep()) it->second = it->second->clonep();
}
}
void AstInitArray::addIndexValuep(uint64_t index, AstNode* newp) {
const auto it = m_map.find(index);
if (it != m_map.end()) {
it->second->valuep(newp);
} else {
AstInitItem* const itemp = new AstInitItem(fileline(), newp);
m_map.emplace(index, itemp);
addInitsp(itemp);
}
}
AstNode* AstInitArray::getIndexValuep(uint64_t index) const {
const auto it = m_map.find(index);
if (it == m_map.end()) {
return nullptr;
} else {
return it->second->valuep();
}
}
AstNode* AstInitArray::getIndexDefaultedValuep(uint64_t index) const {
AstNode* valuep = getIndexValuep(index);
if (!valuep) valuep = defaultp();
return valuep;
}
void AstJumpGo::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
str << " -> ";
@@ -1421,6 +1544,13 @@ void AstJumpGo::dump(std::ostream& str) const {
str << "%Error:UNLINKED";
}
}
const char* AstJumpGo::broken() const {
BROKEN_RTN(!labelp()->brokeExistsBelow());
return nullptr;
}
void AstJumpGo::cloneRelink() {
if (m_labelp->clonep()) m_labelp = m_labelp->clonep();
}
void AstJumpLabel::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
str << " -> ";
@@ -1431,7 +1561,7 @@ void AstJumpLabel::dump(std::ostream& str) const {
}
}
void AstLogOr::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeMath::dump(str);
if (sideEffect()) str << " [SIDE]";
}
void AstMemberSel::dump(std::ostream& str) const {
@@ -1443,8 +1573,15 @@ void AstMemberSel::dump(std::ostream& str) const {
str << "%Error:UNLINKED";
}
}
void AstMemberSel::cloneRelink() {
if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep();
}
const char* AstMemberSel::broken() const {
BROKEN_RTN(m_varp && !m_varp->brokeExists());
return nullptr;
}
void AstMethodCall::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
this->AstNodeFTaskRef::dump(str);
if (isStatement()) str << " [STMT]";
str << " -> ";
if (taskp()) {
@@ -1464,6 +1601,13 @@ void AstModportFTaskRef::dump(std::ostream& str) const {
str << " -> UNLINKED";
}
}
const char* AstModportFTaskRef::broken() const {
BROKEN_RTN(m_ftaskp && !m_ftaskp->brokeExists());
return nullptr;
}
void AstModportFTaskRef::cloneRelink() {
if (m_ftaskp && m_ftaskp->clonep()) m_ftaskp = m_ftaskp->clonep();
}
void AstModportVarRef::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (direction().isAny()) str << " " << direction();
@@ -1474,6 +1618,13 @@ void AstModportVarRef::dump(std::ostream& str) const {
str << " -> UNLINKED";
}
}
const char* AstModportVarRef::broken() const {
BROKEN_RTN(m_varp && !m_varp->brokeExists());
return nullptr;
}
void AstModportVarRef::cloneRelink() {
if (m_varp && m_varp->clonep()) m_varp = m_varp->clonep();
}
void AstPin::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (modVarp()) {
@@ -1484,6 +1635,17 @@ void AstPin::dump(std::ostream& str) const {
}
if (svImplicit()) str << " [.SV]";
}
const char* AstPin::broken() const {
BROKEN_RTN(m_modVarp && !m_modVarp->brokeExists());
BROKEN_RTN(m_modPTypep && !m_modPTypep->brokeExists());
return nullptr;
}
string AstPin::prettyOperatorName() const {
return modVarp()
? ((modVarp()->direction().isAny() ? modVarp()->direction().prettyName() + " " : "")
+ "port connection " + modVarp()->prettyNameQ())
: "port connection";
}
void AstPrintTimeScale::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
str << " " << timeunit();
@@ -1529,8 +1691,25 @@ void AstRefDType::dump(std::ostream& str) const {
str << " -> UNLINKED";
}
}
const char* AstRefDType::broken() const {
BROKEN_RTN(m_typedefp && !m_typedefp->brokeExists());
BROKEN_RTN(m_refDTypep && !m_refDTypep->brokeExists());
BROKEN_RTN(m_classOrPackagep && !m_classOrPackagep->brokeExists());
return nullptr;
}
void AstRefDType::cloneRelink() {
if (m_typedefp && m_typedefp->clonep()) m_typedefp = m_typedefp->clonep();
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
if (m_classOrPackagep && m_classOrPackagep->clonep()) {
m_classOrPackagep = m_classOrPackagep->clonep();
}
}
AstNodeDType* AstRefDType::subDTypep() const {
if (typedefp()) return typedefp()->subDTypep();
return refDTypep(); // Maybe nullptr
}
void AstNodeUOrStructDType::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeDType::dump(str);
if (packed()) str << " [PACKED]";
if (isFourstate()) str << " [4STATE]";
}
@@ -1599,6 +1778,32 @@ void AstNetlist::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " [" << timeunit() << "/" << timeprecision() << "]";
}
const char* AstNetlist::broken() const {
BROKEN_RTN(m_typeTablep && !m_typeTablep->brokeExists());
BROKEN_RTN(m_constPoolp && !m_constPoolp->brokeExists());
BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists());
BROKEN_RTN(m_evalp && !m_evalp->brokeExists());
BROKEN_RTN(m_dpiExportTriggerp && !m_dpiExportTriggerp->brokeExists());
BROKEN_RTN(m_topScopep && !m_topScopep->brokeExists());
return nullptr;
}
AstPackage* AstNetlist::dollarUnitPkgAddp() {
if (!m_dollarUnitPkgp) {
m_dollarUnitPkgp = new AstPackage(fileline(), AstPackage::dollarUnitName());
// packages are always libraries; don't want to make them a "top"
m_dollarUnitPkgp->inLibrary(true);
m_dollarUnitPkgp->modTrace(false); // may reconsider later
m_dollarUnitPkgp->internal(true);
addModulesp(m_dollarUnitPkgp);
}
return m_dollarUnitPkgp;
}
void AstNetlist::createTopScope(AstScope* scopep) {
UASSERT(scopep, "Must not be nullptr");
UASSERT_OBJ(!m_topScopep, scopep, "TopScope already exits");
m_topScopep = new AstTopScope{scopep->modp()->fileline(), scopep};
scopep->modp()->addStmtsp(v3Global.rootp()->topScopep());
}
void AstNodeModule::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " L" << level();
@@ -1616,12 +1821,26 @@ void AstPackageExport::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " -> " << packagep();
}
const char* AstPackageExport ::broken() const {
BROKEN_RTN(!m_packagep || !m_packagep->brokeExists());
return nullptr;
}
void AstPackageExport::cloneRelink() {
if (m_packagep && m_packagep->clonep()) m_packagep = m_packagep->clonep();
}
void AstPackageImport::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " -> " << packagep();
}
const char* AstPackageImport::broken() const {
BROKEN_RTN(!m_packagep || !m_packagep->brokeExists());
return nullptr;
}
void AstPackageImport::cloneRelink() {
if (m_packagep && m_packagep->clonep()) m_packagep = m_packagep->clonep();
}
void AstPatMember::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeMath::dump(str);
if (isDefault()) str << " [DEFAULT]";
}
void AstNodeTriop::dump(std::ostream& str) const { this->AstNodeMath::dump(str); }
@@ -1646,7 +1865,7 @@ void AstTypeTable::dump(std::ostream& str) const {
for (int i = 0; i < static_cast<int>(VBasicDTypeKwd::_ENUM_MAX); ++i) {
if (AstBasicDType* const subnodep = m_basicps[i]) {
str << '\n'; // Newline from caller, so newline first
str << "\t\t" << std::setw(8) << VBasicDTypeKwd(i).ascii();
str << "\t\t" << std::setw(8) << VBasicDTypeKwd{i}.ascii();
str << " -> ";
subnodep->dump(str);
}
@@ -1683,10 +1902,34 @@ string AstQueueDType::prettyDTypeName() const {
if (boundConst()) str += ":" + cvtToStr(boundConst());
return str + "]";
}
void AstWildcardArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[*]";
}
bool AstWildcardArrayDType::same(const AstNode* samep) const {
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
if (!asamep->subDTypep()) return false;
return (subDTypep() == asamep->subDTypep());
}
bool AstWildcardArrayDType::similarDType(AstNodeDType* samep) const {
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
return type() == samep->type() && asamep->subDTypep()
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
}
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[]";
}
bool AstUnsizedArrayDType::same(const AstNode* samep) const {
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
if (!asamep->subDTypep()) return false;
return (subDTypep() == asamep->subDTypep());
}
bool AstUnsizedArrayDType::similarDType(AstNodeDType* samep) const {
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
return type() == samep->type() && asamep->subDTypep()
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
}
void AstEmptyQueueDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "emptyq";
@@ -1734,6 +1977,9 @@ void AstVarRef::dump(std::ostream& str) const {
str << "UNLINKED";
}
}
bool AstVarRef::same(const AstNode* samep) const {
return same(static_cast<const AstVarRef*>(samep));
}
void AstVar::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isSc()) str << " [SC]";
@@ -1766,7 +2012,7 @@ void AstScope::dump(std::ostream& str) const {
str << " [modp=" << reinterpret_cast<const void*>(modp()) << "]";
}
void AstScopeName::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeMath::dump(str);
if (dpiExport()) str << " [DPIEX]";
if (forFormat()) str << " [FMT]";
}
@@ -1778,6 +2024,10 @@ void AstSenItem::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " [" << edgeType().ascii() << "]";
}
void AstStrengthSpec::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " (" << m_s0.ascii() << ", " << m_s1.ascii() << ")";
}
void AstParseRef::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " [" << expect().ascii() << "]";
@@ -1792,6 +2042,13 @@ void AstClassOrPackageRef::dump(std::ostream& str) const {
str << "UNLINKED";
}
}
AstNodeModule* AstClassOrPackageRef::classOrPackagep() const {
AstNode* foundp = m_classOrPackageNodep;
while (auto* const anodep = VN_CAST(foundp, Typedef)) foundp = anodep->subDTypep();
if (auto* const anodep = VN_CAST(foundp, ClassRefDType)) foundp = anodep->classp();
return VN_CAST(foundp, NodeModule);
}
void AstDot::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (colon()) str << " [::]";
@@ -1805,6 +2062,16 @@ void AstActive::dump(std::ostream& str) const {
str << "UNLINKED";
}
}
const char* AstActive::broken() const {
BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists());
return nullptr;
}
void AstActive::cloneRelink() {
if (m_sensesp->clonep()) {
m_sensesp = m_sensesp->clonep();
UASSERT(m_sensesp, "Bad clone cross link: " << this);
}
}
void AstNodeFTaskRef::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
if (classOrPackagep()) str << " pkg=" << nodeAddr(classOrPackagep());
@@ -1833,7 +2100,7 @@ void AstNodeBlock::dump(std::ostream& str) const {
if (unnamed()) str << " [UNNAMED]";
}
void AstBegin::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeBlock::dump(str);
if (generate()) str << " [GEN]";
if (genforp()) str << " [GENFOR]";
if (implied()) str << " [IMPLIED]";
@@ -1859,7 +2126,7 @@ void AstCoverInc::dump(std::ostream& str) const {
}
}
void AstFork::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeBlock::dump(str);
if (!joinType().join()) str << " [" << joinType() << "]";
}
void AstTraceDecl::dump(std::ostream& str) const {
@@ -1908,6 +2175,14 @@ void AstCFunc::dump(std::ostream& str) const {
if (isDestructor()) str << " [DTOR]";
if (isVirtual()) str << " [VIRT]";
}
const char* AstCFunc::broken() const {
BROKEN_RTN((m_scopep && !m_scopep->brokeExists()));
return nullptr;
}
void AstCFunc::cloneRelink() {
if (m_scopep && m_scopep->clonep()) m_scopep = m_scopep->clonep();
}
void AstCUse::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " [" << useType() << "]";
-9422
View File
File diff suppressed because it is too large Load Diff
+22 -24
View File
@@ -27,25 +27,25 @@
#include <utility>
#include <vector>
template <class T_Node, class T_Data, int T_UserN> class AstUserAllocatorBase VL_NOT_FINAL {
template <class T_Node, class T_Data, int T_UserN>
class AstUserAllocatorBase VL_NOT_FINAL {
static_assert(1 <= T_UserN && T_UserN <= 5, "Wrong user pointer number");
static_assert(std::is_base_of<AstNode, T_Node>::value, "T_Node must be an AstNode type");
private:
std::vector<T_Data*> m_allocated;
inline T_Data* getUserp(const T_Node* nodep) const {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
T_Data* getUserp(const T_Node* nodep) const {
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
const VNUser user = nodep->user1u();
return user.to<T_Data*>();
} else if (T_UserN == 2) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
const VNUser user = nodep->user2u();
return user.to<T_Data*>();
} else if (T_UserN == 3) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
const VNUser user = nodep->user3u();
return user.to<T_Data*>();
} else if (T_UserN == 4) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
const VNUser user = nodep->user4u();
return user.to<T_Data*>();
} else {
@@ -54,31 +54,29 @@ private:
}
}
inline void setUserp(T_Node* nodep, T_Data* userp) const {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
nodep->user1u(VNUser(userp));
} else if (T_UserN == 2) {
nodep->user2u(VNUser(userp));
} else if (T_UserN == 3) {
nodep->user3u(VNUser(userp));
} else if (T_UserN == 4) {
nodep->user4u(VNUser(userp));
void setUserp(T_Node* nodep, T_Data* userp) const {
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
nodep->user1u(VNUser{userp});
} else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
nodep->user2u(VNUser{userp});
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
nodep->user3u(VNUser{userp});
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
nodep->user4u(VNUser{userp});
} else {
nodep->user5u(VNUser(userp));
nodep->user5u(VNUser{userp});
}
}
protected:
AstUserAllocatorBase() {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
VNUser1InUse::check();
} else if (T_UserN == 2) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
VNUser2InUse::check();
} else if (T_UserN == 3) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
VNUser3InUse::check();
} else if (T_UserN == 4) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
VNUser4InUse::check();
} else {
VNUser5InUse::check();
@@ -94,7 +92,7 @@ protected:
public:
// Get a reference to the user data. If does not exist, construct it with given arguments.
template <typename... Args> //
template <typename... Args>
T_Data& operator()(T_Node* nodep, Args&&... args) {
T_Data* userp = getUserp(nodep);
if (!userp) {
+29 -31
View File
@@ -28,12 +28,15 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Begin.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <algorithm>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
class BeginState final {
@@ -69,7 +72,6 @@ private:
int m_ifDepth = 0; // Current if depth
// METHODS
VL_DEBUG_FUNC; // Declare debug()
string dot(const string& a, const string& b) {
if (a == "") return b;
@@ -84,7 +86,7 @@ private:
string::size_type pos;
while ((pos = dottedname.find("__DOT__")) != string::npos) {
const string ident = dottedname.substr(0, pos);
dottedname = dottedname.substr(pos + strlen("__DOT__"));
dottedname = dottedname.substr(pos + std::strlen("__DOT__"));
if (nodep->name() != "") {
m_displayScope = dot(m_displayScope, ident);
m_namedScope = dot(m_namedScope, ident);
@@ -114,19 +116,19 @@ private:
}
} else {
// Move to module
m_modp->addStmtp(nodep);
m_modp->addStmtsp(nodep);
}
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
{
m_modp = nodep;
iterateChildren(nodep);
}
}
virtual void visit(AstNodeFTask* nodep) override {
void visit(AstNodeFTask* nodep) override {
UINFO(8, " " << nodep << endl);
// Rename it
if (m_unnamedScope != "") {
@@ -161,7 +163,7 @@ private:
m_ftaskp = nullptr;
}
}
virtual void visit(AstBegin* nodep) override {
void visit(AstBegin* nodep) override {
// Begin blocks were only useful in variable creation, change names and delete
UINFO(8, " " << nodep << endl);
VL_RESTORER(m_displayScope);
@@ -176,11 +178,7 @@ private:
AstNode* addsp = nullptr;
if (AstNode* const stmtsp = nodep->stmtsp()) {
stmtsp->unlinkFrBackWithNext();
if (addsp) {
addsp = addsp->addNextNull(stmtsp);
} else {
addsp = stmtsp;
}
addsp = AstNode::addNext(addsp, stmtsp);
}
if (addsp) {
nodep->replaceWith(addsp);
@@ -190,7 +188,7 @@ private:
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
}
virtual void visit(AstVar* nodep) override {
void visit(AstVar* nodep) override {
if (m_unnamedScope != "") {
// Rename it
nodep->name(dot(m_unnamedScope, nodep->name()));
@@ -199,7 +197,7 @@ private:
liftNode(nodep);
}
}
virtual void visit(AstTypedef* nodep) override {
void visit(AstTypedef* nodep) override {
if (m_unnamedScope != "") {
// Rename it
nodep->name(dot(m_unnamedScope, nodep->name()));
@@ -208,7 +206,7 @@ private:
liftNode(nodep);
}
}
virtual void visit(AstCell* nodep) override {
void visit(AstCell* nodep) override {
UINFO(8, " CELL " << nodep << endl);
if (m_namedScope != "") {
m_statep->userMarkChanged(nodep);
@@ -217,18 +215,18 @@ private:
UINFO(8, " rename to " << nodep->name() << endl);
// Move to module
nodep->unlinkFrBack();
m_modp->addStmtp(nodep);
m_modp->addStmtsp(nodep);
}
iterateChildren(nodep);
}
virtual void visit(AstVarXRef* nodep) override {
void visit(AstVarXRef* nodep) override {
UINFO(9, " VARXREF " << nodep << endl);
if (m_namedScope != "" && nodep->inlinedDots() == "" && !m_ftaskp) {
nodep->inlinedDots(m_namedScope);
UINFO(9, " rescope to " << nodep << endl);
}
}
virtual void visit(AstScopeName* nodep) override {
void visit(AstScopeName* nodep) override {
// If there's a %m in the display text, we add a special node that will contain the name()
// Similar code in V3Inline
if (nodep->user1SetOnce()) return; // Don't double-add text's
@@ -236,20 +234,20 @@ private:
const string scname = nodep->forFormat() ? m_displayScope : m_namedScope;
if (!scname.empty()) {
// To keep correct visual order, must add before other Text's
AstNode* const afterp = nodep->scopeAttrp();
AstText* const afterp = nodep->scopeAttrp();
if (afterp) afterp->unlinkFrBackWithNext();
nodep->scopeAttrp(new AstText{nodep->fileline(), string("__DOT__") + scname});
if (afterp) nodep->scopeAttrp(afterp);
nodep->addScopeAttrp(new AstText{nodep->fileline(), string("__DOT__") + scname});
if (afterp) nodep->addScopeAttrp(afterp);
}
iterateChildren(nodep);
}
virtual void visit(AstCoverDecl* nodep) override {
void visit(AstCoverDecl* nodep) override {
// Don't need to fix path in coverage statements, they're not under
// any BEGINs, but V3Coverage adds them all under the module itself.
iterateChildren(nodep);
}
// VISITORS - LINT CHECK
virtual void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
// Check IFDEPTH warning - could be in other transform files if desire
VL_RESTORER(m_ifDepth);
if (m_ifDepth == -1 || v3Global.opt.ifDepth() < 1) { // Turned off
@@ -263,7 +261,7 @@ private:
}
iterateChildren(nodep);
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
@@ -271,7 +269,7 @@ public:
: m_statep{statep} {
iterate(nodep);
}
virtual ~BeginVisitor() override = default;
~BeginVisitor() override = default;
};
//######################################################################
@@ -284,21 +282,21 @@ private:
// AstNodeFTask::user1p // Node replaced, rename it
// VISITORS
virtual void visit(AstNodeFTaskRef* nodep) override {
void visit(AstNodeFTaskRef* nodep) override {
if (nodep->taskp()->user1()) { // It was converted
UINFO(9, " relinkFTask " << nodep << endl);
nodep->name(nodep->taskp()->name());
}
iterateChildren(nodep);
}
virtual void visit(AstVarRef* nodep) override {
void visit(AstVarRef* nodep) override {
if (nodep->varp()->user1()) { // It was converted
UINFO(9, " relinVarRef " << nodep << endl);
nodep->name(nodep->varp()->name());
}
iterateChildren(nodep);
}
virtual void visit(AstIfaceRefDType* nodep) override {
void visit(AstIfaceRefDType* nodep) override {
// May have changed cell names
// TypeTable is always after all modules, so names are stable
UINFO(8, " IFACEREFDTYPE " << nodep << endl);
@@ -307,12 +305,12 @@ private:
iterateChildren(nodep);
}
//--------------------
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
BeginRelinkVisitor(AstNetlist* nodep, BeginState*) { iterate(nodep); }
virtual ~BeginRelinkVisitor() override = default;
~BeginRelinkVisitor() override = default;
};
//######################################################################
@@ -325,5 +323,5 @@ void V3Begin::debeginAll(AstNetlist* nodep) {
{ BeginVisitor{nodep, &state}; }
if (state.anyFuncInBegin()) { BeginRelinkVisitor{nodep, &state}; }
} // Destruct before checking
V3Global::dumpCheckGlobalTree("begin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("begin", 0, dumpTree() >= 3);
}
+10 -8
View File
@@ -26,12 +26,15 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Branch.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <map>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Branch state, as a visitor of each AstNode
@@ -48,7 +51,6 @@ private:
std::vector<AstCFunc*> m_cfuncsp; // List of all tasks
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void reset() {
m_likely = false;
@@ -62,14 +64,14 @@ private:
}
// VISITORS
virtual void visit(AstNodeIf* nodep) override {
void visit(AstNodeIf* nodep) override {
UINFO(4, " IF: " << nodep << endl);
VL_RESTORER(m_likely);
VL_RESTORER(m_unlikely);
{
// Do if
reset();
iterateAndNextNull(nodep->ifsp());
iterateAndNextNull(nodep->thensp());
const int ifLikely = m_likely;
const int ifUnlikely = m_unlikely;
// Do else
@@ -86,17 +88,17 @@ private:
} // else leave unknown
}
}
virtual void visit(AstNodeCCall* nodep) override {
void visit(AstNodeCCall* nodep) override {
checkUnlikely(nodep);
nodep->funcp()->user1Inc();
iterateChildren(nodep);
}
virtual void visit(AstCFunc* nodep) override {
void visit(AstCFunc* nodep) override {
checkUnlikely(nodep);
m_cfuncsp.push_back(nodep);
iterateChildren(nodep);
}
virtual void visit(AstNode* nodep) override {
void visit(AstNode* nodep) override {
checkUnlikely(nodep);
iterateChildren(nodep);
}
@@ -115,7 +117,7 @@ public:
iterateChildren(nodep);
calc_tasks();
}
virtual ~BranchVisitor() override = default;
~BranchVisitor() override = default;
};
//######################################################################
+19 -16
View File
@@ -25,9 +25,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Broken.h"
#include "V3Ast.h"
#include "V3Global.h"
// This visitor does not edit nodes, and is called at error-exit, so should use constant iterators
#include "V3AstConstOnly.h"
@@ -35,6 +36,8 @@
#include <unordered_map>
#include <unordered_set>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Generation counter for AstNode::m_brokenState
@@ -48,7 +51,7 @@ static class BrokenCntGlobal {
uint8_t m_count = MIN_VALUE;
public:
uint8_t get() {
uint8_t get() const {
UASSERT(MIN_VALUE <= m_count && m_count <= MAX_VALUE, "Invalid generation number");
return m_count;
}
@@ -131,8 +134,8 @@ private:
public:
// METHODS
void clear() { m_linkable.clear(); }
inline void addLinkable(const AstNode* nodep) { m_linkable.emplace(nodep); }
inline bool isLinkable(const AstNode* nodep) const { return m_linkable.count(nodep) != 0; }
void addLinkable(const AstNode* nodep) { m_linkable.emplace(nodep); }
bool isLinkable(const AstNode* nodep) const { return m_linkable.count(nodep) != 0; }
} s_linkableTable;
bool V3Broken::isLinkable(const AstNode* nodep) { return s_linkableTable.isLinkable(nodep); }
@@ -213,27 +216,27 @@ private:
}
return false;
}
virtual void visit(AstNodeAssign* nodep) override {
void visit(AstNodeAssign* nodep) override {
processAndIterate(nodep);
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && nodep->brokeLhsMustBeLvalue()
&& VN_IS(nodep->lhsp(), NodeVarRef)
&& !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
nodep, "Assignment LHS is not an lvalue");
}
virtual void visit(AstRelease* nodep) override {
void visit(AstRelease* nodep) override {
processAndIterate(nodep);
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && VN_IS(nodep->lhsp(), NodeVarRef)
&& !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
nodep, "Release LHS is not an lvalue");
}
virtual void visit(AstScope* nodep) override {
void visit(AstScope* nodep) override {
VL_RESTORER(m_inScope);
{
m_inScope = true;
processAndIterate(nodep);
}
}
virtual void visit(AstNodeVarRef* nodep) override {
void visit(AstNodeVarRef* nodep) override {
processAndIterate(nodep);
// m_inScope because some Vars have initial variable references without scopes
// This might false fire with some debug flags, as not certain we don't have temporary
@@ -251,7 +254,7 @@ private:
}
}
}
virtual void visit(AstCFunc* nodep) override {
void visit(AstCFunc* nodep) override {
UASSERT_OBJ(!m_cfuncp, nodep, "Nested AstCFunc");
m_cfuncp = nodep;
m_localVars.clear();
@@ -269,14 +272,14 @@ private:
m_cfuncp = nullptr;
}
virtual void visit(AstNodeIf* nodep) override {
void visit(AstNodeIf* nodep) override {
// Each branch is a separate local variable scope
pushLocalScope();
processEnter(nodep);
processAndIterate(nodep->condp());
if (AstNode* const ifsp = nodep->ifsp()) {
if (AstNode* const thensp = nodep->thensp()) {
pushLocalScope();
processAndIterateList(ifsp);
processAndIterateList(thensp);
popLocalScope();
}
if (AstNode* const elsesp = nodep->elsesp()) {
@@ -287,21 +290,21 @@ private:
processExit(nodep);
popLocalScope();
}
virtual void visit(AstNodeStmt* nodep) override {
void visit(AstNodeStmt* nodep) override {
// For local variable checking act as if any statement introduces a new scope.
// This is aggressive but conservatively correct.
pushLocalScope();
processAndIterate(nodep);
popLocalScope();
}
virtual void visit(AstVar* nodep) override {
void visit(AstVar* nodep) override {
processAndIterate(nodep);
if (m_cfuncp) {
m_localVars.insert(nodep);
m_localsStack.back().insert(nodep);
}
}
virtual void visit(AstNode* nodep) override {
void visit(AstNode* nodep) override {
// Process not just iterate
processAndIterate(nodep);
}
@@ -309,7 +312,7 @@ private:
public:
// CONSTRUCTORS
explicit BrokenCheckVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~BrokenCheckVisitor() override = default;
~BrokenCheckVisitor() override = default;
};
//######################################################################
+9 -6
View File
@@ -27,13 +27,16 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitCBase.h"
#include "V3CCtors.h"
#include "V3EmitCBase.h"
#include "V3Global.h"
#include <algorithm>
#include <list>
VL_DEFINE_DEBUG_FUNCTIONS;
class VCtorType final {
public:
enum en : uint8_t { MODULE, CLASS, COVERAGE };
@@ -43,7 +46,7 @@ private:
public:
// cppcheck-suppress noExplicitConstructor
inline VCtorType(en _e)
constexpr VCtorType(en _e)
: m_e{_e} {}
bool isClass() const { return m_e == CLASS; }
bool isCoverage() const { return m_e == COVERAGE; }
@@ -76,7 +79,7 @@ private:
if (!preventUnusedStmt.empty()) {
funcp->addStmtsp(new AstCStmt{m_modp->fileline(), preventUnusedStmt});
}
m_modp->addStmtp(funcp);
m_modp->addStmtsp(funcp);
m_numStmts = 0;
return funcp;
}
@@ -135,7 +138,7 @@ void V3CCtors::evalAsserts() {
funcp->isLoose(true);
funcp->slow(false);
funcp->ifdef("VL_DEBUG");
modp->addStmtp(funcp);
modp->addStmtsp(funcp);
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
if (AstVar* const varp = VN_CAST(np, Var)) {
if (varp->isPrimaryInish() && !varp->isSc()) {
@@ -208,7 +211,7 @@ void V3CCtors::cctorsAll() {
// If can be referred to by base pointer, need virtual delete
funcp->isVirtual(classp->isExtended());
funcp->slow(false);
modp->addStmtp(funcp);
modp->addStmtsp(funcp);
}
}
}
+11 -8
View File
@@ -25,12 +25,15 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3CUse.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <set>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Visit within a module all nodes and data types they reference, finding
@@ -50,13 +53,13 @@ class CUseVisitor final : public VNVisitor {
void addNewUse(AstNode* nodep, VUseType useType, const string& name) {
if (m_didUse.emplace(useType, name).second) {
AstCUse* const newp = new AstCUse{nodep->fileline(), useType, name};
m_modp->addStmtp(newp);
m_modp->addStmtsp(newp);
UINFO(8, "Insert " << newp << endl);
}
}
// VISITORS
virtual void visit(AstClassRefDType* nodep) override {
void visit(AstClassRefDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
if (!m_impOnly) addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
// Need to include extends() when we implement, but no need for pointers to know
@@ -66,17 +69,17 @@ class CUseVisitor final : public VNVisitor {
iterateChildren(nodep->classp()); // This also gets all extend classes
}
}
virtual void visit(AstNodeDType* nodep) override {
void visit(AstNodeDType* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
}
virtual void visit(AstNode* nodep) override {
void visit(AstNode* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
if (nodep->dtypep() && !nodep->dtypep()->user1()) iterate(nodep->dtypep());
iterateChildren(nodep);
}
virtual void visit(AstCell* nodep) override {
void visit(AstCell* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
// Currently no IMP_INCLUDE because we include __Syms which has them all
addNewUse(nodep, VUseType::INT_FWD_CLASS, nodep->modp()->name());
@@ -89,7 +92,7 @@ public:
: m_modp(modp) {
iterate(modp);
}
virtual ~CUseVisitor() override = default;
~CUseVisitor() override = default;
VL_UNCOPYABLE(CUseVisitor);
};
@@ -105,5 +108,5 @@ void V3CUse::cUseAll() {
// for each output file and put under that
CUseVisitor{modp};
}
V3Global::dumpCheckGlobalTree("cuse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("cuse", 0, dumpTree() >= 3);
}
+17 -16
View File
@@ -37,13 +37,16 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Case.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3Stats.h"
#include <algorithm>
VL_DEFINE_DEBUG_FUNCTIONS;
#define CASE_OVERLAP_WIDTH 16 // Maximum width we can check for overlaps in
#define CASE_BARF 999999 // Magic width when non-constant
#define CASE_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree
@@ -56,9 +59,8 @@ private:
= nullptr; // Under a CASE value node, if so the relevant case statement
// METHODS
VL_DEBUG_FUNC; // Declare debug()
virtual void visit(AstNodeCase* nodep) override {
void visit(AstNodeCase* nodep) override {
if (VN_IS(nodep, Case) && VN_AS(nodep, Case)->casex()) {
nodep->v3warn(CASEX, "Suggest casez (with ?'s) in place of casex (with X's)");
}
@@ -85,7 +87,7 @@ private:
m_caseExprp = nullptr;
}
}
virtual void visit(AstConst* nodep) override {
void visit(AstConst* nodep) override {
// See also neverItem
if (m_caseExprp && nodep->num().isFourState()) {
if (VN_IS(m_caseExprp, GenCase)) {
@@ -106,12 +108,12 @@ private:
}
}
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit CaseLintVisitor(AstNodeCase* nodep) { iterate(nodep); }
virtual ~CaseLintVisitor() override = default;
~CaseLintVisitor() override = default;
};
//######################################################################
@@ -137,7 +139,6 @@ private:
std::array<AstNode*, 1 << CASE_OVERLAP_WIDTH> m_valueItem;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
bool isCaseTreeFast(AstCase* nodep) {
int width = 0;
@@ -244,7 +245,7 @@ private:
// Convert valueItem from AstCaseItem* to the expression
// Not done earlier, as we may now have a nullptr because it's just a ";" NOP branch
for (uint32_t i = 0; i < numCases; ++i) {
m_valueItem[i] = VN_AS(m_valueItem[i], CaseItem)->bodysp();
m_valueItem[i] = VN_AS(m_valueItem[i], CaseItem)->stmtsp();
}
return true; // All is fine
}
@@ -345,7 +346,7 @@ private:
itemp = VN_AS(itemp->nextp(), CaseItem)) {
if (!itemp->condsp()) {
// Default clause. Just make true, we'll optimize it away later
itemp->condsp(new AstConst(itemp->fileline(), AstConst::BitTrue()));
itemp->addCondsp(new AstConst(itemp->fileline(), AstConst::BitTrue()));
hadDefault = true;
} else {
// Expressioned clause
@@ -396,7 +397,7 @@ private:
}
}
// Replace expression in tree
itemp->condsp(ifexprp);
itemp->addCondsp(ifexprp);
}
}
VL_DO_DANGLING(cexprp->deleteTree(), cexprp);
@@ -419,7 +420,7 @@ private:
AstIf* itemnextp = nullptr;
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), CaseItem)) {
AstNode* const istmtsp = itemp->bodysp(); // Maybe null -- no action.
AstNode* const istmtsp = itemp->stmtsp(); // Maybe null -- no action.
if (istmtsp) istmtsp->unlinkFrBackWithNext();
// Expressioned clause
AstNode* const ifexprp = itemp->condsp()->unlinkFrBack();
@@ -451,7 +452,7 @@ private:
if (itemnextp) {
itemnextp->addElsesp(newp);
} else {
groupnextp->addIfsp(newp); // First in a new group
groupnextp->addThensp(newp); // First in a new group
}
itemnextp = newp;
}
@@ -492,7 +493,7 @@ private:
}
// VISITORS
virtual void visit(AstCase* nodep) override {
void visit(AstCase* nodep) override {
V3Case::caseLint(nodep);
iterateChildren(nodep);
if (debug() >= 9) nodep->dumpTree(cout, " case_old: ");
@@ -509,7 +510,7 @@ private:
}
}
//--------------------
virtual void visit(AstNode* nodep) override {
void visit(AstNode* nodep) override {
if (VN_IS(nodep, Always)) m_alwaysp = nodep;
iterateChildren(nodep);
}
@@ -520,7 +521,7 @@ public:
for (auto& itr : m_valueItem) itr = nullptr;
iterate(nodep);
}
virtual ~CaseVisitor() override {
~CaseVisitor() override {
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
V3Stats::addStat("Optimizations, Cases complex", m_statCaseSlow);
}
@@ -532,7 +533,7 @@ public:
void V3Case::caseAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CaseVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("case", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("case", 0, dumpTree() >= 3);
}
void V3Case::caseLint(AstNodeCase* nodep) {
UINFO(4, __FUNCTION__ << ": " << endl);
+19 -17
View File
@@ -40,12 +40,15 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Cast.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <algorithm>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Cast state, as a visitor of each AstNode
@@ -59,7 +62,6 @@ private:
// STATE
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node
UINFO(4, " NeedCast " << nodep << endl);
@@ -110,25 +112,25 @@ private:
}
// VISITORS
virtual void visit(AstNodeUniop* nodep) override {
void visit(AstNodeUniop* nodep) override {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1());
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
}
virtual void visit(AstNodeBiop* nodep) override {
void visit(AstNodeBiop* nodep) override {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1());
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
}
virtual void visit(AstNodeTriop* nodep) override {
void visit(AstNodeTriop* nodep) override {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1());
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
if (nodep->sizeMattersThs()) ensureCast(nodep->thsp());
}
virtual void visit(AstNodeQuadop* nodep) override {
void visit(AstNodeQuadop* nodep) override {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1()
| nodep->fhsp()->user1());
@@ -137,12 +139,12 @@ private:
if (nodep->sizeMattersThs()) ensureCast(nodep->thsp());
if (nodep->sizeMattersFhs()) ensureCast(nodep->fhsp());
}
virtual void visit(AstCCast* nodep) override {
void visit(AstCCast* nodep) override {
iterateChildren(nodep);
ensureLower32Cast(nodep);
nodep->user1(1);
}
virtual void visit(AstNegate* nodep) override {
void visit(AstNegate* nodep) override {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1());
if (nodep->lhsp()->widthMin() == 1) {
@@ -154,7 +156,7 @@ private:
ensureCast(nodep->lhsp());
}
}
virtual void visit(AstVarRef* nodep) override {
void visit(AstVarRef* nodep) override {
const AstNode* const backp = nodep->backp();
if (nodep->access().isReadOnly() && !VN_IS(backp, CCast) && VN_IS(backp, NodeMath)
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, RedXor) && backp->width()
@@ -165,7 +167,7 @@ private:
}
nodep->user1(1);
}
virtual void visit(AstConst* nodep) override {
void visit(AstConst* nodep) override {
// Constants are of unknown size if smaller than 33 bits, because
// we're too lazy to wrap every constant in the universe in
// ((IData)#).
@@ -173,29 +175,29 @@ private:
}
// Null dereference protection
virtual void visit(AstNullCheck* nodep) override {
void visit(AstNullCheck* nodep) override {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1());
}
virtual void visit(AstCMethodCall* nodep) override {
void visit(AstCMethodCall* nodep) override {
iterateChildren(nodep);
ensureNullChecked(nodep->fromp());
}
virtual void visit(AstMemberSel* nodep) override {
void visit(AstMemberSel* nodep) override {
iterateChildren(nodep);
ensureNullChecked(nodep->fromp());
}
// NOPs
virtual void visit(AstVar*) override {}
void visit(AstVar*) override {}
//--------------------
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit CastVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~CastVisitor() override = default;
~CastVisitor() override = default;
};
//######################################################################
@@ -204,5 +206,5 @@ public:
void V3Cast::castAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CastVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("cast", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("cast", 0, dumpTree() >= 3);
}
+47 -49
View File
@@ -24,26 +24,28 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Cdc.h"
#include "V3Ast.h"
#include "V3Graph.h"
#include "V3Const.h"
#include "V3EmitV.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3Graph.h"
#include <algorithm>
#include <deque>
#include <iomanip>
#include <memory>
VL_DEFINE_DEBUG_FUNCTIONS;
constexpr int CDC_WEIGHT_ASYNC = 0x1000; // Weight for edges that feed async logic
//######################################################################
class CdcBaseVisitor VL_NOT_FINAL : public VNVisitor {
public:
VL_DEBUG_FUNC; // Declare debug()
};
//######################################################################
@@ -66,9 +68,9 @@ public:
, m_srcDomainSet{false}
, m_dstDomainSet{false}
, m_asyncPath{false} {}
virtual ~CdcEitherVertex() override = default;
~CdcEitherVertex() override = default;
// ACCESSORS
virtual FileLine* fileline() const override { return nodep()->fileline(); }
FileLine* fileline() const override { return nodep()->fileline(); }
AstScope* scopep() const { return m_scopep; }
AstNode* nodep() const { return m_nodep; }
AstSenTree* srcDomainp() const { return m_srcDomainp; }
@@ -92,11 +94,11 @@ public:
CdcVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
: CdcEitherVertex{graphp, scopep, varScp}
, m_varScp{varScp} {}
virtual ~CdcVarVertex() override = default;
~CdcVarVertex() override = default;
// ACCESSORS
AstVarScope* varScp() const { return m_varScp; }
virtual string name() const override { return (cvtToHex(m_varScp) + " " + varScp()->name()); }
virtual string dotColor() const override {
string name() const override { return (cvtToHex(m_varScp) + " " + varScp()->name()); }
string dotColor() const override {
return fromFlop() ? "green" : cntAsyncRst() ? "red" : "blue";
}
int cntAsyncRst() const { return m_cntAsyncRst; }
@@ -117,12 +119,10 @@ public:
srcDomainp(sensenodep);
dstDomainp(sensenodep);
}
virtual ~CdcLogicVertex() override = default;
~CdcLogicVertex() override = default;
// ACCESSORS
virtual string name() const override {
return (cvtToHex(nodep()) + "@" + scopep()->prettyName());
}
virtual string dotColor() const override { return hazard() ? "black" : "yellow"; }
string name() const override { return (cvtToHex(nodep()) + "@" + scopep()->prettyName()); }
string dotColor() const override { return hazard() ? "black" : "yellow"; }
bool hazard() const { return m_hazard; }
void setHazard(AstNode* nodep) {
m_hazard = true;
@@ -143,7 +143,7 @@ private:
std::ofstream* const m_ofp = nullptr; // Output file
string m_prefix;
virtual void visit(AstNode* nodep) override {
void visit(AstNode* nodep) override {
*m_ofp << m_prefix;
if (nodep->user3()) {
*m_ofp << " %%";
@@ -170,7 +170,7 @@ public:
, m_prefix{prefix} {
iterate(nodep);
}
virtual ~CdcDumpVisitor() override = default;
~CdcDumpVisitor() override = default;
};
//######################################################################
@@ -180,7 +180,7 @@ private:
int m_maxLineno = 0;
size_t m_maxFilenameLen = 0;
virtual void visit(AstNode* nodep) override {
void visit(AstNode* nodep) override {
iterateChildren(nodep);
// Keeping line+filename lengths separate is much faster than calling ascii().length()
if (nodep->fileline()->lineno() >= m_maxLineno) {
@@ -194,7 +194,7 @@ private:
public:
// CONSTRUCTORS
explicit CdcWidthVisitor(AstNode* nodep) { iterate(nodep); }
virtual ~CdcWidthVisitor() override = default;
~CdcWidthVisitor() override = default;
// ACCESSORS
int maxWidth() const {
size_t width = 1;
@@ -324,13 +324,11 @@ private:
void analyze() {
UINFO(3, __FUNCTION__ << ": " << endl);
// if (debug() > 6) m_graph.dump();
if (debug() > 6) m_graph.dumpDotFilePrefixed("cdc_pre");
if (dumpGraph() > 6) m_graph.dumpDotFilePrefixed("cdc_pre");
// This will MAX across edge weights
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
//
m_graph.removeRedundantEdges(
&V3GraphEdge::followAlwaysTrue); // This will MAX across edge weights
//
m_graph.dumpDotFilePrefixed("cdc_simp");
if (dumpGraph() >= 3) m_graph.dumpDotFilePrefixed("cdc_simp");
//
analyzeReset();
}
@@ -627,21 +625,21 @@ private:
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
{
m_modp = nodep;
iterateChildren(nodep);
}
}
virtual void visit(AstScope* nodep) override {
void visit(AstScope* nodep) override {
UINFO(4, " SCOPE " << nodep << endl);
m_scopep = nodep;
m_logicVertexp = nullptr;
iterateChildren(nodep);
m_scopep = nullptr;
}
virtual void visit(AstActive* nodep) override {
void visit(AstActive* nodep) override {
// Create required blocks and add to module
UINFO(4, " BLOCK " << nodep << endl);
AstNode::user2ClearTree();
@@ -653,7 +651,7 @@ private:
m_domainp = nullptr;
AstNode::user2ClearTree();
}
virtual void visit(AstNodeVarRef* nodep) override {
void visit(AstNodeVarRef* nodep) override {
if (m_scopep) {
UASSERT_OBJ(m_logicVertexp, nodep, "Var ref not under a logic block");
AstVarScope* const varscp = nodep->varScopep();
@@ -681,53 +679,53 @@ private:
}
}
}
virtual void visit(AstAssignDly* nodep) override {
void visit(AstAssignDly* nodep) override {
m_inDly = true;
iterateChildren(nodep);
m_inDly = false;
}
virtual void visit(AstSenItem* nodep) override {
void visit(AstSenItem* nodep) override {
m_inSenItem = true;
iterateChildren(nodep);
m_inSenItem = false;
}
virtual void visit(AstAlways* nodep) override { iterateNewStmt(nodep); }
virtual void visit(AstAlwaysPublic* nodep) override {
void visit(AstAlways* nodep) override { iterateNewStmt(nodep); }
void visit(AstAlwaysPublic* nodep) override {
// CDC doesn't care about public variables
}
virtual void visit(AstCFunc* nodep) override { iterateNewStmt(nodep); }
virtual void visit(AstAssignAlias* nodep) override { iterateNewStmt(nodep); }
virtual void visit(AstAssignW* nodep) override { iterateNewStmt(nodep); }
void visit(AstCFunc* nodep) override { iterateNewStmt(nodep); }
void visit(AstAssignAlias* nodep) override { iterateNewStmt(nodep); }
void visit(AstAssignW* nodep) override { iterateNewStmt(nodep); }
// Math that shouldn't cause us to clear hazard
virtual void visit(AstConst*) override {}
virtual void visit(AstReplicate* nodep) override { iterateChildren(nodep); }
virtual void visit(AstConcat* nodep) override { iterateChildren(nodep); }
virtual void visit(AstNot* nodep) override { iterateChildren(nodep); }
virtual void visit(AstSel* nodep) override {
void visit(AstConst*) override {}
void visit(AstReplicate* nodep) override { iterateChildren(nodep); }
void visit(AstConcat* nodep) override { iterateChildren(nodep); }
void visit(AstNot* nodep) override { iterateChildren(nodep); }
void visit(AstSel* nodep) override {
if (!VN_IS(nodep->lsbp(), Const)) setNodeHazard(nodep);
iterateChildren(nodep);
}
virtual void visit(AstNodeSel* nodep) override {
void visit(AstNodeSel* nodep) override {
if (!VN_IS(nodep->bitp(), Const)) setNodeHazard(nodep);
iterateChildren(nodep);
}
// Ignores
virtual void visit(AstInitial*) override {}
virtual void visit(AstInitialAutomatic*) override {}
virtual void visit(AstInitialStatic*) override {}
virtual void visit(AstTraceDecl*) override {}
virtual void visit(AstCoverToggle*) override {}
virtual void visit(AstNodeDType*) override {}
void visit(AstInitial*) override {}
void visit(AstInitialAutomatic*) override {}
void visit(AstInitialStatic*) override {}
void visit(AstTraceDecl*) override {}
void visit(AstCoverToggle*) override {}
void visit(AstNodeDType*) override {}
//--------------------
// Default
virtual void visit(AstNodeMath* nodep) override {
void visit(AstNodeMath* nodep) override {
setNodeHazard(nodep);
iterateChildren(nodep);
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
@@ -753,7 +751,7 @@ public:
*m_ofp << '\n';
}
}
virtual ~CdcVisitor() override {
~CdcVisitor() override {
if (m_ofp) VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
}
};
+16 -13
View File
@@ -29,12 +29,15 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Ast.h"
#include "V3Changed.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <algorithm>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
class ChangedState final {
@@ -66,7 +69,7 @@ public:
m_tlChgFuncp->isStatic(false);
m_tlChgFuncp->isLoose(true);
m_tlChgFuncp->declPrivate(true);
m_scopetopp->addActivep(m_tlChgFuncp);
m_scopetopp->addBlocksp(m_tlChgFuncp);
// Each change detection function needs at least one AstChangeDet
// to ensure that V3EmitC outputs the necessary code.
maybeCreateMidChg();
@@ -85,7 +88,7 @@ public:
m_chgFuncp->isStatic(false);
m_chgFuncp->isLoose(true);
m_chgFuncp->declPrivate(true);
m_scopetopp->addActivep(m_chgFuncp);
m_scopetopp->addBlocksp(m_chgFuncp);
// Add a top call to it
AstCCall* const callp = new AstCCall{m_scopetopp->fileline(), m_chgFuncp};
@@ -154,13 +157,13 @@ private:
m_state.m_numStmts += initp->nodeCount() + m_varEqnp->widthWords();
}
virtual void visit(AstBasicDType*) override { //
void visit(AstBasicDType*) override { //
newChangeDet();
}
virtual void visit(AstPackArrayDType*) override { //
void visit(AstPackArrayDType*) override { //
newChangeDet();
}
virtual void visit(AstUnpackArrayDType* nodep) override {
void visit(AstUnpackArrayDType* nodep) override {
for (int index = 0; index < nodep->elementsConst(); ++index) {
VL_RESTORER(m_varEqnp);
VL_RESTORER(m_newLvEqnp);
@@ -180,7 +183,7 @@ private:
}
}
}
virtual void visit(AstNodeUOrStructDType* nodep) override {
void visit(AstNodeUOrStructDType* nodep) override {
if (nodep->packedUnsup()) {
newChangeDet();
} else {
@@ -190,7 +193,7 @@ private:
<< m_vscp->varp()->prettyNameQ());
}
}
virtual void visit(AstNode* nodep) override {
void visit(AstNode* nodep) override {
iterateChildren(nodep);
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-general-");
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
@@ -217,9 +220,9 @@ public:
// CHANGEDET(VARREF(_last), VARREF(var))
AstVar* const newvarp
= new AstVar{varp->fileline(), VVarType::MODULETEMP, newvarname, varp};
m_state.m_topModp->addStmtp(newvarp);
m_state.m_topModp->addStmtsp(newvarp);
m_newvscp = new AstVarScope{m_vscp->fileline(), m_state.m_scopetopp, newvarp};
m_state.m_scopetopp->addVarp(m_newvscp);
m_state.m_scopetopp->addVarsp(m_newvscp);
m_varEqnp = new AstVarRef{m_vscp->fileline(), m_vscp, VAccess::READ};
m_newLvEqnp = new AstVarRef{m_vscp->fileline(), m_newvscp, VAccess::WRITE};
@@ -230,7 +233,7 @@ public:
m_newLvEqnp->deleteTree();
m_newRvEqnp->deleteTree();
}
virtual ~ChangedInsertVisitor() override = default;
~ChangedInsertVisitor() override = default;
VL_UNCOPYABLE(ChangedInsertVisitor);
};
@@ -251,5 +254,5 @@ void V3Changed::changedAll(AstNetlist* nodep) {
}
});
V3Global::dumpCheckGlobalTree("changed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("changed", 0, dumpTree() >= 3);
}
+35 -29
View File
@@ -23,9 +23,12 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Class.h"
#include "V3Ast.h"
#include "V3Global.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
@@ -46,14 +49,13 @@ private:
std::vector<std::pair<AstNode*, AstNodeModule*>> m_toPackageMoves;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
virtual void visit(AstClass* nodep) override {
void visit(AstClass* nodep) override {
if (nodep->user1SetOnce()) return;
// Move this class
nodep->name(m_prefix + nodep->name());
nodep->unlinkFrBack();
v3Global.rootp()->addModulep(nodep);
v3Global.rootp()->addModulesp(nodep);
// Make containing package
// Note origName is the same as the class origName so errors look correct
AstClassPackage* const packagep
@@ -61,7 +63,7 @@ private:
packagep->name(nodep->name() + "__Vclpkg");
nodep->classOrPackagep(packagep);
packagep->classp(nodep);
v3Global.rootp()->addModulep(packagep);
v3Global.rootp()->addModulesp(packagep);
// Add package to hierarchy
AstCell* const cellp = new AstCell{packagep->fileline(),
packagep->fileline(),
@@ -71,7 +73,7 @@ private:
nullptr,
nullptr};
cellp->modp(packagep);
v3Global.rootp()->topModulep()->addStmtp(cellp);
v3Global.rootp()->topModulep()->addStmtsp(cellp);
// Find class's scope
// Alternative would be to move this and related to V3Scope
const AstScope* classScopep = nullptr;
@@ -84,7 +86,7 @@ private:
AstScope* const scopep
= new AstScope{nodep->fileline(), packagep, classScopep->name(),
classScopep->aboveScopep(), classScopep->aboveCellp()};
packagep->addStmtp(scopep);
packagep->addStmtsp(scopep);
// Iterate
VL_RESTORER(m_prefix);
VL_RESTORER(m_classPackagep);
@@ -99,7 +101,7 @@ private:
}
nodep->repairCache();
}
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
VL_RESTORER(m_prefix);
{
@@ -108,40 +110,40 @@ private:
}
}
virtual void visit(AstVar* nodep) override {
void visit(AstVar* nodep) override {
iterateChildren(nodep);
if (m_packageScopep) {
if (m_ftaskp && m_ftaskp->lifetime().isStatic()) {
// Move later, or we wouldn't keep interating the class
// We're really moving the VarScope but we might not
// have a pointer to it yet
m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep));
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
}
if (!m_ftaskp && nodep->lifetime().isStatic()) {
m_toPackageMoves.push_back(std::make_pair(nodep, m_classPackagep));
m_toPackageMoves.emplace_back(std::make_pair(nodep, m_classPackagep));
// We're really moving the VarScope but we might not
// have a pointer to it yet
m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep));
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
}
}
}
virtual void visit(AstVarScope* nodep) override {
void visit(AstVarScope* nodep) override {
iterateChildren(nodep);
nodep->varp()->user1p(nodep);
}
virtual void visit(AstNodeFTask* nodep) override {
void visit(AstNodeFTask* nodep) override {
VL_RESTORER(m_ftaskp);
{
m_ftaskp = nodep;
iterateChildren(nodep);
if (m_packageScopep && nodep->lifetime().isStatic()) {
m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep));
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
}
}
}
virtual void visit(AstCFunc* nodep) override {
void visit(AstCFunc* nodep) override {
iterateChildren(nodep);
// Don't move now, or wouldn't keep interating the class
// TODO move function statics only
@@ -149,39 +151,43 @@ private:
// m_toScopeMoves.push_back(std::make_pair(nodep, m_classScopep));
//}
}
virtual void visit(AstInitial* nodep) override {
void visit(AstInitial* nodep) override {
// But not AstInitialAutomatic, which remains under the class
iterateChildren(nodep);
if (m_packageScopep) { m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep)); }
if (m_packageScopep) {
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
}
}
virtual void visit(AstInitialStatic* nodep) override {
void visit(AstInitialStatic* nodep) override {
// But not AstInitialAutomatic, which remains under the class
iterateChildren(nodep);
if (m_packageScopep) { m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep)); }
if (m_packageScopep) {
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
}
}
virtual void visit(AstNodeMath* nodep) override {} // Short circuit
virtual void visit(AstNodeStmt* nodep) override {} // Short circuit
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNodeMath* nodep) override {} // Short circuit
void visit(AstNodeStmt* nodep) override {} // Short circuit
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit ClassVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~ClassVisitor() override {
~ClassVisitor() override {
for (auto moved : m_toScopeMoves) {
AstNode* const nodep = moved.first;
AstScope* const scopep = moved.second;
UINFO(9, "moving " << nodep << " to " << scopep << endl);
if (VN_IS(nodep, NodeFTask)) {
scopep->addActivep(nodep->unlinkFrBack());
scopep->addBlocksp(nodep->unlinkFrBack());
} else if (VN_IS(nodep, Var)) {
AstVarScope* const vscp = VN_AS(nodep->user1p(), VarScope);
vscp->scopep(scopep);
vscp->unlinkFrBack();
scopep->addVarp(vscp);
scopep->addVarsp(vscp);
} else if (VN_IS(nodep, Initial) || VN_IS(nodep, InitialStatic)) {
nodep->unlinkFrBack();
scopep->addActivep(nodep);
scopep->addBlocksp(nodep);
} else {
nodep->v3fatalSrc("Bad case");
}
@@ -191,7 +197,7 @@ public:
AstNodeModule* const modp = moved.second;
UINFO(9, "moving " << nodep << " to " << modp << endl);
nodep->unlinkFrBack();
modp->addStmtp(nodep);
modp->addStmtsp(nodep);
}
}
};
@@ -202,5 +208,5 @@ public:
void V3Class::classAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ClassVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("class", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("class", 0, dumpTree() >= 3);
}
+38 -35
View File
@@ -26,12 +26,15 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Clean.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <algorithm>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Clean state, as a visitor of each AstNode
@@ -53,7 +56,6 @@ private:
const AstNodeModule* m_modp = nullptr;
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// Width resetting
int cppWidth(AstNode* nodep) {
@@ -88,6 +90,7 @@ private:
if (VN_IS(nodep, Var)
|| VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|| VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays
|| VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
@@ -171,134 +174,134 @@ private:
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
{
m_modp = nodep;
iterateChildren(nodep);
}
}
virtual void visit(AstNodeUniop* nodep) override {
void visit(AstNodeUniop* nodep) override {
iterateChildren(nodep);
computeCppWidth(nodep);
if (nodep->cleanLhs()) ensureClean(nodep->lhsp());
setClean(nodep, nodep->cleanOut());
}
virtual void visit(AstNodeBiop* nodep) override {
void visit(AstNodeBiop* nodep) override {
operandBiop(nodep);
setClean(nodep, nodep->cleanOut());
}
virtual void visit(AstAnd* nodep) override {
void visit(AstAnd* nodep) override {
operandBiop(nodep);
setClean(nodep, isClean(nodep->lhsp()) || isClean(nodep->rhsp()));
}
virtual void visit(AstXor* nodep) override {
void visit(AstXor* nodep) override {
operandBiop(nodep);
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
}
virtual void visit(AstOr* nodep) override {
void visit(AstOr* nodep) override {
operandBiop(nodep);
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
}
virtual void visit(AstNodeQuadop* nodep) override {
void visit(AstNodeQuadop* nodep) override {
operandQuadop(nodep);
setClean(nodep, nodep->cleanOut());
}
virtual void visit(AstNodeMath* nodep) override {
void visit(AstNodeMath* nodep) override {
iterateChildren(nodep);
computeCppWidth(nodep);
setClean(nodep, nodep->cleanOut());
}
virtual void visit(AstNodeAssign* nodep) override {
void visit(AstNodeAssign* nodep) override {
iterateChildren(nodep);
computeCppWidth(nodep);
if (nodep->cleanRhs()) ensureClean(nodep->rhsp());
}
virtual void visit(AstText* nodep) override { //
void visit(AstText* nodep) override { //
setClean(nodep, true);
}
virtual void visit(AstScopeName* nodep) override { //
void visit(AstScopeName* nodep) override { //
setClean(nodep, true);
}
virtual void visit(AstCNew* nodep) override {
void visit(AstCNew* nodep) override {
iterateChildren(nodep);
setClean(nodep, true);
}
virtual void visit(AstSel* nodep) override {
void visit(AstSel* nodep) override {
operandTriop(nodep);
setClean(nodep, nodep->cleanOut());
}
virtual void visit(AstUCFunc* nodep) override {
void visit(AstUCFunc* nodep) override {
iterateChildren(nodep);
computeCppWidth(nodep);
setClean(nodep, false);
// We always clean, as we don't trust those pesky users.
if (!VN_IS(nodep->backp(), And)) insertClean(nodep);
ensureCleanAndNext(nodep->bodysp());
ensureCleanAndNext(nodep->exprsp());
}
virtual void visit(AstTraceDecl* nodep) override {
void visit(AstTraceDecl* nodep) override {
// No cleaning, or would loose pointer to enum
iterateChildren(nodep);
}
virtual void visit(AstTraceInc* nodep) override {
void visit(AstTraceInc* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->valuep());
}
virtual void visit(AstTypedef* nodep) override {
void visit(AstTypedef* nodep) override {
// No cleaning, or would loose pointer to enum
iterateChildren(nodep);
}
virtual void visit(AstParamTypeDType* nodep) override {
void visit(AstParamTypeDType* nodep) override {
// No cleaning, or would loose pointer to enum
iterateChildren(nodep);
}
// Control flow operators
virtual void visit(AstNodeCond* nodep) override {
void visit(AstNodeCond* nodep) override {
iterateChildren(nodep);
ensureClean(nodep->condp());
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
setClean(nodep, isClean(nodep->thenp()) && isClean(nodep->elsep()));
}
virtual void visit(AstWhile* nodep) override {
void visit(AstWhile* nodep) override {
iterateChildren(nodep);
ensureClean(nodep->condp());
}
virtual void visit(AstNodeIf* nodep) override {
void visit(AstNodeIf* nodep) override {
iterateChildren(nodep);
ensureClean(nodep->condp());
}
virtual void visit(AstSFormatF* nodep) override {
void visit(AstSFormatF* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->exprsp());
setClean(nodep, true); // generates a string, so not relevant
}
virtual void visit(AstUCStmt* nodep) override {
void visit(AstUCStmt* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->bodysp());
ensureCleanAndNext(nodep->exprsp());
}
virtual void visit(AstNodeCCall* nodep) override {
void visit(AstNodeCCall* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->argsp());
setClean(nodep, true);
}
virtual void visit(AstCMethodHard* nodep) override {
void visit(AstCMethodHard* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->pinsp());
setClean(nodep, true);
}
virtual void visit(AstWith* nodep) override {
void visit(AstWith* nodep) override {
iterateChildren(nodep);
ensureCleanAndNext(nodep->exprp());
setClean(nodep, true);
}
virtual void visit(AstIntfRef* nodep) override {
void visit(AstIntfRef* nodep) override {
iterateChildren(nodep);
setClean(nodep, true); // generates a string, so not relevant
}
//--------------------
// Default: Just iterate
virtual void visit(AstNode* nodep) override {
void visit(AstNode* nodep) override {
iterateChildren(nodep);
computeCppWidth(nodep);
}
@@ -306,7 +309,7 @@ private:
public:
// CONSTRUCTORS
explicit CleanVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~CleanVisitor() override = default;
~CleanVisitor() override = default;
};
//######################################################################
@@ -315,5 +318,5 @@ public:
void V3Clean::cleanAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CleanVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("clean", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("clean", 0, dumpTree() >= 3);
}
+25 -23
View File
@@ -30,12 +30,15 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Clock.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <algorithm>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Convert every WRITE AstVarRef to a READ ref
@@ -87,7 +90,6 @@ private:
AstMTaskBody* m_mtaskBodyp = nullptr; // Current mtask body
// METHODS
VL_DEBUG_FUNC; // Declare debug()
AstVarScope* getCreateLastClk(AstVarScope* vscp) {
if (vscp->user1p()) return static_cast<AstVarScope*>(vscp->user1p());
@@ -99,12 +101,12 @@ private:
const string newvarname
= (string("__Vclklast__") + vscp->scopep()->nameDotless() + "__" + varp->name());
AstVar* const newvarp = new AstVar(vscp->fileline(), VVarType::MODULETEMP, newvarname,
VFlagLogicPacked(), 1);
VFlagLogicPacked{}, 1);
newvarp->noReset(true); // Reset by below assign
m_modp->addStmtp(newvarp);
m_modp->addStmtsp(newvarp);
AstVarScope* const newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp);
vscp->user1p(newvscp);
m_scopep->addVarp(newvscp);
m_scopep->addVarsp(newvscp);
// Add init
AstNode* fromp = new AstVarRef(newvarp->fileline(), vscp, VAccess::READ);
if (v3Global.opt.xInitialEdge()) fromp = new AstNot(fromp->fileline(), fromp);
@@ -201,7 +203,7 @@ private:
funcp->slow(slow);
funcp->isConst(false);
funcp->declPrivate(true);
m_topScopep->scopep()->addActivep(funcp);
m_topScopep->scopep()->addBlocksp(funcp);
return funcp;
}
void splitCheck(AstCFunc* ofuncp) {
@@ -228,7 +230,7 @@ private:
funcp->isStatic(false);
funcp->isLoose(true);
funcp->slow(ofuncp->slow());
m_topScopep->scopep()->addActivep(funcp);
m_topScopep->scopep()->addBlocksp(funcp);
//
AstCCall* const callp = new AstCCall{funcp->fileline(), funcp};
ofuncp->addStmtsp(callp);
@@ -241,7 +243,7 @@ private:
}
// VISITORS
virtual void visit(AstTopScope* nodep) override {
void visit(AstTopScope* nodep) override {
UINFO(4, " TOPSCOPE " << nodep << endl);
m_topScopep = nodep;
m_scopep = nodep->scopep();
@@ -276,7 +278,7 @@ private:
m_topScopep = nullptr;
m_scopep = nullptr;
}
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
// UINFO(4, " MOD " << nodep << endl);
VL_RESTORER(m_modp);
{
@@ -284,7 +286,7 @@ private:
iterateChildren(nodep);
}
}
virtual void visit(AstScope* nodep) override {
void visit(AstScope* nodep) override {
// UINFO(4, " SCOPE " << nodep << endl);
m_scopep = nodep;
iterateChildren(nodep);
@@ -295,14 +297,14 @@ private:
}
m_scopep = nullptr;
}
virtual void visit(AstNodeProcedure* nodep) override {
if (AstNode* const stmtsp = nodep->bodysp()) {
void visit(AstNodeProcedure* nodep) override {
if (AstNode* const stmtsp = nodep->stmtsp()) {
stmtsp->unlinkFrBackWithNext();
nodep->addNextHere(stmtsp);
}
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
virtual void visit(AstCoverToggle* nodep) override {
void visit(AstCoverToggle* nodep) override {
// nodep->dumpTree(cout, "ct:");
// COVERTOGGLE(INC, ORIG, CHANGE) ->
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
@@ -315,11 +317,11 @@ private:
// We could add another IF to detect posedges, and only increment if so.
// It's another whole branch though versus a potential memory miss.
// We'll go with the miss.
newp->addIfsp(new AstAssign(nodep->fileline(), changeWrp, origp->cloneTree(false)));
newp->addThensp(new AstAssign(nodep->fileline(), changeWrp, origp->cloneTree(false)));
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
virtual void visit(AstCFunc* nodep) override {
void visit(AstCFunc* nodep) override {
iterateChildren(nodep);
// Link to global function
if (nodep->isFinal()) {
@@ -328,7 +330,7 @@ private:
m_finalFuncp->addStmtsp(callp);
}
}
virtual void visit(AstSenTree* nodep) override {
void visit(AstSenTree* nodep) override {
// Delete it later; Actives still pointing to it
nodep->unlinkFrBack();
pushDeletep(nodep);
@@ -342,7 +344,7 @@ private:
void addToInitial(AstNode* stmtsp) {
m_initFuncp->addStmtsp(stmtsp); // add to top level function
}
virtual void visit(AstActive* nodep) override {
void visit(AstActive* nodep) override {
// Careful if adding variables here, ACTIVES can be under other ACTIVES
// Need to save and restore any member state in AstUntilStable block
if (!m_topScopep || !nodep->stmtsp()) {
@@ -366,7 +368,7 @@ private:
m_mtaskBodyp->addStmtsp(m_lastIfp);
}
// Move statements to if
m_lastIfp->addIfsp(stmtsp);
m_lastIfp->addThensp(stmtsp);
} else if (nodep->hasInitial() || nodep->hasSettle()) {
nodep->v3fatalSrc("MTask should not include initial/settle logic.");
} else {
@@ -392,7 +394,7 @@ private:
addToEvalLoop(m_lastIfp);
}
// Move statements to if
m_lastIfp->addIfsp(stmtsp);
m_lastIfp->addThensp(stmtsp);
} else if (nodep->hasInitial()) {
// Don't need to: clearLastSen();, as we're adding it to different cfunc
// Move statements to function
@@ -410,7 +412,7 @@ private:
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
}
virtual void visit(AstExecGraph* nodep) override {
void visit(AstExecGraph* nodep) override {
VL_RESTORER(m_mtaskBodyp);
for (m_mtaskBodyp = nodep->mTaskBodiesp(); m_mtaskBodyp;
m_mtaskBodyp = VN_AS(m_mtaskBodyp->nextp(), MTaskBody)) {
@@ -426,7 +428,7 @@ private:
}
//--------------------
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
@@ -436,7 +438,7 @@ public:
// easily without iterating through the tree.
nodep->evalp(m_evalFuncp);
}
virtual ~ClockVisitor() override = default;
~ClockVisitor() override = default;
};
//######################################################################
@@ -445,5 +447,5 @@ public:
void V3Clock::clockAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ ClockVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("clock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("clock", 0, dumpTree() >= 3);
}
+16 -12
View File
@@ -22,16 +22,19 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Combine.h"
#include "V3DupFinder.h"
#include "V3Stats.h"
#include "V3Ast.h"
#include "V3AstUserAllocator.h"
#include "V3DupFinder.h"
#include "V3Global.h"
#include "V3Stats.h"
#include <list>
#include <vector>
VL_DEFINE_DEBUG_FUNCTIONS;
class CombineVisitor final : VNVisitor {
// NODE STATE
// AstNodeModule::user1() List of AstCFuncs in this module (via m_cfuncs)
@@ -56,7 +59,6 @@ class CombineVisitor final : VNVisitor {
VDouble0 m_cfuncsCombined; // Statistic tracking
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void removeEmptyFunctions(std::list<AstCFunc*>& funcps) {
for (funcit_t it = funcps.begin(), nit; it != funcps.end(); it = nit) {
@@ -179,32 +181,32 @@ class CombineVisitor final : VNVisitor {
}
// VISITORS
virtual void visit(AstNetlist* nodep) override {
void visit(AstNetlist* nodep) override {
// Gather functions and references
iterateChildrenConst(nodep);
// Combine functions
process(nodep);
}
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
UASSERT_OBJ(!m_modp, nodep, "Should not nest");
m_modp = nodep;
iterateChildrenConst(nodep);
m_modp = nullptr;
}
virtual void visit(AstCFunc* nodep) override {
void visit(AstCFunc* nodep) override {
iterateChildrenConst(nodep);
if (nodep->dontCombine()) return;
auto& coll = nodep->slow() ? m_cfuncs(m_modp).m_slow : m_cfuncs(m_modp).m_fast;
coll.emplace_back(nodep);
}
virtual void visit(AstCCall* nodep) override {
void visit(AstCCall* nodep) override {
iterateChildrenConst(nodep);
AstCFunc* const funcp = nodep->funcp();
if (funcp->dontCombine()) return;
m_callSites(funcp).emplace_back(nodep);
}
virtual void visit(AstAddrOfCFunc* nodep) override {
void visit(AstAddrOfCFunc* nodep) override {
iterateChildrenConst(nodep);
if (nodep->funcp()->dontCombine()) return;
// LCOV_EXCL_START
@@ -216,11 +218,13 @@ class CombineVisitor final : VNVisitor {
}
//--------------------
virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
// CONSTRUCTORS
explicit CombineVisitor(AstNetlist* nodep) { iterate(nodep); }
~CombineVisitor() { V3Stats::addStat("Optimizations, Combined CFuncs", m_cfuncsCombined); }
~CombineVisitor() override {
V3Stats::addStat("Optimizations, Combined CFuncs", m_cfuncsCombined);
}
public:
static void apply(AstNetlist* netlistp) { CombineVisitor{netlistp}; }
@@ -232,5 +236,5 @@ public:
void V3Combine::combineAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
CombineVisitor::apply(nodep);
V3Global::dumpCheckGlobalTree("combine", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("combine", 0, dumpTree() >= 3);
}
+16 -13
View File
@@ -24,9 +24,12 @@
#include "verilatedos.h"
#include "V3Common.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3EmitCBase.h"
#include "V3Global.h"
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Common component builders
@@ -42,7 +45,7 @@ static void makeVlToString(AstClass* nodep) {
AstNode* const exprp = new AstCMath{nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtp(funcp);
nodep->addStmtsp(funcp);
}
static void makeToString(AstClass* nodep) {
AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"};
@@ -50,10 +53,10 @@ static void makeToString(AstClass* nodep) {
funcp->isStatic(false);
funcp->protect(false);
AstNode* const exprp
= new AstCMath{nodep->fileline(), R"(std::string("'{") + to_string_middle() + "}")", 0};
= new AstCMath{nodep->fileline(), R"(std::string{"'{"} + to_string_middle() + "}")", 0};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtp(funcp);
nodep->addStmtsp(funcp);
}
static void makeToStringMiddle(AstClass* nodep) {
AstCFunc* const funcp
@@ -85,17 +88,17 @@ static void makeToStringMiddle(AstClass* nodep) {
}
}
}
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
string stmt = "out += \"";
if (nodep->extendsp()) {
string stmt = "out += ";
if (!comma.empty()) stmt += "\", \"+ ";
// comma = ", "; // Nothing further so not needed
stmt += nodep->extendsp()->dtypep()->nameProtect();
stmt += EmitCBaseVisitor::prefixNameProtect(nodep->extendsp()->dtypep());
stmt += "::to_string_middle();\n";
nodep->user1(true); // So what we extend dumps this
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
}
funcp->addStmtsp(new AstCStmt{nodep->fileline(), "return out;\n"});
nodep->addStmtp(funcp);
nodep->addStmtsp(funcp);
}
//######################################################################
@@ -103,18 +106,18 @@ static void makeToStringMiddle(AstClass* nodep) {
void V3Common::commonAll() {
UINFO(2, __FUNCTION__ << ": " << endl);
// NODE STATE
// Entire netlist:
// AstClass::user1() -> bool. True if class needs to_string dumper
const VNUser1InUse m_inuser1;
// Create common contents for each module
for (AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
if (AstClass* const classp = VN_CAST(nodep, Class)) {
// NODE STATE
// Entire netlist:
// AstClass::user1() -> bool. True if class needs to_string dumper
const VNUser1InUse m_inuser1;
// Create ToString methods
makeVlToString(classp);
makeToString(classp);
makeToStringMiddle(classp);
}
}
V3Global::dumpCheckGlobalTree("common", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("common", 0, dumpTree() >= 3);
}
+9 -5
View File
@@ -17,15 +17,18 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Config.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3Config.h"
#include <map>
#include <set>
#include <string>
#include <unordered_map>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Resolve wildcards in files, modules, ftasks or variables
@@ -33,7 +36,8 @@
// as wildcards and are accessed by a resolved name. It rebuilds a name lookup
// cache of resolved entities. Entities stored in this container need an update
// function that takes a reference of this type to join multiple entities into one.
template <typename T> class V3ConfigWildcardResolver {
template <typename T>
class V3ConfigWildcardResolver final {
using Map = std::map<const std::string, T>;
Map m_mapWildcard; // Wildcard strings to entities
@@ -191,11 +195,11 @@ public:
const VPragmaType type
= m_inlineValue ? VPragmaType::INLINE_MODULE : VPragmaType::NO_INLINE_MODULE;
AstNode* const nodep = new AstPragma(modp->fileline(), type);
modp->addStmtp(nodep);
modp->addStmtsp(nodep);
}
for (const auto& itr : m_modPragmas) {
AstNode* const nodep = new AstPragma{modp->fileline(), itr};
modp->addStmtp(nodep);
modp->addStmtsp(nodep);
}
}
@@ -307,7 +311,7 @@ public:
if (lineMatch(lineno, VPragmaType::FULL_CASE)) nodep->fullPragma(true);
if (lineMatch(lineno, VPragmaType::PARALLEL_CASE)) nodep->parallelPragma(true);
}
inline void applyIgnores(FileLine* filelinep) {
void applyIgnores(FileLine* filelinep) {
// HOT routine, called each parsed token line of this filename
if (m_lastIgnore.lineno != filelinep->lineno()) {
// UINFO(9, " ApplyIgnores for " << filelinep->ascii() << endl);
+1 -1
View File
@@ -20,9 +20,9 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3Error.h"
#include "V3FileLine.h"
#include "V3Ast.h"
//######################################################################
+241 -174
View File
@@ -23,19 +23,22 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3Const.h"
#include "V3Ast.h"
#include "V3Width.h"
#include "V3Global.h"
#include "V3Simulate.h"
#include "V3Stats.h"
#include "V3String.h"
#include "V3UniqueNames.h"
#include "V3Width.h"
#include <algorithm>
#include <memory>
#include <type_traits>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Utilities
@@ -79,14 +82,50 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
// bool indicating if the term is clean (0/1 value, or if the top bits might be dirty)
using ResultTerm = std::tuple<AstNode*, unsigned, bool>;
struct LeafInfo final { // Leaf node (either AstConst or AstVarRef)
class LeafInfo final { // Leaf node (either AstConst or AstVarRef)
// MEMBERS
bool m_polarity = true;
int m_lsb = 0;
int m_lsb = 0; // LSB of actually used bit of m_refp->varp()
int m_msb = 0; // MSB of actually used bit of m_refp->varp()
int m_wordIdx = -1; // -1 means AstWordSel is not used.
AstVarRef* m_refp = nullptr;
const AstConst* m_constp = nullptr;
int width() const {
public:
// CONSTRUCTORS
LeafInfo() = default;
LeafInfo(const LeafInfo& other) = default;
explicit LeafInfo(int lsb)
: m_lsb{lsb} {}
// METHODS
void setLeaf(AstVarRef* refp) {
UASSERT(!m_refp && !m_constp, "Must be called just once");
m_refp = refp;
m_msb = refp->varp()->widthMin() - 1;
}
void setLeaf(const AstConst* constp) {
UASSERT(!m_refp && !m_constp, "Must be called just once");
m_constp = constp;
m_msb = constp->widthMin() - 1;
}
void updateBitRange(const AstCCast* castp) {
m_msb = std::min(m_msb, m_lsb + castp->width() - 1);
}
void updateBitRange(const AstShiftR* shiftp) {
m_lsb += VN_AS(shiftp->rhsp(), Const)->toUInt();
}
void wordIdx(int i) { m_wordIdx = i; }
void polarity(bool p) { m_polarity = p; }
AstVarRef* refp() const { return m_refp; }
const AstConst* constp() const { return m_constp; }
int wordIdx() const { return m_wordIdx; }
bool polarity() const { return m_polarity; }
int lsb() const { return m_lsb; }
int msb() const { return std::min(m_msb, varWidth() - 1); }
int varWidth() const {
UASSERT(m_refp, "m_refp should be set");
const int width = m_refp->varp()->widthMin();
if (!m_refp->isWide()) {
@@ -289,7 +328,6 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
return ResultTerm{resultp, ops, clean};
}
public:
// CONSTRUCTORS
VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp, int width)
: m_parentp{parent}
@@ -314,7 +352,6 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
std::vector<std::unique_ptr<VarInfo>> m_varInfos; // VarInfo for each variable, [0] is nullptr
// METHODS
VL_DEBUG_FUNC; // Declare debug()
bool isAndTree() const { return VN_IS(m_rootp, And); }
bool isOrTree() const { return VN_IS(m_rootp, Or); }
@@ -339,32 +376,32 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
UINFO(9, "Increment to " << m_ops << " " << nodep << " called from line " << line << "\n");
}
VarInfo& getVarInfo(const LeafInfo& ref) {
UASSERT_OBJ(ref.m_refp, m_rootp, "null varref in And/Or/Xor optimization");
AstNode* nodep = ref.m_refp->varScopep();
if (!nodep) nodep = ref.m_refp->varp(); // Not scoped
UASSERT_OBJ(ref.refp(), m_rootp, "null varref in And/Or/Xor optimization");
AstNode* nodep = ref.refp()->varScopep();
if (!nodep) nodep = ref.refp()->varp(); // Not scoped
int baseIdx = nodep->user4();
if (baseIdx == 0) { // Not set yet
baseIdx = m_varInfos.size();
const int numWords
= ref.m_refp->dtypep()->isWide() ? ref.m_refp->dtypep()->widthWords() : 1;
= ref.refp()->dtypep()->isWide() ? ref.refp()->dtypep()->widthWords() : 1;
m_varInfos.resize(m_varInfos.size() + numWords);
nodep->user4(baseIdx);
}
const size_t idx = baseIdx + std::max(0, ref.m_wordIdx);
const size_t idx = baseIdx + std::max(0, ref.wordIdx());
VarInfo* varInfop = m_varInfos[idx].get();
if (!varInfop) {
varInfop = new VarInfo{this, ref.m_refp, ref.width()};
varInfop = new VarInfo{this, ref.refp(), ref.varWidth()};
m_varInfos[idx].reset(varInfop);
} else {
if (!varInfop->sameVarAs(ref.m_refp))
CONST_BITOP_SET_FAILED("different var (scope?)", ref.m_refp);
if (!varInfop->sameVarAs(ref.refp()))
CONST_BITOP_SET_FAILED("different var (scope?)", ref.refp());
}
return *varInfop;
}
// Traverse down to see AstConst or AstVarRef
LeafInfo findLeaf(AstNode* nodep, bool expectConst) {
LeafInfo info;
LeafInfo info{m_lsb};
{
VL_RESTORER(m_leafp);
m_leafp = &info;
@@ -373,31 +410,34 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
bool ok = !m_failed;
if (expectConst) {
ok &= !info.m_refp && info.m_constp;
ok &= !info.refp() && info.constp();
} else {
ok &= info.m_refp && !info.m_constp;
ok &= info.refp() && !info.constp();
}
return ok ? info : LeafInfo{};
}
// VISITORS
virtual void visit(AstNode* nodep) override {
CONST_BITOP_SET_FAILED("Hit unexpected op", nodep);
void visit(AstNode* nodep) override { CONST_BITOP_SET_FAILED("Hit unexpected op", nodep); }
void visit(AstCCast* nodep) override {
iterateChildren(nodep);
if (m_leafp) m_leafp->updateBitRange(nodep);
}
virtual void visit(AstCCast* nodep) override { iterateChildren(nodep); }
virtual void visit(AstShiftR* nodep) override {
void visit(AstShiftR* nodep) override {
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
CONST_BITOP_RETURN_IF(!constp, nodep->rhsp());
m_lsb += constp->toUInt();
incrOps(nodep, __LINE__);
iterate(nodep->lhsp());
m_leafp->updateBitRange(nodep);
m_lsb -= constp->toUInt();
}
virtual void visit(AstNot* nodep) override {
void visit(AstNot* nodep) override {
CONST_BITOP_RETURN_IF(nodep->widthMin() != 1, nodep);
AstNode* lhsp = nodep->lhsp();
if (AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp();
AstCCast* const castp = VN_CAST(lhsp, CCast);
if (castp) lhsp = castp->lhsp();
CONST_BITOP_RETURN_IF(!VN_IS(lhsp, VarRef) && !VN_IS(lhsp, Xor) && !VN_IS(lhsp, RedXor)
&& !VN_IS(lhsp, ShiftR),
lhsp);
@@ -406,74 +446,74 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
iterateChildren(nodep);
// Don't restore m_polarity for Xor as it counts parity of the entire tree
if (!isXorTree()) m_polarity = !m_polarity;
if (m_leafp && castp) m_leafp->updateBitRange(castp);
}
virtual void visit(AstWordSel* nodep) override {
void visit(AstWordSel* nodep) override {
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
AstConst* const constp = VN_CAST(nodep->bitp(), Const);
CONST_BITOP_RETURN_IF(!constp, nodep->rhsp());
UASSERT_OBJ(m_leafp->m_wordIdx == -1, nodep, "Unexpected nested WordSel");
m_leafp->m_wordIdx = constp->toSInt();
UASSERT_OBJ(m_leafp->wordIdx() == -1, nodep, "Unexpected nested WordSel");
m_leafp->wordIdx(constp->toSInt());
iterate(nodep->fromp());
}
virtual void visit(AstVarRef* nodep) override {
void visit(AstVarRef* nodep) override {
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
UASSERT_OBJ(!m_leafp->m_refp, nodep, m_leafp->m_refp << " is already set");
m_leafp->m_refp = nodep;
m_leafp->m_polarity = m_polarity;
m_leafp->m_lsb = m_lsb;
m_leafp->setLeaf(nodep);
m_leafp->polarity(m_polarity);
}
virtual void visit(AstConst* nodep) override {
void visit(AstConst* nodep) override {
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
UASSERT_OBJ(!m_leafp->m_constp, nodep, m_leafp->m_constp << " is already set");
m_leafp->m_constp = nodep;
m_leafp->m_lsb = m_lsb;
m_leafp->setLeaf(nodep);
}
virtual void visit(AstRedXor* nodep) override {
void visit(AstRedXor* nodep) override {
Restorer restorer{*this};
CONST_BITOP_RETURN_IF(!VN_IS(m_rootp, Xor), nodep);
AstNode* lhsp = nodep->lhsp();
if (const AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp();
const AstCCast* const castp = VN_CAST(lhsp, CCast);
if (castp) lhsp = castp->lhsp();
if (const AstAnd* const andp = VN_CAST(lhsp, And)) { // '^(mask & leaf)'
CONST_BITOP_RETURN_IF(!andp, lhsp);
const LeafInfo& mask = findLeaf(andp->lhsp(), true);
CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp());
CONST_BITOP_RETURN_IF(!mask.constp() || mask.lsb() != 0, andp->lhsp());
const LeafInfo& ref = findLeaf(andp->rhsp(), false);
CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp());
LeafInfo ref = findLeaf(andp->rhsp(), false);
CONST_BITOP_RETURN_IF(!ref.refp(), andp->rhsp());
if (castp) ref.updateBitRange(castp);
restorer.disableRestore(); // Now all subtree succeeded
const V3Number& maskNum = mask.m_constp->num();
const V3Number& maskNum = mask.constp()->num();
incrOps(nodep, __LINE__);
incrOps(andp, __LINE__);
// Mark all bits checked in this reduction
const int maxBitIdx = std::min(ref.m_lsb + maskNum.width(), ref.width());
for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.m_lsb;
const int maxBitIdx = std::min(ref.lsb() + maskNum.width(), ref.msb() + 1);
for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.lsb();
if (maskNum.bitIs0(maskIdx)) continue;
// Set true, m_polarity takes care of the entire parity
m_bitPolarities.emplace_back(ref, true, bitIdx);
}
} else { // '^leaf'
const LeafInfo& ref = findLeaf(lhsp, false);
CONST_BITOP_RETURN_IF(!ref.m_refp, lhsp);
LeafInfo ref = findLeaf(lhsp, false);
CONST_BITOP_RETURN_IF(!ref.refp(), lhsp);
if (castp) ref.updateBitRange(castp);
restorer.disableRestore(); // Now all checks passed
incrOps(nodep, __LINE__);
// Mark all bits checked by this comparison
for (int bitIdx = ref.m_lsb; bitIdx < ref.width(); ++bitIdx) {
for (int bitIdx = ref.lsb(); bitIdx <= ref.msb(); ++bitIdx) {
m_bitPolarities.emplace_back(ref, true, bitIdx);
}
}
}
virtual void visit(AstNodeBiop* nodep) override {
void visit(AstNodeBiop* nodep) override {
if (VN_IS(nodep, And) && isConst(nodep->lhsp(), 1)) { // 1 & _
// Always reach past a plain making AND
Restorer restorer{*this};
@@ -487,12 +527,12 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
for (const bool right : {false, true}) {
Restorer restorer{*this};
LeafInfo leafInfo;
LeafInfo leafInfo{m_lsb};
m_leafp = &leafInfo;
AstNode* opp = right ? nodep->rhsp() : nodep->lhsp();
const bool origFailed = m_failed;
iterate(opp);
if (leafInfo.m_constp || m_failed) {
if (leafInfo.constp() || m_failed) {
// Revert changes in leaf
restorer.restoreNow();
// Reach past a cast then add to frozen nodes to be added to final reduction
@@ -502,14 +542,14 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
continue;
}
restorer.disableRestore(); // Now all checks passed
if (leafInfo.m_refp) {
if (leafInfo.refp()) {
// The conditional on the lsb being in range is necessary for some degenerate
// case, e.g.: (IData)((QData)wide[0] >> 32), or <1-bit-var> >> 1, which is
// just zero
if (leafInfo.m_lsb < leafInfo.width()) {
m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.m_polarity,
leafInfo.m_lsb);
} else if (isAndTree() && leafInfo.m_polarity) {
if (leafInfo.lsb() <= leafInfo.msb()) {
m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.polarity(),
leafInfo.lsb());
} else if (isAndTree() && leafInfo.polarity()) {
// If there is a constant 0 term in an And tree, we must include it. Fudge
// this by adding a bit with both polarities, which will simplify to zero
m_bitPolarities.emplace_back(leafInfo, true, 0);
@@ -525,46 +565,59 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
const AstConst* const constp = VN_CAST(lhsp, Const);
CONST_BITOP_RETURN_IF(!constp, nodep->lhsp());
const bool maskFlip = isOrTree();
const V3Number& compNum = constp->num();
auto setPolarities = [this, &compNum](const LeafInfo& ref, const V3Number* maskp) {
const bool maskFlip = isOrTree();
int constantWidth = compNum.width();
if (maskp) constantWidth = std::max(constantWidth, maskp->width());
const int maxBitIdx = std::max(ref.lsb() + constantWidth, ref.msb() + 1);
// Mark all bits checked by this comparison
for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.lsb();
const bool mask0 = maskp && maskp->bitIs0(maskIdx);
const bool outOfRange = bitIdx > ref.msb();
if (mask0 || outOfRange) { // RHS is 0
if (compNum.bitIs1(maskIdx)) {
// LHS is 1
// And tree: 1 == 0 => always false, set v && !v
// Or tree : 1 != 0 => always true, set v || !v
m_bitPolarities.emplace_back(ref, true, 0);
m_bitPolarities.emplace_back(ref, false, 0);
break;
} else { // This bitIdx is irrelevant
continue;
}
}
const bool polarity = compNum.bitIs1(maskIdx) != maskFlip;
m_bitPolarities.emplace_back(ref, polarity, bitIdx);
}
};
if (const AstAnd* const andp = VN_CAST(nodep->rhsp(), And)) { // comp == (mask & v)
const LeafInfo& mask = findLeaf(andp->lhsp(), true);
CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp());
CONST_BITOP_RETURN_IF(!mask.constp() || mask.lsb() != 0, andp->lhsp());
const LeafInfo& ref = findLeaf(andp->rhsp(), false);
CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp());
CONST_BITOP_RETURN_IF(!ref.refp(), andp->rhsp());
restorer.disableRestore(); // Now all checks passed
const V3Number& maskNum = mask.m_constp->num();
const V3Number& maskNum = mask.constp()->num();
incrOps(nodep, __LINE__);
incrOps(andp, __LINE__);
// Mark all bits checked by this comparison
const int maxBitIdx = std::min(ref.m_lsb + maskNum.width(), ref.width());
for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.m_lsb;
if (maskNum.bitIs0(maskIdx)) continue;
const bool polarity = compNum.bitIs1(maskIdx) != maskFlip;
m_bitPolarities.emplace_back(ref, polarity, bitIdx);
}
setPolarities(ref, &maskNum);
} else { // comp == v
const LeafInfo& ref = findLeaf(nodep->rhsp(), false);
CONST_BITOP_RETURN_IF(!ref.m_refp, nodep->rhsp());
CONST_BITOP_RETURN_IF(!ref.refp(), nodep->rhsp());
restorer.disableRestore(); // Now all checks passed
incrOps(nodep, __LINE__);
// Mark all bits checked by this comparison
const int maxBitIdx = std::min(ref.m_lsb + compNum.width(), ref.width());
for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.m_lsb;
const bool polarity = compNum.bitIs1(maskIdx) != maskFlip;
m_bitPolarities.emplace_back(ref, polarity, bitIdx);
}
setPolarities(ref, nullptr);
}
} else {
CONST_BITOP_SET_FAILED("Mixture of different ops cannot be optimized", nodep);
@@ -700,10 +753,11 @@ public:
if (debug() >= 9) { // LCOV_EXCL_START
cout << "Bitop tree considered: " << endl;
for (AstNode* const termp : termps) termp->dumpTree("Reduced term: ");
for (const std::pair<AstNode*, FrozenNodeInfo>& termp : visitor.m_frozenNodes)
for (const std::pair<AstNode*, FrozenNodeInfo>& termp : visitor.m_frozenNodes) {
termp.first->dumpTree("Frozen term with lsb " + std::to_string(termp.second.m_lsb)
+ " polarity " + std::to_string(termp.second.m_polarity)
+ ": ");
}
cout << "Needs flipping: " << needsFlip << endl;
cout << "Needs cleaning: " << needsCleaning << endl;
cout << "Size: " << resultOps << " input size: " << visitor.m_ops << endl;
@@ -840,7 +894,6 @@ private:
V3UniqueNames m_concswapNames; // For generating unique temporary variable names
// METHODS
VL_DEBUG_FUNC; // Declare debug()
bool operandConst(AstNode* nodep) { return VN_IS(nodep, Const); }
bool operandAsvConst(const AstNode* nodep) {
@@ -989,20 +1042,19 @@ private:
// as high as possible, which is usally the right choice, except for this.
AstNodeCond* const condp = VN_CAST(nodep->rhsp(), NodeCond);
if (!condp) return false;
if (!VN_IS(condp->expr1p(), Const) && !VN_IS(condp->expr2p(), Const)) return false;
if (!VN_IS(condp->thenp(), Const) && !VN_IS(condp->elsep(), Const)) return false;
AstConst* const maskp = VN_CAST(nodep->lhsp(), Const);
if (!maskp) return false;
UINFO(4, "AND(CONSTm, CONDcond(c, i, e))->CONDcond(c, AND(m,i), AND(m, e)) " << nodep
<< endl);
AstNodeCond* const newp = static_cast<AstNodeCond*>(
condp->cloneType(condp->condp()->unlinkFrBack(),
new AstAnd(nodep->fileline(), maskp->cloneTree(false),
condp->expr1p()->unlinkFrBack()),
new AstAnd(nodep->fileline(), maskp->cloneTree(false),
condp->expr2p()->unlinkFrBack())));
AstNodeCond* const newp = static_cast<AstNodeCond*>(condp->cloneType(
condp->condp()->unlinkFrBack(),
new AstAnd(nodep->fileline(), maskp->cloneTree(false), condp->thenp()->unlinkFrBack()),
new AstAnd(nodep->fileline(), maskp->cloneTree(false),
condp->elsep()->unlinkFrBack())));
newp->dtypeFrom(nodep);
newp->expr1p()->dtypeFrom(nodep); // As And might have been to change widths
newp->expr2p()->dtypeFrom(nodep);
newp->thenp()->dtypeFrom(nodep); // As And might have been to change widths
newp->elsep()->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return true;
@@ -1034,7 +1086,7 @@ private:
if (orLIsRedundant && orRIsRedundant) {
nodep->replaceWith(
new AstConst(nodep->fileline(), AstConst::DtypedValue(), nodep->dtypep(), 0));
new AstConst(nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()));
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return true;
} else if (orLIsRedundant) {
@@ -1390,19 +1442,19 @@ private:
}
}
bool ifSameAssign(const AstNodeIf* nodep) {
const AstNodeAssign* const ifp = VN_CAST(nodep->ifsp(), NodeAssign);
const AstNodeAssign* const elsep = VN_CAST(nodep->elsesp(), NodeAssign);
if (!ifp || ifp->nextp()) return false; // Must be SINGLE statement
if (!elsep || elsep->nextp()) return false;
if (ifp->type() != elsep->type()) return false; // Can't mix an assigndly and an assign
if (!ifp->lhsp()->sameGateTree(elsep->lhsp())) return false;
if (!ifp->rhsp()->gateTree()) return false;
if (!elsep->rhsp()->gateTree()) return false;
const AstNodeAssign* const thensp = VN_CAST(nodep->thensp(), NodeAssign);
const AstNodeAssign* const elsesp = VN_CAST(nodep->elsesp(), NodeAssign);
if (!thensp || thensp->nextp()) return false; // Must be SINGLE statement
if (!elsesp || elsesp->nextp()) return false;
if (thensp->type() != elsesp->type()) return false; // Can't mix an assigndly with assign
if (!thensp->lhsp()->sameGateTree(elsesp->lhsp())) return false;
if (!thensp->rhsp()->gateTree()) return false;
if (!elsesp->rhsp()->gateTree()) return false;
return true;
}
bool operandIfIf(const AstNodeIf* nodep) {
if (nodep->elsesp()) return false;
const AstNodeIf* const lowerIfp = VN_CAST(nodep->ifsp(), NodeIf);
const AstNodeIf* const lowerIfp = VN_CAST(nodep->thensp(), NodeIf);
if (!lowerIfp || lowerIfp->nextp()) return false;
if (nodep->type() != lowerIfp->type()) return false;
if (afterComment(lowerIfp->elsesp())) return false;
@@ -1710,6 +1762,7 @@ private:
lp->rhsp(lrp);
nodep->lhsp(llp);
nodep->rhsp(rlp);
nodep->dtypep(llp->dtypep()); // dtype of Biop is before shift.
VL_DO_DANGLING(rp->deleteTree(), rp);
VL_DO_DANGLING(rrp->deleteTree(), rrp);
// nodep->dumpTree(cout, " repShiftSame_new: ");
@@ -2023,7 +2076,7 @@ private:
AstSel* const sel2p = new AstSel(conp->fileline(), rhs2p, lsb2, msb2 - lsb2 + 1);
// Make new assigns of same flavor as old one
//*** Not cloneTree; just one node.
AstNode* newp = nullptr;
AstNodeAssign* newp = nullptr;
if (!need_temp) {
AstNodeAssign* const asn1ap = VN_AS(nodep->cloneType(lc1p, sel1p), NodeAssign);
AstNodeAssign* const asn2ap = VN_AS(nodep->cloneType(lc2p, sel2p), NodeAssign);
@@ -2043,8 +2096,8 @@ private:
AstVar* const temp2p
= new AstVar(sel2p->fileline(), VVarType::BLOCKTEMP,
m_concswapNames.get(sel2p), VFlagLogicPacked(), msb2 - lsb2 + 1);
m_modp->addStmtp(temp1p);
m_modp->addStmtp(temp2p);
m_modp->addStmtsp(temp1p);
m_modp->addStmtsp(temp2p);
AstNodeAssign* const asn1ap
= VN_AS(nodep->cloneType(
new AstVarRef(sel1p->fileline(), temp1p, VAccess::WRITE), sel1p),
@@ -2196,11 +2249,11 @@ private:
//----------------------------------------
// VISITORS
virtual void visit(AstNetlist* nodep) override {
void visit(AstNetlist* nodep) override {
// Iterate modules backwards, in bottom-up order. That's faster
iterateChildrenBackwards(nodep);
}
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
{
m_modp = nodep;
@@ -2208,7 +2261,7 @@ private:
iterateChildren(nodep);
}
}
virtual void visit(AstCFunc* nodep) override {
void visit(AstCFunc* nodep) override {
// No ASSIGNW removals under funcs, we've long eliminated INITIALs
// (We should perhaps rename the assignw's to just assigns)
VL_RESTORER(m_wremove);
@@ -2217,7 +2270,7 @@ private:
iterateChildren(nodep);
}
}
virtual void visit(AstScope* nodep) override {
void visit(AstScope* nodep) override {
// No ASSIGNW removals under scope, we've long eliminated INITIALs
VL_RESTORER(m_wremove);
VL_RESTORER(m_scopep);
@@ -2314,16 +2367,17 @@ private:
}
// Special cases
virtual void visit(AstConst*) override {} // Already constant
void visit(AstConst*) override {} // Already constant
virtual void visit(AstCell* nodep) override {
void visit(AstCell* nodep) override {
if (m_params) {
iterateAndNextNull(nodep->paramsp());
} else {
iterateChildren(nodep);
}
}
virtual void visit(AstPin* nodep) override { iterateChildren(nodep); }
void visit(AstClassOrPackageRef* nodep) override { iterateChildren(nodep); }
void visit(AstPin* nodep) override { iterateChildren(nodep); }
void replaceLogEq(AstLogEq* nodep) {
// LOGEQ(a,b) => AstLogAnd{AstLogOr{AstLogNot{a},b},AstLogOr{AstLogNot{b},a}}
@@ -2505,7 +2559,7 @@ private:
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
virtual void visit(AstAttrOf* nodep) override {
void visit(AstAttrOf* nodep) override {
VL_RESTORER(m_attrp);
{
m_attrp = nodep;
@@ -2513,7 +2567,7 @@ private:
}
}
virtual void visit(AstArraySel* nodep) override {
void visit(AstArraySel* nodep) override {
iterateAndNextNull(nodep->bitp());
if (VN_IS(nodep->bitp(), Const)
&& VN_IS(nodep->fromp(), VarRef)
@@ -2539,7 +2593,7 @@ private:
}
m_selp = nullptr;
}
virtual void visit(AstNodeVarRef* nodep) override {
void visit(AstNodeVarRef* nodep) override {
iterateChildren(nodep);
UASSERT_OBJ(nodep->varp(), nodep, "Not linked");
bool did = false;
@@ -2593,7 +2647,7 @@ private:
<< nodep->varp()->prettyNameQ());
}
}
virtual void visit(AstEnumItemRef* nodep) override {
void visit(AstEnumItemRef* nodep) override {
iterateChildren(nodep);
UASSERT_OBJ(nodep->itemp(), nodep, "Not linked");
bool did = false;
@@ -2618,7 +2672,7 @@ private:
}
}
// virtual void visit(AstCvtPackString* nodep) override {
// void visit(AstCvtPackString* nodep) override {
// Not constant propagated (for today) because AstNodeMath::isOpaque is set
// Someday if lower is constant, convert to quoted "string".
@@ -2626,7 +2680,7 @@ private:
// Only one if it's not in a list
return (!nodep->nextp() && nodep->backp()->nextp() != nodep);
}
virtual void visit(AstSenItem* nodep) override {
void visit(AstSenItem* nodep) override {
iterateChildren(nodep);
if (m_doNConst
&& (VN_IS(nodep->sensp(), Const) || VN_IS(nodep->sensp(), EnumItemRef)
@@ -2693,7 +2747,7 @@ private:
}
};
virtual void visit(AstSenTree* nodep) override {
void visit(AstSenTree* nodep) override {
iterateChildren(nodep);
if (m_doExpensive) {
// cout<<endl; nodep->dumpTree(cout, "ssin: ");
@@ -2777,17 +2831,17 @@ private:
//-----
// Zero elimination
virtual void visit(AstNodeAssign* nodep) override {
void visit(AstNodeAssign* nodep) override {
iterateChildren(nodep);
if (m_doNConst && replaceNodeAssign(nodep)) return;
}
virtual void visit(AstAssignAlias* nodep) override {
void visit(AstAssignAlias* nodep) override {
// Don't perform any optimizations, keep the alias around
}
virtual void visit(AstAssignVarScope* nodep) override {
void visit(AstAssignVarScope* nodep) override {
// Don't perform any optimizations, the node won't be linked yet
}
virtual void visit(AstAssignW* nodep) override {
void visit(AstAssignW* nodep) override {
iterateChildren(nodep);
if (m_doNConst && replaceNodeAssign(nodep)) return;
AstNodeVarRef* const varrefp = VN_CAST(
@@ -2796,6 +2850,7 @@ private:
if (m_wremove && !m_params && m_doNConst && m_modp && operandConst(nodep->rhsp())
&& !VN_AS(nodep->rhsp(), Const)->num().isFourState()
&& 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)
@@ -2806,14 +2861,14 @@ private:
varrefp->unlinkFrBack();
AstInitial* const newinitp = new AstInitial(
nodep->fileline(), new AstAssign(nodep->fileline(), varrefp, exprp));
m_modp->addStmtp(newinitp);
m_modp->addStmtsp(newinitp);
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
// Set the initial value right in the variable so we can constant propagate
AstNode* const initvaluep = exprp->cloneTree(false);
varrefp->varp()->valuep(initvaluep);
}
}
virtual void visit(AstRelease* nodep) override {
void visit(AstRelease* nodep) override {
if (AstConcat* const concatp = VN_CAST(nodep->lhsp(), Concat)) {
FileLine* const flp = nodep->fileline();
AstRelease* const newLp = new AstRelease{flp, concatp->lhsp()->unlinkFrBack()};
@@ -2826,7 +2881,7 @@ private:
}
}
virtual void visit(AstNodeIf* nodep) override {
void visit(AstNodeIf* nodep) override {
iterateChildren(nodep);
if (m_doNConst) {
if (const AstConst* const constp = VN_CAST(nodep->condp(), Const)) {
@@ -2836,7 +2891,7 @@ private:
keepp = nodep->elsesp();
} else if (!m_doV || constp->isNeqZero()) { // Might be X in Verilog
UINFO(4, "IF(!0,{x},{any}) => {x}: " << nodep << endl);
keepp = nodep->ifsp();
keepp = nodep->thensp();
} else {
UINFO(4, "IF condition is X, retaining: " << nodep << endl);
return;
@@ -2848,7 +2903,7 @@ private:
nodep->unlinkFrBack();
}
VL_DO_DANGLING(nodep->deleteTree(), nodep);
} else if (!afterComment(nodep->ifsp()) && !afterComment(nodep->elsesp())) {
} else if (!afterComment(nodep->thensp()) && !afterComment(nodep->elsesp())) {
if (!isTreePureRecurse(nodep->condp())) {
// Condition has side effect - leave - perhaps in
// future simplify to remove all but side effect terms
@@ -2856,27 +2911,27 @@ private:
// Empty block, remove it
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
} else if (!afterComment(nodep->ifsp())) {
} else if (!afterComment(nodep->thensp())) {
UINFO(4, "IF({x}) nullptr {...} => IF(NOT{x}}: " << nodep << endl);
AstNode* const condp = nodep->condp();
AstNode* const elsesp = nodep->elsesp();
condp->unlinkFrBackWithNext();
elsesp->unlinkFrBackWithNext();
if (nodep->ifsp()) { // Must have been comment
nodep->ifsp()->unlinkFrBackWithNext()->deleteTree();
if (nodep->thensp()) { // Must have been comment
nodep->thensp()->unlinkFrBackWithNext()->deleteTree();
}
nodep->condp(new AstLogNot(condp->fileline(),
condp)); // LogNot, as C++ optimization also possible
nodep->addIfsp(elsesp);
nodep->addThensp(elsesp);
} else if (((VN_IS(nodep->condp(), Not) && nodep->condp()->width() == 1)
|| VN_IS(nodep->condp(), LogNot))
&& nodep->ifsp() && nodep->elsesp()) {
&& nodep->thensp() && nodep->elsesp()) {
UINFO(4, "IF(NOT {x}) => IF(x) swapped if/else" << nodep << endl);
AstNode* const condp
= VN_AS(nodep->condp(), NodeUniop)->lhsp()->unlinkFrBackWithNext();
AstNode* const ifsp = nodep->ifsp()->unlinkFrBackWithNext();
AstNode* const thensp = nodep->thensp()->unlinkFrBackWithNext();
AstNode* const elsesp = nodep->elsesp()->unlinkFrBackWithNext();
AstIf* const ifp = new AstIf(nodep->fileline(), condp, elsesp, ifsp);
AstIf* const ifp = new AstIf(nodep->fileline(), condp, elsesp, thensp);
ifp->branchPred(nodep->branchPred().invert());
nodep->replaceWith(ifp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
@@ -2884,25 +2939,25 @@ private:
UINFO(
4,
"IF({a}) ASSIGN({b},{c}) else ASSIGN({b},{d}) => ASSIGN({b}, {a}?{c}:{d})\n");
AstNodeAssign* const ifp = VN_AS(nodep->ifsp(), NodeAssign);
AstNodeAssign* const elsep = VN_AS(nodep->elsesp(), NodeAssign);
ifp->unlinkFrBack();
AstNodeAssign* const thensp = VN_AS(nodep->thensp(), NodeAssign);
AstNodeAssign* const elsesp = VN_AS(nodep->elsesp(), NodeAssign);
thensp->unlinkFrBack();
AstNode* const condp = nodep->condp()->unlinkFrBack();
AstNode* const truep = ifp->rhsp()->unlinkFrBack();
AstNode* const falsep = elsep->rhsp()->unlinkFrBack();
ifp->rhsp(new AstCond(truep->fileline(), condp, truep, falsep));
nodep->replaceWith(ifp);
AstNode* const truep = thensp->rhsp()->unlinkFrBack();
AstNode* const falsep = elsesp->rhsp()->unlinkFrBack();
thensp->rhsp(new AstCond(truep->fileline(), condp, truep, falsep));
nodep->replaceWith(thensp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
} else if (false // Disabled, as vpm assertions are faster
// without due to short-circuiting
&& operandIfIf(nodep)) {
UINFO(9, "IF({a}) IF({b}) => IF({a} && {b})" << endl);
AstNodeIf* const lowerIfp = VN_AS(nodep->ifsp(), NodeIf);
AstNodeIf* const lowerIfp = VN_AS(nodep->thensp(), NodeIf);
AstNode* const condp = nodep->condp()->unlinkFrBack();
AstNode* const lowerIfsp = lowerIfp->ifsp()->unlinkFrBackWithNext();
AstNode* const lowerThensp = lowerIfp->thensp()->unlinkFrBackWithNext();
AstNode* const lowerCondp = lowerIfp->condp()->unlinkFrBackWithNext();
nodep->condp(new AstLogAnd(lowerIfp->fileline(), condp, lowerCondp));
lowerIfp->replaceWith(lowerIfsp);
lowerIfp->replaceWith(lowerThensp);
VL_DO_DANGLING(lowerIfp->deleteTree(), lowerIfp);
} else if (operandBoolShift(nodep->condp())) {
replaceBoolShift(nodep->condp());
@@ -2910,7 +2965,7 @@ private:
}
}
virtual void visit(AstDisplay* nodep) override {
void visit(AstDisplay* nodep) override {
// DISPLAY(SFORMAT(text1)),DISPLAY(SFORMAT(text2)) -> DISPLAY(SFORMAT(text1+text2))
iterateChildren(nodep);
if (stmtDisplayDisplay(nodep)) return;
@@ -2959,12 +3014,14 @@ private:
pformatp->text(pformatp->text() + nformatp->text());
if (!prevp->addNewline() && nodep->addNewline()) pformatp->text(pformatp->text() + "\n");
if (nformatp->exprsp()) pformatp->addExprsp(nformatp->exprsp()->unlinkFrBackWithNext());
if (nformatp->scopeNamep())
pformatp->scopeNamep(nformatp->scopeNamep()->unlinkFrBackWithNext());
if (AstScopeName* const scopeNamep = nformatp->scopeNamep()) {
scopeNamep->unlinkFrBackWithNext();
pformatp->scopeNamep(scopeNamep);
}
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return true;
}
virtual void visit(AstSFormatF* nodep) override {
void visit(AstSFormatF* nodep) override {
// Substitute constants into displays. The main point of this is to
// simplify assertion methodologies which call functions with display's.
// This eliminates a pile of wide temps, and makes the C a whole lot more readable.
@@ -3031,17 +3088,17 @@ private:
}
}
virtual void visit(AstFuncRef* nodep) override {
void visit(AstFuncRef* nodep) override {
iterateChildren(nodep);
if (m_params) { // Only parameters force us to do constant function call propagation
replaceWithSimulation(nodep);
}
}
virtual void visit(AstArg* nodep) override {
void visit(AstArg* nodep) override {
// replaceWithSimulation on the Arg's parent FuncRef replaces these
iterateChildren(nodep);
}
virtual void visit(AstWhile* nodep) override {
void visit(AstWhile* nodep) override {
const bool oldHasJumpDelay = m_hasJumpDelay;
m_hasJumpDelay = false;
{ iterateChildren(nodep); }
@@ -3067,22 +3124,22 @@ private:
}
}
}
virtual void visit(AstInitArray* nodep) override { iterateChildren(nodep); }
virtual void visit(AstInitItem* nodep) override { iterateChildren(nodep); }
virtual void visit(AstUnbounded* nodep) override { iterateChildren(nodep); }
void visit(AstInitArray* nodep) override { iterateChildren(nodep); }
void visit(AstInitItem* nodep) override { iterateChildren(nodep); }
void visit(AstUnbounded* nodep) override { iterateChildren(nodep); }
// These are converted by V3Param. Don't constify as we don't want the
// from() VARREF to disappear, if any.
// If output of a presel didn't get consted, chances are V3Param didn't visit properly
virtual void visit(AstNodePreSel*) override {}
void visit(AstNodePreSel*) override {}
// Ignored, can eliminate early
virtual void visit(AstSysIgnore* nodep) override {
void visit(AstSysIgnore* nodep) override {
iterateChildren(nodep);
if (m_doNConst) VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
// Simplify
virtual void visit(AstBasicDType* nodep) override {
void visit(AstBasicDType* nodep) override {
iterateChildren(nodep);
nodep->cvtRangeConst();
}
@@ -3090,11 +3147,11 @@ private:
//-----
// Jump elimination
virtual void visit(AstDelay* nodep) override {
void visit(AstDelay* nodep) override {
iterateChildren(nodep);
m_hasJumpDelay = true;
}
virtual void visit(AstJumpGo* nodep) override {
void visit(AstJumpGo* nodep) override {
iterateChildren(nodep);
// Jump to label where label immediately follows label is not useful
if (nodep->labelp() == VN_CAST(nodep->nextp(), JumpLabel)) {
@@ -3125,7 +3182,7 @@ private:
m_hasJumpDelay = true;
}
virtual void visit(AstJumpBlock* nodep) override {
void visit(AstJumpBlock* nodep) override {
// Because JumpLabels disable many optimizations,
// remove JumpLabels that are not pointed to by any AstJumpGos
// Note this assumes all AstJumpGos are underneath the given label; V3Broken asserts this
@@ -3244,19 +3301,19 @@ private:
TREEOPC("AstAnd {$lhsp.isOne, matchRedundantClean(nodep)}", "DONE") // 1 & (a == b) -> (IData)(a == b)
// Trinary ops
// Note V3Case::Sel requires Cond to always be conditionally executed in C to prevent core dump!
TREEOP ("AstNodeCond{$condp.isZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr2p)");
TREEOP ("AstNodeCond{$condp.isNeqZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr1p)");
TREEOPA("AstNodeCond{$condp.isZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr2p)");
TREEOPA("AstNodeCond{$condp.isNeqZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr1p)");
TREEOP ("AstNodeCond{$condp, operandsSame($expr1p,,$expr2p)}","replaceWChild(nodep,$expr1p)");
TREEOP ("AstNodeCond{$condp.isZero, $thenp, $elsep}", "replaceWChild(nodep,$elsep)");
TREEOP ("AstNodeCond{$condp.isNeqZero, $thenp, $elsep}", "replaceWChild(nodep,$thenp)");
TREEOPA("AstNodeCond{$condp.isZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$elsep)");
TREEOPA("AstNodeCond{$condp.isNeqZero, $thenp.castConst, $elsep.castConst}", "replaceWChild(nodep,$thenp)");
TREEOP ("AstNodeCond{$condp, operandsSame($thenp,,$elsep)}","replaceWChild(nodep,$thenp)");
// This visit function here must allow for short-circuiting.
TREEOPS("AstCond {$lhsp.isZero}", "replaceWIteratedThs(nodep)");
TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)");
TREEOP ("AstCond{$condp.castNot, $expr1p, $expr2p}", "AstCond{$condp->op1p(), $expr2p, $expr1p}");
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isAllOnes, $expr2p}", "AstLogOr {$condp, $expr2p}"); // a?1:b == a||b
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isZero}", "AstLogAnd{$condp, $expr1p}"); // a?b:0 == a&&b
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isAllOnes}", "AstLogOr {AstNot{$condp}, $expr1p}"); // a?b:1 == ~a||b
TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isZero, $expr2p}", "AstLogAnd{AstNot{$condp}, $expr2p}"); // a?0:b == ~a&&b
TREEOP ("AstCond{$condp.castNot, $thenp, $elsep}", "AstCond{$condp->op1p(), $elsep, $thenp}");
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isAllOnes, $elsep}", "AstLogOr {$condp, $elsep}"); // a?1:b == a||b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isZero}", "AstLogAnd{$condp, $thenp}"); // a?b:0 == a&&b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp, $elsep.isAllOnes}", "AstLogOr {AstNot{$condp}, $thenp}"); // a?b:1 == ~a||b
TREEOP ("AstNodeCond{$condp.width1, $thenp.width1, $thenp.isZero, $elsep}", "AstLogAnd{AstNot{$condp}, $elsep}"); // a?0:b == ~a&&b
TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())");
// Prefer constants on left, since that often needs a shift, it lets
// constant red remove the shift
@@ -3479,7 +3536,7 @@ private:
// Note we can't convert EqCase/NeqCase to Eq/Neq here because that would break 3'b1x1==3'b101
//-----
virtual void visit(AstNode* nodep) override {
void visit(AstNode* nodep) override {
// Default: Just iterate
if (m_required) {
if (VN_IS(nodep, NodeDType) || VN_IS(nodep, Range) || VN_IS(nodep, SliceSel)) {
@@ -3526,9 +3583,13 @@ public:
}
// clang-format on
}
virtual ~ConstVisitor() override {
~ConstVisitor() override {
if (m_doCpp) {
V3Stats::addStat("Optimizations, Const bit op reduction", m_statBitOpReduction);
if (m_globalPass) {
V3Stats::addStat("Optimizations, Const bit op reduction", m_statBitOpReduction);
} else {
V3Stats::addStatSum("Optimizations, Const bit op reduction", m_statBitOpReduction);
}
}
}
@@ -3603,7 +3664,7 @@ void V3Const::constifyAllLint(AstNetlist* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_V_WARN, /* globalPass: */ true};
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
}
void V3Const::constifyCpp(AstNetlist* nodep) {
@@ -3612,7 +3673,7 @@ void V3Const::constifyCpp(AstNetlist* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ true};
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const_cpp", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("const_cpp", 0, dumpTree() >= 3);
}
AstNode* V3Const::constifyEdit(AstNode* nodep) {
@@ -3621,6 +3682,12 @@ AstNode* V3Const::constifyEdit(AstNode* nodep) {
return nodep;
}
AstNode* V3Const::constifyEditCpp(AstNode* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ false};
nodep = visitor.mainAcceptEdit(nodep);
return nodep;
}
void V3Const::constifyAllLive(AstNetlist* nodep) {
// Only call from Verilator.cpp, as it uses user#'s
// This only pushes constants up, doesn't make any other edits
@@ -3630,7 +3697,7 @@ void V3Const::constifyAllLive(AstNetlist* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_LIVE, /* globalPass: */ true};
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
}
void V3Const::constifyAll(AstNetlist* nodep) {
@@ -3640,7 +3707,7 @@ void V3Const::constifyAll(AstNetlist* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_V_EXPENSIVE, /* globalPass: */ true};
(void)visitor.mainAcceptEdit(nodep);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("const", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("const", 0, dumpTree() >= 3);
}
AstNode* V3Const::constifyExpensiveEdit(AstNode* nodep) {
+3
View File
@@ -39,6 +39,9 @@ public:
static void constifyCpp(AstNetlist* nodep);
// Only the current node and lower
// Return new node that may have replaced nodep
static AstNode* constifyEditCpp(AstNode* nodep);
// Only the current node and lower
// Return new node that may have replaced nodep
static AstNode* constifyEdit(AstNode* nodep);
// Only the current node and lower, with special SenTree optimization
// Return new node that may have replaced nodep
+69 -66
View File
@@ -27,13 +27,16 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Coverage.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <map>
#include <unordered_map>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Coverage state, as a visitor of each AstNode
@@ -85,7 +88,6 @@ private:
m_handleLines; // All line numbers for a given m_stateHandle
// METHODS
VL_DEBUG_FUNC; // Declare debug()
const char* varIgnoreToggle(AstVar* nodep) {
// Return true if this shouldn't be traced
@@ -118,7 +120,7 @@ private:
AstCoverDecl* const declp = new AstCoverDecl(fl, page, comment, linescov, offset);
declp->hier(hier);
m_modp->addStmtp(declp);
m_modp->addStmtsp(declp);
UINFO(9, "new " << declp << endl);
AstCoverInc* const incp = new AstCoverInc(fl, declp);
@@ -127,13 +129,13 @@ private:
incp->findUInt32DType());
varp->trace(true);
varp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
m_modp->addStmtp(varp);
m_modp->addStmtsp(varp);
UINFO(5, "New coverage trace: " << varp << endl);
AstAssign* const assp = new AstAssign(
incp->fileline(), new AstVarRef(incp->fileline(), varp, VAccess::WRITE),
new AstAdd(incp->fileline(), new AstVarRef(incp->fileline(), varp, VAccess::READ),
new AstConst(incp->fileline(), AstConst::WidthedValue(), 32, 1)));
incp->addNext(assp);
AstNode::addNext<AstNode, AstNode>(incp, assp);
}
return incp;
}
@@ -208,7 +210,7 @@ private:
}
// VISITORS - BOTH
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
const AstNodeModule* const origModp = m_modp;
VL_RESTORER(m_modp);
VL_RESTORER(m_state);
@@ -226,9 +228,9 @@ private:
}
}
virtual void visit(AstNodeProcedure* nodep) override { iterateProcedure(nodep); }
virtual void visit(AstWhile* nodep) override { iterateProcedure(nodep); }
virtual void visit(AstNodeFTask* nodep) override {
void visit(AstNodeProcedure* nodep) override { iterateProcedure(nodep); }
void visit(AstWhile* nodep) override { iterateProcedure(nodep); }
void visit(AstNodeFTask* nodep) override {
if (!nodep->dpiImport()) iterateProcedure(nodep);
}
void iterateProcedure(AstNode* nodep) {
@@ -244,11 +246,11 @@ private:
= newCoverInc(nodep->fileline(), "", "v_line", "block",
linesCov(m_state, nodep), 0, traceNameForLine(nodep, "block"));
if (AstNodeProcedure* const itemp = VN_CAST(nodep, NodeProcedure)) {
itemp->addStmtp(newp);
itemp->addStmtsp(newp);
} else if (AstNodeFTask* const itemp = VN_CAST(nodep, NodeFTask)) {
itemp->addStmtsp(newp);
} else if (AstWhile* const itemp = VN_CAST(nodep, While)) {
itemp->addBodysp(newp);
itemp->addStmtsp(newp);
} else {
nodep->v3fatalSrc("Bad node type");
}
@@ -257,7 +259,7 @@ private:
}
// VISITORS - TOGGLE COVERAGE
virtual void visit(AstVar* nodep) override {
void visit(AstVar* nodep) override {
iterateChildren(nodep);
if (m_modp && !m_inToggleOff && !m_state.m_inModOff && nodep->fileline()->coverageOn()
&& v3Global.opt.coverageToggle()) {
@@ -278,19 +280,19 @@ private:
// We'll do this, and make the if(...) coverinc later.
// Add signal to hold the old value
const string newvarname = string("__Vtogcov__") + nodep->shortName();
const string newvarname = std::string{"__Vtogcov__"} + nodep->shortName();
AstVar* const chgVarp
= new AstVar(nodep->fileline(), VVarType::MODULETEMP, newvarname, nodep);
chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
m_modp->addStmtp(chgVarp);
m_modp->addStmtsp(chgVarp);
// Create bucket for each dimension * bit.
// This is necessarily an O(n^2) expansion, which is why
// we limit coverage to signals with < 256 bits.
ToggleEnt newvec(string(""),
new AstVarRef(nodep->fileline(), nodep, VAccess::READ),
new AstVarRef(nodep->fileline(), chgVarp, VAccess::WRITE));
ToggleEnt newvec{std::string{""},
new AstVarRef{nodep->fileline(), nodep, VAccess::READ},
new AstVarRef{nodep->fileline(), chgVarp, VAccess::WRITE}};
toggleVarRecurse(nodep->dtypeSkipRefp(), 0, newvec, nodep, chgVarp);
newvec.cleanup();
}
@@ -303,7 +305,7 @@ private:
newCoverInc(varp->fileline(), "", "v_toggle", varp->name() + above.m_comment, "", 0,
""),
above.m_varRefp->cloneTree(true), above.m_chgRefp->cloneTree(true));
m_modp->addStmtp(newp);
m_modp->addStmtsp(newp);
}
void toggleVarRecurse(AstNodeDType* dtypep, int depth, // per-iteration
@@ -313,11 +315,12 @@ private:
for (int index_docs = bdtypep->lo(); index_docs < bdtypep->hi() + 1;
++index_docs) {
const int index_code = index_docs - bdtypep->lo();
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code, 1),
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code, 1));
ToggleEnt newent{above.m_comment + std::string{"["} + cvtToStr(index_docs)
+ "]",
new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true),
index_code, 1},
new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code, 1}};
toggleVarBottom(newent, varp);
newent.cleanup();
}
@@ -327,11 +330,11 @@ private:
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
const int index_code = index_docs - adtypep->lo();
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
new AstArraySel(varp->fileline(),
above.m_varRefp->cloneTree(true), index_code),
new AstArraySel(varp->fileline(),
above.m_chgRefp->cloneTree(true), index_code));
ToggleEnt newent{above.m_comment + std::string{"["} + cvtToStr(index_docs) + "]",
new AstArraySel{varp->fileline(),
above.m_varRefp->cloneTree(true), index_code},
new AstArraySel{varp->fileline(),
above.m_chgRefp->cloneTree(true), index_code}};
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth + 1, newent, varp,
chgVarp);
newent.cleanup();
@@ -340,11 +343,11 @@ private:
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
const AstNodeDType* const subtypep = adtypep->subDTypep()->skipRefp();
const int index_code = index_docs - adtypep->lo();
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code * subtypep->width(), subtypep->width()),
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code * subtypep->width(), subtypep->width()));
ToggleEnt newent{above.m_comment + std::string{"["} + cvtToStr(index_docs) + "]",
new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true),
index_code * subtypep->width(), subtypep->width()},
new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code * subtypep->width(), subtypep->width()}};
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth + 1, newent, varp,
chgVarp);
newent.cleanup();
@@ -355,11 +358,11 @@ private:
itemp = VN_AS(itemp->nextp(), MemberDType)) {
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp();
const int index_code = itemp->lsb();
ToggleEnt newent(above.m_comment + string(".") + itemp->name(),
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code, subtypep->width()),
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code, subtypep->width()));
ToggleEnt newent{above.m_comment + std::string{"."} + itemp->name(),
new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true),
index_code, subtypep->width()},
new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code, subtypep->width()}};
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
newent.cleanup();
}
@@ -367,9 +370,9 @@ private:
// Arbitrarily handle only the first member of the union
if (const AstMemberDType* const itemp = adtypep->membersp()) {
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp();
ToggleEnt newent(above.m_comment + string(".") + itemp->name(),
ToggleEnt newent{above.m_comment + std::string{"."} + itemp->name(),
above.m_varRefp->cloneTree(true),
above.m_chgRefp->cloneTree(true));
above.m_chgRefp->cloneTree(true)};
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
newent.cleanup();
}
@@ -381,12 +384,12 @@ private:
// VISITORS - LINE COVERAGE
// Note not AstNodeIf; other types don't get covered
virtual void visit(AstIf* nodep) override {
void visit(AstIf* nodep) override {
UINFO(4, " IF: " << nodep << endl);
if (m_state.m_on) {
// An else-if. When we iterate the if, use "elsif" marking
const bool elsif
= nodep->ifsp() && VN_IS(nodep->elsesp(), If) && !nodep->elsesp()->nextp();
= nodep->thensp() && VN_IS(nodep->elsesp(), If) && !nodep->elsesp()->nextp();
if (elsif) VN_AS(nodep->elsesp(), If)->user1(true);
const bool first_elsif = !nodep->user1() && elsif;
const bool cont_elsif = nodep->user1() && elsif;
@@ -401,7 +404,7 @@ private:
CheckState elseState;
{
createHandle(nodep);
iterateAndNextNull(nodep->ifsp());
iterateAndNextNull(nodep->thensp());
lineTrack(nodep);
ifState = m_state;
}
@@ -420,9 +423,9 @@ private:
// Normal if. Linecov shows what's inside the if (not condition that is
// always executed)
UINFO(4, " COVER-branch: " << nodep << endl);
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_branch", "if",
linesCov(ifState, nodep), 0,
traceNameForLine(nodep, "if")));
nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_branch", "if",
linesCov(ifState, nodep), 0,
traceNameForLine(nodep, "if")));
// The else has a column offset of 1 to uniquify it relative to the if
// As "if" and "else" are more than one character wide, this won't overlap
// another token
@@ -434,18 +437,18 @@ private:
else if (first_elsif || cont_elsif) {
UINFO(4, " COVER-elsif: " << nodep << endl);
if (ifState.lineCoverageOn(nodep)) {
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "elsif",
linesCov(ifState, nodep), 0,
traceNameForLine(nodep, "elsif")));
nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_line", "elsif",
linesCov(ifState, nodep), 0,
traceNameForLine(nodep, "elsif")));
}
// and we don't insert the else as the child if-else will do so
} else {
// Cover as separate blocks (not a branch as is not two-legged)
if (ifState.lineCoverageOn(nodep)) {
UINFO(4, " COVER-half-if: " << nodep << endl);
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "if",
linesCov(ifState, nodep), 0,
traceNameForLine(nodep, "if")));
nodep->addThensp(newCoverInc(nodep->fileline(), "", "v_line", "if",
linesCov(ifState, nodep), 0,
traceNameForLine(nodep, "if")));
}
if (elseState.lineCoverageOn(nodep)) {
UINFO(4, " COVER-half-el: " << nodep << endl);
@@ -458,7 +461,7 @@ private:
}
UINFO(9, " done HANDLE " << m_state.m_handle << " for " << nodep << endl);
}
virtual void visit(AstCaseItem* nodep) override {
void visit(AstCaseItem* nodep) override {
// We don't add an explicit "default" coverage if not provided,
// as we already have a warning when there is no default.
UINFO(4, " CASEI: " << nodep << endl);
@@ -466,38 +469,38 @@ private:
VL_RESTORER(m_state);
{
createHandle(nodep);
iterateAndNextNull(nodep->bodysp());
iterateAndNextNull(nodep->stmtsp());
if (m_state.lineCoverageOn(nodep)) { // if the case body didn't disable it
lineTrack(nodep);
UINFO(4, " COVER: " << nodep << endl);
nodep->addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case",
nodep->addStmtsp(newCoverInc(nodep->fileline(), "", "v_line", "case",
linesCov(m_state, nodep), 0,
traceNameForLine(nodep, "case")));
}
}
}
}
virtual void visit(AstCover* nodep) override {
void visit(AstCover* nodep) override {
UINFO(4, " COVER: " << nodep << endl);
VL_RESTORER(m_state);
{
m_state.m_on = true; // Always do cover blocks, even if there's a $stop
createHandle(nodep);
iterateChildren(nodep);
if (!nodep->coverincp() && v3Global.opt.coverageUser()) {
if (!nodep->coverincsp() && v3Global.opt.coverageUser()) {
// Note the name may be overridden by V3Assert processing
lineTrack(nodep);
nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover",
linesCov(m_state, nodep), 0,
m_beginHier + "_vlCoverageUserTrace"));
nodep->addCoverincsp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover",
linesCov(m_state, nodep), 0,
m_beginHier + "_vlCoverageUserTrace"));
}
}
}
virtual void visit(AstStop* nodep) override {
void visit(AstStop* nodep) override {
UINFO(4, " STOP: " << nodep << endl);
m_state.m_on = false;
}
virtual void visit(AstPragma* nodep) override {
void visit(AstPragma* nodep) override {
if (nodep->pragType() == VPragmaType::COVERAGE_BLOCK_OFF) {
// Skip all NEXT nodes under this block, and skip this if/case branch
UINFO(4, " OFF: h" << m_state.m_handle << " " << nodep << endl);
@@ -508,7 +511,7 @@ private:
lineTrack(nodep);
}
}
virtual void visit(AstBegin* nodep) override {
void visit(AstBegin* nodep) override {
// Record the hierarchy of any named begins, so we can apply to user
// coverage points. This is because there may be cov points inside
// generate blocks; each point should get separate consideration.
@@ -527,7 +530,7 @@ private:
}
// VISITORS - BOTH
virtual void visit(AstNode* nodep) override {
void visit(AstNode* nodep) override {
iterateChildren(nodep);
lineTrack(nodep);
}
@@ -535,7 +538,7 @@ private:
public:
// CONSTRUCTORS
explicit CoverageVisitor(AstNetlist* rootp) { iterateChildren(rootp); }
virtual ~CoverageVisitor() override = default;
~CoverageVisitor() override = default;
};
//######################################################################
@@ -544,5 +547,5 @@ public:
void V3Coverage::coverage(AstNetlist* rootp) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CoverageVisitor{rootp}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("coverage", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("coverage", 0, dumpTree() >= 3);
}
+10 -8
View File
@@ -20,13 +20,16 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3CoverageJoin.h"
#include "V3DupFinder.h"
#include "V3Global.h"
#include "V3Stats.h"
#include <vector>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// CoverageJoin state, as a visitor of each AstNode
@@ -41,7 +44,6 @@ private:
VDouble0 m_statToggleJoins; // Statistic tracking
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void detectDuplicates() {
UINFO(9, "Finding duplicates\n");
@@ -87,24 +89,24 @@ private:
}
// VISITORS
virtual void visit(AstNetlist* nodep) override {
void visit(AstNetlist* nodep) override {
// Find all Coverage's
iterateChildren(nodep);
// Simplify
detectDuplicates();
}
virtual void visit(AstCoverToggle* nodep) override {
void visit(AstCoverToggle* nodep) override {
m_toggleps.push_back(nodep);
iterateChildren(nodep);
}
//--------------------
virtual void visit(AstNodeMath*) override {} // Accelerate
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNodeMath*) override {} // Accelerate
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit CoverageJoinVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~CoverageJoinVisitor() override {
~CoverageJoinVisitor() override {
V3Stats::addStat("Coverage, Toggle points joined", m_statToggleJoins);
}
};
@@ -115,5 +117,5 @@ public:
void V3CoverageJoin::coverageJoin(AstNetlist* rootp) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ CoverageJoinVisitor{rootp}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("coveragejoin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("coveragejoin", 0, dumpTree() >= 3);
}
+31 -30
View File
@@ -36,13 +36,16 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Dead.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <map>
#include <vector>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Dead state, as a visitor of each AstNode
@@ -77,7 +80,6 @@ private:
bool m_sideEffect = false; // Side effects discovered in assign RHS
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void checkAll(AstNode* nodep) {
if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves
@@ -85,7 +87,7 @@ private:
}
if (AstNode* const subnodep = nodep->getChildDTypep()) subnodep->user1Inc();
}
void checkVarRef(AstNodeVarRef* nodep) {
void checkVarRef(AstNodeVarRef* nodep) const {
if (nodep->classOrPackagep() && m_elimCells) nodep->classOrPackagep(nullptr);
}
void checkDType(AstNodeDType* nodep) {
@@ -101,7 +103,7 @@ private:
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
void visit(AstNodeModule* nodep) override {
if (m_modp) m_modp->user1Inc(); // e.g. Class under Package
VL_RESTORER(m_modp);
{
@@ -119,12 +121,12 @@ private:
}
}
}
virtual void visit(AstCFunc* nodep) override {
void visit(AstCFunc* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->scopep()) nodep->scopep()->user1Inc();
}
virtual void visit(AstScope* nodep) override {
void visit(AstScope* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->aboveScopep()) nodep->aboveScopep()->user1Inc();
@@ -135,14 +137,14 @@ private:
m_scopesp.push_back(nodep);
}
}
virtual void visit(AstCell* nodep) override {
void visit(AstCell* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
m_cellsp.push_back(nodep);
nodep->modp()->user1Inc();
}
virtual void visit(AstNodeVarRef* nodep) override {
void visit(AstNodeVarRef* nodep) override {
// Note NodeAssign skips calling this in some cases
iterateChildren(nodep);
checkAll(nodep);
@@ -154,7 +156,7 @@ private:
if (nodep->varp()) nodep->varp()->user1Inc();
if (nodep->classOrPackagep()) nodep->classOrPackagep()->user1Inc();
}
virtual void visit(AstNodeFTaskRef* nodep) override {
void visit(AstNodeFTaskRef* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->classOrPackagep()) {
@@ -165,11 +167,11 @@ private:
}
}
}
virtual void visit(AstMethodCall* nodep) override {
void visit(AstMethodCall* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
}
virtual void visit(AstRefDType* nodep) override {
void visit(AstRefDType* nodep) override {
iterateChildren(nodep);
checkDType(nodep);
checkAll(nodep);
@@ -183,7 +185,7 @@ private:
}
}
}
virtual void visit(AstClassRefDType* nodep) override {
void visit(AstClassRefDType* nodep) override {
iterateChildren(nodep);
checkDType(nodep);
checkAll(nodep);
@@ -196,12 +198,12 @@ private:
}
if (nodep->classp()) nodep->classp()->user1Inc();
}
virtual void visit(AstNodeDType* nodep) override {
void visit(AstNodeDType* nodep) override {
iterateChildren(nodep);
checkDType(nodep);
checkAll(nodep);
}
virtual void visit(AstEnumItemRef* nodep) override {
void visit(AstEnumItemRef* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->classOrPackagep()) {
@@ -213,13 +215,13 @@ private:
}
checkAll(nodep);
}
virtual void visit(AstMemberSel* nodep) override {
void visit(AstMemberSel* nodep) override {
iterateChildren(nodep);
if (nodep->varp()) nodep->varp()->user1Inc();
if (nodep->fromp()->dtypep()) nodep->fromp()->dtypep()->user1Inc(); // classref
checkAll(nodep);
}
virtual void visit(AstModport* nodep) override {
void visit(AstModport* nodep) override {
iterateChildren(nodep);
if (m_elimCells) {
if (!nodep->varsp()) {
@@ -229,14 +231,14 @@ private:
}
checkAll(nodep);
}
virtual void visit(AstSelLoopVars* nodep) override {
void visit(AstSelLoopVars* nodep) override {
// Var under a SelLoopVars means we haven't called V3Width to remove them yet
VL_RESTORER(m_selloopvarsp);
m_selloopvarsp = nodep;
iterateChildren(nodep);
checkAll(nodep);
}
virtual void visit(AstTypedef* nodep) override {
void visit(AstTypedef* nodep) override {
iterateChildren(nodep);
if (m_elimCells && !nodep->attrPublic()) {
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
@@ -247,20 +249,20 @@ private:
// Normal modules may disappear, e.g. if they are parameterized then removed
if (nodep->attrPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
}
virtual void visit(AstVarScope* nodep) override {
void visit(AstVarScope* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->scopep()) nodep->scopep()->user1Inc();
if (mightElimVar(nodep->varp())) m_vscsp.push_back(nodep);
}
virtual void visit(AstVar* nodep) override {
void visit(AstVar* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (nodep->isSigPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
if (m_selloopvarsp) nodep->user1Inc();
if (mightElimVar(nodep)) m_varsp.push_back(nodep);
}
virtual void visit(AstNodeAssign* nodep) override {
void visit(AstNodeAssign* nodep) override {
// See if simple assignments to variables may be eliminated because
// that variable is never used.
// Similar code in V3Life
@@ -283,7 +285,7 @@ private:
}
//-----
virtual void visit(AstNode* nodep) override {
void visit(AstNode* nodep) override {
if (nodep->isOutputter()) m_sideEffect = true;
iterateChildren(nodep);
checkAll(nodep);
@@ -316,7 +318,7 @@ private:
}
}
}
bool mightElimVar(AstVar* nodep) {
bool mightElimVar(AstVar* nodep) const {
if (nodep->isSigPublic()) return false; // Can't elim publics!
if (nodep->isIO() || nodep->isClassMember()) return false;
if (nodep->isTemp() && !nodep->isTrace()) return true;
@@ -445,7 +447,7 @@ public:
// We may have removed some datatypes, cleanup
nodep->typeTablep()->repairCache();
}
virtual ~DeadVisitor() override = default;
~DeadVisitor() override = default;
};
//######################################################################
@@ -454,30 +456,29 @@ public:
void V3Dead::deadifyModules(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, false, false, false, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadModules", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTree() >= 6);
}
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, false, true, false, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypes", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTree() >= 3);
}
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, false, true, true, false}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0,
v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTree() >= 3);
}
void V3Dead::deadifyAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, true, true, false, true}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAll", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTree() >= 3);
}
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DeadVisitor{nodep, true, true, true, true}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTree() >= 3);
}
+20 -18
View File
@@ -51,15 +51,18 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Delayed.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3Stats.h"
#include <algorithm>
#include <deque>
#include <map>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Delayed state, as a visitor of each AstNode
@@ -101,7 +104,6 @@ private:
std::unordered_map<const AstVarScope*, int> m_scopeVecMap; // Next var number for each scope
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void markVarUsage(AstNodeVarRef* nodep, bool blocking) {
// Ignore if warning is disabled on this reference (used by V3Force).
@@ -152,13 +154,13 @@ private:
varp = new AstVar(oldvarscp->fileline(), VVarType::BLOCKTEMP, name,
VFlagBitPacked(), width);
}
addmodp->addStmtp(varp);
addmodp->addStmtsp(varp);
m_modVarMap.emplace(std::make_pair(addmodp, name), varp);
}
AstVarScope* const varscp
= new AstVarScope(oldvarscp->fileline(), oldvarscp->scopep(), varp);
oldvarscp->scopep()->addVarp(varscp);
oldvarscp->scopep()->addVarsp(varscp);
return varscp;
}
@@ -355,33 +357,33 @@ private:
postLogicp = new AstIf(nodep->fileline(),
new AstVarRef(nodep->fileline(), setvscp, VAccess::READ));
UINFO(9, " Created " << postLogicp << endl);
finalp->addStmtp(postLogicp);
finalp->addStmtsp(postLogicp);
finalp->user3p(setvscp); // Remember IF's vset variable
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
}
postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp));
postLogicp->addThensp(new AstAssign(nodep->fileline(), selectsp, valreadp));
return newlhsp;
}
// VISITORS
virtual void visit(AstNetlist* nodep) override {
void visit(AstNetlist* nodep) override {
// VV***** We reset all userp() on the netlist
m_modVarMap.clear();
iterateChildren(nodep);
}
virtual void visit(AstScope* nodep) override {
void visit(AstScope* nodep) override {
UINFO(4, " MOD " << nodep << endl);
AstNode::user3ClearTree();
iterateChildren(nodep);
}
virtual void visit(AstCFunc* nodep) override {
void visit(AstCFunc* nodep) override {
VL_RESTORER(m_cfuncp);
{
m_cfuncp = nodep;
iterateChildren(nodep);
}
}
virtual void visit(AstActive* nodep) override {
void visit(AstActive* nodep) override {
m_activep = nodep;
VL_RESTORER(m_inInitial);
{
@@ -391,7 +393,7 @@ private:
iterateChildren(nodep);
}
}
virtual void visit(AstAssignDly* nodep) override {
void visit(AstAssignDly* nodep) override {
m_inDly = true;
m_nextDlyp
= VN_CAST(nodep->nextp(), AssignDly); // Next assignment in same block, maybe nullptr.
@@ -425,7 +427,7 @@ private:
m_nextDlyp = nullptr;
}
virtual void visit(AstVarRef* nodep) override {
void visit(AstVarRef* nodep) override {
if (!nodep->user2Inc()) { // Not done yet
if (m_inDly && nodep->access().isWriteOrRW()) {
UINFO(4, "AssignDlyVar: " << nodep << endl);
@@ -487,18 +489,18 @@ private:
}
}
virtual void visit(AstNodeReadWriteMem* nodep) override {
void visit(AstNodeReadWriteMem* nodep) override {
VL_RESTORER(m_ignoreBlkAndNBlk);
m_ignoreBlkAndNBlk = true; // $readmem/$writemem often used in mem models
// so we will suppress BLKANDNBLK warnings
iterateChildren(nodep);
}
virtual void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE
void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc(
"For statements should have been converted to while statements in V3Begin");
}
virtual void visit(AstWhile* nodep) override {
void visit(AstWhile* nodep) override {
VL_RESTORER(m_inLoop);
{
m_inLoop = true;
@@ -507,12 +509,12 @@ private:
}
//--------------------
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit DelayedVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~DelayedVisitor() override {
~DelayedVisitor() override {
V3Stats::addStat("Optimizations, Delayed shared-sets", m_statSharedSet);
}
};
@@ -523,5 +525,5 @@ public:
void V3Delayed::delayedAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DelayedVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("delayed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
V3Global::dumpCheckGlobalTree("delayed", 0, dumpTree() >= 3);
}
+16 -17
View File
@@ -26,13 +26,16 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Depth.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3UniqueNames.h"
#include <algorithm>
VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
class DepthVisitor final : public VNVisitor {
@@ -48,7 +51,6 @@ private:
V3UniqueNames m_tempNames; // For generating unique temporary variable names
// METHODS
VL_DEBUG_FUNC; // Declare debug()
void createDeepTemp(AstNode* nodep) {
UINFO(6, " Deep " << nodep << endl);
@@ -68,14 +70,11 @@ private:
// Put assignment before the referencing statement
AstAssign* const assp = new AstAssign{
nodep->fileline(), new AstVarRef{nodep->fileline(), varp, VAccess::WRITE}, nodep};
VNRelinker linker2;
m_stmtp->unlinkFrBack(&linker2);
assp->addNext(m_stmtp);
linker2.relink(assp);
m_stmtp->addHereThisAsNext(assp);
}
// VISITORS
virtual void visit(AstCFunc* nodep) override {
void visit(AstCFunc* nodep) override {
VL_RESTORER(m_cfuncp);
VL_RESTORER(m_mtaskbodyp);
{
@@ -87,7 +86,7 @@ private:
iterateChildren(nodep);
}
}
virtual void visit(AstMTaskBody* nodep) override {
void visit(AstMTaskBody* nodep) override {
VL_RESTORER(m_cfuncp);
VL_RESTORER(m_mtaskbodyp);
{
@@ -108,7 +107,7 @@ private:
iterateChildren(nodep);
}
}
virtual void visit(AstNodeStmt* nodep) override {
void visit(AstNodeStmt* nodep) override {
if (!nodep->isStatement()) {
iterateChildren(nodep);
} else {
@@ -116,8 +115,8 @@ private:
}
}
// Operators
virtual void visit(AstNodeTermop* nodep) override {}
virtual void visit(AstNodeMath* nodep) override {
void visit(AstNodeTermop* nodep) override {}
void visit(AstNodeMath* nodep) override {
// We have some operator defines that use 2 parens, so += 2.
{
VL_RESTORER(m_depth);
@@ -144,19 +143,19 @@ private:
m_cfuncp->isStatic(false);
}
}
virtual void visit(AstUCFunc* nodep) override {
void visit(AstUCFunc* nodep) override {
needNonStaticFunc(nodep);
iterateChildren(nodep);
}
virtual void visit(AstUCStmt* nodep) override {
void visit(AstUCStmt* nodep) override {
needNonStaticFunc(nodep);
visitStmt(nodep);
}
//--------------------
// Default: Just iterate
virtual void visit(AstVar*) override {} // Don't hit varrefs under vars
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstVar*) override {} // Don't hit varrefs under vars
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
@@ -164,7 +163,7 @@ public:
: m_tempNames{"__Vdeeptemp"} {
iterate(nodep);
}
virtual ~DepthVisitor() override = default;
~DepthVisitor() override = default;
};
//######################################################################
@@ -173,5 +172,5 @@ public:
void V3Depth::depthAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ DepthVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("depth", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
V3Global::dumpCheckGlobalTree("depth", 0, dumpTree() >= 6);
}

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