Compare commits

...
307 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
Wilson Snyder 7c79f0d431 Version bump (Changes update) 2022-06-19 15:10:23 -04:00
Wilson Snyder 41b9b30b9b Version bump 2022-06-19 15:01:17 -04:00
Wilson Snyder 1fa82ffb7b Commentary: Update ChangeLog 2022-06-19 15:00:10 -04:00
Wilson Snyder 4f93ac6477 Internals: Style modernization. No functional change intended. 2022-06-15 18:49:32 -04:00
Krzysztof Bieganski f7533010c6 Internals: Add setNoopt() function to LifeVisitor (#3468) 2022-06-15 18:11:03 -04:00
Todd Strader 47b650d821 Fix public unpacked input ports (#3465) 2022-06-15 07:41:59 -04:00
Geza Lore 0c2c097377 Add -fno-merge-cond-motion option
This disables code motion during V3MergeCond, for debugging.
2022-06-13 14:16:11 +01:00
Geza Lore d721f70690 Commentary 2022-06-13 12:14:37 +01:00
Kevin Kiningham ea8aaa21e8 Fix compile error under strict C++11 mode (#3463) 2022-06-13 12:14:02 +01:00
Kamil Rakoczy 660d1059b0 With --no-decoration, remove output whitespace (#3460)
Signed-off-by: Kamil Rakoczy <[email protected]>
2022-06-10 07:26:33 -04:00
Wilson Snyder e7dc2de14b Fix BLKANDNBLK on $readmem/$writemem (#3379). 2022-06-04 12:43:18 -04:00
Wilson Snyder 32100a3c91 Tests: Fix racy tests for develop-v5. 2022-06-04 12:17:04 -04:00
Wilson Snyder 67f7432dd7 Commentary (#3436). 2022-06-04 08:37:42 -04:00
Wilson Snyder 59dc2853e3 Support concat assignment to packed array (#3446). 2022-06-03 21:32:13 -04:00
Wilson Snyder ada58465b2 Add -f<optimization> options to replace -O<letter> options (#3436). 2022-06-03 20:43:16 -04:00
Wilson Snyder 173f57c636 Changed --no-merge-const-pool to -fno-merge-const-pool (#3436). 2022-06-03 19:41:59 -04:00
Wilson Snyder 1f3e8640f7 Examples: -Os should be a compile flag, not Verilator flag. 2022-06-03 18:45:39 -04:00
Wilson Snyder 6039e9dcc3 Commentary 2022-06-02 21:32:22 -04:00
Yutetsu TAKATSUKASA d64f979f99 Fix BitOpTree optimization to consider polarity of frozen node (#3445) (#3459)
* Tests: add a test to another failing case of #3445

* Consider polarity as lsb in BitOpTree optimization.
2022-06-01 09:26:16 +09:00
Huanghuang Zhou 0c53d19113 Commentary: InstrCountVisitor documentation (#3457)
Signed-off-by: huanghuang.zhou <[email protected]>
2022-05-31 07:10:58 -04:00
Geza Lore c64a07fd09 CI: fix cache keys in test jobs 2022-05-30 18:35:59 +01:00
Geza Lore 694919b9d1 CI: add ubuntu-22.04 regressions 2022-05-30 18:34:41 +01:00
Geza Lore 606b35853b Configure and compile with C++17 on Ubuntu 22.04
The packaged libsystemc on Ubuntu 22.04 uses C++17, so default to that
on that platform. Keep C++14 elsewhere.
2022-05-30 18:34:41 +01:00
Yutetsu TAKATSUKASAandGeza Lore 26b7452178 Fix #3445 of BitOpTreeOpt (#3453)
* Tests: Check BitOpTree statistics in t_const_opt.

* Tests: Add a test to reproduce #3445

* Fix #3445. Don't forget LSB of frozen node in BitOpTreeOpt.

* Apply suggestions from code review

Co-authored-by: Geza Lore <[email protected]>
2022-05-30 19:33:06 +09:00
Geza Lore b51f887567 Perform VCD tracing in parallel when using --threads (#3449)
VCD tracing is now parallelized using the same thread pool as the model.
We achieve this by breaking the top level trace functions into multiple
top level functions (as many as --threads), and after emitting the time
stamp to the VCD file on the main thread, we execute the tracing
functions in parallel on the same thread pool as the model (which we
pass to the trace file during registration), tracing into a secondary
per thread buffer. The main thread will then stitch (memcpy) the buffers
together into the output file.

This makes the `--trace-threads` option redundant with `--trace`, which
now only affects `--trace-fst`. FST tracing uses the previous offloading
scheme.

This obviously helps a lot in VCD tracing performance, and I have seen
better than Amdahl speedup, namely I get 3.9x on XiangShan 4T (2.7x on
OpenTitan 4T).
2022-05-29 19:08:39 +01:00
Geza Lore c4b8675d77 Always inline some small, hot trace routines 2022-05-28 12:47:09 +01:00
Geza Lore a7cd7a1ed9 Initialize VerilatedTrace members in class 2022-05-28 12:47:07 +01:00
Geza Lore a48c779367 Rename verilated_trace_imp.cpp -> verilated_trace_imp.h
Also fix file header to describe purpose of this file.
2022-05-28 12:20:35 +01:00
Geza Lore cf1eccc24f Make local function 'static' in verilated_profiler.h 2022-05-28 12:17:39 +01:00
Geza Lore d45caca011 Remove legacy VCD tracing API
This has not been used by Verilator for a while, but was kept for
compatibility with some external code. Now removed.
2022-05-28 12:07:24 +01:00
Geza Lore 0722f47539 Improve V3MergeCond by reordering statements (#3125)
V3MergeCond merges consecutive conditional `_ = cond ? _ : _` and
`if (cond) ...` statements. This patch adds an analysis and ordering
phase that moves statements with identical conditions closer to each
other, in order to enable more merging opportunities. This in turn
eliminates a lot of repeated conditionals which reduced dynamic branch
count and branch misprediction rate. Observed 6.5% improvement on
multi-threaded large designs, at the cost of less than 2% increase in
Verilation speed.
2022-05-27 16:57:51 +01:00
github action a372e010bd Apply 'make format' 2022-05-25 04:51:51 +00:00
Wilson Snyder 530817191e Support non-ANSI interface port declarations (#3439). 2022-05-25 00:50:50 -04:00
Geza Lore 1282548a1c Only consider definitions in t_flag_csplit 2022-05-20 18:02:00 +01:00
Geza Lore c7610ed044 Fix FST tracing thread in CMake build 2022-05-20 17:04:46 +01:00
Geza Lore ffc1c51526 Commentary 2022-05-20 16:44:53 +01:00
Geza Lore b130a8cfeb Add -DVM_TRACE_VCD in model builds with Make with --trace 2022-05-20 16:44:38 +01:00
Geza Lore 551bd284dd Rename some internals related to multi-threaded tracing
Rename the implementation internals of current multi-threaded tracing to
be "offload mode". No functional change, nor user interface change
intended.
2022-05-20 16:44:35 +01:00
Krzysztof Bieganski 9edccfdffa Initial support for intra-assignment timing controls, net delays (#3427)
This is a pre-PR to #3363.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-05-17 19:19:44 +01:00
Geza Lore 1a056f6db9 Fix invalid conditional merging when starting at 'c = c ? a : b'
Fixes #3409.
2022-05-17 18:36:40 +01:00
Krzysztof Bieganski e018eb7bac Support AstClass::repairCache() after V3Class (#3431)
This is a pre-PR to #3363.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-05-17 09:22:43 -04:00
Krzysztof Bieganski 0a91ddf38a Tests: Better grep check in t_foreach (#3435)
This is a pre-PR to #3363.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-05-17 09:20:59 -04:00
Krzysztof Bieganski 67bb2c640e Tests: Rename t_timing_clkgen to t_timing_clkgen1 (#3430)
This is a pre-PR to #3363, which will introduce more clock gen tests.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-05-17 09:19:51 -04:00
Krzysztof Bieganski 561eaa311d Tests: Enable CI testing with GCC 10 (#3432)
This is a pre-PR to #3363.

Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-05-17 09:19:06 -04:00
Krzysztof Bieganski 3f7a248ed4 Refactor some of the Begin handling to a separate function (#3426)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-05-16 20:45:33 +01:00
Krzysztof Bieganski ecaa07a72a Rename AstTimingControl to AstEventControl (#3425)
Signed-off-by: Krzysztof Bieganski <[email protected]>
2022-05-16 20:44:41 +01:00
Wilson Snyder 99bdc27be3 Internals: Cleanup some statics, trivial part towards (#3419) 2022-05-15 14:26:55 -04:00
Wilson Snyder c3c46967dc Tests: Appease sanitizer (#3121). 2022-05-15 11:50:52 -04:00
Wilson Snyder 7f1a9239ab Commentary, fix typo (#3121) 2022-05-15 11:14:07 -04:00
Geza Lore c8102c8ffe Fix typo 2022-05-15 16:01:35 +01:00
Wilson Snyder 5aa12e9b51 Add assert when VerilatedContext is mis-deleted (#3121). 2022-05-15 10:51:03 -04:00
Geza Lore 829437b20b Commentary - dependencies 2022-05-15 15:25:46 +01:00
Wilson Snyder 3c4131d45d Fix 'with' operator with type casting (#3387). 2022-05-15 09:53:48 -04:00
Wilson Snyder ae8d8ee1ac Fix crash with misuse of display. 2022-05-15 09:29:45 -04:00
Geza Lore 89ec3d16dc Allow const nodes in VNRef
No functional change.
2022-05-15 13:30:07 +01:00
Wilson Snyder 7602c983c5 Commentary 2022-05-14 18:16:31 -04:00
HungMingWu 560efb2c9e Internals: Fix memory leak in V3FileLine (#3407) (#3408). No functional change intended. 2022-05-14 18:15:38 -04:00
Wilson Snyder c2328ef46a Spelling fixes. 2022-05-14 16:12:57 -04:00
Wilson Snyder f6035447ae Internals: Use mutable for mutexes. No functional change. 2022-05-13 07:21:39 -04:00
Wilson Snyder 38438b3373 Internals: Cleanup some defaults. No functional change. 2022-05-12 23:30:39 -04:00
Wilson Snyder 71dedccbbe Support compile time trace signal selection with tracing_on/off (#3323). 2022-05-12 22:28:08 -04:00
Martin Stadler cddbe4642f Fix cmake rules to support higher-level targest (#3377) (#3386).
Don't add linker flags as generator expression to support linking `TARGET` to higher-level targets in a top-level CMakeLists.txt file.
2022-05-11 21:33:05 -04:00
Wilson Snyder bdfdc737a0 Internals: Cleanup V3Config. No functional change intended. 2022-05-11 00:47:52 -04:00
Wilson Snyder 3d045c3aee Internals: Cleanup some verilog.y formatting. No functional change. 2022-05-09 00:37:51 -04:00
HungMingWu 9583f152ee Fix compile error when enable VL_LEAK_CHECKS (#3411).
Signed-off-by: HungMingWu <[email protected]>
2022-05-08 20:49:13 -04:00
Wilson Snyder 5b2755d28d Untabify verilog.y (#3412). No functional change. 2022-05-08 20:46:18 -04:00
Kamil Rakoczy 9378259779 Fix UNOPTFLAT warning from initial static var (#3406)
Signed-off-by: Kamil Rakoczy <[email protected]>
2022-05-06 10:24:03 +02:00
Wilson Snyder 3d762282b9 Fix hang with large case statement optimization (#3405). 2022-05-05 07:02:52 -04:00
Wilson Snyder 30783e6a79 devel release 2022-05-02 22:23:05 -04:00
Wilson Snyder aa86c777f4 Version bump 2022-05-02 22:17:20 -04:00
Geza Lore a2792785fe Add V3GraphVertex::dotRank to add GraphViz ranks to graph dumps
This is a simple debugging aid to allow constraining the graph layout
via GraphViz rank directives. Note this is not related in any way to the
vertex 'rank' attribute used by some of the graph algorithms.

No functional change.
2022-05-02 10:27:26 +01:00
Wilson Snyder 267315e7d4 Commentary: Update ChangeLog 2022-05-01 22:01:30 -04:00
Wilson Snyder 99d39cb6ff Fix autoconf warning 2022-05-01 10:43:04 -04:00
Wilson Snyder 915ceb2d04 Tests: Untabify tests. No functional change. 2022-05-01 10:10:00 -04:00
Geza Lore 49c90ecbce Issue consistent INITIALDLY/COMBDLY/BLKSEQ warnings
Some cases of warnings about the use of blocking and non-blocking
assignments in combinational vs sequential processes were suppressed in
a way that is inconsistent with the *actual* current execution model of
Verilator. Turning these back on to, well, warn the user that these might
cause unexpected results. V5 will clean these up, but until then err on
the side of caution.

Fixes #864.
2022-04-29 17:05:44 +01:00
Geza Lore 8395004d25 Add AstNode::exists and AstNode::forall predicates 2022-04-29 15:44:22 +01:00
Kamil Rakoczy 5de1c619c8 Fix foreach segmentation fault (#3400). 2022-04-28 06:11:31 -04:00
Yoda Lee a6d678d41d Fix hang in generate symbol references (#3391) (#3398) 2022-04-27 18:40:36 -04:00
Aliaksei Chapyzhenka 2b91d764b5 Added missing #include <memory> (#3392)
Fixes #3390
2022-04-23 20:11:46 +01:00
Geza Lore 62337bb6ac Future proofing some tests. No functional change. 2022-04-23 15:12:52 +01:00
Geza Lore 9abab2c366 Add separate AstInitialStatic node for static initializers
Static variable initializers run before initial blocks, so use an
explicitly different procedure type for them. This also enables us to
now raise errors for assignments to const variables in initial blocks.
2022-04-23 15:12:49 +01:00
Geza Lore b22e368b25 Add default parameters to some Ast nodes for convenience
Also update usage to utilize. No functional change.
2022-04-23 14:47:16 +01:00
Geza Lore a9cd2998e5 Don't mangle run-time library method names. 2022-04-23 14:47:16 +01:00
Geza Lore f1ea30f257 Use iterate*Const V3EmitV visitors. No functional change. 2022-04-23 14:47:12 +01:00
Geza Lore 0b74e9b354 Ensure topological ordering of module list.
At the end of V3Param, fix up the module list to be topologically
sorted. We need to do this at the end as a later instantiation of a
recursive module might instantiate an earlier specialization, which we
cannot know until we processed everything. The rest of the compiler
depends on the module list being topologically sorted.

Fixes #3393
2022-04-23 13:25:27 +01:00
Geza Lore 8189416d0c Partial cleanup of V3Param. No functional change. 2022-04-23 13:03:52 +01:00
Geza Lore 5f0e1fae7f Simplify and clarify reporting of enclosing instance
Rename AstNodeModule::hierName -> someInstanceName and explain that this
is only used for user messages.

Rename AstNode::locationStr -> instanceStr and simplify implementation.
In particular, do not report an instance if we can't find a reasonable
guess.
2022-04-22 23:38:23 +01:00
HungMingWu 880a9be3b1 Internal: Add C++20ish reverse_view for range loops. No functional change (#3388).
Signed-off-by: HungMingWu <[email protected]>
2022-04-18 13:03:56 -04:00
Wilson Snyder 7bfc1a00a7 Fix tracing interfaces inside interfaces (#3309). 2022-04-14 09:14:44 -04:00
Julien Margetts baff64a43d Add VK_USER_OBJS dependency to --create-lib library (#3370) (#3382). 2022-04-12 07:04:31 -04:00
github action b7f2bb0e80 Apply 'make format' 2022-04-12 10:54:48 +00:00
HungMingWu 08e0a397d3 Fix debugi-V3Param null pointer fault (#3380) (#3381)
Signed-off-by: HungMingWu <[email protected]>
2022-04-12 06:53:52 -04:00
Wilson Snyder 5f333be947 Internals: Dump TraceDecl codes. 2022-04-10 19:40:27 -04:00
Wilson Snyder f5f4e15ce2 Fix filenames with dots overwriting debug .vpp files (#3373). 2022-04-10 10:33:16 -04:00
Geza Lore fbd568dc47 Prep for multiple AstExecGraph. No functional change. 2022-04-10 12:00:17 +01:00
Geza Lore c79ea88576 Fix incorrect localization when encountering non-leaf functions.
Fixes #3286.
2022-04-09 20:30:39 +01:00
Wilson Snyder 9be4e7b576 Fix Bison 3.8.2 error (#3366). 2022-03-31 19:14:13 -04:00
Wilson Snyder 33105f017c Commentary 2022-03-30 20:17:59 -04:00
Wilson Snyder e02f97854c Deprecate 'vluint64_t' and similar types (#3255). 2022-03-27 15:27:40 -04:00
Wilson Snyder 960d0de361 Tests: Cleanup vluint_t/vlsint_t in tests, part of (#3255) 2022-03-27 15:03:25 -04:00
Wilson Snyder 31ce1fdfd3 Tests: Check legacy part of (#3255) 2022-03-27 14:50:49 -04:00
Wilson Snyder 9821381059 Tests: new t_lint_latch_5 test (#2997). 2022-03-27 14:42:36 -04:00
Wilson Snyder 3f7bf3d2dc Fix MSVC localtime_s (#3124). 2022-03-27 13:59:18 -04:00
Geza Lore f9e69984ff Set vlSymsp in modules at construction time.
This ensures it's available from very early on. No functional change.
2022-03-27 16:10:20 +01:00
Geza Lore b1b5b5dfe2 Improve run-time profiling
The --prof-threads option has been split into two independent options:
1. --prof-exec, for collecting verilator_gantt and other execution
related profiling data, and
2. --prof-pgo, for collecting data needed for PGO

The implementation of execution profiling is extricated from
VlThreadPool and is now a separate class VlExecutionProfiler. This means
--prof-exec can now be used for single-threaded models (though it does
not measure a lot of things just yet). For consistency VerilatedProfiler
is renamed VlPgoProfiler. Both VlExecutionProfiler and VlPgoProfiler are
in verilated_profiler.{h/cpp}, but can be used completely independently.

Also re-worked the execution profile format so it now only emits events
without holding onto any temporaries. This is in preparation for some
future optimizations that would be hindered by the introduction of function
locals via AstText.

Also removed the Barrier event. Clearing the profile buffers is not
notably more expensive as the profiling records are trivially
destructible.
2022-03-27 15:57:30 +02:00
Geza Lore c7440b250f Validate integer run-time arguments 2022-03-26 22:58:47 +00:00
Geza Lore bab8462789 Rebuild run-time library if generated makefile changes
The generated makefile contains compiler options that are passed when
building the run-time library, so re-build if it changes.
2022-03-26 21:29:03 +00:00
Wilson Snyder 4eaa6fdd06 Internals: Use python pass appropriately. No functional change intended. 2022-03-26 15:57:52 -04:00
Wilson Snyder 38b2845e2a Tests: Fix whitespace test and resulting fallout. 2022-03-26 15:41:32 -04:00
Xi Zhang 14d24213a8 Support LoongArch ISA multithreading (#3353) (#3354) 2022-03-17 09:04:47 -04:00
Wilson Snyder c61258c0d0 Commentary (#3351) 2022-03-14 09:10:03 -04:00
Yutetsu TAKATSUKASA 47226236f4 Internals: Resolve potential SEGV risk (#3350) 2022-03-13 18:13:51 +09:00
Wilson Snyder e744c21f56 devel release 2022-03-12 13:17:48 -05:00
1117 changed files with 58939 additions and 45281 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
+19 -3
View File
@@ -29,7 +29,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, ubuntu-18.04]
os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04]
compiler:
- { cc: clang, cxx: clang++ }
- { cc: gcc, cxx: g++ }
@@ -37,9 +37,16 @@ jobs:
exclude:
# Build pull requests only with ubuntu-20.04 and without m32
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-22.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-20.04
- {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-22.04, m32: 1}
include:
# Build GCC 10 on ubuntu-20.04
- os: ubuntu-20.04
compiler: { cc: gcc-10, cxx: g++-10 }
m32: 0
runs-on: ${{ matrix.os }}
name: Build | ${{ matrix.os }} | ${{ matrix.compiler.cc }} ${{ matrix.m32 && '| -m32' || '' }}
env:
@@ -90,7 +97,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, ubuntu-18.04]
os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04]
compiler:
- { cc: clang, cxx: clang++ }
- { cc: gcc, cxx: g++ }
@@ -99,9 +106,18 @@ jobs:
exclude:
# Build pull requests only with ubuntu-20.04 and without m32
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-18.04' || 'do-not-exclude' }}
- os: ${{ github.event_name == 'pull_request' && 'ubuntu-22.04' || 'do-not-exclude' }}
- m32: ${{ github.event_name == 'pull_request' && 1 || 'do-not-exclude' }}
# Build -m32 only on ubuntu-20.04
- {os: ubuntu-18.04, m32: 1}
- {os: ubuntu-22.04, m32: 1}
include:
# Test with GCC 10 on ubuntu-20.04 without m32
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: dist-vlt-0}
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: dist-vlt-1}
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: dist-vlt-2}
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: vltmt-0}
- {os: ubuntu-20.04, compiler: { cc: gcc-10, cxx: g++-10 }, m32: 0, suite: vltmt-1}
runs-on: ${{ matrix.os }}
name: Test | ${{ matrix.os }} | ${{ matrix.compiler.cc }} | ${{ matrix.suite }} ${{ matrix.m32 && '| -m32' || '' }}
env:
@@ -110,7 +126,7 @@ jobs:
CI_M32: ${{ matrix.m32 }}
CC: ${{ matrix.compiler.cc }}
CXX: ${{ matrix.compiler.cxx }}
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}-${ matrix.suite }}
CACHE_BASE_KEY: test-${{ matrix.os }}-${{ matrix.compiler.cc }}-m32=${{ matrix.m32 }}-${{ matrix.suite }}
CCACHE_MAXSIZE: 64M # Per build matrix entry (2160M in total)
VERILATOR_ARCHIVE: verilator-${{ github.sha }}-${{ matrix.os }}-${{ matrix.compiler.cc }}${{ matrix.m32 && '-m32' || '' }}.tar.gz
steps:
+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
+126 -13
View File
@@ -8,6 +8,119 @@ 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
==========================
**Major:**
* VCD tracing is now parallelized with --threads (#3449). [Geza Lore, Shunyao CAD]
**Minor:**
* Add -f<optimization> options to replace -O<letter> options (#3436).
* Changed --no-merge-const-pool to -fno-merge-const-pool (#3436).
* Changed --no-decoration to remove output whitespace (#3460). [Kamil Rakoczy]
* Support compile time trace signal selection with tracing_on/off (#3323). [Shunyao CAD]
* Support non-ANSI interface port declarations (#3439). [Geza Lore, Shunyao CAD]
* Support concat assignment to packed array (#3446).
* 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/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]
* Fix cmake rules to support higher-level targets (#3377) (#3386). [Martin Stadler]
* Fix BLKANDNBLK on $readmem/$writemem (#3379). [Alex Solomatnikov]
* Fix 'with' operator with type casting (#3387). [xiak95]
* Fix incorrect conditional merging (#3409). [Raynard Qiao]
* Fix passing VL_TRACE_FST_WRITER_THREAD in CMake build. [Geza Lore, Shunyao CAD]
* Fix compile error under strict C++11 mode (#3463). [Kevin Kiningham]
* Fix public unpacked input ports (#3465). [Todd Strader]
Verilator 4.222 2022-05-02
==========================
**Minor:**
* Split --prof-threads into --prof-exec and --prof-pgo (#3365). [Geza Lore, Shunyao CAD]
* Deprecate 'vluint64_t' and similar types (#3255).
* Raise error on assignment to const in initial blocks. [Geza Lore, Shunyao CAD]
* Issue INITIALDLY/COMBDLY/BLKSEQ warnings consistent with Verilator execution. [Geza Lore, Shunyao CAD]
* Support LoongArch ISA multithreading (#3353) (#3354). [Xi Zhang]
* Fix MSVC localtime_s (#3124).
* Fix Bison 3.8.2 error (#3366). [elike-ypq]
* Fix rare bug in -Oz (V3Localize) (#3286). [Geza Lore, Shunyao CAD]
* Fix tracing interfaces inside interfaces (#3309). [Kevin Millis]
* Fix filenames with dots overwriting debug .vpp files (#3373).
* Fix including VK_USER_OBJS in make library (#3370) (#3382). [Julien Margetts]
* Fix hang in generate symbol references (#3391) (#3398). [Yoda Lee]
* Fix missing #include <memory> (#3392). [Aliaksei Chapyzhenka]
* Fix crash in recursive module inlining (#3393). [david-sawatzke]
* Fix --protect-ids mangling names of library methods. [Geza Lore, Shunyao CAD]
* Fix foreach segmentation fault (#3400). [Kamil Rakoczy]
Verilator 4.220 2022-03-12
==========================
@@ -20,7 +133,7 @@ Verilator 4.220 2022-03-12
* Add VERILATOR_VERSION_INTEGER for determining API (#3343). [Larry Doolittle]
* Improve various V3Combine algorithm details (#3328). [Yutetsu TAKATSUKASA]
* Improve various V3Order algorithm details. [Geza Lore]
* Fix macOS arm64 build (#3285) (#3291). [Guokai Chen]
* Fix MacOS arm64 build (#3285) (#3291). [Guokai Chen]
* Fix signed number operation (#3294) (#3308). [Raynard Qiao]
* Fix FST traces to include vector range (#3296) (#3297). [Jamie Iles]
* Fix skipping public enum values with four-state values (#3303).
@@ -85,7 +198,7 @@ Verilator 4.216 2021-12-05
**Minor:**
* Internal code cleanups and improvements. [Geza Lore]
* Improve --thread verilation-time performance.
* Improve --thread Verilation-time performance.
* Support task name in $display %m (#3211). [Julie Schwartz]
* Make 'bit', 'logic' and 'time' types unsigned by default. [Geza Lore]
* Optimize $random concatenates/selects (#3114).
@@ -109,7 +222,7 @@ Verilator 4.214 2021-10-17
**Major:**
* Add profile-guided optmization of mtasks (#3150).
* Add profile-guided optimization of mtasks (#3150).
**Minor:**
@@ -139,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
@@ -175,7 +288,7 @@ Verilator 4.210 2021-07-07
* Add --prof-c to pass profiling to compiler (#3059). [Alexander Grobman]
* Optimize a lot more model variables into function locals (#3027). [Geza Lore]
* Support middle-of-design nested topmodules (#3026). [Dan Petrisko]
* Support middle-of-design nested top modules (#3026). [Dan Petrisko]
* Remove deprecated --no-relative-cfuncs option (#3024). [Geza Lore]
* Remove deprecated --inhibit-sim option (#3035). [Geza Lore]
* Merge const static data globally into a new constant pool (#3013). [Geza Lore]
@@ -204,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]
@@ -214,10 +327,10 @@ Verilator 4.204 2021-06-12
* Fix to emit 'else if' without nesting (#2944). [Geza Lore]
* Fix part select issues in LATCH warning (#2948) (#2938). [Julien Margetts]
* Fix to not emit empty files with low split limits (#2961). [Geza Lore]
* Fix merging of assignments in C++ code (#2970). [Ruper Swarbrick]
* Fix merging of assignments in C++ code (#2970). [Rupert Swarbrick]
* Fix unused variable warnings (#2991). [Pieter Kapsenberg]
* Fix --protect-ids when using SV classes (#2994). [Geza Lore]
* Fix constant function calls with uninit value (#2995). [yanx21]
* Fix constant function calls with uninitialized value (#2995). [yanx21]
* Fix Makefiles to support Windows EXEEXT usage (#3008). [Miodrag Milanovic]
@@ -255,7 +368,7 @@ Verilator 4.202 2021-04-24
* Fix Cygwin example compile issues (#2856). [Mark Shaw]
* Fix select of with index variable (#2880). [Alexander Grobman]
* Fix cmake version number to be numeric (#2881). [Yuri Victorovich]
* Fix MinGW not supportting 'localtime_r' (#2882). [HyungKi Jeong]
* Fix MinGW not supporting 'localtime_r' (#2882). [HyungKi Jeong]
* Fix cast from packed, typedef'ed interface signal (#2884). [Todd Strader]
* Fix VPI package reported as vpiModule (#2885). [Todd Strader]
* Fix dumping waveforms to multiple FST files (#2889). [David Metz]
@@ -319,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:**
@@ -379,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]
@@ -402,7 +515,7 @@ Verilator 4.102 2020-10-15
* Support # as a comment in -f files (#2497). [phantom-killua]
* Support 'this' (#2585). [Rafal Kapuscik]
* Support defines for FST tracing (#2592). [Markus Krause]
* Support non-overapping implication inside properties (#1292). [Peter Monsson]
* Support non-overlapping implication inside properties (#1292). [Peter Monsson]
* Fix timescale with --hierarchical (#2554). [Yutetsu TAKATSUKASA]
* Fix cmake build with --hierarchical (#2560). [Yutetsu TAKATSUKASA]
* Fix -G dropping public indication (#2561). [Andrew Goessling]
@@ -428,7 +541,7 @@ Verilator 4.100 2020-09-07
* Support (with limitations) class extern, class extends, virtual class.
* Support $urandom, $urandom_range without stability.
* Support assume property. [Peter Monsson]
* Support non-overapping implication inside properties (#1292). [Peter Monsson]
* Support non-overlapping implication inside properties (#1292). [Peter Monsson]
* Fix false DECLFILENAME on black-boxed modules (#2430). [Philipp Wagner]
* Fix naming of "id : begin" blocks.
* Fix class constructor error on assignments to const.
+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)
+19 -17
View File
@@ -21,14 +21,14 @@ Welcome to Verilator
.. list-table::
* - **Welcome to Verilator, the fastest Verilog/SystemVerilog simulator.**
* Accepts synthesizable Verilog or SystemVerilog
* Accepts Verilog or SystemVerilog
* Performs lint code-quality checks
* Compiles into multithreaded C++, or SystemC
* Creates XML to front-end your own tools
- |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,25 +55,25 @@ 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++). The resulting
executable performs the design simulation. Verilator also supports linking
its 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 NC-Verilog, 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
===========
Verilator does not simply convert Verilog HDL to C++ or SystemC. Rather,
Verilator does not directly translate Verilog HDL to C++ or SystemC. Rather,
Verilator compiles your code into a much faster optimized and optionally
thread-partitioned model, which is in turn wrapped inside a C++/SystemC
module. The results are a compiled Verilog model that executes even on a
@@ -84,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
============================
+37 -27
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
@@ -319,7 +324,9 @@ detailed descriptions of these arguments.
-f <file> Parse arguments from a file
-FI <file> Force include of a file
--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
@@ -327,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
--no-merge-const-pool Disable merging of different types in const pool
--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 vluint64_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-threads Enable generating gantt chart data for threads
--protect-key <key> Key for symbol protection
--prof-exec Enable generating execution profile for gantt chart
--prof-pgo Enable generating profiling data for PGO
--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
@@ -385,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
@@ -404,7 +410,7 @@ detailed descriptions of these arguments.
--trace-max-width <width> Maximum array depth for tracing
--trace-params Enable tracing of parameters
--trace-structs Enable tracing structure names
--trace-threads <threads> Enable waveform creation on separate threads
--trace-threads <threads> Enable FST waveform creation on separate threads
--trace-underscore Enable tracing of _signals
-U<var> Undefine preprocessor define
--unroll-count <loops> Tune maximum loop iterations
@@ -412,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
@@ -426,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
@@ -445,10 +455,10 @@ description of these arguments.
+verilator+error+limit+<value> Set error limit
+verilator+help Display help
+verilator+noassert Disable assert checking
+verilator+prof+threads+file+<filename> Set profile filename
+verilator+prof+threads+start+<value> Set profile starting point
+verilator+prof+threads+window+<value> Set profile duration
+verilator+prof+vlt+file+<filename> Set profile guided filename
+verilator+prof+exec+file+<filename> Set execution profile filename
+verilator+prof+exec+start+<value> Set execution profile starting point
+verilator+prof+exec+window+<value> Set execution profile duration
+verilator+prof+vlt+file+<filename> Set PGO profile filename
+verilator+rand+reset+<value> Set random reset technique
+verilator+seed+<value> Set random seed
+verilator+V Verbose version and config
+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']
+165 -144
View File
@@ -9,7 +9,7 @@ import re
import statistics
# from pprint import pprint
Threads = collections.defaultdict(lambda: {})
Threads = collections.defaultdict(lambda: collections.defaultdict(lambda: {}))
Mtasks = collections.defaultdict(lambda: {})
Evals = collections.defaultdict(lambda: {})
EvalLoops = collections.defaultdict(lambda: {})
@@ -30,12 +30,12 @@ def process(filename):
def read_data(filename):
with open(filename) as fh:
re_prof = re.compile(
r'^VLPROF mtask\s(\d+)\sstart\s(\d+)\selapsed\s(\d+)\spredict_start\s(\d+)\spredict_cost\s(\d+)\scpu\s(\d+)\son thread (\d+)'
)
re_eval = re.compile(r'^VLPROF eval\sstart\s(\d+)\selapsed\s(\d+)')
re_loop = re.compile(
r'^VLPROF eval_loop\sstart\s(\d+)\selapsed\s(\d+)')
re_thread = re.compile(r'^VLPROFTHREAD (\d+)$')
re_record = re.compile(r'^VLPROFEXEC (\S+) (\d+)(.*)$')
re_payload_mtaskBegin = re.compile(
r'id (\d+) predictStart (\d+) cpu (\d+)')
re_payload_mtaskEnd = re.compile(r'id (\d+) predictCost (\d+)')
re_arg1 = re.compile(r'VLPROF arg\s+(\S+)\+([0-9.]*)\s*')
re_arg2 = re.compile(r'VLPROF arg\s+(\S+)\s+([0-9.]*)\s*$')
re_stat = re.compile(r'VLPROF stat\s+(\S+)\s+([0-9.]+)')
@@ -43,47 +43,60 @@ def read_data(filename):
re_proc_cpu = re.compile(r'VLPROFPROC processor\s*:\s*(\d+)\s*$')
re_proc_dat = re.compile(r'VLPROFPROC ([a-z_ ]+)\s*:\s*(.*)$')
cpu = None
thread = None
lastEvalBeginTick = None
lastEvalLoopBeginTick = None
for line in fh:
if re_prof.match(line):
match = re_prof.match(line)
mtask = int(match.group(1))
start = int(match.group(2))
elapsed_time = int(match.group(3))
end = start + elapsed_time
predict_start = int(match.group(4))
predict_cost = int(match.group(5))
cpu = int(match.group(6))
thread = int(match.group(7))
if start not in Threads[thread]:
Threads[thread][start] = {}
Threads[thread][start]['mtask'] = mtask
Threads[thread][start]['end'] = end
Threads[thread][start]['cpu'] = cpu
Threads[thread][start]['predict_start'] = predict_start
Threads[thread][start]['predict_cost'] = predict_cost
if 'elapsed' not in Mtasks[mtask]:
Mtasks[mtask] = {'end': 0, 'elapsed': 0}
Mtasks[mtask]['thread'] = thread
Mtasks[mtask]['elapsed'] += elapsed_time
Mtasks[mtask]['predict_start'] = predict_start
Mtasks[mtask]['predict_cost'] = predict_cost
Mtasks[mtask]['end'] = max(Mtasks[mtask]['end'], end)
elif re_eval.match(line):
match = re_eval.match(line)
start = int(match.group(1))
elapsed_time = int(match.group(2))
Evals[start]['start'] = start
Evals[start]['end'] = start + elapsed_time
elif re_loop.match(line):
match = re_loop.match(line)
start = int(match.group(1))
elapsed_time = int(match.group(2))
EvalLoops[start]['start'] = start
EvalLoops[start]['end'] = start + elapsed_time
elif re.match(r'^VLPROFTHREAD', line):
None # pylint: disable=pointless-statement
recordMatch = re_record.match(line)
if recordMatch:
kind, tick, payload = recordMatch.groups()
tick = int(tick)
payload = payload.strip()
if kind == "EVAL_BEGIN":
Evals[tick]['start'] = tick
lastEvalBeginTick = tick
elif kind == "EVAL_END":
Evals[lastEvalBeginTick]['end'] = tick
lastEvalBeginTick = None
elif kind == "EVAL_LOOP_BEGIN":
EvalLoops[tick]['start'] = tick
lastEvalLoopBeginTick = tick
elif kind == "EVAL_LOOP_END":
EvalLoops[lastEvalLoopBeginTick]['end'] = tick
lastEvalLoopBeginTick = None
elif kind == "MTASK_BEGIN":
mtask, predict_start, ecpu = re_payload_mtaskBegin.match(
payload).groups()
mtask = int(mtask)
predict_start = int(predict_start)
ecpu = int(ecpu)
Threads[thread][tick]['mtask'] = mtask
Threads[thread][tick]['predict_start'] = predict_start
Threads[thread][tick]['cpu'] = ecpu
if 'elapsed' not in Mtasks[mtask]:
Mtasks[mtask] = {'end': 0, 'elapsed': 0}
Mtasks[mtask]['begin'] = tick
Mtasks[mtask]['thread'] = thread
Mtasks[mtask]['predict_start'] = predict_start
elif kind == "MTASK_END":
mtask, predict_cost = re_payload_mtaskEnd.match(
payload).groups()
mtask = int(mtask)
predict_cost = int(predict_cost)
begin = Mtasks[mtask]['begin']
Threads[thread][begin]['end'] = tick
Threads[thread][begin]['predict_cost'] = predict_cost
Mtasks[mtask]['elapsed'] += tick - begin
Mtasks[mtask]['predict_cost'] = predict_cost
Mtasks[mtask]['end'] = max(Mtasks[mtask]['end'], tick)
elif Args.debug:
print("-Unknown execution trace record: %s" % line)
elif re_thread.match(line):
thread = int(re_thread.match(line).group(1))
elif re.match(r'^VLPROF(THREAD|VERSION)', line):
pass
elif re_arg1.match(line):
match = re_arg1.match(line)
Global['args'][match.group(1)] = match.group(2)
@@ -105,7 +118,7 @@ def read_data(filename):
value = re.sub(r'\s+$', '', value)
Global['cpuinfo'][cpu][term] = value
elif re.match(r'^#', line):
None # pylint: disable=pointless-statement
pass
elif Args.debug:
print("-Unk: %s" % line)
# TODO -- this is parsing text printed by a client.
@@ -131,11 +144,13 @@ def report():
plus = "+" if re.match(r'^\+', arg) else " "
print(" %s%s%s" % (arg, plus, Global['args'][arg]))
nthreads = len(Threads)
nthreads = int(Global['stats']['threads'])
Global['cpus'] = {}
for thread in Threads:
# Make potentially multiple characters per column
for start in Threads[thread]:
if not Threads[thread][start]:
continue
cpu = Threads[thread][start]['cpu']
elapsed = Threads[thread][start]['end'] - start
if cpu not in Global['cpus']:
@@ -169,74 +184,79 @@ def report():
print("\nAnalysis:")
print(" Total threads = %d" % nthreads)
print(" Total mtasks = %d" % len(Mtasks))
ncpus = len(Global['cpus'])
ncpus = max(len(Global['cpus']), 1)
print(" Total cpus used = %d" % ncpus)
print(" Total yields = %d" % int(Global['stats']['yields']))
print(" Total yields = %d" %
int(Global['stats'].get('yields', 0)))
print(" Total evals = %d" % len(Evals))
print(" Total eval loops = %d" % len(EvalLoops))
print(" Total eval time = %d rdtsc ticks" %
Global['measured_last_end'])
print(" Longest mtask time = %d rdtsc ticks" % long_mtask_time)
print(" All-thread mtask time = %d rdtsc ticks" %
measured_mt_mtask_time)
long_efficiency = long_mtask_time / (Global.get('measured_last_end', 1)
or 1)
print(" Longest-thread efficiency = %0.1f%%" % (long_efficiency * 100.0))
mt_efficiency = measured_mt_mtask_time / (
Global.get('measured_last_end', 1) * nthreads or 1)
print(" All-thread efficiency = %0.1f%%" % (mt_efficiency * 100.0))
print(" All-thread speedup = %0.1f" % (mt_efficiency * nthreads))
if Global['rdtsc_cycle_time'] > 0:
ut = measured_mt_mtask_time / Global['rdtsc_cycle_time']
print("tot_mtask_cpu=" + measured_mt_mtask_time + " cyc=" +
Global['rdtsc_cycle_time'] + " ut=" + ut)
if Mtasks:
print(" Total eval time = %d rdtsc ticks" %
Global['measured_last_end'])
print(" Longest mtask time = %d rdtsc ticks" % long_mtask_time)
print(" All-thread mtask time = %d rdtsc ticks" %
measured_mt_mtask_time)
long_efficiency = long_mtask_time / (Global.get(
'measured_last_end', 1) or 1)
print(" Longest-thread efficiency = %0.1f%%" %
(long_efficiency * 100.0))
mt_efficiency = measured_mt_mtask_time / (
Global.get('measured_last_end', 1) * nthreads or 1)
print(" All-thread efficiency = %0.1f%%" %
(mt_efficiency * 100.0))
print(" All-thread speedup = %0.1f" %
(mt_efficiency * nthreads))
if Global['rdtsc_cycle_time'] > 0:
ut = measured_mt_mtask_time / Global['rdtsc_cycle_time']
print("tot_mtask_cpu=" + measured_mt_mtask_time + " cyc=" +
Global['rdtsc_cycle_time'] + " ut=" + ut)
predict_mt_efficiency = predict_mt_mtask_time / (
Global.get('predict_last_end', 1) * nthreads or 1)
print("\nPrediction (what Verilator used for scheduling):")
print(" All-thread efficiency = %0.1f%%" %
(predict_mt_efficiency * 100.0))
print(" All-thread speedup = %0.1f" %
(predict_mt_efficiency * nthreads))
predict_mt_efficiency = predict_mt_mtask_time / (
Global.get('predict_last_end', 1) * nthreads or 1)
print("\nPrediction (what Verilator used for scheduling):")
print(" All-thread efficiency = %0.1f%%" %
(predict_mt_efficiency * 100.0))
print(" All-thread speedup = %0.1f" %
(predict_mt_efficiency * nthreads))
p2e_ratios = []
min_p2e = 1000000
min_mtask = None
max_p2e = -1000000
max_mtask = None
p2e_ratios = []
min_p2e = 1000000
min_mtask = None
max_p2e = -1000000
max_mtask = None
for mtask in sorted(Mtasks.keys()):
if Mtasks[mtask]['elapsed'] > 0:
if Mtasks[mtask]['predict_cost'] == 0:
Mtasks[mtask]['predict_cost'] = 1 # don't log(0) below
p2e_ratio = math.log(Mtasks[mtask]['predict_cost'] /
Mtasks[mtask]['elapsed'])
p2e_ratios.append(p2e_ratio)
for mtask in sorted(Mtasks.keys()):
if Mtasks[mtask]['elapsed'] > 0:
if Mtasks[mtask]['predict_cost'] == 0:
Mtasks[mtask]['predict_cost'] = 1 # don't log(0) below
p2e_ratio = math.log(Mtasks[mtask]['predict_cost'] /
Mtasks[mtask]['elapsed'])
p2e_ratios.append(p2e_ratio)
if p2e_ratio > max_p2e:
max_p2e = p2e_ratio
max_mtask = mtask
if p2e_ratio < min_p2e:
min_p2e = p2e_ratio
min_mtask = mtask
if p2e_ratio > max_p2e:
max_p2e = p2e_ratio
max_mtask = mtask
if p2e_ratio < min_p2e:
min_p2e = p2e_ratio
min_mtask = mtask
print("\nStatistics:")
print(" min log(p2e) = %0.3f" % min_p2e, end="")
print(" from mtask %d (predict %d," %
(min_mtask, Mtasks[min_mtask]['predict_cost']),
end="")
print(" elapsed %d)" % Mtasks[min_mtask]['elapsed'])
print(" max log(p2e) = %0.3f" % max_p2e, end="")
print(" from mtask %d (predict %d," %
(max_mtask, Mtasks[max_mtask]['predict_cost']),
end="")
print(" elapsed %d)" % Mtasks[max_mtask]['elapsed'])
print("\nMTask statistics:")
print(" min log(p2e) = %0.3f" % min_p2e, end="")
print(" from mtask %d (predict %d," %
(min_mtask, Mtasks[min_mtask]['predict_cost']),
end="")
print(" elapsed %d)" % Mtasks[min_mtask]['elapsed'])
print(" max log(p2e) = %0.3f" % max_p2e, end="")
print(" from mtask %d (predict %d," %
(max_mtask, Mtasks[max_mtask]['predict_cost']),
end="")
print(" elapsed %d)" % Mtasks[max_mtask]['elapsed'])
stddev = statistics.pstdev(p2e_ratios)
mean = statistics.mean(p2e_ratios)
print(" mean = %0.3f" % mean)
print(" stddev = %0.3f" % stddev)
print(" e ^ stddev = %0.3f" % math.exp(stddev))
stddev = statistics.pstdev(p2e_ratios)
mean = statistics.mean(p2e_ratios)
print(" mean = %0.3f" % mean)
print(" stddev = %0.3f" % stddev)
print(" e ^ stddev = %0.3f" % math.exp(stddev))
report_cpus()
@@ -375,44 +395,45 @@ def write_vcd(filename):
vcd['values'][eval_start][elcode] = n
vcd['values'][eval_end][elcode] = None
# Predicted graph
for eval_start in EvalLoops:
eval_end = EvalLoops[eval_start]['end']
# Compute scale so predicted graph is of same width as eval
measured_scaling = (eval_end -
eval_start) / Global['predict_last_end']
# Predict mtasks that fill the time the eval occupied
for mtask in Mtasks:
thread = Mtasks[mtask]['thread']
pred_scaled_start = eval_start + int(
Mtasks[mtask]['predict_start'] * measured_scaling)
pred_scaled_end = eval_start + int(
(Mtasks[mtask]['predict_start'] +
Mtasks[mtask]['predict_cost']) * measured_scaling)
if pred_scaled_start == pred_scaled_end:
continue
if Mtasks:
# Predicted graph
for eval_start in EvalLoops:
eval_end = EvalLoops[eval_start]['end']
# Compute scale so predicted graph is of same width as eval
measured_scaling = (eval_end -
eval_start) / Global['predict_last_end']
# Predict mtasks that fill the time the eval occupied
for mtask in Mtasks:
thread = Mtasks[mtask]['thread']
pred_scaled_start = eval_start + int(
Mtasks[mtask]['predict_start'] * measured_scaling)
pred_scaled_end = eval_start + int(
(Mtasks[mtask]['predict_start'] +
Mtasks[mtask]['predict_cost']) * measured_scaling)
if pred_scaled_start == pred_scaled_end:
continue
sig = "predicted_thread%d_mtask" % thread
if sig not in vcd['sigs']['predicted_threads']:
vcd['sigs']['predicted_threads'][sig] = code
code += 1
mcode = vcd['sigs']['predicted_threads'][sig]
sig = "predicted_thread%d_mtask" % thread
if sig not in vcd['sigs']['predicted_threads']:
vcd['sigs']['predicted_threads'][sig] = code
code += 1
mcode = vcd['sigs']['predicted_threads'][sig]
vcd['values'][pred_scaled_start][mcode] = mtask
vcd['values'][pred_scaled_end][mcode] = None
vcd['values'][pred_scaled_start][mcode] = mtask
vcd['values'][pred_scaled_end][mcode] = None
parallelism['predicted'][pred_scaled_start] += 1
parallelism['predicted'][pred_scaled_end] -= 1
parallelism['predicted'][pred_scaled_start] += 1
parallelism['predicted'][pred_scaled_end] -= 1
# Parallelism graph
for measpred in ('measured', 'predicted'):
vcd['sigs']['Stats']["%s_parallelism" % measpred] = code
pcode = code
code += 1
value = 0
for time in sorted(parallelism[measpred].keys()):
value += parallelism[measpred][time]
vcd['values'][time][pcode] = value
# Parallelism graph
for measpred in ('measured', 'predicted'):
vcd['sigs']['Stats']["%s_parallelism" % measpred] = code
pcode = code
code += 1
value = 0
for time in sorted(parallelism[measpred].keys()):
value += parallelism[measpred][time]
vcd['values'][time][pcode] = value
# Create output file
fh.write("$version Generated by verilator_gantt $end\n")
@@ -476,10 +497,10 @@ parser.add_argument('--no-vcd',
action='store_true')
parser.add_argument('--vcd',
help='filename for vcd outpue',
default='profile_threads.vcd')
default='profile_exec.vcd')
parser.add_argument('filename',
help='input profile_threads.dat filename to process',
default='profile_threads.dat')
help='input profile_exec.dat filename to process',
default='profile_exec.dat')
Args = parser.parse_args()
+11 -3
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;
}
@@ -54,8 +58,12 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then
if [ "$CI_OS_NAME" = "linux" ]; then
sudo apt-get update
sudo apt-get install libfl-dev libgoogle-perftools-dev ccache
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then
sudo apt-get install libfl-dev ccache
if [ "$CI_RUNS_ON" != "ubuntu-22.04" ]; then
# Some conflict of libunwind verison on 22.04, can live without it for now
sudo apt-get install libgoogle-perftools-dev
fi
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
sudo apt-get install libsystemc libsystemc-dev
fi
if [ "$COVERAGE" = 1 ]; then
@@ -85,7 +93,7 @@ elif [ "$CI_BUILD_STAGE_NAME" = "test" ]; then
sudo apt-get update
# libfl-dev needed for internal coverage's test runs
sudo apt-get install gdb gtkwave lcov libfl-dev ccache
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ]; then
if [ "$CI_RUNS_ON" = "ubuntu-20.04" ] || [ "$CI_RUNS_ON" = "ubuntu-22.04" ]; then
sudo apt-get install libsystemc-dev
fi
if [ "$CI_M32" = 1 ]; then
+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 \
+14 -10
View File
@@ -5,16 +5,16 @@
# 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.220 2022-03-12],
[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_HEADER(src/config_build.h)
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)
# Version
@@ -348,14 +348,18 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE)
# Flag to select newest language standard supported
# Macros work such that first option that passes is the one we take
# Currently enabled c++14 due to packaged SystemC dependency
# c++14 is the newest that Verilator is regressed to support
# Currently enable c++17/c++14 due to packaged SystemC dependency
# c++17 is the newest that Verilator is regularly tested to support
# c++11 is the oldest that Verilator supports
# gnu is requried for Cygwin to compile verilated.h successfully
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
case "$(which lsb_release 2>&1 > /dev/null && lsb_release -d)" in
*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)
;;
esac
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++14)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++14)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++11)
+1
View File
@@ -2,3 +2,4 @@
*.html
*.pdf
_build
guide/spelling.txt
+18
View File
@@ -5,10 +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
@@ -31,14 +34,17 @@ Gianfranco Costamagna
Glen Gibb
Graham Rushton
Guokai Chen
Gustav Svensk
Harald Heckmann
Howard Su
Huang Rui
Huanghuang Zhou
HungMingWu
HyungKi Jeong
Iru Cai
Ivan Vnučec
Iztok Jeras
Jake Merdich
James Hanlon
James Hutchinson
James Pallister
@@ -54,6 +60,7 @@ Josh Redford
Julie Schwartz
Julien Margetts
Kaleb Barrett
Kamil Rakoczy
Kanad Kanhere
Keith Colbert
Kevin Kiningham
@@ -65,21 +72,26 @@ Lukasz Dalek
Maarten De Braekeleer
Maciej Sobkowski
Marco Widmer
Mariusz Glebocki
Markus Krause
Marlon James
Marshal Qiao
Martin Schmidt
Martin Stadler
Matthew Ballance
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
@@ -91,6 +103,7 @@ Rafal Kapuscik
Raynard Qiao
Richard Myers
Rupert Swarbrick
Ryszard Rozak
Samuel Riedel
Sean Cross
Sebastien Van Cauwenberghe
@@ -107,12 +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
+2
View File
@@ -35,6 +35,7 @@ default:
# Intermediate rules
vl-extract: ../bin/verilator ../Changes
ln -sf ../spelling.txt guide/spelling.txt
mkdir -p _build/gen
$(PYTHON3) bin/vl_sphinx_extract ../bin/verilator
sed 's/`/\&96;/g' < ../Changes > _build/gen/Changes
@@ -77,6 +78,7 @@ clean mostlyclean distclean maintainer-clean::
rm -f *.pg *.pgs *.toc *.tp *.tps *.vr *.vrs *.idx
rm -f *.ev *.evs *.ov *.ovs *.cv *.cvs *.ma *.mas
rm -f *.tex
rm -f guide/spelling.txt
distclean maintainer-clean::
rm -f *.info* *.1 *.html *.pdf $(INFOS)
+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,
+20 -58
View File
@@ -86,58 +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
vluint64_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
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
@@ -150,7 +109,7 @@ netlist as an instantiation.
The SC_MODULE gets the same pinout as the Verilog module, with the
following type conversions: Pins of a single bit become bool. Pins 2-32
bits wide become uint32_t's. Pins 33-64 bits wide become sc_bv's or
vluint64_t's depending on the :vlopt:`--no-pins64` option. Wider pins
uint64_t's depending on the :vlopt:`--no-pins64` option. Wider pins
become sc_bv's. (Uints simulate the fastest so are used where possible.)
Model internals, including lower level sub-modules are not pure SystemC
@@ -200,7 +159,7 @@ DPI System Task/Functions
-------------------------
Verilator extends the DPI format to allow using the same scheme to
efficiently add system functions. Simply use a dollar-sign prefixed system
efficiently add system functions. Use a dollar-sign prefixed system
function name for the import, but note it must be escaped.
.. code-block:: sv
@@ -257,7 +216,9 @@ with respect to that top level module, then the scope could be set with
#include "svdpi.h"
...
svSetScope(svGetScopeFromName("TOP.dut"));
const svScope scope = svGetScopeFromName("TOP.dut");
assert(scope); // Check for nullptr if scope not found
svSetScope(scope);
(Remember that Verilator adds a "TOP" to the top of the module hierarchy.)
@@ -427,7 +388,7 @@ accesses the above signal "readme" would be:
#include "verilated.h"
#include "verilated_vpi.h" // Required to get definitions
vluint64_t main_time = 0; // See comments in first example
uint64_t main_time = 0; // See comments in first example
double sc_time_stamp() { return main_time; }
void read_and_check() {
@@ -444,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
@@ -506,7 +468,7 @@ structure. If a ``VerilatedContext`` is not created prior to creating a
model, a default global one is created automatically.
The ``Verilated::`` methods, including the ``Verilated::commandArgs`` call
shown above, simply call VerilatedContext methods using the default global
shown above, call VerilatedContext methods using the default global
VerilatedContext. (Technically they operate on the last one used by a
given thread.) If you are using multiple simulation contexts you should
not use the Verilated:: methods, and instead always use VerilatedContext
+2 -2
View File
@@ -23,14 +23,14 @@ Contributors
Many people have provided ideas and other assistance with Verilator.
Verilator is receiving major development support from the `CHIPS Alliance
<https://chipsalliance.org>`_.
<https://chipsalliance.org>`_ and `Shunyao CAD <https://shunyaocad.com>`_.
Previous major corporate sponsors of Verilator, by providing significant
contributions of time or funds included include: Atmel Corporation, Cavium
Inc., Compaq Corporation, Digital Equipment Corporation, Embecosm Ltd.,
Hicamp Systems, Intel Corporation, Mindspeed Technologies Inc., MicroTune
Inc., picoChip Designs Ltd., Sun Microsystems Inc., Nauticus Networks Inc.,
and SiCortex Inc.
SiCortex Inc, and Shunyao CAD.
The people who have contributed major functionality are: Byron Bradley,
Jeremy Bennett, Lane Brooks, John Coiner, Duane Galbi, Geza Lore, Todd
+16
View File
@@ -19,3 +19,19 @@ Verilated_heavy.h
Option `--cdc`
The experimental `--cdc` option is believed to be generally unused and is
planned for removal no sooner than January 2023.
Option `-O<letter>`
The debug `-O<letter>` options have been replaced with
`-fno-<optimization>` debug options to match GCC. The old options are
planned for removal no sooner than June 2023.
Option `--prof-threads`
The `--prof-threads` option has been superseded by the `--prof-exec` and
`--prof-pgo` options and is planned for removal no sooner than April 2023.
Verilated model options `+verilator+prof+threads+*`
The `+verilator+prof+threads+start`, `+verilator+prof+threads+window` and
`+verilator+prof+threads+file` options have been superseded by the
`+verilator+prof+exec+start`, `+verilator+prof+exec+window` and
`+verilator+prof+exec+file` options respectively and are planned for removal
no sooner than April 2023.
+23 -11
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
@@ -38,33 +38,45 @@ Summary:
Display help and exit.
.. option:: +verilator+prof+threads+file+<filename>
.. option:: +verilator+prof+exec+file+<filename>
When a model was Verilated using :vlopt:`--prof-threads`, sets the
When a model was Verilated using :vlopt:`--prof-exec`, sets the
simulation runtime filename to dump to. Defaults to
:file:`profile_threads.dat`.
:file:`profile_exec.dat`.
.. option:: +verilator+prof+threads+start+<value>
.. option:: +verilator+prof+exec+start+<value>
When a model was Verilated using :vlopt:`--prof-threads`, the simulation
When a model was Verilated using :vlopt:`--prof-exec`, the simulation
runtime will wait until $time is at this value (expressed in units of
the time precision), then start the profiling warmup, then
capturing. Generally this should be set to some time that is well within
the normal operation of the simulation, i.e. outside of reset. If 0, the
dump is disabled. Defaults to 1.
.. option:: +verilator+prof+threads+window+<value>
.. option:: +verilator+prof+exec+window+<value>
When a model was Verilated using :vlopt:`--prof-threads`, after $time
reaches :vlopt:`+verilator+prof+threads+start+\<value\>`, Verilator will
When a model was Verilated using :vlopt:`--prof-exec`, after $time
reaches :vlopt:`+verilator+prof+exec+start+\<value\>`, Verilator will
warm up the profiling for this number of eval() calls, then will capture
the profiling of this number of eval() calls. Defaults to 2, which
makes sense for a single-clock-domain module where it's typical to want
to capture one posedge eval() and one negedge eval().
.. option:: +verilator+prof+threads+file+<filename>
Deprecated. Alias for :vlopt:`+verilator+prof+exec+file+\<filename\>`
.. option:: +verilator+prof+threads+start+<value>
Deprecated. Alias for :vlopt:`+verilator+prof+exec+start+\<value\>`
.. option:: +verilator+prof+threads+window+<value>
Deprecated. Alias for :vlopt:`+verilator+prof+exec+window+\<filename\>`
.. option:: +verilator+prof+vlt+file+<filename>
When a model was Verilated using :vlopt:`--prof-threads`, sets the
When a model was Verilated using :vlopt:`--prof-pgo`, sets the
profile-guided optimization data runtime filename to dump to. Defaults
to :file:`profile.vlt`.
@@ -84,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
+276 -141
View File
@@ -99,10 +99,10 @@ Summary:
.. option:: --bbox-sys
Black box any unknown $system task or function calls. System tasks will
simply become no-operations, and system functions will be replaced with
unsized zero. Arguments to such functions will be parsed, but not
otherwise checked. This prevents errors when linting in the presence of
company specific PLI calls.
become no-operations, and system functions will be replaced with unsized
zero. Arguments to such functions will be parsed, but not otherwise
checked. This prevents errors when linting in the presence of company
specific PLI calls.
Using this argument will likely cause incorrect simulation.
@@ -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++
@@ -431,6 +471,78 @@ Summary:
flattening large designs may require significant CPU time, memory and
storage.
.. option:: -fno-acyc-simp
.. option:: -fno-assemble
.. option:: -fno-case
.. option:: -fno-combine
.. option:: -fno-const
.. option:: -fno-const-bit-op-tree
.. option:: -fno-dedup
.. option:: -fno-expand
.. option:: -fno-gate
.. option:: -fno-inline
.. option:: -fno-life
.. option:: -fno-life-post
.. option:: -fno-localize
.. option:: -fno-merge-cond
.. option:: -fno-merge-cond-motion
.. option:: -fno-merge-const-pool
.. option:: -fno-reloop
.. option:: -fno-reorder
.. option:: -fno-split
.. option:: -fno-subst
.. option:: -fno-subst-const
.. option:: -fno-table
Rarely needed. Disables one of the internal optimization steps. These
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
@@ -531,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>
@@ -568,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>
@@ -592,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
@@ -625,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
@@ -648,40 +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:: --no-merge-const-pool
.. option:: --MMD
Rarely needed. In order to minimize cache footprint, values of different
data type, that are yet emitted identically in C++ are merged in the
constant pool. This option disables this and causes every constant pool
entry with a distinct data type to be emitted separately.
.. 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
@@ -703,9 +795,9 @@ Summary:
Rarely needed. Enables or disables a specific optimizations, with the
optimization selected based on the letter passed. A lowercase letter
disables an optimization, an upper case letter enables it. This is
intended for debugging use only; see the source code for
version-dependent mappings of optimizations to -O letters.
disables an optimization, an upper case letter enables it. This option
is deprecated and the various `-f<optimization>` arguments should be
used instead.
.. option:: -o <executable>
@@ -760,15 +852,10 @@ 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>
bits wide should use sc_bv's instead of uint32/vluint64_t's. The
bits wide should use sc_bv's instead of uint32/uint64_t's. The
default is "--pins-bv 65", and the value must be less than or equal
to 65. Versions before Verilator 3.671 defaulted to "--pins-bv 33".
The more sc_bv is used, the worse for performance. Use the
@@ -797,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
@@ -826,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
@@ -845,27 +946,19 @@ Summary:
Using :vlopt:`--prof-cfuncs` also enables :vlopt:`--prof-c`.
.. option:: --prof-exec
Enable collection of execution trace, that can be converted into a gantt
chart with verilator_gantt See :ref:`Execution Profiling`.
.. option:: --prof-pgo
Enable collection of profiling data for profile guided Verilation. Currently
this is only useful with :vlopt:`--threads`. See :ref:`Thread PGO`.
.. option:: --prof-threads
Enable gantt chart data collection for threaded builds. See :ref:`Thread
Profiling` and :ref:`Thread PGO`.
.. 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").
Deprecated. Same as --prof-exec and --prof-pgo together.
.. option:: --protect-ids
@@ -887,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
@@ -898,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
@@ -995,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
@@ -1032,7 +1146,8 @@ Summary:
is not thread safe. With "--threads 1", the generated model is single
threaded but may run in a multithreaded environment. With "--threads N",
where N >= 2, the model is generated to run multithreaded on up to N
threads. See :ref:`Multithreading`.
threads. See :ref:`Multithreading`. This option also applies to
:vlopt:`--trace` (but not :vlopt:`--trace-fst`).
.. option:: --threads-dpi all
@@ -1110,7 +1225,8 @@ Summary:
Having tracing compiled in may result in some small performance losses,
even when tracing is not turned on during model execution.
See also :vlopt:`--trace-threads` option.
When using :vlopt:`--threads`, VCD tracing is parallelized, using the
same number of threads as passed to :vlopt:`--threads`.
.. option:: --trace-coverage
@@ -1164,12 +1280,12 @@ Summary:
.. option:: --trace-threads *threads*
Enable waveform tracing using separate threads. This is typically faster
in simulation runtime but uses more total compute. This option is
independent of, and works with, both :vlopt:`--trace` and
:vlopt:`--trace-fst`. Different trace formats can take advantage of
more trace threads to varying degrees. Currently VCD tracing can utilize
at most "--trace-threads 1", and FST tracing can utilize at most
"--trace-threads 2". This overrides :vlopt:`--no-threads` .
in simulation runtime but uses more total compute. This option only
applies to :vlopt:`--trace-fst`. FST tracing can utilize at most
"--trace-threads 2". This overrides :vlopt:`--no-threads`.
This option is accepted, but has absolutely no effect with
:vlopt:`--trace`, which respects :vlopt:`--threads` instead.
.. option:: --trace-underscore
@@ -1638,7 +1754,7 @@ The grammar of configuration commands is as follows:
.. option:: sc_bv -module "<modulename>" [-function "<funcname>"] -var "<signame>"
Sets the port to be of :code:`sc_bv<{width}>` type, instead of bool,
vluint32_t or vluint64_t. Same as :option:`/*verilator&32;sc_bv*/`
uint32_t or uint64_t. Same as :option:`/*verilator&32;sc_bv*/`
metacomment.
.. option:: sformat [-module "<modulename>"] [-task "<taskname>"] -var "<signame>"
@@ -1667,9 +1783,28 @@ The grammar of configuration commands is as follows:
.. option:: tracing_off [-file "<filename>" [-lines <line> [ - <line> ]]]
Enable/disable waveform tracing for all future signals declared in the
specified filename (or wildcard with '\*' or '?', or all files if
omitted) and range of line numbers (or all lines if omitted).
.. option:: tracing_on [-scope "<scopename>" [-levels <levels> ]]
For tracing_off, instances below any module in the files/ranges
specified will also not be traced.
.. option:: tracing_off [-scope "<scopename>" [-levels <levels> ]]
Enable/disable waveform tracing for all future signals declared in
all files.
With -file, enable/disable waveform tracing in the specified
filename (or wildcard with '\*' or '?'), and -line range of line
numbers (or all lines if omitted).
For tracing_off with -file, instances below any module in the
files/ranges specified will also not be traced. To overcome this
feature, use tracing_on on the upper module declaration and on any
cells, or use the -scope flavor of the command.
With -scope enable/disable waveform tracing for the specified scope (or
wildcard with '\*' or '?'), and optional --levels number of levels
below. These controls only take place after other file/line/module
based controls have indicated the signal should be traced.
With -levels (used with -scope), the number of levels below that
scope which the rule is to match, where 0 means all levels below, 1
the exact level as the provided scope, and 2 meaning an additional
level of children below the provided scope, etc.
+1 -1
View File
@@ -72,7 +72,7 @@ verilator_gantt Arguments
.. option:: <filename>
The filename to read data from, defaults to "profile_threads.dat".
The filename to read data from, defaults to "profile_exec.dat".
.. option:: --help
+2 -2
View File
@@ -217,7 +217,7 @@ or "`ifdef`"'s may break other tools.
:option:`public_flat` signals.
To force a marked signal from C++, set the corresponding `__VforceVal`
variable to the desired value, and the `__VforceEn` signal to the bitmask
variable to the desired value, and the `__VforceEn` signal to the bit-mask
indicating which bits of the signal to force. To force all bits of the
target signal, set `__VforceEn` to all ones. To release the signal (or part
thereof), set appropriate bits of the `__VforceEn` signal to zero.
@@ -445,7 +445,7 @@ or "`ifdef`"'s may break other tools.
.. option:: /*verilator&32;sc_bv*/
Used after a port declaration. It sets the port to be of
:code:`sc_bv<{width}>` type, instead of bool, vluint32_t or vluint64_t.
:code:`sc_bv<{width}>` type, instead of bool, uint32_t or uint64_t.
This may be useful if the port width is parameterized and the
instantiating C++ code wants to always have a sc_bv so it can accept any
width. In general you should avoid using this attribute when not
+33 -18
View File
@@ -72,23 +72,38 @@ a good thing for getting working silicon.
Will Verilator output remain under my own license/copyright?
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Yes, it's just like using GCC on your programs; this is why Verilator uses
the "GNU **Lesser** Public License Version 3" instead of the more typical
"GNU Public License". See the licenses for details, but in brief, if you
change Verilator itself or the header files Verilator includes, you must
make the source code available under the GNU Lesser Public License.
However, Verilator output (the Verilated code) only "include"s the licensed
files, and so you are **not** required to open-source release any output
from Verilator.
Your SystemVerilog, VPI/DPI, or main() C++ code remains under your own license.
It's just like how using GCC on your programs does not change the copyright
of your program; this is why Verilator uses the "GNU **Lesser** Public
License Version 3" instead of the more typical "GNU Public License". See
the licenses for details.
Some examples:
* Any SystemVerilog or other input fed into Verilator remain your own.
* Any of your VPI/DPI C++ routines that Verilator calls remain your own.
* Any of your main() C++ code that calls into Verilator remain your own.
* If you change Verilator itself, for example changing or adding a file
under the src/ directory in the repository, you must make the source code
available under the GNU Lesser Public License.
* If you change a header Verilator provides, for example under include/ in
the repository, you must make the source code available under the GNU
Lesser Public License.
You also have the option of using the Perl Artistic License, which again
does not require you to release your Verilog or generated code, and also
allows you to modify Verilator for internal use without distributing the
modified version. But please contribute back to the community!
does not require you to release your Verilog, C++, or generated code. This
license also allows you to modify Verilator for internal use without
distributing the modified version. But please contribute back to the
community!
One limit is that you cannot under either license release a closed-source
Verilog simulation product incorporating Verilator. That is you can have a
commercial product, but must make the source code available.
Under both license you can offer a commercial product that is based on
Verilator either directly or embedded within. However under both licenses,
any changes you make to Verilator for such a product must be open sourced.
As is standard with Open Source, contributions back to Verilator will be
placed under the Verilator copyright and LGPL/Artistic license. Small test
@@ -354,8 +369,8 @@ also use the "import DPI" SystemVerilog feature to call C code (see the
chapter above). There is also limited VPI access to public signals.
If you want something more complex, since Verilator emits standard C++
code, you can simply write your own C++ routines that can access and modify
signal values without needing any PLI interface code, and call it with
code, you can write your own C++ routines that can access and modify signal
values without needing any PLI interface code, and call it with
$c("{any_c++_statement}").
See the :ref:`Connecting` section.
@@ -445,7 +460,7 @@ the signal, as you would any other member variable.
Signals are the smallest of 8-bit unsigned chars (equivalent to uint8_t),
16-bit unsigned shorts (uint16_t), 32-bit unsigned longs (uint32_t), or
64-bit unsigned long longs (uint64_t) that fits the width of the signal.
Generally, you can use just uint32_t's for 1 to 32 bits, or vluint64_t for
Generally, you can use just uint32_t's for 1 to 32 bits, or uint64_t for
1 to 64 bits, and the compiler will properly up-convert smaller entities.
Note even signed ports are declared as unsigned; you must sign extend
yourself to the appropriate signal width.
@@ -482,7 +497,7 @@ by your code or you'll get strange results.
Should a module be in Verilog or SystemC?
"""""""""""""""""""""""""""""""""""""""""
Sometimes there is a block that just interconnects instances, and have a
Sometimes there is a block that only interconnects instances, and have a
choice as to if you write it in Verilog or SystemC. Everything else being
equal, best performance is when Verilator sees all of the design. So, look
at the hierarchy of your design, labeling instances as to if they are
+17 -11
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:
@@ -120,7 +126,7 @@ In certain debug and other modes, it also creates:
- Debugging graph files (from --debug)
* - *{prefix}{misc}*\ .tree
- Debugging files (from --debug)
* - {mod_prefix}_{each_verilog_module}*{__n}*\ .vpp
* - {mod_prefix}_{each_verilog_base_filename}*\ .vpp
- Pre-processed verilog (from --debug)
After running Make, the C++ compiler may produce the following:
@@ -155,13 +161,13 @@ The Verilated executable may produce the following:
* - gmon.out
- GCC/clang code profiler output, often fed into :command:`verilator_profcfunc`
* - profile.vlt
- -profile data file for :ref:`Thread PGO`
* - profile_threads.dat
- -profile-threads data file for :command:`verilator_gantt`
- --prof-pgo data file for :ref:`Thread PGO`
* - profile_exec.dat
- --prof-exec data file for :command:`verilator_gantt`
Verilator_gantt may produce the following:
.. list-table::
* - profile_threads.vcd
* - profile_exec.vcd
- Gantt report waveform output
+14 -6
View File
@@ -87,7 +87,7 @@ compiles under all the options above, plus using MSVC++.
Install Prerequisites
---------------------
To build or run Verilator you need these standard packages:
To build or run Verilator, you need these standard packages:
::
@@ -98,13 +98,20 @@ To build or run Verilator you need these standard packages:
sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error)
sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
To build or run the following are optional but should be installed for good
performance:
To build or run Verilator, the following are optional but should be installed
for good performance:
::
sudo apt-get install ccache # If present at build, needed for run
sudo apt-get install libgoogle-perftools-dev numactl perl-doc
sudo apt-get install libgoogle-perftools-dev numactl
The following is optional but is recommended for nicely rendered command line
help when running Verilator:
::
sudo apt-get install perl-doc
To build Verilator you will need to install these packages; these do not
need to be present to run Verilator:
@@ -117,8 +124,9 @@ 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 pip3 install sphinx sphinx_rtd_theme breathe
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
cpan install Parallel::Forker
+6 -6
View File
@@ -92,7 +92,7 @@ appropriate code to detect failing cases at simulation runtime and print an
Verilator likewise also asserts any "unique" or "priority" SystemVerilog
keywords on case statement, as well as "unique" on if statements. However,
"priority if" is currently simply ignored.
"priority if" is currently ignored.
.. _Language Limitations:
@@ -174,9 +174,9 @@ Structures and Unions
---------------------
Presently Verilator only supports packed structs and packed unions. Rand
and randc tags on members are simply ignored. All structures and unions
are represented as a single vector, which means that generating one member
of a structure from blocking, and another from non-blocking assignments is
and randc tags on members are ignored. All structures and unions are
represented as a single vector, which means that generating one member of a
structure from blocking, and another from non-blocking assignments is
unsupported.
@@ -343,7 +343,7 @@ Verilator supports the procedural `force` (and corresponding `release`)
statement. The behavior of the `force` statement however does not entirely
comply with the IEEE 1800 SystemVerilog standard. According to the standard,
when a procedural statement of the form `force a = b;` is executed, the
simulation should behave as if from that point onwards, a continuous
simulation should behave as if from that point forwards, a continuous
assignment `assign a = b;` have been added to override the drivers of `a`.
More specifically: the value of `a` should be updated, whenever the value of
`b` changes, all the way until a `release a;` statement is executed.
@@ -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.
+23 -23
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:
@@ -279,26 +279,25 @@ To use profiling:
is being spent.
.. _Thread Profiling:
.. _Execution Profiling:
Thread Profiling
================
Execution Profiling
===================
When using multithreaded mode (:vlopt:`--threads`), it is useful to see
statistics and visualize how well the multiple CPUs are being utilized.
For performance optimization, it is useful to see statistics and visualize how
execution time is distributed in a verilated model.
With the :vlopt:`--prof-threads` option, Verilator will:
With the :vlopt:`--prof-exec` option, Verilator will:
* Add code to the Verilated model to record the start and end time of each
macro-task across a number of calls to eval. (What is a macro-task? See
the Verilator internals document (:file:`docs/internals.rst` in the
distribution.)
* Add code to the Verilated model to record execution flow.
* Add code to save profiling data in non-human-friendly form to the file
specified with :vlopt:`+verilator+prof+threads+file+\<filename\>`.
specified with :vlopt:`+verilator+prof+exec+file+\<filename\>`.
* Add code to save profiling data for thread profile-guided
optimization. See :ref:`Thread PGO`.
* In multi-threaded models, add code to record the start and end time of each
macro-task across a number of calls to eval. (What is a macro-task? See the
Verilator internals document (:file:`docs/internals.rst` in the
distribution.)
The :command:`verilator_gantt` program may then be run to transform the
saved profiling file into a nicer visual format and produce some related
@@ -406,8 +405,8 @@ others as they prove beneficial.
Thread Profile-Guided Optimization
----------------------------------
Verilator supports thread profile-guided optimization (Thread PGO) to
improve multithreaded performance.
Verilator supports profile-guided optimization (Verilation) of multi-threaded
models (Thread PGO) to improve performance.
When using multithreading, Verilator computes how long macro tasks take and
tries to balance those across threads. (What is a macro-task? See the
@@ -417,13 +416,14 @@ balanced, leading to decreased performance. Thread PGO allows collecting
profiling data to replace the estimates and better optimize these
decisions.
To use Thread PGO, Verilate the model with the :vlopt:`--prof-threads`
option.
To use Thread PGO, Verilate the model with the :vlopt:`--prof-pgo` option. This
will code to the verilated model to save profiling data for profile-guided
optimization.
Run the model executable. When the executable exits, it will create a
profile.vlt file.
Rerun Verilator, optionally omitting the :vlopt:`--prof-threads` option,
Rerun Verilator, optionally omitting the :vlopt:`--prof-pgo` option,
and adding the profile.vlt generated earlier to the command line.
Note there is no Verilator equivalent to GCC's --fprofile-use. Verilator's
@@ -433,8 +433,8 @@ directly without any prefix.
If results from multiple simulations are to be used in generating the
optimization, multiple simulation's profile.vlt may be concatenated
externally, or each of the files may be fed as separate command line
options into Verilator. Verilator will simply sum the profile results, so
a longer running test will have proportionally more weight for optimization
options into Verilator. Verilator will sum the profile results, so a
longer running test will have proportionally more weight for optimization
than a shorter running test.
If you provide any profile feedback data to Verilator, and it cannot use
+15 -11
View File
@@ -221,9 +221,13 @@ model, it may be beneficial to performance to adjust the
influences the partitioning of the model by adjusting the assumed execution
time of DPI imports.
The :vlopt:`--trace-threads` options can be used to produce trace dumps
using multiple threads. If :vlopt:`--trace-threads` is set without
:vlopt:`--threads`, then :vlopt:`--trace-threads` will imply
When using :vlopt:`--trace` to perform VCD tracing, the VCD trace
construction is parallelized using the same number of threads as specified
with :vlopt:`--threads`, and is executed on the same thread pool as the model.
The :vlopt:`--trace-threads` options can be used with :vlopt:`--trace-fst`
to offload FST tracing using multiple threads. If :vlopt:`--trace-threads` is
given without :vlopt:`--threads`, then :vlopt:`--trace-threads` will imply
:vlopt:`--threads 1 <--threads>`, i.e.: the support libraries will be
thread safe.
@@ -231,12 +235,12 @@ With :vlopt:`--trace-threads 0 <--trace-threads>`, trace dumps are produced
on the main thread. This again gives the highest single thread performance.
With :vlopt:`--trace-threads {N} <--trace-threads>`, where N is at least 1,
N additional threads will be created and managed by the trace files (e.g.:
VerilatedVcdC or VerilatedFstC), to generate the trace dump. The main
thread will be released to proceed with execution as soon as possible,
though some blocking of the main thread is still necessary while capturing
the trace. Different trace formats can utilize a various number of
threads. See the :vlopt:`--trace-threads` option.
up to N additional threads will be created and managed by the trace files
(e.g.: VerilatedFstC), to offload construction of the trace dump. The main
thread will be released to proceed with execution as soon as possible, though
some blocking of the main thread is still necessary while capturing the
trace. FST tracing can utilize up to 2 offload threads, so there is no use
of setting :vlopt:`--trace-threads` higher than 2 at the moment.
When running a multithreaded model, the default Linux task scheduler often
works against the model, by assuming threads are short lived, and thus
@@ -265,7 +269,7 @@ This will limit memory to socket 0, and threads to cores 0, 1, 2, 3,
(presumably on socket 0) optimizing performance. Of course this must be
adjusted if you want another simulator using e.g. socket 1, or if you
Verilated with a different number of threads. To see what CPUs are
actually used, use :vlopt:`--prof-threads`.
actually used, use :vlopt:`--prof-exec`.
Multithreaded Verilog and Library Support
@@ -441,7 +445,7 @@ SystemC include directories and link to the SystemC libraries.
.. describe:: TRACE_THREADS
Optional. Generated multi-threaded trace dumping, same as
Optional. Generated multi-threaded FST trace dumping, same as
"--trace-threads".
.. describe:: TOP_MODULE
+8 -8
View File
@@ -12,7 +12,7 @@ Disabling Warnings
Warnings may be disabled in multiple ways:
#. Disable the warning in the source code. When the warning is printed it
will include a warning code. Simply surround the offending line with a
will include a warning code. Surround the offending line with a
:code:`/*verilator&32;lint_off*/` and :code:`/*verilator&32;lint_on*/`
metacomment pair:
@@ -162,7 +162,7 @@ List Of Warnings
always @(posedge clk) foo[0] <= ...
always_comb foo[1] = ...
Simply use a different register for the flop:
Instead use a different register for the flop:
.. code-block:: sv
@@ -284,7 +284,7 @@ List Of Warnings
.. TODO better example
Warns that it is simply better style to use casez, and "?" in place of
Warns that it is better style to use casez, and "?" in place of
"x"'s. See
`http://www.sunburst-design.com/papers/CummingsSNUG1999Boston_FullParallelCase_rev1_1.pdf
<http://www.sunburst-design.com/papers/CummingsSNUG1999Boston_FullParallelCase_rev1_1.pdf>`_
@@ -1212,10 +1212,10 @@ List Of Warnings
.. include:: ../../docs/gen/ex_STMTDLY_msg.rst
This is a warning because Verilator does not support delayed statements.
It will simply ignore all such delays. In many cases ignoring a delay
might be harmless, but if the delayed statement is, as in this example,
used to cause some important action at a later time, it might be an
important difference.
It will ignore all such delays. In many cases ignoring a delay might be
harmless, but if the delayed statement is, as in this example, used to
cause some important action at a later time, it might be an important
difference.
Some possible workarounds:
@@ -1506,7 +1506,7 @@ List Of Warnings
Error that a construct might be legal according to IEEE but is not
currently supported by Verilator.
A typical workaround is to recode the construct into a simpler and more
A typical workaround is to rewrite the construct into a simpler and more
common alternative language construct.
Alternatively, check if the construct is supported by other tools, and
+101 -22
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
@@ -268,13 +269,13 @@ Estimating Logic Costs
To compute the cost of any given path through the graph, Verilator
estimates an execution cost for each task. Each macro-task has an execution
cost which is simply the sum of its tasks' costs. We assume that
communication overhead and synchronization overhead are zero, so the cost
of any given path through the graph is simply the sum of macro-task
execution costs. Sarkar does almost the same thing, except that he has
nonzero estimates for synchronization costs.
cost which is the sum of its tasks' costs. We assume that communication
overhead and synchronization overhead are zero, so the cost of any given
path through the graph is the sum of macro-task execution costs. Sarkar
does almost the same thing, except that he has nonzero estimates for
synchronization costs.
Verilator's cost estimates are assigned by ``InstrCountCostVisitor``. This
Verilator's cost estimates are assigned by ``InstrCountVisitor``. This
class is perhaps the most fragile piece of the multithread
implementation. It's easy to have a bug where you count something cheap
(eg. accessing one element of a huge array) as if it were expensive (eg.
@@ -301,7 +302,7 @@ prerequisites on other threads have finished.
The synchronization cost is cheap if the prereqs are done. If they're not,
fragmentation (idle CPU cores waiting) is possible. This is the major
source of overhead in this approach. The ``--prof-threads`` switch and the
source of overhead in this approach. The ``--prof-exec`` switch and the
``verilator_gantt`` script can visualize the time lost to such
fragmentation.
@@ -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
@@ -817,7 +895,7 @@ which you can install using cpan.
There are some traps to avoid when running regression tests
- When checking the MANIFEST, the test will barf on unexpected code in the
- When checking the MANIFEST, the test will fail on unexpected code in the
Verilator tree. So make sure to keep any such code outside the tree.
- Not all Linux systems install Perldoc by default. This is needed for 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
@@ -1186,7 +1265,7 @@ anticipated to be ever implemented for the reasons indicated.
IEEE 1800-2017 3.3 modules within modules
Little/no tool support, and arguably not a good practice.
IEEE 1800-2017 6.12 "shortreal"
Little/no tool support, and easily simply promoted to real.
Little/no tool support, and easily promoted to real.
IEEE 1800-2017 11.11 Min, typ, max
No SDF support so will always use typical.
IEEE 1800-2017 11.12 "let"
+55
View File
@@ -2,9 +2,11 @@ ABCp
Accellera
Affe
Aleksander
Alexandre
Ami
Amir
Anastasiadis
Anikin
Antonin
Antwerpen
Arasanipalai
@@ -70,6 +72,7 @@ Eda
Eddleston
Egbert
Egil
Ehab
Eiler
Eivind
El
@@ -82,6 +85,7 @@ Fabrizio
Fekete
Ferrandi
Flachs
Flavien
Foletto
Forker
Francillon
@@ -104,6 +108,7 @@ Goessling
Gonnen
Goorah
Gossner
Graybeal
Grobman
Gunter
Guo
@@ -121,6 +126,7 @@ Hornung
Hossell
Hsu
Hyperthreading
Ibrahim
Iles
Inlines
Inout
@@ -134,6 +140,7 @@ Jannis
Jens
Jeras
Jiuyang
Joannou
Joly
Jullien
Junji
@@ -147,6 +154,7 @@ Kamendje
Kandadi
Kaplan
Karge
Karlsson
Katz
Katzman
Keren
@@ -161,6 +169,7 @@ Kolecki
Koonce
Korteland
Koszek
Kouping
Kravitz
Krolnik
Kruse
@@ -194,6 +203,7 @@ Mednick
Michiels
Microsystems
Milanovic
Millis
MinW
Mindspeed
Miodrag
@@ -224,12 +234,14 @@ Petr
Piechotka
Piersall
Plunkett
Popolon
Popov
Prabhat
Prabhu
Prateek
Pre
Preprocess
Pretet
Priyadharshini
Pullup
Pulver
@@ -262,12 +274,15 @@ Shi
Shinkarovsky
Shirakawa
Shuba
Shunyao
Slatter
SoC
Sobhan
Sokorac
Solaris
Solomatnikov
Solt
Southwell
Srini
Stamness
Stephane
@@ -286,6 +301,7 @@ SystemVerilog
Takatsukasa
Tambe
Tariq
Tejada
Tengstrand
Terpstra
Thiede
@@ -293,8 +309,10 @@ Thierry
Thyer
Tichelaar
Tomov
Tood
Topa
Tota
Trefor
Tresidder
Tri
Tristate
@@ -304,11 +322,13 @@ Ubixum
Uints
Undefines
Unsized
Urbach
Uselib
Usha
Usuario
VERILATOR
Vasu
Vdeeptemp
Vdly
Vemumtab
Vemuri
@@ -351,7 +371,9 @@ Zhang
abirkmanis
accessor
accessors
adrienlemasle
agrobman
ahouska
al
ala
andit
@@ -372,8 +394,12 @@ backtrace
backtraces
basename
bbox
benchmarking
biguint
biops
bitOpTree
bitOpTree
bitop
bitstoreal
blackbox
bokke
@@ -389,6 +415,7 @@ casez
casted
cb
ccache
ccall
cdc
ceil
celldefine
@@ -411,6 +438,8 @@ concat
concats
config
const
constexpr
constpool
coredump
countbits
countones
@@ -426,6 +455,7 @@ da
dat
datadir
datafiles
david
ddd
deassign
debugi
@@ -434,6 +464,7 @@ defname
defparam
demangling
der
dereference
desassign
destructor
detections
@@ -462,6 +493,7 @@ dumpvars
dut
dx
elab
elike
elsif
endcase
endcelldefine
@@ -480,15 +512,18 @@ enums
env
envvar
eof
errae
erroring
et
eval
evals
exe
executables
expr
extern
fanin
fasttrace
fauto
fbranch
fclose
fdisplay
@@ -503,6 +538,7 @@ filt
flto
flushCall
fopen
forceable
foreach
fprintf
fprofile
@@ -521,6 +557,7 @@ funcs
fwrite
gantt
gcc
gcda
gdb
genblk
genvar
@@ -552,6 +589,7 @@ incdir
includer
inferfaces
inhibitSim
initarray
initializer
initializers
inits
@@ -570,6 +608,7 @@ killua
lang
lcov
ld
leavinel
len
libext
libgoogle
@@ -584,6 +623,7 @@ ln
loc
localparam
localparams
localtime
logicals
longint
lsb
@@ -618,6 +658,7 @@ multithreaded
multithreading
mutexes
mux
myftptoyman
mysignal
namespace
nand
@@ -642,15 +683,19 @@ onehot
ooo
oprofile
oversubscription
parallelized
param
parameterized
params
parens
pawel
pc
pdf
perf
perftools
pgo
picoChip
pinIndex
pinout
plusargs
pmos
@@ -676,6 +721,7 @@ prev
printf
printtimescale
profcfunc
profiler
prototyptes
ps
pthread
@@ -703,12 +749,14 @@ regs
reloop
resetall
respecified
rodata
rr
rst
runtime
runtimes
rw
sVerilator
sawatzke
sc
scalared
sccanf
@@ -724,6 +772,7 @@ specparam
splitme
spp
sqrt
src
srcdir
srcfile
sscanf
@@ -743,6 +792,7 @@ subcells
subexpressions
submodule
submodules
substring
sv
svBitVal
svBitVecVal
@@ -777,6 +827,7 @@ trunc
txt
typ
typedef
typedef'ed
typedefed
typedefs
typename
@@ -790,6 +841,7 @@ uniquified
unistd
unlink
unlinked
unnamedblk
unopt
unoptflat
unoptimizable
@@ -839,11 +891,14 @@ writeme
writemem
writememb
writememh
xiak
xin
xml
xnor
xout
xuejiazidi
yanx
ypq
yurivict
zdave
Øyvind
+1 -1
View File
@@ -33,5 +33,5 @@ add_executable(example ../make_tracing_c/sim_main.cpp)
# Add the Verilated circuit to the target
verilate(example COVERAGE TRACE
INCLUDE_DIRS "../make_tracing_c"
VERILATOR_ARGS -f ../make_tracing_c/input.vc -Os -x-assign 0
VERILATOR_ARGS -f ../make_tracing_c/input.vc -x-assign fast
SOURCES ../make_tracing_c/top.v)
+1 -1
View File
@@ -45,7 +45,7 @@ set_property(
# Add the Verilated circuit to the target
verilate(example SYSTEMC COVERAGE TRACE
INCLUDE_DIRS "../make_tracing_sc"
VERILATOR_ARGS -f ../make_tracing_sc/input.vc -Os -x-assign 0
VERILATOR_ARGS -f ../make_tracing_sc/input.vc -x-assign fast
SOURCES ../make_tracing_sc/top.v)
verilator_link_systemc(example)
+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
@@ -33,7 +33,7 @@ VERILATOR_FLAGS =
# Generate C++
VERILATOR_FLAGS += -cc
# Optimize
VERILATOR_FLAGS += -Os -x-assign 0
VERILATOR_FLAGS += -x-assign fast
# Warn abount lint issues; may not want this on less solid designs
VERILATOR_FLAGS += -Wall
# This example does not use vl_time_stamp but rather
+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;
+1 -1
View File
@@ -36,7 +36,7 @@ VERILATOR_FLAGS += -cc --exe
# Generate makefile dependencies (not shown as complicates the Makefile)
#VERILATOR_FLAGS += -MMD
# Optimize
VERILATOR_FLAGS += -Os -x-assign 0
VERILATOR_FLAGS += -x-assign fast
# Warn abount lint issues; may not want this on less solid designs
VERILATOR_FLAGS += -Wall
# Make waveforms
+3 -1
View File
@@ -34,6 +34,8 @@ int main(int argc, char** argv, char** env) {
// Using unique_ptr is similar to
// "VerilatedContext* contextp = new VerilatedContext" then deleting at end.
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
// Do not instead make Vtop as a file-scope static variable, as the
// "C++ static initialization order fiasco" may cause a crash
// Set debug level, 0 is off, 9 is highest presently used
// May be overridden by commandArgs argument parsing
@@ -69,7 +71,7 @@ int main(int argc, char** argv, char** env) {
// Historical note, before Verilator 4.200 Verilated::gotFinish()
// was used above in place of contextp->gotFinish().
// Most of the contextp-> calls can use Verilated:: calls instead;
// the Verilated:: versions simply assume there's a single context
// the Verilated:: versions just assume there's a single context
// being used (per thread). It's faster and clearer to use the
// newer contextp-> versions.
+1 -1
View File
@@ -37,7 +37,7 @@ VERILATOR_FLAGS += -sc --exe
# Generate makefile dependencies (not shown as complicates the Makefile)
#VERILATOR_FLAGS += -MMD
# Optimize
VERILATOR_FLAGS += -Os -x-assign 0
VERILATOR_FLAGS += -x-assign fast
# Warn abount lint issues; may not want this on less solid designs
VERILATOR_FLAGS += -Wall
# Make waveforms
+6 -6
View File
@@ -58,11 +58,11 @@ int sc_main(int argc, char* argv[]) {
// Define interconnect
sc_signal<bool> reset_l;
sc_signal<vluint32_t> in_small;
sc_signal<vluint64_t> in_quad;
sc_signal<uint32_t> in_small;
sc_signal<uint64_t> in_quad;
sc_signal<sc_bv<70>> in_wide;
sc_signal<vluint32_t> out_small;
sc_signal<vluint64_t> out_quad;
sc_signal<uint32_t> out_small;
sc_signal<uint64_t> out_quad;
sc_signal<sc_bv<70>> out_wide;
// Construct the Verilated model, from inside Vtop.h
@@ -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 = {}
+241 -149
View File
@@ -47,36 +47,44 @@
#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 <sstream>
#include <sys/stat.h> // mkdir
#include <list>
#include <cstdlib>
#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;
//===========================================================================
// Static sanity checks
static_assert(sizeof(vluint8_t) == 1, "vluint8_t is missized");
static_assert(sizeof(vluint16_t) == 2, "vluint8_t is missized");
static_assert(sizeof(vluint32_t) == 4, "vluint8_t is missized");
static_assert(sizeof(vluint64_t) == 8, "vluint8_t is missized");
static_assert(sizeof(uint8_t) == 1, "uint8_t is missized");
static_assert(sizeof(uint16_t) == 2, "uint8_t is missized");
static_assert(sizeof(uint32_t) == 4, "uint8_t is missized");
static_assert(sizeof(uint64_t) == 8, "uint8_t is missized");
//===========================================================================
// Global variables
@@ -243,21 +251,21 @@ std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
return out;
}
vluint64_t _vl_dbg_sequence_number() VL_MT_SAFE {
uint64_t _vl_dbg_sequence_number() VL_MT_SAFE {
#ifdef VL_THREADED
static std::atomic<vluint64_t> sequence;
static std::atomic<uint64_t> sequence;
#else
static vluint64_t sequence = 0;
static uint64_t sequence = 0;
#endif
return ++sequence;
}
vluint32_t VL_THREAD_ID() VL_MT_SAFE {
uint32_t VL_THREAD_ID() VL_MT_SAFE {
#ifdef VL_THREADED
// Alternative is to use std::this_thread::get_id, but that returns a
// hard-to-read number and is very slow
static std::atomic<vluint32_t> s_nextId(0);
static VL_THREAD_LOCAL vluint32_t t_myId = ++s_nextId;
static std::atomic<uint32_t> s_nextId(0);
static VL_THREAD_LOCAL uint32_t t_myId = ++s_nextId;
return t_myId;
#else
return 0;
@@ -295,7 +303,7 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
//===========================================================================
// Random -- Mostly called at init time, so not inline.
static vluint32_t vl_sys_rand32() VL_MT_UNSAFE {
static uint32_t vl_sys_rand32() VL_MT_UNSAFE {
// Return random 32-bits using system library.
// Used only to construct seed for Verilator's PNRG.
static VerilatedMutex s_mutex;
@@ -308,9 +316,9 @@ static vluint32_t vl_sys_rand32() VL_MT_UNSAFE {
#endif
}
vluint64_t vl_rand64() VL_MT_SAFE {
static VL_THREAD_LOCAL vluint64_t t_state[2];
static VL_THREAD_LOCAL vluint32_t t_seedEpoch = 0;
uint64_t vl_rand64() VL_MT_SAFE {
static VL_THREAD_LOCAL uint64_t t_state[2];
static VL_THREAD_LOCAL uint32_t t_seedEpoch = 0;
// For speed, we use a thread-local epoch number to know when to reseed
// A thread always belongs to a single context, so this works out ok
if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) {
@@ -324,7 +332,7 @@ vluint64_t vl_rand64() VL_MT_SAFE {
if (VL_COUNTONES_I(t_state[1]) < 10) t_state[1] = ~t_state[1];
}
// Xoroshiro128+ algorithm
const vluint64_t result = t_state[0] + t_state[1];
const uint64_t result = t_state[0] + t_state[1];
t_state[1] ^= t_state[0];
t_state[0] = (((t_state[0] << 55) | (t_state[0] >> 9)) ^ t_state[1] ^ (t_state[1] << 14));
t_state[1] = (t_state[1] << 36) | (t_state[1] >> 28);
@@ -409,11 +417,11 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
const int vw = VL_WORDS_I(vmsbp1); // aka "n" in the algorithm
if (vw == 1) { // Single divisor word breaks rest of algorithm
vluint64_t k = 0;
uint64_t k = 0;
for (int j = uw - 1; j >= 0; --j) {
const vluint64_t unw64 = ((k << 32ULL) + static_cast<vluint64_t>(lwp[j]));
owp[j] = unw64 / static_cast<vluint64_t>(rwp[0]);
k = unw64 - static_cast<vluint64_t>(owp[j]) * static_cast<vluint64_t>(rwp[0]);
const uint64_t unw64 = ((k << 32ULL) + static_cast<uint64_t>(lwp[j]));
owp[j] = unw64 / static_cast<uint64_t>(rwp[0]);
k = unw64 - static_cast<uint64_t>(owp[j]) * static_cast<uint64_t>(rwp[0]);
}
if (is_modulus) {
owp[0] = k;
@@ -423,8 +431,8 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
}
// +1 word as we may shift during normalization
vluint32_t un[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here
vluint32_t vn[VL_MULS_MAX_WORDS + 1]; // v normalized
uint32_t un[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here
uint32_t vn[VL_MULS_MAX_WORDS + 1]; // v normalized
// Zero for ease of debugging and to save having to zero for shifts
// Note +1 as loop will use extra word
@@ -433,7 +441,7 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
// Algorithm requires divisor MSB to be set
// Copy and shift to normalize divisor so MSB of vn[vw-1] is set
const int s = 31 - VL_BITBIT_I(vmsbp1 - 1); // shift amount (0...31)
const vluint32_t shift_mask = s ? 0xffffffff : 0; // otherwise >> 32 won't mask the value
const uint32_t shift_mask = s ? 0xffffffff : 0; // otherwise >> 32 won't mask the value
for (int i = vw - 1; i > 0; --i) {
vn[i] = (rwp[i] << s) | (shift_mask & (rwp[i - 1] >> (32 - s)));
}
@@ -453,10 +461,10 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
// Main loop
for (int j = uw - vw; j >= 0; --j) {
// Estimate
const vluint64_t unw64 = (static_cast<vluint64_t>(un[j + vw]) << 32ULL
| static_cast<vluint64_t>(un[j + vw - 1]));
vluint64_t qhat = unw64 / static_cast<vluint64_t>(vn[vw - 1]);
vluint64_t rhat = unw64 - qhat * static_cast<vluint64_t>(vn[vw - 1]);
const uint64_t unw64
= (static_cast<uint64_t>(un[j + vw]) << 32ULL | static_cast<uint64_t>(un[j + vw - 1]));
uint64_t qhat = unw64 / static_cast<uint64_t>(vn[vw - 1]);
uint64_t rhat = unw64 - qhat * static_cast<uint64_t>(vn[vw - 1]);
again:
if (qhat >= 0x100000000ULL || ((qhat * vn[vw - 2]) > ((rhat << 32ULL) + un[j + vw - 2]))) {
@@ -465,10 +473,10 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
if (rhat < 0x100000000ULL) goto again;
}
vlsint64_t t = 0; // Must be signed
vluint64_t k = 0;
int64_t t = 0; // Must be signed
uint64_t k = 0;
for (int i = 0; i < vw; ++i) {
const vluint64_t p = qhat * vn[i]; // Multiply by estimate
const uint64_t p = qhat * vn[i]; // Multiply by estimate
t = un[i + j] - k - (p & 0xFFFFFFFFULL); // Subtract
un[i + j] = t;
k = (p >> 32ULL) - (t >> 32ULL);
@@ -482,7 +490,7 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WData
owp[j]--;
k = 0;
for (int i = 0; i < vw; ++i) {
t = static_cast<vluint64_t>(un[i + j]) + static_cast<vluint64_t>(vn[i]) + k;
t = static_cast<uint64_t>(un[i + j]) + static_cast<uint64_t>(vn[i]) + k;
un[i + j] = t;
k = t >> 32ULL;
}
@@ -613,7 +621,7 @@ double VL_ITOR_D_W(int lbits, const WDataInP lwp) VL_PURE {
}
double VL_ISTOR_D_W(int lbits, const WDataInP lwp) VL_PURE {
if (!VL_SIGN_W(lbits, lwp)) return VL_ITOR_D_W(lbits, lwp);
vluint32_t pos[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here
uint32_t pos[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here
VL_NEGATE_W(VL_WORDS_I(lbits), pos, lwp);
_vl_clean_inplace_w(lbits, pos);
return -VL_ITOR_D_W(lbits, pos);
@@ -672,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) {
@@ -687,10 +698,11 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
const WDataInP integer = VL_DIV_WWW(b, tmp0, shifted, fracDigitsPow10);
const WDataInP frac = VL_MODDIV_WWW(b, tmp1, shifted, fracDigitsPow10);
const WDataInP max64Bit
= VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits<vluint64_t>::max()); // breaks shifted
= 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
@@ -711,7 +723,7 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
fracDigits, VL_SET_QW(frac), suffix.c_str());
}
} else {
const vluint64_t integer64 = VL_SET_QW(integer);
const uint64_t integer64 = VL_SET_QW(integer);
if (!fracDigits) {
digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRIu64 "%s", integer64,
suffix.c_str());
@@ -878,9 +890,9 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA
int digits = 0;
std::string append;
if (lbits <= VL_QUADSIZE) {
digits = VL_SNPRINTF(
t_tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRId64,
static_cast<vlsint64_t>(VL_EXTENDS_QQ(lbits, lbits, ld)));
digits
= VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRId64,
static_cast<int64_t>(VL_EXTENDS_QQ(lbits, lbits, ld)));
append = t_tmp;
} else {
if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) {
@@ -991,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);
@@ -1023,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) {
@@ -1047,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.
@@ -1061,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;
@@ -1183,7 +1192,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
_vl_vsss_skipspace(fp, floc, fromp, fstr);
_vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "0123456789+-xXzZ?_");
if (!t_tmp[0]) goto done;
vlsint64_t ld = 0;
int64_t ld = 0;
std::sscanf(t_tmp, "%30" PRId64, &ld);
VL_SET_WQ(owp, ld);
break;
@@ -1197,7 +1206,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
// cppcheck-suppress unusedStructMember // It's used
union {
double r;
vlsint64_t ld;
int64_t ld;
} u;
u.r = std::strtod(t_tmp, nullptr);
VL_SET_WQ(owp, u.ld);
@@ -1452,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 {
@@ -1605,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;
}
@@ -1642,7 +1652,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
VL_ZERO_RESET_W(rbits, rwp);
switch (std::tolower(fmt)) {
case 'd': {
vlsint64_t lld = 0;
int64_t lld = 0;
std::sscanf(dp, "%30" PRId64, &lld);
VL_SET_WQ(rwp, lld);
break;
@@ -1677,7 +1687,7 @@ IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_M
VL_SET_WQ(rwp, VL_CVT_Q_D(temp));
break;
}
default: // Other simulators simply return 0 in these cases and don't error out
default: // Other simulators return 0 in these cases and don't error out
return 0;
}
_vl_clean_inplace_w(rbits, rwp);
@@ -1767,7 +1777,7 @@ std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_MT_SAFE {
std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
std::string lstring = lhs;
const vlsint32_t rhs_s = rhs; // To signed value
const int32_t rhs_s = rhs; // To signed value
// 6.16.2:str.putc(i, c) does not change the value when i < 0 || i >= str.len() || c == 0
if (0 <= rhs_s && rhs < lhs.length() && ths != 0) lstring[rhs] = ths;
return lstring;
@@ -1775,15 +1785,15 @@ std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE {
CData v = 0;
const vlsint32_t rhs_s = rhs; // To signed value
const int32_t rhs_s = rhs; // To signed value
// 6.16.3:str.getc(i) returns 0 if i < 0 || i >= str.len()
if (0 <= rhs_s && rhs < lhs.length()) v = lhs[rhs];
return v;
}
std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE {
const vlsint32_t rhs_s = rhs; // To signed value
const vlsint32_t ths_s = ths; // To signed value
const int32_t rhs_s = rhs; // To signed value
const int32_t ths_s = ths; // To signed value
// 6.16.8:str.substr(i, j) returns an empty string when i < 0 || j < i || j >= str.len()
if (rhs_s < 0 || ths_s < rhs_s || ths >= lhs.length()) return "";
// Second parameter of std::string::substr(i, n) is length, not position as in SystemVerilog
@@ -1822,7 +1832,7 @@ static const char* memhFormat(int nBits) {
return t_buf;
}
static const char* formatBinary(int nBits, vluint32_t bits) {
static const char* formatBinary(int nBits, uint32_t bits) {
assert((nBits >= 1) && (nBits <= 32));
static VL_THREAD_LOCAL char t_buf[64];
@@ -1839,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
@@ -1974,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;
@@ -2026,9 +2034,9 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
}
} else if (m_bits <= 64) {
const QData* const datap = reinterpret_cast<const QData*>(valuep);
const vluint64_t value = VL_MASK_Q(m_bits) & *datap;
const vluint32_t lo = value & 0xffffffff;
const vluint32_t hi = value >> 32;
const uint64_t value = VL_MASK_Q(m_bits) & *datap;
const uint32_t lo = value & 0xffffffff;
const uint32_t hi = value >> 32;
if (m_hex) {
fprintf(m_fp, memhFormat(m_bits - 32), hi);
fprintf(m_fp, "%08x\n", lo);
@@ -2157,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",
@@ -2234,8 +2219,8 @@ double vl_time_multiplier(int scale) VL_PURE {
return pow10[scale];
}
}
vluint64_t vl_time_pow10(int n) {
static const vluint64_t pow10[20] = {
uint64_t vl_time_pow10(int n) {
static const uint64_t pow10[20] = {
1ULL,
10ULL,
100ULL,
@@ -2279,7 +2264,7 @@ VerilatedContext::VerilatedContext()
: m_impdatap{new VerilatedContextImpData} {
Verilated::lastContextp(this);
Verilated::threadContextp(this);
m_ns.m_profThreadsFilename = "profile_threads.dat";
m_ns.m_profExecFilename = "profile_exec.dat";
m_ns.m_profVltFilename = "profile.vlt";
m_fdps.resize(31);
std::fill(m_fdps.begin(), m_fdps.end(), static_cast<FILE*>(nullptr));
@@ -2288,11 +2273,22 @@ VerilatedContext::VerilatedContext()
}
// Must declare here not in interface, as otherwise forward declarations not known
VerilatedContext::~VerilatedContext() {}
VerilatedContext::~VerilatedContext() {
checkMagic(this);
m_magic = 0x1; // Arbitrary but 0x1 is what Verilator src uses for a deleted pointer
}
void VerilatedContext::checkMagic(const VerilatedContext* contextp) {
if (VL_UNLIKELY(!contextp || contextp->m_magic != MAGIC)) {
VL_FATAL_MT("", 0, "", // LCOV_EXCL_LINE
"Attempt to create model using a bad/deleted VerilatedContext pointer");
}
}
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 {
@@ -2312,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 "";
@@ -2347,21 +2343,21 @@ void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_s.m_gotFinish = flag;
}
void VerilatedContext::profThreadsStart(vluint64_t flag) VL_MT_SAFE {
void VerilatedContext::profExecStart(uint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profThreadsStart = flag;
m_ns.m_profExecStart = flag;
}
void VerilatedContext::profThreadsWindow(vluint64_t flag) VL_MT_SAFE {
void VerilatedContext::profExecWindow(uint64_t flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profThreadsWindow = flag;
m_ns.m_profExecWindow = flag;
}
void VerilatedContext::profThreadsFilename(const std::string& flag) VL_MT_SAFE {
void VerilatedContext::profExecFilename(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_ns.m_profThreadsFilename = flag;
m_ns.m_profExecFilename = flag;
}
std::string VerilatedContext::profThreadsFilename() const VL_MT_SAFE {
std::string VerilatedContext::profExecFilename() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_profThreadsFilename;
return m_ns.m_profExecFilename;
}
void VerilatedContext::profVltFilename(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
@@ -2417,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
@@ -2447,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
@@ -2506,13 +2556,16 @@ std::pair<int, char**> VerilatedContextImp::argc_argv() VL_MT_SAFE_EXCLUDES(m_ar
void VerilatedContextImp::commandArgVl(const std::string& arg) {
if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) {
std::string value;
std::string str;
uint64_t u64;
if (arg == "+verilator+debug") {
Verilated::debug(4);
} else if (commandArgVlValue(arg, "+verilator+debugi+", value /*ref*/)) {
Verilated::debug(std::atoi(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+error+limit+", value /*ref*/)) {
errorLimit(std::atoi(value.c_str()));
} else if (commandArgVlUint64(arg, "+verilator+debugi+", u64, 0,
std::numeric_limits<int>::max())) {
Verilated::debug(static_cast<int>(u64));
} else if (commandArgVlUint64(arg, "+verilator+error+limit+", u64, 0,
std::numeric_limits<int>::max())) {
errorLimit(static_cast<int>(u64));
} else if (arg == "+verilator+help") {
VerilatedImp::versionDump();
VL_PRINTF_MT("For help, please see 'verilator --help'\n");
@@ -2520,18 +2573,22 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
"Exiting due to command line argument (not an error)");
} else if (arg == "+verilator+noassert") {
assertOn(false);
} else if (commandArgVlValue(arg, "+verilator+prof+threads+start+", value /*ref*/)) {
profThreadsStart(std::atoll(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+prof+threads+window+", value /*ref*/)) {
profThreadsWindow(std::atol(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+prof+threads+file+", value /*ref*/)) {
profThreadsFilename(value);
} else if (commandArgVlValue(arg, "+verilator+prof+vlt+file+", value /*ref*/)) {
profVltFilename(value);
} else if (commandArgVlValue(arg, "+verilator+rand+reset+", value /*ref*/)) {
randReset(std::atoi(value.c_str()));
} else if (commandArgVlValue(arg, "+verilator+seed+", value /*ref*/)) {
randSeed(std::atoi(value.c_str()));
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+start+", u64)
|| commandArgVlUint64(arg, "+verilator+prof+threads+start+", u64)) {
profExecStart(u64);
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+window+", u64, 1)
|| commandArgVlUint64(arg, "+verilator+prof+threads+window+", u64, 1)) {
profExecWindow(u64);
} else if (commandArgVlString(arg, "+verilator+prof+exec+file+", str)
|| commandArgVlString(arg, "+verilator+prof+threads+file+", str)) {
profExecFilename(str);
} else if (commandArgVlString(arg, "+verilator+prof+vlt+file+", str)) {
profVltFilename(str);
} else if (commandArgVlUint64(arg, "+verilator+rand+reset+", u64, 0, 2)) {
randReset(static_cast<int>(u64));
} else if (commandArgVlUint64(arg, "+verilator+seed+", u64, 1,
std::numeric_limits<int>::max())) {
randSeed(static_cast<int>(u64));
} else if (arg == "+verilator+V") {
VerilatedImp::versionDump(); // Someday more info too
VL_FATAL_MT("COMMAND_LINE", 0, "",
@@ -2541,12 +2598,14 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
VL_FATAL_MT("COMMAND_LINE", 0, "",
"Exiting due to command line argument (not an error)");
} else {
VL_PRINTF_MT("%%Warning: Unknown +verilator runtime argument: '%s'\n", arg.c_str());
const std::string msg = "Unknown runtime argument: " + arg;
VL_FATAL_MT("COMMAND_LINE", 0, "", msg.c_str());
}
}
}
bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::string& prefix,
std::string& valuer) {
bool VerilatedContextImp::commandArgVlString(const std::string& arg, const std::string& prefix,
std::string& valuer) {
const size_t len = prefix.length();
if (0 == std::strncmp(prefix.c_str(), arg.c_str(), len)) {
valuer = arg.substr(len);
@@ -2556,6 +2615,30 @@ bool VerilatedContextImp::commandArgVlValue(const std::string& arg, const std::s
}
}
bool VerilatedContextImp::commandArgVlUint64(const std::string& arg, const std::string& prefix,
uint64_t& valuer, uint64_t min, uint64_t max) {
std::string str;
if (commandArgVlString(arg, prefix, str)) {
const auto fail = [&](const std::string& extra = "") {
std::stringstream ss;
ss << "Argument '" << prefix << "' must be an unsigned integer";
if (min != std::numeric_limits<uint64_t>::min()) ss << ", greater than " << min - 1;
if (max != std::numeric_limits<uint64_t>::max()) ss << ", less than " << max + 1;
if (!extra.empty()) ss << ". " << extra;
const std::string& msg = ss.str();
VL_FATAL_MT("COMMAND_LINE", 0, "", msg.c_str());
};
if (std::any_of(str.begin(), str.end(), [](int c) { return !std::isdigit(c); })) fail();
char* end;
valuer = std::strtoull(str.c_str(), &end, 10);
if (errno == ERANGE) fail("Value out of range of uint64_t");
if (valuer < min || valuer > max) fail();
return true;
}
return false;
}
//======================================================================
// VerilatedContext:: + VerilatedContextImp:: Methods - random
@@ -2564,20 +2647,19 @@ void VerilatedContext::randSeed(int val) VL_MT_SAFE {
// and so the rand seed's mutex must also be static
const VerilatedLockGuard lock{VerilatedContextImp::s().s_randMutex};
m_s.m_randSeed = val;
const vluint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
const uint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
// Obververs must see new epoch AFTER seed updated
#ifdef VL_THREADED
std::atomic_signal_fence(std::memory_order_release);
#endif
VerilatedContextImp::s().s_randSeedEpoch = newEpoch;
}
vluint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
uint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
if (randSeed() != 0) {
return ((static_cast<vluint64_t>(randSeed()) << 32)
^ (static_cast<vluint64_t>(randSeed())));
return ((static_cast<uint64_t>(randSeed()) << 32) ^ (static_cast<uint64_t>(randSeed())));
} else {
return ((static_cast<vluint64_t>(vl_sys_rand32()) << 32)
^ (static_cast<vluint64_t>(vl_sys_rand32())));
return ((static_cast<uint64_t>(vl_sys_rand32()) << 32)
^ (static_cast<uint64_t>(vl_sys_rand32())));
}
}
@@ -2624,6 +2706,7 @@ const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
VerilatedSyms::VerilatedSyms(VerilatedContext* contextp)
: _vm_contextp__(contextp ? contextp : Verilated::threadContextp()) {
VerilatedContext::checkMagic(_vm_contextp__);
Verilated::threadContextp(_vm_contextp__);
#ifdef VL_THREADED
__Vm_evalMsgQp = new VerilatedEvalMsgQueue;
@@ -2631,6 +2714,7 @@ VerilatedSyms::VerilatedSyms(VerilatedContext* contextp)
}
VerilatedSyms::~VerilatedSyms() {
VerilatedContext::checkMagic(_vm_contextp__);
#ifdef VL_THREADED
delete __Vm_evalMsgQp;
#endif
@@ -2722,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 {
@@ -2805,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
@@ -2821,8 +2913,8 @@ VerilatedModule::~VerilatedModule() {
// VerilatedVar:: Methods
// cppcheck-suppress unusedFunction // Used by applications
vluint32_t VerilatedVarProps::entSize() const {
vluint32_t size = 1;
uint32_t VerilatedVarProps::entSize() const {
uint32_t size = 1;
switch (vltype()) {
case VLVT_PTR: size = sizeof(void*); break;
case VLVT_UINT8: size = sizeof(CData); break;
@@ -2845,7 +2937,7 @@ void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const
if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr;
if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr;
const int indxAdj = indx - low(dim);
vluint8_t* bytep = reinterpret_cast<vluint8_t*>(datap);
uint8_t* bytep = reinterpret_cast<uint8_t*>(datap);
// If on index 1 of a 2 index array, then each index 1 is index2sz*entsz
size_t slicesz = entSize();
for (int d = dim + 1; d <= m_udims; ++d) slicesz *= elements(d);
@@ -2866,7 +2958,7 @@ VerilatedScope::~VerilatedScope() {
}
void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp,
const char* identifier, vlsint8_t timeunit,
const char* identifier, int8_t timeunit,
const Type& type) VL_MT_UNSAFE {
// Slowpath - called once/scope at construction
// We don't want the space and reference-count access overhead of strings.
+114 -50
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;
@@ -101,11 +105,11 @@ class VerilatedVcdSc;
// clang-format off
// P // Packed data of bit type (C/S/I/Q/W)
using CData = vluint8_t; ///< Data representing 'bit' of 1-8 packed bits
using SData = vluint16_t; ///< Data representing 'bit' of 9-16 packed bits
using IData = vluint32_t; ///< Data representing 'bit' of 17-32 packed bits
using QData = vluint64_t; ///< Data representing 'bit' of 33-64 packed bits
using EData = vluint32_t; ///< Data representing one element of WData array
using CData = uint8_t; ///< Data representing 'bit' of 1-8 packed bits
using SData = uint16_t; ///< Data representing 'bit' of 9-16 packed bits
using IData = uint32_t; ///< Data representing 'bit' of 17-32 packed bits
using QData = uint64_t; ///< Data representing 'bit' of 33-64 packed bits
using EData = uint32_t; ///< Data representing one element of WData array
using WData = EData; ///< Data representing >64 packed bits (used as pointer)
// F = float; // No typedef needed; Verilator uses float
// D = double; // No typedef needed; Verilator uses double
@@ -115,7 +119,7 @@ using WData = EData; ///< Data representing >64 packed bits (used as poin
using WDataInP = const WData*; ///< 'bit' of >64 packed bits as array input to a function
using WDataOutP = WData*; ///< 'bit' of >64 packed bits as array output from a function
enum VerilatedVarType : vluint8_t {
enum VerilatedVarType : uint8_t {
VLVT_UNKNOWN = 0,
VLVT_PTR, // Pointer to something
VLVT_UINT8, // AKA CData
@@ -143,11 +147,11 @@ enum VerilatedVarFlags {
// Mutex and threading support
// Return current thread ID (or 0), not super fast, cache if needed
extern vluint32_t VL_THREAD_ID() VL_MT_SAFE;
extern uint32_t VL_THREAD_ID() VL_MT_SAFE;
#if VL_THREADED
#define VL_LOCK_SPINS 50000 /// Number of times to spin for a mutex before relaxing
#define VL_LOCK_SPINS 50000 /// Number of times to spin for a mutex before yielding
/// Mutex, wrapped to allow -fthread_safety checks
class VL_CAPABILITY("mutex") VerilatedMutex final {
@@ -226,7 +230,7 @@ public:
class VerilatedAssertOneThread final {
// MEMBERS
#if defined(VL_THREADED) && defined(VL_DEBUG)
vluint32_t m_threadid; // Thread that is legal
uint32_t m_threadid; // Thread that is legal
public:
// CONSTRUCTORS
// The constructor establishes the thread id for all later calls.
@@ -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.)
@@ -320,10 +354,10 @@ protected:
bool m_fatalOnVpiError = true; // Fatal on vpi error/unsupported
bool m_gotError = false; // A $finish statement executed
bool m_gotFinish = false; // A $finish or $stop statement executed
vluint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy
// Slow path
vlsint8_t m_timeunit; // Time unit as 0..15
vlsint8_t m_timeprecision; // Time precision as 0..15
int8_t m_timeunit; // Time unit as 0..15
int8_t m_timeprecision; // Time precision as 0..15
int m_errorCount = 0; // Number of errors
int m_errorLimit = 1; // Stop on error number
int m_randReset = 0; // Random reset: 0=all 0s, 1=all 1s, 2=random
@@ -344,10 +378,10 @@ protected:
struct NonSerialized { // Non-serialized information
// These are reloaded from on command-line settings, so do not need to persist
// Fast path
vluint64_t m_profThreadsStart = 1; // +prof+threads starting time
vluint32_t m_profThreadsWindow = 2; // +prof+threads window size
uint64_t m_profExecStart = 1; // +prof+exec+start time
uint32_t m_profExecWindow = 2; // +prof+exec+window size
// Slow path
std::string m_profThreadsFilename; // +prof+threads filename
std::string m_profExecFilename; // +prof+exec+file filename
std::string m_profVltFilename; // +prof+vlt filename
} m_ns;
@@ -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()
@@ -374,6 +418,10 @@ protected:
// List of free descriptors in the MCT region [4, 32)
std::vector<IData> m_fdFreeMct VL_GUARDED_BY(m_fdMutex);
// Magic to check for bad construction
static constexpr uint64_t MAGIC = 0xC35F9A6E5298EE6EULL; // SHA256 "VerilatedContext"
uint64_t m_magic = MAGIC;
private:
// CONSTRUCTORS
VL_UNCOPYABLE(VerilatedContext);
@@ -409,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
@@ -468,16 +516,16 @@ public:
/// timeInc, operating on the thread's context.
///
/// * Else, if VL_TIME_STAMP64 is defined, time comes from the legacy
/// 'vluint64_t vl_time_stamp64()' which must a function be defined by
/// 'uint64_t vl_time_stamp64()' which must a function be defined by
/// the user's wrapper.
///
/// * Else, time comes from the legacy 'double sc_time_stamp()' which
/// must be a function defined by the user's wrapper.
vluint64_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(vluint64_t value) VL_MT_SAFE { m_s.m_time = value; }
void time(uint64_t value) VL_MT_SAFE { m_s.m_time = value; }
/// Advance current simulation time. See time() for side effect details
void timeInc(vluint64_t add) VL_MT_UNSAFE { m_s.m_time += add; }
void timeInc(uint64_t add) VL_MT_UNSAFE { m_s.m_time += add; }
/// Return time units as power-of-ten
int timeunit() const VL_MT_SAFE { return -m_s.m_timeunit; }
/// Set time units as power-of-ten
@@ -491,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);
@@ -506,25 +560,32 @@ 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);
std::string dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
// Internal: --prof-threads related settings
void profThreadsStart(vluint64_t flag) VL_MT_SAFE;
vluint64_t profThreadsStart() const VL_MT_SAFE { return m_ns.m_profThreadsStart; }
void profThreadsWindow(vluint64_t flag) VL_MT_SAFE;
vluint32_t profThreadsWindow() const VL_MT_SAFE { return m_ns.m_profThreadsWindow; }
void profThreadsFilename(const std::string& flag) VL_MT_SAFE;
std::string profThreadsFilename() const VL_MT_SAFE;
// Internal: --prof-exec related settings
void profExecStart(uint64_t flag) VL_MT_SAFE;
uint64_t profExecStart() const VL_MT_SAFE { return m_ns.m_profExecStart; }
void profExecWindow(uint64_t flag) VL_MT_SAFE;
uint32_t profExecWindow() const VL_MT_SAFE { return m_ns.m_profExecWindow; }
void profExecFilename(const std::string& flag) VL_MT_SAFE;
std::string profExecFilename() const VL_MT_SAFE;
void profVltFilename(const std::string& flag) VL_MT_SAFE;
std::string profVltFilename() const VL_MT_SAFE;
@@ -535,6 +596,10 @@ public: // But for internal use only
// Internal: Serialization setup
static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); }
void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; }
// Internal: Check magic number
static void checkMagic(const VerilatedContext* contextp);
void selfTestClearMagic() { m_magic = 0x2; }
};
//===========================================================================
@@ -559,7 +624,7 @@ public: // But for internal use only
class VerilatedScope final {
public:
enum Type : vluint8_t {
enum Type : uint8_t {
SCOPE_MODULE,
SCOPE_OTHER
}; // Type of a scope, currently module is only interesting
@@ -572,28 +637,28 @@ private:
VerilatedVarNameMap* m_varsp = nullptr; // Variable map
const char* m_namep = nullptr; // Scope name (Slowpath)
const char* m_identifierp = nullptr; // Identifier of scope (with escapes removed)
vlsint8_t m_timeunit = 0; // Timeunit in negative power-of-10
int8_t m_timeunit = 0; // Timeunit in negative power-of-10
Type m_type = SCOPE_OTHER; // Type of the scope
public: // But internals only - called from VerilatedModule's
VerilatedScope() = default;
~VerilatedScope();
void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp,
const char* identifier, vlsint8_t timeunit, const Type& type) VL_MT_UNSAFE;
const char* identifier, int8_t timeunit, const Type& type) VL_MT_UNSAFE;
void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
void varInsert(int finalize, const char* namep, void* datap, bool isParam,
VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE;
// ACCESSORS
const char* name() const { return m_namep; }
const char* identifier() const { return m_identifierp; }
vlsint8_t timeunit() const { return m_timeunit; }
inline VerilatedSyms* symsp() const { return m_symsp; }
int8_t timeunit() const { return m_timeunit; }
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
@@ -636,9 +701,9 @@ class Verilated final {
// Fast path
VerilatedContext* t_contextp = nullptr; // Thread's context
#ifdef VL_THREADED
vluint32_t t_mtaskId = 0; // mtask# executing on this thread
uint32_t t_mtaskId = 0; // mtask# executing on this thread
// Messages maybe pending on thread, needs end-of-eval calls
vluint32_t t_endOfEvalReqd = 0;
uint32_t t_endOfEvalReqd = 0;
#endif
const VerilatedScope* t_dpiScopep = nullptr; // DPI context scope
const char* t_dpiFilename = nullptr; // DPI context filename
@@ -662,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; }
@@ -760,11 +825,11 @@ public:
/// Return VerilatedContext::randSeed using current thread's VerilatedContext
static int randSeed() VL_MT_SAFE { return Verilated::threadContextp()->randSeed(); }
/// Call VerilatedContext::time using current thread's VerilatedContext
static void time(vluint64_t val) VL_MT_SAFE { Verilated::threadContextp()->time(val); }
static void time(uint64_t val) VL_MT_SAFE { Verilated::threadContextp()->time(val); }
/// Return VerilatedContext::time using current thread's VerilatedContext
static vluint64_t time() VL_MT_SAFE { return Verilated::threadContextp()->time(); }
static uint64_t time() VL_MT_SAFE { return Verilated::threadContextp()->time(); }
/// Call VerilatedContext::timeInc using current thread's VerilatedContext
static void timeInc(vluint64_t add) VL_MT_UNSAFE { Verilated::threadContextp()->timeInc(add); }
static void timeInc(uint64_t add) VL_MT_UNSAFE { Verilated::threadContextp()->timeInc(add); }
// Deprecated
static int timeunit() VL_MT_SAFE { return Verilated::threadContextp()->timeunit(); }
static int timeprecision() VL_MT_SAFE { return Verilated::threadContextp()->timeprecision(); }
@@ -822,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)
@@ -850,8 +914,8 @@ public:
#ifdef VL_THREADED
// Internal: Set the mtaskId, called when an mtask starts
// Per thread, so no need to be in VerilatedContext
static void mtaskId(vluint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; }
static vluint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; }
static void mtaskId(uint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; }
static uint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; }
static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; }
static void endOfEvalReqdDec() VL_MT_SAFE { --t_s.t_endOfEvalReqd; }
@@ -869,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
+6 -21
View File
@@ -56,6 +56,7 @@ VK_CPPFLAGS_ALWAYS += \
-DVM_SC=$(VM_SC) \
-DVM_TRACE=$(VM_TRACE) \
-DVM_TRACE_FST=$(VM_TRACE_FST) \
-DVM_TRACE_VCD=$(VM_TRACE_VCD) \
$(CFG_CXXFLAGS_NO_UNUSED) \
ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users
@@ -141,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
@@ -186,13 +167,17 @@ 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,
# but keeping the distinction for compatibility for now.
VK_GLOBAL_OBJS = $(addsuffix .o, $(VM_GLOBAL_FAST) $(VM_GLOBAL_SLOW))
# Need to re-build if the generated makefile changes, as compiler options might
# have changed.
$(VK_GLOBAL_OBJS): $(VM_PREFIX).mk
ifneq ($(VM_PARALLEL_BUILDS),1)
# Fast build for small designs: All .cpp files in one fell swoop. This
# saves total compute, but can be slower if only a little changes. It is
+19 -18
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>
@@ -59,7 +61,7 @@ public: // But only local to this file
}
}
virtual ~VerilatedCovImpItem() = default;
virtual vluint64_t count() const = 0;
virtual uint64_t count() const = 0;
virtual void zero() const = 0;
};
@@ -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 vluint64_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;
};
//=============================================================================
@@ -103,7 +106,7 @@ private:
using ItemList = std::deque<VerilatedCovImpItem*>;
// MEMBERS
VerilatedMutex m_mutex; // Protects all members
mutable VerilatedMutex m_mutex; // Protects all members
ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex); // Unique arbitrary value for values
IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex); // Unique arbitrary value for keys
ItemList m_items VL_GUARDED_BY(m_mutex); // List of all items
@@ -122,11 +125,7 @@ public:
protected:
friend class VerilatedCovContext;
virtual ~VerilatedCovImp() override { clearGuts(); }
static VerilatedCovImp& imp() VL_MT_SAFE {
static VerilatedCovImp s_singleton;
return s_singleton;
}
~VerilatedCovImp() override { clearGuts(); }
private:
// PRIVATE METHODS
@@ -209,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;
@@ -372,7 +371,7 @@ public:
os << "# SystemC::Coverage-3\n";
// Build list of events; totalize if collapsing hierarchy
std::map<const std::string, std::pair<std::string, vluint64_t>> eventCounts;
std::map<const std::string, std::pair<std::string, uint64_t>> eventCounts;
for (const auto& itemp : m_items) {
std::string name;
std::string hier;
@@ -439,11 +438,11 @@ void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
}
void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->write(filenamep); }
void VerilatedCovContext::_inserti(vluint32_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<vluint32_t>{itemp});
void VerilatedCovContext::_inserti(uint32_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<uint32_t>{itemp});
}
void VerilatedCovContext::_inserti(vluint64_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<vluint64_t>{itemp});
void VerilatedCovContext::_inserti(uint64_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<uint64_t>{itemp});
}
void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE {
impp()->insertf(filename, lineno);
@@ -515,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);
}
+10 -7
View File
@@ -26,6 +26,7 @@
#define VERILATOR_VERILATED_COV_H_
#include "verilatedos.h"
#include "verilated.h"
#include <iostream>
@@ -72,7 +73,7 @@ class VerilatedCovImp;
///
/// Example:
///
/// vluint32_t m_cases[10]; // Storage for coverage data
/// uint32_t m_cases[10]; // Storage for coverage data
/// constructor() {
/// // Initialize
/// for (int i = 0; i < 10; ++i) m_cases[i] = 0;
@@ -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,14 +122,15 @@ 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
// Do not call directly; use VL_COVER_INSERT or higher level macros instead
// _insert1: Remember item pointer with count. (Not const, as may add zeroing function)
void _inserti(vluint32_t* itemp) VL_MT_SAFE;
void _inserti(vluint64_t* itemp) VL_MT_SAFE;
void _inserti(uint32_t* itemp) VL_MT_SAFE;
void _inserti(uint64_t* itemp) VL_MT_SAFE;
// _insert2: Set default filename and line number
void _insertf(const char* filename, int lineno) VL_MT_SAFE;
// _insert3: Set parameters
@@ -157,8 +160,8 @@ protected:
friend class VerilatedCovImp;
// CONSTRUCTORS
// Internal: Only made as part of VerilatedCovImp
VerilatedCovContext() {}
virtual ~VerilatedCovContext() {}
VerilatedCovContext() = default;
~VerilatedCovContext() override = default;
// METHODS
// Internal: access to implementation class
+4 -2
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");
@@ -417,7 +419,7 @@ static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int narg
}
//======================================================================
// DPI accessors that simply call above functions
// DPI accessors that call above functions
void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) {
const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
+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"
+65 -29
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
@@ -83,15 +83,16 @@ static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE
//=============================================================================
// Specialization of the generics for this trace format
#define VL_DERIVED_T VerilatedFst
#include "verilated_trace_imp.cpp"
#undef VL_DERIVED_T
#define VL_SUB_T VerilatedFst
#define VL_BUF_T VerilatedFstBuffer
#include "verilated_trace_imp.h"
#undef VL_SUB_T
#undef VL_BUF_T
//=============================================================================
// VerilatedFst
VerilatedFst::VerilatedFst(void* fst)
: m_fst{fst} {}
VerilatedFst::VerilatedFst(void* /*fst*/) {}
VerilatedFst::~VerilatedFst() {
if (m_fst) fstWriterClose(m_fst);
@@ -104,14 +105,12 @@ 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();
VerilatedTrace<VerilatedFst>::traceInit();
Super::traceInit();
// Clear the scope stack
auto it = m_curScope.begin();
@@ -133,23 +132,23 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
VerilatedTrace<VerilatedFst>::closeBase();
Super::closeBase();
fstWriterClose(m_fst);
m_fst = nullptr;
}
void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
VerilatedTrace<VerilatedFst>::flushBase();
Super::flushBase();
fstWriterFlushContext(m_fst);
}
void VerilatedFst::emitTimeChange(vluint64_t timeui) { fstWriterEmitTimeChange(m_fst, timeui); }
void VerilatedFst::emitTimeChange(uint64_t timeui) { fstWriterEmitTimeChange(m_fst, timeui); }
//=============================================================================
// Decl
void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, uint32_t elements,
unsigned int minValbits, const char** itemNamesp,
const char** itemValuesp) {
const fstEnumHandle enumNum
@@ -157,12 +156,12 @@ void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elem
m_local2fstdtype[dtypenum] = enumNum;
}
void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void VerilatedFst::declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, bool bussed, int msb,
int lsb) {
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
const bool enabled = VerilatedTrace<VerilatedFst>::declCode(code, name, bits, false);
const bool enabled = Super::declCode(code, name, bits, false);
if (!enabled) return;
std::string nameasstr = namePrefix() + name;
@@ -224,39 +223,76 @@ void VerilatedFst::declare(vluint32_t code, const char* name, int dtypenum, fstV
}
}
void VerilatedFst::declBit(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void VerilatedFst::declBit(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum) {
declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 0, 0);
}
void VerilatedFst::declBus(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void VerilatedFst::declBus(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
}
void VerilatedFst::declQuad(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void VerilatedFst::declQuad(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
}
void VerilatedFst::declArray(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void VerilatedFst::declArray(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
declare(code, name, dtypenum, vardir, vartype, array, arraynum, true, msb, lsb);
}
void VerilatedFst::declDouble(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void VerilatedFst::declDouble(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum) {
declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 63, 0);
}
//=============================================================================
// Get/commit trace buffer
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer() {
#ifdef VL_THREADED
if (offload()) return new OffloadBuffer{*this};
#endif
return new Buffer{*this};
}
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;
}
//=============================================================================
// Configure
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
// Note: emit* are only ever called from one place (full* in
// verilated_trace_imp.cpp, which is included in this file at the top),
// verilated_trace_imp.h, which is included in this file at the top),
// so always inline them.
VL_ATTR_ALWINLINE
void VerilatedFst::emitBit(vluint32_t code, CData newval) {
void VerilatedFstBuffer::emitBit(uint32_t code, CData newval) {
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
fstWriterEmitValueChange(m_fst, m_symbolp[code], newval ? "1" : "0");
}
VL_ATTR_ALWINLINE
void VerilatedFst::emitCData(vluint32_t code, CData newval, int bits) {
void VerilatedFstBuffer::emitCData(uint32_t code, CData newval, int bits) {
char buf[VL_BYTESIZE];
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
cvtCDataToStr(buf, newval << (VL_BYTESIZE - bits));
@@ -264,7 +300,7 @@ void VerilatedFst::emitCData(vluint32_t code, CData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedFst::emitSData(vluint32_t code, SData newval, int bits) {
void VerilatedFstBuffer::emitSData(uint32_t code, SData newval, int bits) {
char buf[VL_SHORTSIZE];
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
cvtSDataToStr(buf, newval << (VL_SHORTSIZE - bits));
@@ -272,7 +308,7 @@ void VerilatedFst::emitSData(vluint32_t code, SData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedFst::emitIData(vluint32_t code, IData newval, int bits) {
void VerilatedFstBuffer::emitIData(uint32_t code, IData newval, int bits) {
char buf[VL_IDATASIZE];
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
cvtIDataToStr(buf, newval << (VL_IDATASIZE - bits));
@@ -280,7 +316,7 @@ void VerilatedFst::emitIData(vluint32_t code, IData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedFst::emitQData(vluint32_t code, QData newval, int bits) {
void VerilatedFstBuffer::emitQData(uint32_t code, QData newval, int bits) {
char buf[VL_QUADSIZE];
VL_DEBUG_IFDEF(assert(m_symbolp[code]););
cvtQDataToStr(buf, newval << (VL_QUADSIZE - bits));
@@ -288,7 +324,7 @@ void VerilatedFst::emitQData(vluint32_t code, QData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedFst::emitWData(vluint32_t code, const WData* newvalp, int bits) {
void VerilatedFstBuffer::emitWData(uint32_t code, const WData* newvalp, int bits) {
int words = VL_WORDS_I(bits);
char* wp = m_strbuf;
// Convert the most significant word
@@ -304,6 +340,6 @@ void VerilatedFst::emitWData(vluint32_t code, const WData* newvalp, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedFst::emitDouble(vluint32_t code, double newval) {
void VerilatedFstBuffer::emitDouble(uint32_t code, double newval) {
fstWriterEmitValueChange(m_fst, m_symbolp[code], &newval);
}
+88 -42
View File
@@ -31,60 +31,64 @@
#include <string>
#include <vector>
class VerilatedFstBuffer;
//=============================================================================
// VerilatedFst
// Base class to create a Verilator FST dump
// This is an internally used class - see VerilatedFstC for what to call from applications
class VerilatedFst final : public VerilatedTrace<VerilatedFst> {
class VerilatedFst final : public VerilatedTrace<VerilatedFst, VerilatedFstBuffer> {
public:
using Super = VerilatedTrace<VerilatedFst, VerilatedFstBuffer>;
private:
// Give the superclass access to private bits (to avoid virtual functions)
friend class VerilatedTrace<VerilatedFst>;
friend VerilatedFstBuffer; // Give the buffer access to the private bits
//=========================================================================
// FST specific internals
void* m_fst;
std::map<vluint32_t, fstHandle> m_code2symbol;
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(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, bool bussed, int msb, int lsb);
protected:
//=========================================================================
// Implementation of VerilatedTrace interface
// Implementations of protected virtual methods for VerilatedTrace
virtual void emitTimeChange(vluint64_t timeui) override;
// Called when the trace moves forward to a new time point
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(); }
// Implementations of duck-typed methods for VerilatedTrace. These are
// called from only one place (namely full*) so always inline them.
inline void emitBit(vluint32_t code, CData newval);
inline void emitCData(vluint32_t code, CData newval, int bits);
inline void emitSData(vluint32_t code, SData newval, int bits);
inline void emitIData(vluint32_t code, IData newval, int bits);
inline void emitQData(vluint32_t code, QData newval, int bits);
inline void emitWData(vluint32_t code, const WData* newvalp, int bits);
inline void emitDouble(vluint32_t code, double newval);
// Trace buffer management
Buffer* getTraceBuffer() override;
void commitTraceBuffer(Buffer*) override;
// Configure sub-class
void configure(const VerilatedTraceConfig&) override;
public:
//=========================================================================
// External interface to client code
// (All must be threadsafe)
// CONSTRUCTOR
explicit VerilatedFst(void* fst = nullptr);
~VerilatedFst();
// METHODS - All must be thread safe
// Open the file; call isOpen() to see if errors
void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
// Close the file
@@ -97,33 +101,75 @@ public:
//=========================================================================
// Internal interface to Verilator generated code
// Inside dumping routines, declare a data type
void declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
unsigned int minValbits, const char** itemNamesp, const char** itemValuesp);
// Inside dumping routines, declare a signal
void declBit(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void declBit(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum);
void declBus(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void declBus(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
void declQuad(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void declQuad(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
void declArray(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void declArray(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
void declDouble(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
void declDouble(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
fstVarType vartype, bool array, int arraynum);
void declDTypeEnum(int dtypenum, const char* name, uint32_t elements, unsigned int minValbits,
const char** itemNamesp, const char** itemValuesp);
};
#ifndef DOXYGEN
// Declare specialization here as it's used in VerilatedFstC just below
template <> void VerilatedTrace<VerilatedFst>::dump(vluint64_t timeui);
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const char* unitp);
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const std::string& unit);
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const char* unitp);
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const std::string& unit);
template <> void VerilatedTrace<VerilatedFst>::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 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;
// code to fstHande map, as an array
const fstHandle* const m_symbolp = m_owner.m_symbolp;
// String buffer long enough to hold maxBits() chars
char* const m_strbuf = m_owner.m_strbuf;
// CONSTRUCTOR
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 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);
};
//=============================================================================
// VerilatedFstC
/// Create a FST dump file in C standalone (no SystemC) simulations.
@@ -155,12 +201,12 @@ public:
/// Write one cycle of dump data
/// Call with the current context's time just after eval'ed,
/// e.g. ->dump(contextp->time())
void dump(vluint64_t timeui) { m_sptrace.dump(timeui); }
void dump(uint64_t timeui) { m_sptrace.dump(timeui); }
/// Write one cycle of dump data - backward compatible and to reduce
/// conversion warnings. It's better to use a vluint64_t time instead.
void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
/// conversion warnings. It's better to use a uint64_t time instead.
void dump(double timestamp) { dump(static_cast<uint64_t>(timestamp)); }
void dump(uint32_t timestamp) { dump(static_cast<uint64_t>(timestamp)); }
void dump(int timestamp) { dump(static_cast<uint64_t>(timestamp)); }
// METHODS - Internal/backward compatible
// \protectedsection
@@ -186,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 )
+73 -85
View File
@@ -90,7 +90,7 @@ extern WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp);
extern IData VL_RANDOM_SEEDED_II(IData& seedr) VL_MT_SAFE;
extern IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE;
inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) {
const vluint64_t rnd = vl_rand64();
const uint64_t rnd = vl_rand64();
if (VL_LIKELY(hi > lo)) {
// (hi - lo + 1) can be zero when hi is UINT_MAX and lo is zero
if (VL_UNLIKELY(hi - lo + 1 == 0)) return rnd;
@@ -112,15 +112,6 @@ extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp);
/// Zero reset a signal (slow - else use VL_ZERO_W)
extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp);
#if VL_THREADED
/// Return high-precision counter for profiling, or 0x0 if not available
inline QData VL_RDTSC_Q() {
vluint64_t val;
VL_RDTSC(val);
return val;
}
#endif
extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
const VerilatedContext* contextp) VL_MT_SAFE;
@@ -155,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
//=========================================================================
@@ -219,29 +210,27 @@ static inline QData VL_CVT_Q_D(double lhs) VL_PURE {
// Return double from lhs (numeric) unsigned
double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE {
return static_cast<double>(static_cast<vluint32_t>(lhs));
return static_cast<double>(static_cast<uint32_t>(lhs));
}
static inline double VL_ITOR_D_Q(int, QData lhs) VL_PURE {
return static_cast<double>(static_cast<vluint64_t>(lhs));
return static_cast<double>(static_cast<uint64_t>(lhs));
}
// Return double from lhs (numeric) signed
double VL_ISTOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
static inline double VL_ISTOR_D_I(int lbits, IData lhs) VL_PURE {
if (lbits == 32) return static_cast<double>(static_cast<vlsint32_t>(lhs));
if (lbits == 32) return static_cast<double>(static_cast<int32_t>(lhs));
VlWide<VL_WQ_WORDS_E> lwp;
VL_SET_WI(lwp, lhs);
return VL_ISTOR_D_W(lbits, lwp);
}
static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_PURE {
if (lbits == 64) return static_cast<double>(static_cast<vlsint64_t>(lhs));
if (lbits == 64) return static_cast<double>(static_cast<int64_t>(lhs));
VlWide<VL_WQ_WORDS_E> lwp;
VL_SET_WQ(lwp, lhs);
return VL_ISTOR_D_W(lbits, lwp);
}
// Return QData from double (numeric)
static inline IData VL_RTOI_I_D(double lhs) VL_PURE {
return static_cast<vlsint32_t>(VL_TRUNC(lhs));
}
static inline IData VL_RTOI_I_D(double lhs) VL_PURE { return static_cast<int32_t>(VL_TRUNC(lhs)); }
// Sign extend such that if MSB set, we get ffff_ffff, else 0s
// (Requires clean input)
@@ -267,51 +256,33 @@ 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
// Already defined: extern sc_time sc_time_stamp();
inline vluint64_t vl_time_stamp64() { return sc_time_stamp().value(); }
inline uint64_t vl_time_stamp64() { return sc_time_stamp().value(); }
#else // Non-SystemC
# if !defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY)
# ifdef VL_TIME_STAMP64
// vl_time_stamp64() may be optionally defined by the user to return time.
// On MSVC++ weak symbols are not supported so must be declared, or define
// VL_TIME_CONTEXT.
extern vluint64_t vl_time_stamp64() VL_ATTR_WEAK;
extern uint64_t vl_time_stamp64() VL_ATTR_WEAK;
# else
// sc_time_stamp() may be optionally defined by the user to return time.
// On MSVC++ weak symbols are not supported so must be declared, or define
// VL_TIME_CONTEXT.
extern double sc_time_stamp() VL_ATTR_WEAK; // Verilator 4.032 and newer
inline vluint64_t vl_time_stamp64() {
inline uint64_t vl_time_stamp64() {
// clang9.0.1 requires & although we really do want the weak symbol value
return VL_LIKELY(&sc_time_stamp) ? static_cast<vluint64_t>(sc_time_stamp()) : 0;
return VL_LIKELY(&sc_time_stamp) ? static_cast<uint64_t>(sc_time_stamp()) : 0;
}
# endif
# endif
#endif
inline vluint64_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))
@@ -336,7 +307,7 @@ inline vluint64_t VerilatedContext::time() const VL_MT_SAFE {
// Return time precision as multiplier of time units
double vl_time_multiplier(int scale) VL_PURE;
// Return power of 10. e.g. returns 100 if n==2
vluint64_t vl_time_pow10(int n) VL_PURE;
uint64_t vl_time_pow10(int n) VL_PURE;
#ifdef VL_DEBUG
/// Evaluate statement if VL_DEBUG defined
@@ -477,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); \
}
@@ -521,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); \
}
@@ -882,46 +870,46 @@ static inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) V
static inline IData VL_GTS_III(int lbits, IData lhs, IData rhs) VL_PURE {
// For lbits==32, this becomes just a single instruction, otherwise ~5.
// GCC 3.3.4 sign extension bugs on AMD64 architecture force us to use quad logic
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
return lhs_signed > rhs_signed;
}
static inline IData VL_GTS_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
return lhs_signed > rhs_signed;
}
static inline IData VL_GTES_III(int lbits, IData lhs, IData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
return lhs_signed >= rhs_signed;
}
static inline IData VL_GTES_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
return lhs_signed >= rhs_signed;
}
static inline IData VL_LTS_III(int lbits, IData lhs, IData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
return lhs_signed < rhs_signed;
}
static inline IData VL_LTS_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
return lhs_signed < rhs_signed;
}
static inline IData VL_LTES_III(int lbits, IData lhs, IData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc
return lhs_signed <= rhs_signed;
}
static inline IData VL_LTES_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
return lhs_signed <= rhs_signed;
}
@@ -1015,13 +1003,13 @@ static inline WDataOutP VL_MUL_W(int words, WDataOutP owp, WDataInP const lwp,
}
static inline IData VL_MULS_III(int lbits, IData lhs, IData rhs) VL_PURE {
const vlsint32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs);
const vlsint32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs);
const int32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs);
const int32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs);
return lhs_signed * rhs_signed;
}
static inline QData VL_MULS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE {
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
return lhs_signed * rhs_signed;
}
@@ -1067,30 +1055,30 @@ static inline IData VL_DIVS_III(int lbits, IData lhs, IData rhs) VL_PURE {
if (VL_UNLIKELY(rhs == 0)) return 0;
// -MAX / -1 cannot be represented in twos complement, and will cause SIGFPE
if (VL_UNLIKELY(lhs == 0x80000000 && rhs == 0xffffffff)) return 0;
const vlsint32_t lhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, lhs);
const vlsint32_t rhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, rhs);
const int32_t lhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, lhs);
const int32_t rhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, rhs);
return lhs_signed / rhs_signed;
}
static inline QData VL_DIVS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE {
if (VL_UNLIKELY(rhs == 0)) return 0;
// -MAX / -1 cannot be represented in twos complement, and will cause SIGFPE
if (VL_UNLIKELY(lhs == 0x8000000000000000ULL && rhs == 0xffffffffffffffffULL)) return 0;
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, rhs);
return lhs_signed / rhs_signed;
}
static inline IData VL_MODDIVS_III(int lbits, IData lhs, IData rhs) VL_PURE {
if (VL_UNLIKELY(rhs == 0)) return 0;
if (VL_UNLIKELY(lhs == 0x80000000 && rhs == 0xffffffff)) return 0;
const vlsint32_t lhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, lhs);
const vlsint32_t rhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, rhs);
const int32_t lhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, lhs);
const int32_t rhs_signed = VL_EXTENDS_II(VL_IDATASIZE, lbits, rhs);
return lhs_signed % rhs_signed;
}
static inline QData VL_MODDIVS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE {
if (VL_UNLIKELY(rhs == 0)) return 0;
if (VL_UNLIKELY(lhs == 0x8000000000000000ULL && rhs == 0xffffffffffffffffULL)) return 0;
const vlsint64_t lhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, lhs);
const vlsint64_t rhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, rhs);
const int64_t lhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, lhs);
const int64_t rhs_signed = VL_EXTENDS_QQ(VL_QUADSIZE, lbits, rhs);
return lhs_signed % rhs_signed;
}
@@ -1421,8 +1409,8 @@ static inline IData VL_STREAML_FAST_III(int lbits, IData ld, IData rd_log2) VL_P
// ret = 10324---
IData ret = ld;
if (rd_log2) {
const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits
const vluint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS)
const uint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits
const uint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS)
const IData msbMask = VL_MASK_I(lbitsRem) << lbitsFloor; // mask to sel only bits in MSS
ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_UL(1) << rd_log2) - lbitsRem));
}
@@ -1441,8 +1429,8 @@ static inline QData VL_STREAML_FAST_QQI(int lbits, QData ld, IData rd_log2) VL_P
// Pre-shift bits in most-significant slice (see comment in VL_STREAML_FAST_III)
QData ret = ld;
if (rd_log2) {
const vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2);
const vluint32_t lbitsRem = lbits - lbitsFloor;
const uint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2);
const uint32_t lbitsRem = lbits - lbitsFloor;
const QData msbMask = lbitsFloor == 64 ? 0ULL : VL_MASK_Q(lbitsRem) << lbitsFloor;
ret = (ret & ~msbMask) | ((ret & msbMask) << ((1ULL << rd_log2) - lbitsRem));
}
@@ -1940,8 +1928,8 @@ static inline QData VL_RTOIROUND_Q_D(double lhs) VL_PURE {
if (lhs == 0.0) return 0;
const QData q = VL_CVT_Q_D(lhs);
const int lsb = static_cast<int>((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52;
const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
vluint64_t out = 0;
const uint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
uint64_t out = 0;
if (lsb < 0) {
out = mantissa >> -lsb;
} else if (lsb < 64) {
@@ -1961,7 +1949,7 @@ static inline WDataOutP VL_RTOIROUND_W_D(int obits, WDataOutP owp, double lhs) V
if (lhs == 0.0) return owp;
const QData q = VL_CVT_Q_D(lhs);
const int lsb = static_cast<int>((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52;
const vluint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
const uint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
if (lsb < 0) {
VL_SET_WQ(owp, mantissa >> -lsb);
} else if (lsb < obits) {
+32 -32
View File
@@ -34,6 +34,7 @@
#include <algorithm>
#include <deque>
#include <limits>
#include <map>
#include <numeric>
#include <set>
@@ -64,7 +65,7 @@ public:
private:
// MEMBERS
vluint32_t m_mtaskId; // MTask that did enqueue
uint32_t m_mtaskId; // MTask that did enqueue
std::function<void()> m_cb; // Lambda to execute when message received
public:
// CONSTRUCTORS
@@ -77,7 +78,7 @@ public:
VerilatedMsg& operator=(const VerilatedMsg&) = default;
VerilatedMsg& operator=(VerilatedMsg&&) = default;
// METHODS
vluint32_t mtaskId() const { return m_mtaskId; }
uint32_t mtaskId() const { return m_mtaskId; }
// Execute the lambda function
void run() const { m_cb(); }
};
@@ -88,9 +89,9 @@ public:
class VerilatedEvalMsgQueue final {
using VerilatedThreadQueue = std::multiset<VerilatedMsg, VerilatedMsg::Cmp>;
std::atomic<vluint64_t> m_depth; // Current depth of queue (see comments below)
std::atomic<uint64_t> m_depth; // Current depth of queue (see comments below)
VerilatedMutex m_mutex; // Mutex protecting queue
mutable VerilatedMutex m_mutex; // Mutex protecting queue
VerilatedThreadQueue m_queue VL_GUARDED_BY(m_mutex); // Message queue
public:
// CONSTRUCTORS
@@ -142,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);
@@ -187,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;
}
@@ -235,17 +235,16 @@ 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
vluint64_t randSeedDefault64() const VL_MT_SAFE;
static vluint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; }
uint64_t randSeedDefault64() const VL_MT_SAFE;
static uint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; }
// METHODS - timeformat
int timeFormatUnits() const VL_MT_SAFE {
@@ -271,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;
@@ -297,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;
@@ -343,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
@@ -358,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;
@@ -394,7 +392,11 @@ protected:
// METHODS - protected
void commandArgsAddGuts(int argc, const char** argv);
void commandArgVl(const std::string& arg);
bool commandArgVlValue(const std::string& arg, const std::string& prefix, std::string& valuer);
bool commandArgVlString(const std::string& arg, const std::string& prefix,
std::string& valuer);
bool commandArgVlUint64(const std::string& arg, const std::string& prefix, uint64_t& valuer,
uint64_t min = std::numeric_limits<uint64_t>::min(),
uint64_t max = std::numeric_limits<uint64_t>::max());
void commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex);
};
@@ -459,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()) {
@@ -474,17 +475,18 @@ 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 simply iterate.
// Slow ok - called once/scope on destruction, so we only iterate.
const VerilatedLockGuard lock{s().m_userMapMutex};
for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) {
if (it->first.first == scopep) {
@@ -507,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};
@@ -529,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
+225
View File
@@ -0,0 +1,225 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//=============================================================================
//
// Code available from: https://verilator.org
//
// Copyright 2012-2022 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//=============================================================================
///
/// \file
/// \brief Verilated run-time profiling implementation code
///
//=============================================================================
#include "verilatedos.h"
#include "verilated_profiler.h"
#if VL_THREADED
#include "verilated_threads.h"
#endif
#include <fstream>
#include <string>
//=============================================================================
// Globals
// Internal note: Globals may multi-construct, see verilated.cpp top.
VL_THREAD_LOCAL VlExecutionProfiler::ExecutionTrace VlExecutionProfiler::t_trace;
constexpr const char* const VlExecutionRecord::s_ascii[];
//=============================================================================
// VlPgoProfiler implementation
uint16_t VlExecutionRecord::getcpu() {
#if defined(__linux)
return sched_getcpu(); // TODO: this is a system call. Not exactly cheap.
#elif defined(__APPLE__) && !defined(__arm64__)
uint32_t info[4];
__cpuid_count(1, 0, info[0], info[1], info[2], info[3]);
// info[1] is EBX, bits 24-31 are APIC ID
if ((info[3] & (1 << 9)) == 0) {
return -1; // no APIC on chip
} else {
return (unsigned)info[1] >> 24;
}
#elif defined(_WIN32)
return GetCurrentProcessorNumber();
#else
return 0;
#endif
}
//=============================================================================
// VlExecutionProfiler implementation
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(VerilatedContext& context)
: m_context{context} {
// Setup profiling on main thread
setupThread(0);
}
void VlExecutionProfiler::configure() {
if (VL_UNLIKELY(m_enabled)) {
--m_windowCount;
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 = m_context.profExecFilename();
dump(fileName.c_str(), tickEnd);
m_enabled = false;
}
return;
}
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() >= 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 = 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};
exists = !m_traceps.emplace(threadId, &t_trace).second;
}
if (VL_UNLIKELY(exists)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "multiple initialization of profiler on some thread");
}
}
void VlExecutionProfiler::clear() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
for (const auto& pair : m_traceps) {
ExecutionTrace* const tracep = pair.second;
const size_t reserve = roundUptoMultipleOf<RESERVED_TRACE_CAPACITY>(tracep->size());
tracep->clear();
tracep->reserve(reserve);
}
}
void VlExecutionProfiler::dump(const char* filenamep, uint64_t tickEnd)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
VL_DEBUG_IF(VL_DBG_MSGF("+prof+exec writing to '%s'\n", filenamep););
FILE* const fp = std::fopen(filenamep, "w");
if (VL_UNLIKELY(!fp)) { VL_FATAL_MT(filenamep, 0, "", "+prof+exec+file file not writable"); }
// TODO Perhaps merge with verilated_coverage output format, so can
// have a common merging and reporting tool, etc.
fprintf(fp, "VLPROFVERSION 2.0 # Verilator execution profile version 2.0\n");
fprintf(fp, "VLPROF arg +verilator+prof+exec+start+%" PRIu64 "\n",
Verilated::threadContextp()->profExecStart());
fprintf(fp, "VLPROF arg +verilator+prof+exec+window+%u\n",
Verilated::threadContextp()->profExecWindow());
const unsigned threads = static_cast<unsigned>(m_traceps.size());
fprintf(fp, "VLPROF stat threads %u\n", threads);
#ifdef VL_THREADED
fprintf(fp, "VLPROF stat yields %" PRIu64 "\n", VlMTaskVertex::yields());
#endif
// Copy /proc/cpuinfo into this output so verilator_gantt can be run on
// a different machine
{
const std::unique_ptr<std::ifstream> ifp{new std::ifstream("/proc/cpuinfo")};
if (!ifp->fail()) {
std::string line;
while (std::getline(*ifp, line)) { fprintf(fp, "VLPROFPROC %s\n", line.c_str()); }
}
}
for (const auto& pair : m_traceps) {
const uint32_t threadId = pair.first;
ExecutionTrace* const tracep = pair.second;
fprintf(fp, "VLPROFTHREAD %" PRIu32 "\n", threadId);
for (const VlExecutionRecord& er : *tracep) {
const char* const name = VlExecutionRecord::s_ascii[static_cast<uint8_t>(er.m_type)];
const uint64_t time = er.m_tick - m_tickBegin;
fprintf(fp, "VLPROFEXEC %s %" PRIu64, name, time);
switch (er.m_type) {
case VlExecutionRecord::Type::EVAL_BEGIN:
case VlExecutionRecord::Type::EVAL_END:
case VlExecutionRecord::Type::EVAL_LOOP_BEGIN:
case VlExecutionRecord::Type::EVAL_LOOP_END:
// No payload
fprintf(fp, "\n");
break;
case VlExecutionRecord::Type::MTASK_BEGIN: {
const auto& payload = er.m_payload.mtaskBegin;
fprintf(fp, " id %u predictStart %u cpu %u\n", payload.m_id,
payload.m_predictStart, payload.m_cpu);
break;
}
case VlExecutionRecord::Type::MTASK_END: {
const auto& payload = er.m_payload.mtaskEnd;
fprintf(fp, " id %u predictCost %u\n", payload.m_id, payload.m_predictCost);
break;
}
default: abort(); // LCOV_EXCL_LINE
}
}
}
fprintf(fp, "VLPROF stat ticks %" PRIu64 "\n", tickEnd - m_tickBegin);
std::fclose(fp);
}
+186 -44
View File
@@ -12,7 +12,7 @@
//=============================================================================
///
/// \file
/// \brief Verilated general profiling header
/// \brief Verilated run-time profiling header
///
/// This file is not part of the Verilated public-facing API.
/// It is only for internal use by Verilated library routines.
@@ -23,58 +23,206 @@
#define VERILATOR_VERILATED_PROFILER_H_
#include "verilatedos.h"
#include "verilated.h" // for VerilatedMutex and clang annotations
#include <deque>
#include "verilated.h"
#include <array>
#include <atomic>
#include <cassert>
#include <string>
#include <type_traits>
#include <vector>
class VlExecutionProfiler;
class VlThreadPool;
//=============================================================================
// Macros to simplify generated code
#define VL_EXEC_TRACE_ADD_RECORD(vlSymsp) \
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 QData VL_CPU_TICK() {
uint64_t val;
VL_GET_CPU_TICK(val);
return val;
}
//=============================================================================
// Private class used by VlExecutionProfiler
#define _VL_FOREACH_APPLY(macro, arg) macro(arg, #arg)
// clang-format off
#define FOREACH_VlExecutionRecord_TYPE(macro) \
_VL_FOREACH_APPLY(macro, EVAL_BEGIN) \
_VL_FOREACH_APPLY(macro, EVAL_END) \
_VL_FOREACH_APPLY(macro, EVAL_LOOP_BEGIN) \
_VL_FOREACH_APPLY(macro, EVAL_LOOP_END) \
_VL_FOREACH_APPLY(macro, MTASK_BEGIN) \
_VL_FOREACH_APPLY(macro, MTASK_END)
// clang-format on
class VlExecutionRecord final {
friend class VlExecutionProfiler;
// TYPES
enum class Type : uint8_t {
#define VL_FOREACH_MACRO(id, name) id,
FOREACH_VlExecutionRecord_TYPE(VL_FOREACH_MACRO)
#undef VL_FOREACH_MACRO
};
static constexpr const char* const s_ascii[] = {
#define VL_FOREACH_MACRO(id, name) name,
FOREACH_VlExecutionRecord_TYPE(VL_FOREACH_MACRO)
#undef VL_FOREACH_MACRO
};
union Payload {
struct {
uint32_t m_id; // MTask id
uint32_t m_predictStart; // Time scheduler predicted would start
uint32_t m_cpu; // Executing CPU id
} mtaskBegin;
struct {
uint32_t m_id; // MTask id
uint32_t m_predictCost; // How long scheduler predicted would take
} mtaskEnd;
};
// STATE
// Layout below allows efficient packing.
const uint64_t m_tick = VL_CPU_TICK(); // Tick at construction
Payload m_payload; // The record payload
Type m_type; // The record type
static_assert(alignof(uint64_t) >= alignof(Payload), "Padding not allowed");
static_assert(alignof(Payload) >= alignof(Type), "Padding not allowed");
static uint16_t getcpu(); // Return currently executing CPU id
// Profile record, private class used only by this header
class VerilatedProfilerRec final {
const std::string m_name; // Hashed name of mtask/etc
const size_t m_counterNumber = 0; // Which counter has data
public:
// CONSTRUCTOR
VlExecutionRecord() = default;
// METHODS
VerilatedProfilerRec(size_t counterNumber, const std::string& name)
: m_name{name}
, m_counterNumber{counterNumber} {}
VerilatedProfilerRec() = default;
size_t counterNumber() const { return m_counterNumber; }
std::string name() const { return m_name; }
void evalBegin() { m_type = Type::EVAL_BEGIN; }
void evalEnd() { m_type = Type::EVAL_END; }
void evalLoopBegin() { m_type = Type::EVAL_LOOP_BEGIN; }
void evalLoopEnd() { m_type = Type::EVAL_LOOP_END; }
void mtaskBegin(uint32_t id, uint32_t predictStart) {
m_payload.mtaskBegin.m_id = id;
m_payload.mtaskBegin.m_predictStart = predictStart;
m_payload.mtaskBegin.m_cpu = getcpu();
m_type = Type::MTASK_BEGIN;
}
void mtaskEnd(uint32_t id, uint32_t predictCost) {
m_payload.mtaskEnd.m_id = id;
m_payload.mtaskEnd.m_predictCost = predictCost;
m_type = Type::MTASK_END;
}
};
// Create some number of bucketed profilers
template <std::size_t T_Entries> class VerilatedProfiler final {
// Counters are stored packed, all together, versus in VerilatedProfilerRec to
// reduce cache effects
std::array<vluint64_t, T_Entries> m_counters{}; // Time spent on this record
std::deque<VerilatedProfilerRec> m_records; // Record information
static_assert(std::is_trivially_destructible<VlExecutionRecord>::value,
"VlExecutionRecord should be trivially destructible for fast buffer clearing");
//=============================================================================
// VlExecutionProfiler is for collecting profiling data about model execution
class VlExecutionProfiler final : public VerilatedVirtualBase {
// CONSTANTS
// In order to try to avoid dynamic memory allocations during the actual profiling phase,
// trace buffers are pre-allocated to be able to hold [a multiple] of this many records.
static constexpr size_t RESERVED_TRACE_CAPACITY = 4096;
// TYPES
// Execution traces are recorded into thread local vectors. We can append records of profiling
// events to this vector with very low overhead, and then dump them out later. This prevents
// the overhead of printf/malloc/IO from corrupting the profiling data. It's super cheap to
// append a VlProfileRec struct on the end of a pre-allocated vector; this is the only cost we
// pay in real-time during a profiling cycle. Internal note: Globals may multi-construct, see
// verilated.cpp top.
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
std::map<uint32_t, ExecutionTrace*> m_traceps VL_GUARDED_BY(m_mutex);
bool m_enabled = false; // Is profiling currently enabled
uint64_t m_tickBegin = 0; // Sample time (rdtsc() on x86) at beginning of collection
uint64_t m_lastStartReq = 0; // Last requested profiling start (in simulation time)
uint32_t m_windowCount = 0; // Track our position in the cache warmup and profile window
public:
// CONSTRUCTOR
explicit VlExecutionProfiler(VerilatedContext& context);
~VlExecutionProfiler() override = default;
// METHODS
// Is profiling enabled
bool enabled() const { return m_enabled; }
// Append a trace record to the trace buffer of the current thread
static VlExecutionRecord& addRecord() {
t_trace.emplace_back();
return t_trace.back();
}
// Configure profiler (called in beginning of 'eval')
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 {
// TYPES
struct Record final {
const std::string m_name; // Hashed name of mtask/etc
const size_t m_counterNumber = 0; // Which counter has data
};
// Counters are stored packed, all together to reduce cache effects
std::array<uint64_t, T_Entries> m_counters; // Time spent on this record
std::vector<Record> m_records; // Record information
public:
// METHODS
VerilatedProfiler() = default;
~VerilatedProfiler() = default;
VlPgoProfiler() = default;
~VlPgoProfiler() = default;
void write(const char* modelp, const std::string& filename) VL_MT_SAFE;
void addCounter(size_t counter, const std::string& name) {
VL_DEBUG_IF(assert(counter < T_Entries););
m_records.emplace_back(VerilatedProfilerRec{counter, name});
m_records.emplace_back(Record{name, counter});
}
void startCounter(size_t counter) {
vluint64_t val;
VL_RDTSC(val);
// -= so when we add end time in stopCounter, we already subtracted
// out, without needing to hold another temporary
m_counters[counter] -= val;
}
void stopCounter(size_t counter) {
vluint64_t val;
VL_RDTSC(val);
m_counters[counter] += val;
// -= so when we add end time in stopCounter, the net effect is adding the difference,
// without needing to hold onto a temporary
m_counters[counter] -= VL_CPU_TICK();
}
void stopCounter(size_t counter) { m_counters[counter] += VL_CPU_TICK(); }
};
template <std::size_t T_Entries>
void VerilatedProfiler<T_Entries>::write(const char* modelp,
const std::string& filename) VL_MT_SAFE {
void VlPgoProfiler<T_Entries>::write(const char* modelp, const std::string& filename) VL_MT_SAFE {
static VerilatedMutex s_mutex;
const VerilatedLockGuard lock{s_mutex};
@@ -82,20 +230,15 @@ void VerilatedProfiler<T_Entries>::write(const char* modelp,
// So when we have multiple models in an executable, possibly even
// running on different threads, each will have a different symtab so
// each will collect is own data correctly. However when each is
// destroid we need to get all the data, not keep overwriting and only
// destroyed we need to get all the data, not keep overwriting and only
// get the last model's data.
static bool s_firstCall = true;
VL_DEBUG_IF(VL_DBG_MSGF("+prof+vlt+file writing to '%s'\n", filename.c_str()););
FILE* fp = nullptr;
if (!s_firstCall) fp = std::fopen(filename.c_str(), "a");
if (VL_UNLIKELY(!fp))
fp = std::fopen(filename.c_str(), "w"); // firstCall, or doesn't exist yet
FILE* const fp = std::fopen(filename.c_str(), s_firstCall ? "w" : "a");
if (VL_UNLIKELY(!fp)) {
VL_FATAL_MT(filename.c_str(), 0, "", "+prof+vlt+file file not writable");
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr
return; // LCOV_EXCL_LINE
}
s_firstCall = false;
@@ -104,10 +247,9 @@ void VerilatedProfiler<T_Entries>::write(const char* modelp,
fprintf(fp, "// Verilated model profile-guided optimization data dump file\n");
fprintf(fp, "`verilator_config\n");
for (const auto& it : m_records) {
const std::string& name = it.name();
for (const Record& rec : m_records) {
fprintf(fp, "profile_data -model \"%s\" -mtask \"%s\" -cost 64'd%" PRIu64 "\n", modelp,
name.c_str(), m_counters[it.counterNumber()]);
rec.m_name.c_str(), m_counters[rec.m_counterNumber]);
}
std::fclose(fp);
+11 -9
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
@@ -63,8 +65,8 @@ static const char* const VLTSAVE_TRAILER_STR = "vltsaved";
bool VerilatedDeserialize::readDiffers(const void* __restrict datap,
size_t size) VL_MT_UNSAFE_ONE {
bufferCheck();
const vluint8_t* __restrict dp = static_cast<const vluint8_t* __restrict>(datap);
vluint8_t miss = 0;
const uint8_t* __restrict dp = static_cast<const uint8_t* __restrict>(datap);
uint8_t miss = 0;
while (size--) miss |= (*dp++ ^ *m_cp++);
return (miss != 0);
}
@@ -192,7 +194,7 @@ void VerilatedRestore::close() VL_MT_UNSAFE_ONE {
void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
m_assertOne.check();
if (VL_UNLIKELY(!isOpen())) return;
const vluint8_t* wp = m_bufp;
const uint8_t* wp = m_bufp;
while (true) {
const ssize_t remaining = (m_cp - wp);
if (remaining == 0) break;
@@ -219,8 +221,8 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
m_assertOne.check();
if (VL_UNLIKELY(!isOpen())) return;
// Move remaining characters down to start of buffer. (No memcpy, overlaps allowed)
vluint8_t* rp = m_bufp;
for (vluint8_t* sp = m_cp; sp < m_endp; *rp++ = *sp++) {} // Overlaps
uint8_t* rp = m_bufp;
for (uint8_t* sp = m_cp; sp < m_endp; *rp++ = *sp++) {} // Overlaps
m_endp = m_bufp + (m_endp - m_cp);
m_cp = m_bufp; // Reset buffer
// Read into buffer starting at m_endp
+35 -33
View File
@@ -23,6 +23,7 @@
#define VERILATOR_VERILATED_SAVE_C_H_
#include "verilatedos.h"
#include "verilated.h"
#include <string>
@@ -40,8 +41,8 @@ class VerilatedSerialize VL_NOT_FINAL {
protected:
// MEMBERS
// For speed, keep m_cp as the first member of this structure
vluint8_t* m_cp; // Current pointer into m_bufp buffer
vluint8_t* m_bufp; // Output buffer
uint8_t* m_cp; // Current pointer into m_bufp buffer
uint8_t* m_bufp; // Output buffer
bool m_isOpen = false; // True indicates open file/stream
std::string m_filename; // Filename, for error messages
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
@@ -58,7 +59,7 @@ protected:
public:
/// Construct
VerilatedSerialize() {
m_bufp = new vluint8_t[bufferSize()];
m_bufp = new uint8_t[bufferSize()];
m_cp = m_bufp;
}
/// Flish, close, and destruct
@@ -77,12 +78,12 @@ 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 vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap;
const uint8_t* __restrict dp = static_cast<const uint8_t* __restrict>(datap);
while (size) {
bufferCheck();
size_t blk = size;
if (blk > bufferInsertSize()) blk = bufferInsertSize();
const vluint8_t* __restrict maxp = dp + blk;
const uint8_t* __restrict maxp = dp + blk;
for (; dp < maxp; *m_cp++ = *dp++) {}
size -= blk;
}
@@ -111,9 +112,9 @@ class VerilatedDeserialize VL_NOT_FINAL {
protected:
// MEMBERS
// For speed, keep m_cp as the first member of this structure
vluint8_t* m_cp; // Current pointer into m_bufp buffer
vluint8_t* m_bufp; // Output buffer
vluint8_t* m_endp = nullptr; // Last valid byte in m_bufp buffer
uint8_t* m_cp; // Current pointer into m_bufp buffer
uint8_t* m_bufp; // Output buffer
uint8_t* m_endp = nullptr; // Last valid byte in m_bufp buffer
bool m_isOpen = false; // True indicates open file/stream
std::string m_filename; // Filename, for error messages
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
@@ -131,7 +132,7 @@ protected:
public:
/// Construct
VerilatedDeserialize() {
m_bufp = new vluint8_t[bufferSize()];
m_bufp = new uint8_t[bufferSize()];
m_cp = m_bufp;
}
/// Destruct
@@ -150,12 +151,12 @@ public:
virtual void flush() VL_MT_UNSAFE_ONE {}
/// Read data from stream
VerilatedDeserialize& read(void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
vluint8_t* __restrict dp = static_cast<vluint8_t* __restrict>(datap);
uint8_t* __restrict dp = static_cast<uint8_t* __restrict>(datap);
while (size) {
bufferCheck();
size_t blk = size;
if (blk > bufferInsertSize()) blk = bufferInsertSize();
const vluint8_t* __restrict maxp = dp + blk;
const uint8_t* __restrict maxp = dp + blk;
for (; dp < maxp; *dp++ = *m_cp++) {}
size -= blk;
}
@@ -165,7 +166,7 @@ public:
// Internal use:
// Read a datum and compare with expected value
VerilatedDeserialize& readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
VerilatedDeserialize& readAssert(vluint64_t data) VL_MT_UNSAFE_ONE {
VerilatedDeserialize& readAssert(uint64_t data) VL_MT_UNSAFE_ONE {
return readAssert(&data, sizeof(data));
}
@@ -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,35 +230,35 @@ 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;
};
//=============================================================================
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint64_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint64_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint64_t& rhs) {
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint64_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint32_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint32_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint32_t& rhs) {
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint32_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint16_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint16_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint16_t& rhs) {
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint16_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const vluint8_t& rhs) {
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint8_t& rhs) {
return os.write(&rhs, sizeof(rhs));
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint8_t& rhs) {
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint8_t& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const bool& rhs) {
@@ -279,15 +280,16 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) {
return os.read(&rhs, sizeof(rhs));
}
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const std::string& rhs) {
const vluint32_t len = rhs.length();
const uint32_t len = rhs.length();
os << len;
return os.write(rhs.data(), len);
}
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& rhs) {
vluint32_t len = 0;
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);
@@ -295,7 +297,7 @@ VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhs
template <class T_Key, class T_Value>
VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
os << rhs.atDefault();
const vluint32_t len = rhs.size();
const uint32_t len = rhs.size();
os << len;
for (const auto& i : rhs) {
const T_Key index = i.first; // Copy to get around const_iterator
@@ -307,10 +309,10 @@ VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Val
template <class T_Key, class T_Value>
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
os >> rhs.atDefault();
vluint32_t len = 0;
uint32_t len = 0;
os >> len;
rhs.clear();
for (vluint32_t i = 0; i < len; ++i) {
for (uint32_t i = 0; i < len; ++i) {
T_Key index;
T_Value value;
os >> index;
+2 -2
View File
@@ -39,10 +39,10 @@
// This class is thread safe (though most of SystemC is not).
class VlScBvExposer final : public sc_bv_base {
public:
static const vluint32_t* sp_datap(const sc_bv_base& base) VL_MT_SAFE {
static const uint32_t* sp_datap(const sc_bv_base& base) VL_MT_SAFE {
return static_cast<const VlScBvExposer*>(&base)->sp_datatp();
}
const vluint32_t* sp_datatp() const { return reinterpret_cast<vluint32_t*>(m_data); }
const uint32_t* sp_datatp() const { return reinterpret_cast<uint32_t*>(m_data); }
// Above reads this protected element in sc_bv_base:
// sc_digit* m_data; // data array
};
+3 -4
View File
@@ -69,9 +69,9 @@ public:
class VerilatedVarProps VL_NOT_FINAL {
// TYPES
static constexpr vluint32_t MAGIC = 0xddc4f829UL;
static constexpr uint32_t MAGIC = 0xddc4f829UL;
// MEMBERS
const vluint32_t m_magic; // Magic number
const uint32_t m_magic; // Magic number
const VerilatedVarType m_vltype; // Data type
const VerilatedVarFlags m_vlflags; // Direction
const int m_pdims; // Packed dimensions, 0 = none
@@ -134,7 +134,6 @@ public:
initUnpacked(ulims);
}
public:
~VerilatedVarProps() = default;
// METHODS
bool magicOk() const { return m_magic == MAGIC; }
@@ -142,7 +141,7 @@ public:
VerilatedVarFlags vldir() const {
return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR);
}
vluint32_t entSize() const;
uint32_t entSize() const;
bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); }
// DPI compatible C standard layout
bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }
+1
View File
@@ -30,6 +30,7 @@
#define VERILATOR_VERILATED_SYMS_H_
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_sym_props.h"
+41 -156
View File
@@ -22,10 +22,10 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_threads.h"
#include <cstdio>
#include <fstream>
#include <memory>
#include <string>
@@ -34,14 +34,12 @@
// Internal note: Globals may multi-construct, see verilated.cpp top.
std::atomic<vluint64_t> VlMTaskVertex::s_yields;
VL_THREAD_LOCAL VlThreadPool::ProfileTrace* VlThreadPool::t_profilep = nullptr;
std::atomic<uint64_t> VlMTaskVertex::s_yields;
//=============================================================================
// VlMTaskVertex
VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
VlMTaskVertex::VlMTaskVertex(uint32_t upstreamDepCount)
: m_upstreamDepsDone{0}
, m_upstreamDepCount{upstreamDepCount} {
assert(atomic_is_lock_free(&m_upstreamDepsDone));
@@ -50,175 +48,62 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
//=============================================================================
// VlWorkerThread
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling)
VlWorkerThread::VlWorkerThread(VerilatedContext* contextp)
: m_ready_size{0}
, m_poolp{poolp}
, m_profiling{profiling} // Must init this last -- after setting up fields that it might read:
, m_exiting{false}
, m_cthread{startWorker, this}
, 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();
}
void VlWorkerThread::workerLoop() {
if (VL_UNLIKELY(m_profiling)) m_poolp->setupProfilingClientThread();
ExecRec work;
work.m_fnp = nullptr;
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(m_profiling)) m_poolp->tearDownProfilingClientThread();
static void shutdownTask(void*, bool) {
// Deliberately empty, we use the address of this function as a magic number
}
void VlWorkerThread::startWorker(VlWorkerThread* workerp) {
Verilated::threadContextp(workerp->m_contextp);
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;
// Wait for the first task without spinning, in case the thread is never actually used.
dequeWork</* SpinWait: */ false>(&work);
while (true) {
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, VerilatedContext* contextp) {
Verilated::threadContextp(contextp);
workerp->workerLoop();
}
//=============================================================================
// VlThreadPool
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling)
: m_profiling{profiling} {
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
const unsigned cpus = std::thread::hardware_concurrency();
if (cpus < nThreads + 1) {
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 + 1);
}
}
// Create'em
for (int i = 0; i < nThreads; ++i) {
m_workers.push_back(new VlWorkerThread{this, contextp, profiling});
}
// Set up a profile buffer for the current thread too -- on the
// assumption that it's the same thread that calls eval and may be
// donated to run mtasks during the eval.
if (VL_UNLIKELY(m_profiling)) setupProfilingClientThread();
VlThreadPool::VlThreadPool(VerilatedContext* contextp, unsigned nThreads) {
for (unsigned i = 0; i < nThreads; ++i) m_workers.push_back(new VlWorkerThread{contextp});
}
VlThreadPool::~VlThreadPool() {
// Each ~WorkerThread will wait for its thread to exit.
for (auto& i : m_workers) delete i;
if (VL_UNLIKELY(m_profiling)) tearDownProfilingClientThread();
}
void VlThreadPool::tearDownProfilingClientThread() {
assert(t_profilep);
delete t_profilep;
t_profilep = nullptr;
}
void VlThreadPool::setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex) {
assert(!t_profilep);
t_profilep = new ProfileTrace;
// Reserve some space in the thread-local profiling buffer;
// try not to malloc while collecting profiling.
t_profilep->reserve(4096);
{
const VerilatedLockGuard lock{m_mutex};
m_allProfiles.insert(t_profilep);
}
}
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
for (const auto& profilep : m_allProfiles) {
// Every thread's profile trace gets a copy of rec.
profilep->emplace_back(rec);
}
}
void VlThreadPool::profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
VL_DEBUG_IF(VL_DBG_MSGF("+prof+threads writing to '%s'\n", filenamep););
FILE* const fp = std::fopen(filenamep, "w");
if (VL_UNLIKELY(!fp)) {
VL_FATAL_MT(filenamep, 0, "", "+prof+threads+file file not writable");
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr
return; // LCOV_EXCL_LINE
}
// TODO Perhaps merge with verilated_coverage output format, so can
// have a common merging and reporting tool, etc.
fprintf(fp, "VLPROFTHREAD 1.1 # Verilator thread profile dump version 1.1\n");
fprintf(fp, "VLPROF arg --threads %" PRIu64 "\n", vluint64_t(m_workers.size() + 1));
fprintf(fp, "VLPROF arg +verilator+prof+threads+start+%" PRIu64 "\n",
Verilated::threadContextp()->profThreadsStart());
fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n",
Verilated::threadContextp()->profThreadsWindow());
fprintf(fp, "VLPROF stat yields %" PRIu64 "\n", VlMTaskVertex::yields());
// Copy /proc/cpuinfo into this output so verilator_gantt can be run on
// a different machine
{
const std::unique_ptr<std::ifstream> ifp{new std::ifstream("/proc/cpuinfo")};
if (!ifp->fail()) {
std::string line;
while (std::getline(*ifp, line)) { fprintf(fp, "VLPROFPROC %s\n", line.c_str()); }
}
}
vluint32_t thread_id = 0;
for (const auto& pi : m_allProfiles) {
++thread_id;
bool printing = false; // False while in warmup phase
for (const auto& ei : *pi) {
switch (ei.m_type) {
case VlProfileRec::TYPE_BARRIER: //
printing = true;
break;
case VlProfileRec::TYPE_EVAL:
if (!printing) break;
fprintf(fp,
"VLPROF eval start %" PRIu64 " elapsed %" PRIu64 " cpu %u on thread %u\n",
ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime), ei.m_cpu,
thread_id);
break;
case VlProfileRec::TYPE_EVAL_LOOP:
if (!printing) break;
fprintf(fp,
"VLPROF eval_loop start %" PRIu64 " elapsed %" PRIu64
" cpu %u on thread %u\n",
ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime), ei.m_cpu,
thread_id);
break;
case VlProfileRec::TYPE_MTASK_RUN:
if (!printing) break;
fprintf(fp,
"VLPROF mtask %d"
" start %" PRIu64 " elapsed %" PRIu64
" predict_start %u predict_cost %u cpu %u on thread %u\n",
ei.m_mtaskId, ei.m_startTime - tickStart, (ei.m_endTime - ei.m_startTime),
ei.m_predictStart, ei.m_predictCost, ei.m_cpu, thread_id);
break;
default: assert(false); break; // LCOV_EXCL_LINE
}
}
}
fprintf(fp, "VLPROF stat ticks %" PRIu64 "\n", tickEnd - tickStart);
std::fclose(fp);
}
+40 -124
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
@@ -35,8 +36,10 @@
#error "verilated_threads.h/cpp expected VL_THREADED (from verilator --threads)"
#endif
#include <atomic>
#include <condition_variable>
#include <set>
#include <thread>
#include <vector>
// clang-format off
@@ -48,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.
@@ -58,7 +64,7 @@ using VlExecFnp = void (*)(VlSelfP, bool);
// Track dependencies for a single MTask.
class VlMTaskVertex final {
// MEMBERS
static std::atomic<vluint64_t> s_yields; // Statistics
static std::atomic<uint64_t> s_yields; // Statistics
// On even cycles, _upstreamDepsDone increases as upstream
// dependencies complete. When it reaches _upstreamDepCount,
@@ -76,8 +82,8 @@ class VlMTaskVertex final {
// during done-notification. Nobody's quantified that cost though.
// If we were really serious about shrinking this class, we could
// use 16-bit types here...)
std::atomic<vluint32_t> m_upstreamDepsDone;
const vluint32_t m_upstreamDepCount;
std::atomic<uint32_t> m_upstreamDepsDone;
const uint32_t m_upstreamDepCount;
public:
// CONSTRUCTORS
@@ -85,10 +91,10 @@ public:
// 'upstreamDepCount' is the number of upstream MTaskVertex's
// that must notify this MTaskVertex before it will become ready
// to run.
explicit VlMTaskVertex(vluint32_t upstreamDepCount);
explicit VlMTaskVertex(uint32_t upstreamDepCount);
~VlMTaskVertex() = default;
static vluint64_t yields() { return s_yields; }
static uint64_t yields() { return s_yields; }
static void yieldThread() {
++s_yields; // Statistics
std::this_thread::yield();
@@ -97,24 +103,24 @@ 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 vluint32_t upstreamDepsDone
const uint32_t upstreamDepsDone
= 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
assert(upstreamDepsDone <= m_upstreamDepCount);
return (upstreamDepsDone == m_upstreamDepCount);
} else {
const vluint32_t upstreamDepsDone_prev
const uint32_t upstreamDepsDone_prev
= m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
assert(upstreamDepsDone_prev > 0);
return (upstreamDepsDone_prev == 1);
}
}
inline bool areUpstreamDepsDone(bool evenCycle) const {
const vluint32_t target = evenCycle ? m_upstreamDepCount : 0;
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();
@@ -127,77 +133,14 @@ public:
}
};
// Profiling support
class VlProfileRec final {
protected:
friend class VlThreadPool;
enum VlProfileE { TYPE_MTASK_RUN, TYPE_EVAL, TYPE_EVAL_LOOP, TYPE_BARRIER };
// Layout below allows efficient packing.
// Leave endTime first, so no math needed to calculate address in endRecord
vluint64_t m_endTime = 0; // Tick at end of execution
vluint64_t m_startTime = 0; // Tick at start of execution
vluint32_t m_mtaskId = 0; // Mtask we're logging
vluint32_t m_predictStart = 0; // Time scheduler predicted would start
vluint32_t m_predictCost = 0; // How long scheduler predicted would take
VlProfileE m_type = TYPE_BARRIER; // Record type
unsigned m_cpu; // Execution CPU number (at start anyways)
public:
class Barrier {};
VlProfileRec() = default;
explicit VlProfileRec(Barrier) { m_cpu = getcpu(); }
void startEval(vluint64_t time) {
m_type = VlProfileRec::TYPE_EVAL;
m_startTime = time;
m_cpu = getcpu();
}
void startEvalLoop(vluint64_t time) {
m_type = VlProfileRec::TYPE_EVAL_LOOP;
m_startTime = time;
m_cpu = getcpu();
}
void startRecord(vluint64_t time, vluint32_t mtask, vluint32_t predictStart,
vluint32_t predictCost) {
m_type = VlProfileRec::TYPE_MTASK_RUN;
m_mtaskId = mtask;
m_predictStart = predictStart;
m_predictCost = predictCost;
m_startTime = time;
m_cpu = getcpu();
}
void endRecord(vluint64_t time) { m_endTime = time; }
static int getcpu() { // Return current executing CPU
#if defined(__linux)
return sched_getcpu();
#elif defined(__APPLE__) && !defined(__arm64__)
vluint32_t info[4];
__cpuid_count(1, 0, info[0], info[1], info[2], info[3]);
// info[1] is EBX, bits 24-31 are APIC ID
if ((info[3] & (1 << 9)) == 0) {
return -1; // no APIC on chip
} else {
return (unsigned)info[1] >> 24;
}
#elif defined(_WIN32)
return GetCurrentProcessorNumber();
#else
return 0;
#endif
}
};
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}
@@ -205,7 +148,7 @@ private:
};
// MEMBERS
VerilatedMutex m_mutex;
mutable VerilatedMutex m_mutex;
std::condition_variable_any m_cv;
// Only notify the condition_variable if the worker is waiting
bool m_waiting VL_GUARDED_BY(m_mutex) = false;
@@ -217,28 +160,24 @@ private:
// Store the size atomically, so we can spin wait
std::atomic<size_t> m_ready_size;
VlThreadPool* const m_poolp; // Our associated thread pool
const bool m_profiling; // Is profiling enabled?
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(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling);
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()) {
@@ -252,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;
{
@@ -264,55 +202,33 @@ 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);
static void startWorker(VlWorkerThread* workerp, VerilatedContext* contextp);
};
class VlThreadPool final {
// TYPES
using ProfileTrace = std::vector<VlProfileRec>;
class VlThreadPool final : public VerilatedVirtualBase {
// MEMBERS
std::vector<VlWorkerThread*> m_workers; // our workers
const bool m_profiling; // is profiling enabled?
// Support profiling -- we can append records of profiling events
// to this vector with very low overhead, and then dump them out
// later. This prevents the overhead of printf/malloc/IO from
// corrupting the profiling data. It's super cheap to append
// a VlProfileRec struct on the end of a pre-allocated vector;
// this is the only cost we pay in real-time during a profiling cycle.
// Internal note: Globals may multi-construct, see verilated.cpp top.
static VL_THREAD_LOCAL ProfileTrace* t_profilep;
std::set<ProfileTrace*> m_allProfiles VL_GUARDED_BY(m_mutex);
VerilatedMutex m_mutex;
public:
// CONSTRUCTORS
// 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, bool profiling);
~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];
}
inline VlProfileRec* profileAppend() {
t_profilep->emplace_back();
return &(t_profilep->back());
}
void profileAppendAll(const VlProfileRec& rec) VL_MT_SAFE_EXCLUDES(m_mutex);
void profileDump(const char* filenamep, vluint64_t tickStart, vluint64_t tickEnd)
VL_MT_SAFE_EXCLUDES(m_mutex);
// In profiling mode, each executing thread must call
// this once to setup profiling state:
void setupProfilingClientThread() VL_MT_SAFE_EXCLUDES(m_mutex);
void tearDownProfilingClientThread();
private:
VL_UNCOPYABLE(VlThreadPool);
+293 -147
View File
@@ -28,26 +28,35 @@
#include "verilated_trace_defs.h"
#include <bitset>
#include <condition_variable>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#ifdef VL_TRACE_THREADED
# include <condition_variable>
#ifdef VL_THREADED
# include <deque>
# include <thread>
#endif
// clang-format on
#ifdef VL_TRACE_THREADED
class VlThreadPool;
template <class T_Buffer>
class VerilatedTraceBuffer;
template <class T_Buffer>
class VerilatedTraceOffloadBuffer;
#ifdef VL_THREADED
//=============================================================================
// Threaded tracing
// 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:
VerilatedMutex m_mutex; // Protects m_queue
mutable VerilatedMutex m_mutex; // Protects m_queue
std::condition_variable_any m_cv;
std::deque<T> m_queue VL_GUARDED_BY(m_mutex);
@@ -88,11 +97,11 @@ public:
// Commands used by thread tracing. Anonymous enum in class, as we want
// it scoped, but we also want the automatic conversion to integer types.
class VerilatedTraceCommand final {
class VerilatedTraceOffloadCommand final {
public:
// These must all fit in 4 bit at the moment, as the tracing routines
// pack parameters in the top bits.
enum : vluint8_t {
enum : uint8_t {
CHG_BIT_0 = 0x0,
CHG_BIT_1 = 0x1,
CHG_CDATA = 0x2,
@@ -102,36 +111,65 @@ public:
CHG_WDATA = 0x6,
CHG_DOUBLE = 0x8,
// TODO: full..
TIME_CHANGE = 0xd,
TIME_CHANGE = 0xc,
TRACE_BUFFER = 0xd,
END = 0xe, // End of buffer
SHUTDOWN = 0xf // Shutdown worker thread, also marks end of buffer
};
};
#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
// VerilatedTrace uses F-bounded polymorphism to access duck-typed
// implementations in the format specific derived class, which must be passed
// as the type parameter T_Derived
template <class T_Derived> class VerilatedTrace VL_NOT_FINAL {
// T_Trace is the format specific subclass of VerilatedTrace.
// 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 = VerilatedTraceBuffer<T_Buffer>;
using OffloadBuffer = VerilatedTraceOffloadBuffer<T_Buffer>;
//=========================================================================
// Generic tracing internals
using initCb_t = void (*)(void*, T_Derived*, vluint32_t); // Type of init callbacks
using dumpCb_t = void (*)(void*, T_Derived*); // Type of all but init callbacks
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
// assignment operator on inserting into collections, so they don't work
// with const fields...
union {
initCb_t m_initCb; // The callback function
dumpCb_t m_dumpCb; // The callback function
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)
CallbackRecord(initCb_t cb, void* userp)
@@ -140,65 +178,110 @@ 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} {}
};
vluint32_t* m_sigs_oldvalp; // Old value store
EData* m_sigs_enabledp; // Bit vector of enabled codes (nullptr = all on)
vluint64_t m_timeLastDump; // Last time we did a dump
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
Buffer* const m_bufp; // The buffer pointer to pass to the callback
std::atomic<bool> m_ready{false}; // The ready flag
mutable VerilatedMutex m_mutex; // Mutex for suspension until ready
std::condition_variable_any m_cv; // Condition variable for suspension
bool m_waiting VL_GUARDED_BY(m_mutex) = false; // Whether a thread is suspended in wait()
void wait();
ParallelWorkerData(dumpCb_t cb, void* userp, Buffer* bufp)
: m_cb{cb}
, m_userp{userp}
, m_bufp{bufp} {}
};
// Passed a ParallelWorkerData*, second argument is ignored
static void parallelWorkerTask(void*, bool);
#endif
protected:
uint32_t* m_sigs_oldvalp = nullptr; // Previous value store
EData* m_sigs_enabledp = nullptr; // Bit vector of enabled codes (nullptr = all on)
private:
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
std::vector<CallbackRecord> m_initCbs; // Routines to initialize traciong
std::vector<CallbackRecord> m_initCbs; // Routines to initialize tracing
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
bool m_fullDump; // Whether a full dump is required on the next call to 'dump'
vluint32_t m_nextCode; // Next code number to assign
vluint32_t m_numSignals; // Number of distinct signals
vluint32_t m_maxBits; // Number of bits in the widest signal
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
uint32_t m_maxBits = 0; // Number of bits in the widest signal
std::vector<std::string> m_namePrefixStack{""}; // Path prefixes to add to signal names
std::vector<std::pair<int, std::string>> m_dumpvars; // dumpvar() entries
char m_scopeEscape;
double m_timeRes; // Time resolution (ns/ms etc)
double m_timeUnit; // Time units (ns/ms etc)
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 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_Derived*'. Use 'self()->'
// 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_Derived* self() { return static_cast<T_Derived*>(this); }
T_Trace* self() { return static_cast<T_Trace*>(this); }
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_THREADED
// Number of total trace buffers that have been allocated
vluint32_t m_numTraceBuffers;
// Size of trace buffers
size_t m_traceBufferSize;
#ifdef VL_THREADED
// Number of total offload buffers that have been allocated
uint32_t m_numOffloadBuffers = 0;
// Size of offload buffers
size_t m_offloadBufferSize = 0;
// Buffers handed to worker for processing
VerilatedThreadQueue<vluint32_t*> m_buffersToWorker;
VerilatedThreadQueue<uint32_t*> m_offloadBuffersToWorker;
// Buffers returned from worker after processing
VerilatedThreadQueue<vluint32_t*> m_buffersFromWorker;
VerilatedThreadQueue<uint32_t*> m_offloadBuffersFromWorker;
protected:
// Write pointer into current buffer
vluint32_t* m_traceBufferWritep;
// End of trace buffer
vluint32_t* m_traceBufferEndp;
// The worker thread itself
uint32_t* m_offloadBufferWritep = nullptr;
// End of offload buffer
uint32_t* m_offloadBufferEndp = nullptr;
private:
// The offload worker thread itself
std::unique_ptr<std::thread> m_workerThread;
// Get a new trace buffer that can be populated. May block if none available
vluint32_t* getTraceBuffer();
// Get a new offload buffer that can be populated. May block if none available
uint32_t* getOffloadBuffer();
// The function executed by the worker thread
void workerThreadMain();
// The function executed by the offload worker thread
void offloadWorkerThreadMain();
// Wait until given buffer is placed in m_buffersFromWorker
void waitForBuffer(const vluint32_t* bufferp);
// Wait until given offload buffer is placed in m_offloadBuffersFromWorker
void waitForOffloadBuffer(const uint32_t* bufferp);
// Shut down and join worker, if it's running, otherwise do nothing
void shutdownWorker();
void shutdownOffloadWorker();
#endif
// CONSTRUCTORS
@@ -208,13 +291,12 @@ protected:
//=========================================================================
// Internals available to format specific implementations
VerilatedMutex m_mutex; // Ensure dump() etc only called from single thread
mutable VerilatedMutex m_mutex; // Ensure dump() etc only called from single thread
vluint32_t nextCode() const { return m_nextCode; }
vluint32_t numSignals() const { return m_numSignals; }
vluint32_t maxBits() const { return m_maxBits; }
uint32_t nextCode() const { return m_nextCode; }
uint32_t numSignals() const { return m_numSignals; }
uint32_t maxBits() const { return m_maxBits; }
void fullDump(bool value) { m_fullDump = value; }
vluint64_t timeLastDump() { return m_timeLastDump; }
double timeRes() const { return m_timeRes; }
double timeUnit() const { return m_timeUnit; }
@@ -223,7 +305,7 @@ protected:
void traceInit() VL_MT_UNSAFE;
// Declare new signal and return true if enabled
bool declCode(vluint32_t code, const char* namep, vluint32_t bits, bool tri);
bool declCode(uint32_t code, const char* namep, uint32_t bits, bool tri);
// Is this an escape?
bool isScopeEscape(char c) { return std::isspace(c) || c == m_scopeEscape; }
@@ -235,17 +317,32 @@ 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
// Called when the trace moves forward to a new time point
virtual void emitTimeChange(vluint64_t timeui) = 0;
virtual void emitTimeChange(uint64_t timeui) = 0;
// These hooks are called before a full or change based dump is produced.
// The return value indicates whether to proceed with the dump.
virtual bool preFullDump() = 0;
virtual bool preChangeDump() = 0;
// Trace buffer management
virtual Buffer* getTraceBuffer() = 0;
virtual void commitTraceBuffer(Buffer*) = 0;
// Configure sub-class
virtual void configure(const VerilatedTraceConfig&) = 0;
public:
//=========================================================================
// External interface to client code
@@ -264,21 +361,53 @@ public:
void dumpvars(int level, const std::string& hier) VL_MT_SAFE;
// Call
void dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex);
void dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex);
//=========================================================================
// Internal interface to Verilator generated code
//=========================================================================
// 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) VL_MT_SAFE;
void addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void addCleanupCb(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; }
void pushNamePrefix(const std::string&);
void popNamePrefix(unsigned count = 1);
};
//=============================================================================
// 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_Buffer>
class VerilatedTraceBuffer VL_NOT_FINAL : public T_Buffer {
protected:
// Type of the owner trace file
using Trace = typename std::remove_cv<
typename std::remove_reference<decltype(T_Buffer::m_owner)>::type>::type;
static_assert(std::has_virtual_destructor<T_Buffer>::value, "");
static_assert(std::is_base_of<VerilatedTrace<Trace, T_Buffer>, Trace>::value, "");
friend Trace; // Give the trace file access to the private bits
friend std::default_delete<VerilatedTraceBuffer<T_Buffer>>;
uint32_t* const m_sigs_oldvalp; // Previous value store
EData* const m_sigs_enabledp; // Bit vector of enabled codes (nullptr = all on)
explicit VerilatedTraceBuffer(Trace& owner);
~VerilatedTraceBuffer() override = default;
public:
//=========================================================================
// Hot path internal interface to Verilator generated code
@@ -288,108 +417,51 @@ public:
// these here, but we cannot afford dynamic dispatch for calling these as
// this is very hot code during tracing.
// duck-typed void emitBit(vluint32_t code, CData newval) = 0;
// duck-typed void emitCData(vluint32_t code, CData newval, int bits) = 0;
// duck-typed void emitSData(vluint32_t code, SData newval, int bits) = 0;
// duck-typed void emitIData(vluint32_t code, IData newval, int bits) = 0;
// duck-typed void emitQData(vluint32_t code, QData newval, int bits) = 0;
// duck-typed void emitWData(vluint32_t code, const WData* newvalp, int bits) = 0;
// duck-typed void emitDouble(vluint32_t code, double newval) = 0;
// duck-typed void emitBit(uint32_t code, CData newval) = 0;
// duck-typed void emitCData(uint32_t code, CData newval, int bits) = 0;
// duck-typed void emitSData(uint32_t code, SData newval, int bits) = 0;
// duck-typed void emitIData(uint32_t code, IData newval, int bits) = 0;
// duck-typed void emitQData(uint32_t code, QData newval, int bits) = 0;
// duck-typed void emitWData(uint32_t code, const WData* newvalp, int bits) = 0;
// duck-typed void emitDouble(uint32_t code, double newval) = 0;
vluint32_t* oldp(vluint32_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(vluint32_t* oldp, CData newval);
void fullCData(vluint32_t* oldp, CData newval, int bits);
void fullSData(vluint32_t* oldp, SData newval, int bits);
void fullIData(vluint32_t* oldp, IData newval, int bits);
void fullQData(vluint32_t* oldp, QData newval, int bits);
void fullWData(vluint32_t* oldp, const WData* newvalp, int bits);
void fullDouble(vluint32_t* oldp, double newval);
void fullBit(uint32_t* oldp, CData newval);
void fullCData(uint32_t* oldp, CData newval, int bits);
void fullSData(uint32_t* oldp, SData newval, int bits);
void fullIData(uint32_t* oldp, IData newval, int bits);
void fullQData(uint32_t* oldp, QData newval, int bits);
void fullWData(uint32_t* oldp, const WData* newvalp, int bits);
void fullDouble(uint32_t* oldp, double newval);
#ifdef VL_TRACE_THREADED
// Threaded tracing. Just dump everything in the trace buffer
inline void chgBit(vluint32_t code, CData newval) {
m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_BIT_0 | newval;
m_traceBufferWritep[1] = code;
m_traceBufferWritep += 2;
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
inline void chgCData(vluint32_t code, CData newval, int bits) {
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_CDATA;
m_traceBufferWritep[1] = code;
m_traceBufferWritep[2] = newval;
m_traceBufferWritep += 3;
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
inline void chgSData(vluint32_t code, SData newval, int bits) {
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_SDATA;
m_traceBufferWritep[1] = code;
m_traceBufferWritep[2] = newval;
m_traceBufferWritep += 3;
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
inline void chgIData(vluint32_t code, IData newval, int bits) {
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_IDATA;
m_traceBufferWritep[1] = code;
m_traceBufferWritep[2] = newval;
m_traceBufferWritep += 3;
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
inline void chgQData(vluint32_t code, QData newval, int bits) {
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_QDATA;
m_traceBufferWritep[1] = code;
*reinterpret_cast<QData*>(m_traceBufferWritep + 2) = newval;
m_traceBufferWritep += 4;
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
inline void chgWData(vluint32_t code, const WData* newvalp, int bits) {
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_WDATA;
m_traceBufferWritep[1] = code;
m_traceBufferWritep += 2;
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_traceBufferWritep++ = newvalp[i]; }
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
inline void chgDouble(vluint32_t code, double newval) {
m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_DOUBLE;
m_traceBufferWritep[1] = code;
// cppcheck-suppress invalidPointerCast
*reinterpret_cast<double*>(m_traceBufferWritep + 2) = newval;
m_traceBufferWritep += 4;
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
}
#define CHG(name) chg##name##Impl
#else
#define CHG(name) chg##name
#endif
// In non-threaded mode, these are called directly by the trace callbacks,
// and are called chg*. In threaded mode, they are called by the worker
// 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
inline void CHG(Bit)(vluint32_t* oldp, CData newval) {
const vluint32_t diff = *oldp ^ newval;
VL_ATTR_ALWINLINE void chgBit(uint32_t* oldp, CData newval) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
}
inline void CHG(CData)(vluint32_t* oldp, CData newval, int bits) {
const vluint32_t diff = *oldp ^ newval;
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);
}
inline void CHG(SData)(vluint32_t* oldp, SData newval, int bits) {
const vluint32_t diff = *oldp ^ newval;
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);
}
inline void CHG(IData)(vluint32_t* oldp, IData newval, int bits) {
const vluint32_t diff = *oldp ^ newval;
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);
}
inline void CHG(QData)(vluint32_t* oldp, QData newval, int bits) {
const vluint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
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);
}
inline void CHG(WData)(vluint32_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);
@@ -397,11 +469,85 @@ public:
}
}
}
inline void CHG(Double)(vluint32_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);
}
#undef CHG
};
#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
File diff suppressed because it is too large Load Diff
+80 -49
View File
@@ -73,7 +73,7 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
//===================================================================
// Shuffle RNG
extern vluint64_t vl_rand64() VL_MT_SAFE;
extern uint64_t vl_rand64() VL_MT_SAFE;
class VlURNG final {
public:
@@ -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;
@@ -241,7 +245,7 @@ public:
int size() const { return m_deque.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_deque.clear(); }
void erase(vlsint32_t index) {
void erase(int32_t index) {
if (VL_LIKELY(index >= 0 && index < m_deque.size()))
m_deque.erase(m_deque.begin() + index);
}
@@ -288,8 +292,8 @@ public:
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(vlsint32_t index) {
static T_Value s_throwAway;
T_Value& at(int32_t index) {
static VL_THREAD_LOCAL T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
s_throwAway = atDefault();
@@ -299,8 +303,8 @@ public:
}
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(vlsint32_t index) const {
static T_Value s_throwAway;
const T_Value& at(int32_t index) const {
static VL_THREAD_LOCAL T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
return atDefault();
@@ -309,18 +313,18 @@ public:
}
}
// function void q.insert(index, value);
void insert(vlsint32_t index, const T_Value& value) {
void insert(int32_t index, const T_Value& value) {
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return;
m_deque.insert(m_deque.begin() + index, value);
}
// Return slice q[lsb:msb]
VlQueue slice(vlsint32_t lsb, vlsint32_t msb) const {
VlQueue slice(int32_t lsb, int32_t msb) const {
VlQueue out;
if (VL_UNLIKELY(lsb < 0)) lsb = 0;
if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
for (vlsint32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
for (int32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
return out;
}
@@ -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,18 +418,20 @@ 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 it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue::cons(*it);
for (auto& item : vlstd::reverse_view(m_deque)) {
if (with_func(index, item)) return VlQueue::cons(item);
--index;
}
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 it = m_deque.rbegin(); it != m_deque.rend(); ++it) {
if (with_func(index, *it)) return VlQueue<IData>::cons(index);
for (auto& item : vlstd::reverse_view(m_deque)) {
if (with_func(index, item)) return VlQueue<IData>::cons(index);
--index;
}
return VlQueue<IData>{};
@@ -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
+177 -357
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
@@ -62,12 +62,24 @@ constexpr unsigned VL_TRACE_MAX_VCD_CODE_SIZE = 5; // Maximum length of a VCD s
// cache-lines.
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) {
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
size_t mask = N - 1;
return (value + mask) & ~mask;
}
//=============================================================================
// Specialization of the generics for this trace format
#define VL_DERIVED_T VerilatedVcd
#include "verilated_trace_imp.cpp"
#undef VL_DERIVED_T
#define VL_SUB_T VerilatedVcd
#define VL_BUF_T VerilatedVcdBuffer
#include "verilated_trace_imp.h"
#undef VL_SUB_T
#undef VL_BUF_T
//=============================================================================
//=============================================================================
@@ -108,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) {
@@ -168,11 +180,11 @@ 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();
}
void VerilatedVcd::emitTimeChange(vluint64_t timeui) {
void VerilatedVcd::emitTimeChange(uint64_t timeui) {
printStr("#");
printQuad(timeui);
printStr("\n");
@@ -183,7 +195,7 @@ void VerilatedVcd::makeNameMap() {
deleteNameMap();
m_namemapp = new NameMap;
VerilatedTrace<VerilatedVcd>::traceInit();
Super::traceInit();
// Though not speced, it's illegal to generate a vcd with signals
// not under any module - it crashes at least two viewers.
@@ -218,13 +230,19 @@ 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_THREADED
if (parallel()) {
assert(m_numBuffers == m_freeBuffers.size());
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
}
#endif
}
void VerilatedVcd::closePrev() {
// This function is on the flush() call path
if (!isOpen()) return;
VerilatedTrace<VerilatedVcd>::flushBase();
Super::flushBase();
bufferFlush();
m_isOpen = false;
m_filep->close();
@@ -245,20 +263,15 @@ 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 VerilatedTrace<VerilatedVcd>::flush(), so we just
// closePrev() called Super::flush(), so we just
// need to shut down the tracing thread here.
VerilatedTrace<VerilatedVcd>::closeBase();
Super::closeBase();
}
void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
VerilatedTrace<VerilatedVcd>::flushBase();
Super::flushBase();
bufferFlush();
}
@@ -270,19 +283,19 @@ void VerilatedVcd::printStr(const char* str) {
}
}
void VerilatedVcd::printQuad(vluint64_t n) {
void VerilatedVcd::printQuad(uint64_t n) {
constexpr size_t LEN_STR_QUAD = 40;
char buf[LEN_STR_QUAD];
VL_SNPRINTF(buf, LEN_STR_QUAD, "%" PRIu64, n);
printStr(buf);
}
void VerilatedVcd::bufferResize(vluint64_t minsize) {
void VerilatedVcd::bufferResize(size_t minsize) {
// minsize is size of largest write. We buffer at least 8 times as much data,
// writing when we are 3/4 full (with thus 2*minsize remaining free)
if (VL_UNLIKELY(minsize > m_wrChunkSize)) {
const char* oldbufp = m_wrBufp;
m_wrChunkSize = minsize * 2;
m_wrChunkSize = roundUpToMultipleOf<1024>(minsize * 2);
m_wrBufp = new char[m_wrChunkSize * 8];
std::memcpy(m_wrBufp, oldbufp, m_writep - oldbufp);
m_writep = m_wrBufp + (m_writep - oldbufp);
@@ -292,7 +305,7 @@ void VerilatedVcd::bufferResize(vluint64_t minsize) {
}
void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
// This function can be called from the trace thread
// This function can be called from the trace offload thread
// This function is on the flush() call path
// We add output data to m_writep.
// When it gets nearly full we dump it using this routine which calls write()
@@ -328,7 +341,7 @@ void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
//=============================================================================
// VCD string code
char* VerilatedVcd::writeCode(char* writep, vluint32_t code) {
char* VerilatedVcd::writeCode(char* writep, uint32_t code) {
*writep++ = static_cast<char>('!' + code % 94);
code /= 94;
while (code) {
@@ -459,18 +472,20 @@ void VerilatedVcd::dumpHeader() {
deleteNameMap();
}
void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep, bool array,
void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, bool array,
int arraynum, bool tri, bool bussed, int msb, int lsb) {
const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
const bool enabled = VerilatedTrace<VerilatedVcd>::declCode(code, name, bits, tri);
const bool enabled = Super::declCode(code, name, bits, tri);
if (m_suffixes.size() <= nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE) {
m_suffixes.resize(nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE * 2, 0);
}
// Make sure write buffer is large enough (one character per bit), plus header
bufferResize(bits + 1024);
// Keep upper bound on bytes a single signal cna emit into the buffer
m_maxSignalBytes = std::max<size_t>(m_maxSignalBytes, bits + 32);
// Make sure write buffer is large enough, plus header
bufferResize(m_maxSignalBytes + 1024);
if (!enabled) return;
@@ -497,38 +512,29 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
// 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) {
@@ -544,56 +550,99 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep,
m_namemapp->emplace(hiername, decl);
}
void VerilatedVcd::declBit(vluint32_t code, const char* name, bool array, int arraynum) {
void VerilatedVcd::declBit(uint32_t code, const char* name, bool array, int arraynum) {
declare(code, name, "wire", array, arraynum, false, false, 0, 0);
}
void VerilatedVcd::declBus(vluint32_t code, const char* name, bool array, int arraynum, int msb,
void VerilatedVcd::declBus(uint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, "wire", array, arraynum, false, true, msb, lsb);
}
void VerilatedVcd::declQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb,
void VerilatedVcd::declQuad(uint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, "wire", array, arraynum, false, true, msb, lsb);
}
void VerilatedVcd::declArray(vluint32_t code, const char* name, bool array, int arraynum, int msb,
void VerilatedVcd::declArray(uint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, "wire", array, arraynum, false, true, msb, lsb);
}
void VerilatedVcd::declDouble(vluint32_t code, const char* name, bool array, int arraynum) {
void VerilatedVcd::declDouble(uint32_t code, const char* name, bool array, int arraynum) {
declare(code, name, "real", array, arraynum, false, false, 63, 0);
}
#ifdef VL_TRACE_VCD_OLD_API
void VerilatedVcd::declTriBit(vluint32_t code, const char* name, bool array, int arraynum) {
declare(code, name, "wire", array, arraynum, true, false, 0, 0);
}
void VerilatedVcd::declTriBus(vluint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb) {
declare(code, name, "wire", array, arraynum, true, true, msb, lsb);
}
void VerilatedVcd::declTriQuad(vluint32_t code, const char* name, bool array, int arraynum,
int msb, int lsb) {
declare(code, name, "wire", array, arraynum, true, true, msb, lsb);
}
void VerilatedVcd::declTriArray(vluint32_t code, const char* name, bool array, int arraynum,
int msb, int lsb) {
declare(code, name, "wire", array, arraynum, true, true, msb, lsb);
}
#endif // VL_TRACE_VCD_OLD_API
//=============================================================================
// Trace rendering prinitives
// Get/commit trace buffer
static inline void
VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
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();
}
#endif
// Return the buffer
return bufp;
}
static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
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
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#endif
} else {
// Needs adjusting for emitTimeChange
m_writep = bufp->m_writep;
}
delete bufp;
}
//=============================================================================
// VerilatedVcdBuffer implementation
//=============================================================================
// Trace rendering primitives
static void VerilatedVcdCCopyAndAppendNewLine(char* writep,
const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
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.
#ifdef VL_X86_64
// Copy the whole 8 bytes in one go, this works on little-endian machines
// supporting unaligned stores.
*reinterpret_cast<vluint64_t*>(writep) = *reinterpret_cast<const vluint64_t*>(suffixp);
*reinterpret_cast<uint64_t*>(writep) = *reinterpret_cast<const uint64_t*>(suffixp);
#else
// Portable variant
writep[0] = suffixp[0];
@@ -606,26 +655,59 @@ static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* s
#endif
}
void VerilatedVcd::finishLine(vluint32_t code, char* writep) {
const char* const suffixp = m_suffixes.data() + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) {
const char* const suffixp = m_suffixes + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
VL_DEBUG_IFDEF(assert(suffixp[0]););
VerilatedVcdCCopyAndAppendNewLine(writep, suffixp);
// Now write back the write pointer incremented by the actual size of the
// suffix, which was stored in the last byte of the suffix buffer entry.
m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];
bufferCheck();
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
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;
}
}
}
//=============================================================================
// emit* trace routines
// Note: emit* are only ever called from one place (full* in
// verilated_trace_imp.cpp, which is included in this file at the top),
// verilated_trace_imp.h, which is included in this file at the top),
// so always inline them.
VL_ATTR_ALWINLINE
void VerilatedVcd::emitBit(vluint32_t code, CData newval) {
void VerilatedVcdBuffer::emitBit(uint32_t code, CData newval) {
// Don't prefetch suffix as it's a bit too late;
char* wp = m_writep;
*wp++ = '0' | static_cast<char>(newval);
@@ -633,7 +715,7 @@ void VerilatedVcd::emitBit(vluint32_t code, CData newval) {
}
VL_ATTR_ALWINLINE
void VerilatedVcd::emitCData(vluint32_t code, CData newval, int bits) {
void VerilatedVcdBuffer::emitCData(uint32_t code, CData newval, int bits) {
char* wp = m_writep;
*wp++ = 'b';
cvtCDataToStr(wp, newval << (VL_BYTESIZE - bits));
@@ -641,7 +723,7 @@ void VerilatedVcd::emitCData(vluint32_t code, CData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedVcd::emitSData(vluint32_t code, SData newval, int bits) {
void VerilatedVcdBuffer::emitSData(uint32_t code, SData newval, int bits) {
char* wp = m_writep;
*wp++ = 'b';
cvtSDataToStr(wp, newval << (VL_SHORTSIZE - bits));
@@ -649,7 +731,7 @@ void VerilatedVcd::emitSData(vluint32_t code, SData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedVcd::emitIData(vluint32_t code, IData newval, int bits) {
void VerilatedVcdBuffer::emitIData(uint32_t code, IData newval, int bits) {
char* wp = m_writep;
*wp++ = 'b';
cvtIDataToStr(wp, newval << (VL_IDATASIZE - bits));
@@ -657,7 +739,7 @@ void VerilatedVcd::emitIData(vluint32_t code, IData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedVcd::emitQData(vluint32_t code, QData newval, int bits) {
void VerilatedVcdBuffer::emitQData(uint32_t code, QData newval, int bits) {
char* wp = m_writep;
*wp++ = 'b';
cvtQDataToStr(wp, newval << (VL_QUADSIZE - bits));
@@ -665,7 +747,7 @@ void VerilatedVcd::emitQData(vluint32_t code, QData newval, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedVcd::emitWData(vluint32_t code, const WData* newvalp, int bits) {
void VerilatedVcdBuffer::emitWData(uint32_t code, const WData* newvalp, int bits) {
int words = VL_WORDS_I(bits);
char* wp = m_writep;
*wp++ = 'b';
@@ -682,272 +764,10 @@ void VerilatedVcd::emitWData(vluint32_t code, const WData* newvalp, int bits) {
}
VL_ATTR_ALWINLINE
void VerilatedVcd::emitDouble(vluint32_t code, double newval) {
void VerilatedVcdBuffer::emitDouble(uint32_t code, double newval) {
char* wp = m_writep;
// Buffer can't overflow before VL_SNPRINTF; we sized during declaration
VL_SNPRINTF(wp, m_wrChunkSize, "r%.16g", newval);
VL_SNPRINTF(wp, m_maxSignalBytes, "r%.16g", newval);
wp += std::strlen(wp);
finishLine(code, wp);
}
#ifdef VL_TRACE_VCD_OLD_API
void VerilatedVcd::fullBit(vluint32_t code, const vluint32_t newval) {
// Note the &1, so we don't require clean input -- makes more common no change case faster
*oldp(code) = newval;
*m_writep++ = ('0' + static_cast<char>(newval & 1));
m_writep = writeCode(m_writep, code);
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullBus(vluint32_t code, const vluint32_t newval, int bits) {
*oldp(code) = newval;
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
*m_writep++ = ((newval & (1L << bit)) ? '1' : '0');
}
*m_writep++ = ' ';
m_writep = writeCode(m_writep, code);
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullQuad(vluint32_t code, const vluint64_t newval, int bits) {
(*(reinterpret_cast<vluint64_t*>(oldp(code)))) = newval;
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
*m_writep++ = ((newval & (1ULL << bit)) ? '1' : '0');
}
*m_writep++ = ' ';
m_writep = writeCode(m_writep, code);
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullArray(vluint32_t code, const vluint32_t* newval, int bits) {
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) { oldp(code)[word] = newval[word]; }
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
*m_writep++ = ((newval[(bit / 32)] & (1L << (bit & 0x1f))) ? '1' : '0');
}
*m_writep++ = ' ';
m_writep = writeCode(m_writep, code);
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullArray(vluint32_t code, const vluint64_t* newval, int bits) {
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) { oldp(code)[word] = newval[word]; }
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
*m_writep++ = ((newval[(bit / 64)] & (1ULL << (bit & 0x3f))) ? '1' : '0');
}
*m_writep++ = ' ';
m_writep = writeCode(m_writep, code);
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
oldp(code)[0] = newval;
oldp(code)[1] = newtri;
*m_writep++ = "01zz"[newval | (newtri << 1)];
m_writep = writeCode(m_writep, code);
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri,
int bits) {
oldp(code)[0] = newval;
oldp(code)[1] = newtri;
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
*m_writep++ = "01zz"[((newval >> bit) & 1) | (((newtri >> bit) & 1) << 1)];
}
*m_writep++ = ' ';
m_writep = writeCode(m_writep, code);
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint64_t newtri,
int bits) {
(*(reinterpret_cast<vluint64_t*>(oldp(code)))) = newval;
(*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) = newtri;
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
*m_writep++ = "01zz"[((newval >> bit) & 1ULL) | (((newtri >> bit) & 1ULL) << 1ULL)];
}
*m_writep++ = ' ';
m_writep = writeCode(m_writep, code);
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullTriArray(vluint32_t code, const vluint32_t* newvalp,
const vluint32_t* newtrip, int bits) {
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
oldp(code)[word * 2] = newvalp[word];
oldp(code)[word * 2 + 1] = newtrip[word];
}
*m_writep++ = 'b';
for (int bit = bits - 1; bit >= 0; --bit) {
vluint32_t valbit = (newvalp[(bit / 32)] >> (bit & 0x1f)) & 1;
vluint32_t tribit = (newtrip[(bit / 32)] >> (bit & 0x1f)) & 1;
*m_writep++ = "01zz"[valbit | (tribit << 1)];
}
*m_writep++ = ' ';
m_writep = writeCode(m_writep, code);
*m_writep++ = '\n';
bufferCheck();
}
void VerilatedVcd::fullDouble(vluint32_t code, const double newval) {
// cppcheck-suppress invalidPointerCast
(*(reinterpret_cast<double*>(oldp(code)))) = newval;
// Buffer can't overflow before VL_SNPRINTF; we sized during declaration
VL_SNPRINTF(m_writep, m_wrChunkSize, "r%.16g", newval);
m_writep += std::strlen(m_writep);
*m_writep++ = ' ';
m_writep = writeCode(m_writep, code);
*m_writep++ = '\n';
bufferCheck();
}
#endif // VL_TRACE_VCD_OLD_API
//======================================================================
//======================================================================
//======================================================================
#ifdef VERILATED_VCD_TEST
#include <iostream>
extern void verilated_trace_imp_selftest();
vluint32_t v1, v2, s1, s2[3];
vluint32_t tri96[3];
vluint32_t tri96__tri[3];
vluint64_t quad96[2];
vluint64_t tquad;
vluint64_t tquad__tri;
vluint8_t ch;
vluint64_t timestamp = 1;
double doub = 0.0;
float flo = 0.0f;
void vcdInit(void*, VerilatedVcd* vcdp, vluint32_t) {
vcdp->scopeEscape('.');
vcdp->pushNamePrefix("top.");
/**/ vcdp->declBus(0x2, "v1", -1, 0, 5, 1);
/**/ vcdp->declBus(0x3, "v2", -1, 0, 6, 1);
/**/ vcdp->pushNamePrefix("sub1.");
/***/ vcdp->declBit(0x4, "s1", -1, 0);
/***/ vcdp->declBit(0x5, "ch", -1, 0);
/**/ vcdp->popNamePrefix();
/**/ vcdp->pushNamePrefix("sub2.");
/***/ vcdp->declArray(0x6, "s2", -1, 0, 40, 3);
/**/ vcdp->popNamePrefix();
vcdp->popNamePrefix();
// Note need to add 3 for next code.
vcdp->pushNamePrefix("top2.");
/**/ vcdp->declBus(0x2, "t2v1", -1, 0, 4, 1);
/**/ vcdp->declTriBit(0x10, "io1", -1, 0);
/**/ vcdp->declTriBus(0x12, "io5", -1, 0, 4, 0);
/**/ vcdp->declTriArray(0x16, "io96", -1, 0, 95, 0);
/**/ // Note need to add 6 for next code.
/**/ vcdp->declDouble(0x1c, "doub", -1, 0);
/**/ // Note need to add 2 for next code.
/**/ vcdp->declArray(0x20, "q2", -1, 0, 95, 0);
/**/ // Note need to add 4 for next code.
/**/ vcdp->declTriQuad(0x24, "tq", -1, 0, 63, 0);
/**/ // Note need to add 4 for next code.
vcdp->popNamePrefix();
}
void vcdFull(void*, VerilatedVcd* vcdp) {
vcdp->fullBus(0x2, v1, 5);
vcdp->fullBus(0x3, v2, 7);
vcdp->fullBit(0x4, s1);
vcdp->fullBus(0x5, ch, 2);
vcdp->fullArray(0x6, &s2[0], 38);
vcdp->fullTriBit(0x10, tri96[0] & 1, tri96__tri[0] & 1);
vcdp->fullTriBus(0x12, tri96[0] & 0x1f, tri96__tri[0] & 0x1f, 5);
vcdp->fullTriArray(0x16, tri96, tri96__tri, 96);
vcdp->fullDouble(0x1c, doub);
vcdp->fullArray(0x20, &quad96[0], 96);
vcdp->fullTriQuad(0x24, tquad, tquad__tri, 64);
}
void vcdChange(void*, VerilatedVcd* vcdp) {
vcdp->chgBus(0x2, v1, 5);
vcdp->chgBus(0x3, v2, 7);
vcdp->chgBit(0x4, s1);
vcdp->chgBus(0x5, ch, 2);
vcdp->chgArray(0x6, &s2[0], 38);
vcdp->chgTriBit(0x10, tri96[0] & 1, tri96__tri[0] & 1);
vcdp->chgTriBus(0x12, tri96[0] & 0x1f, tri96__tri[0] & 0x1f, 5);
vcdp->chgTriArray(0x16, tri96, tri96__tri, 96);
vcdp->chgDouble(0x1c, doub);
vcdp->chgArray(0x20, &quad96[0], 96);
vcdp->chgTriQuad(0x24, tquad, tquad__tri, 64);
}
// clang-format off
void vcdTestMain(const char* filenamep) {
verilated_trace_imp_selftest();
v1 = v2 = s1 = 0;
s2[0] = s2[1] = s2[2] = 0;
tri96[2] = tri96[1] = tri96[0] = 0;
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = ~0;
quad96[1] = quad96[0] = 0;
ch = 0;
doub = 0;
tquad = tquad__tri = 0;
{
VerilatedVcdC* vcdp = new VerilatedVcdC;
vcdp->evcd(true);
vcdp->set_time_unit("1ms");
vcdp->set_time_unit(std::string{"1ms"});
vcdp->set_time_resolution("1ns");
vcdp->set_time_resolution(std::string{"1ns"});
vcdp->spTrace()->addInitCb(&vcdInit, 0);
vcdp->spTrace()->addFullCb(&vcdFull, 0);
vcdp->spTrace()->addChgCb(&vcdChange, 0);
vcdp->open(filenamep);
// Dumping
vcdp->dump(++timestamp);
v1 = 0xfff;
tri96[2] = 4; tri96[1] = 2; tri96[0] = 1;
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = ~0; // Still tri
quad96[1] = 0xffffffff; quad96[0] = 0;
doub = 1.5;
flo = 1.4f;
vcdp->dump(++timestamp);
v2 = 0x1;
s2[1] = 2;
tri96__tri[2] = tri96__tri[1] = tri96__tri[0] = 0; // enable w/o data change
quad96[1] = 0; quad96[0] = ~0;
doub = -1.66e13;
flo = 0.123f;
tquad = 0x00ff00ff00ff00ffULL;
tquad__tri = 0x0000fffff0000ffffULL;
vcdp->dump(++timestamp);
ch = 2;
tri96[2] = ~4; tri96[1] = ~2; tri96[0] = ~1;
doub = -3.33e-13;
vcdp->dump(++timestamp);
vcdp->dump(++timestamp);
# ifdef VERILATED_VCD_TEST_64BIT
const vluint64_t bytesPerDump = 15ULL;
for (vluint64_t i = 0; i < ((1ULL << 32) / bytesPerDump); i++) {
v1 = i;
vcdp->dump(++timestamp);
}
# endif
vcdp->close();
VL_DO_CLEAR(delete vcdp, vcdp = nullptr);
}
}
#endif
// clang-format on
//********************************************************************
// ;compile-command: "v4make test_regress/t/t_trace_c_api.pl"
//
// Local Variables:
// End:
+202 -299
View File
@@ -28,7 +28,196 @@
#include <string>
#include <vector>
class VerilatedVcd;
class VerilatedVcdBuffer;
class VerilatedVcdFile;
//=============================================================================
// VerilatedVcd
// Base class to create a Verilator VCD dump
// This is an internally used class - see VerilatedVcdC for what to call from applications
class VerilatedVcd VL_NOT_FINAL : public VerilatedTrace<VerilatedVcd, VerilatedVcdBuffer> {
public:
using Super = VerilatedTrace<VerilatedVcd, VerilatedVcdBuffer>;
private:
friend VerilatedVcdBuffer; // Give the buffer access to the private bits
//=========================================================================
// VCD specific internals
VerilatedVcdFile* m_filep; // File we're writing to
bool m_fileNewed; // m_filep needs destruction
bool m_isOpen = false; // True indicates open file
std::string m_filename; // Filename we're writing to (if open)
uint64_t m_rolloverSize = 0; // File size to rollover at
int m_modDepth = 0; // Depth of module hierarchy
char* m_wrBufp; // Output buffer
char* m_wrFlushp; // Output buffer flush trigger location
char* m_writep; // Write pointer into output buffer
size_t m_wrChunkSize; // Output buffer size
size_t m_maxSignalBytes = 0; // Upper bound on number of bytes a single signal can generate
uint64_t m_wroteBytes = 0; // Number of bytes written to this file
std::vector<char> m_suffixes; // VCD line end string codes + metadata
using NameMap = std::map<const std::string, const std::string>;
NameMap* m_namemapp = nullptr; // List of names for the header
#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
#endif
void bufferResize(size_t minsize);
void bufferFlush() VL_MT_UNSAFE_ONE;
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();
}
void openNextImp(bool incFilename);
void closePrev();
void closeErr();
void makeNameMap();
void deleteNameMap();
void printIndent(int level_change);
void printStr(const char* str);
void printQuad(uint64_t n);
void printTime(uint64_t timeui);
void declare(uint32_t code, const char* name, const char* wirep, bool array, int arraynum,
bool tri, bool bussed, int msb, int lsb);
void dumpHeader();
static char* writeCode(char* writep, uint32_t code);
// CONSTRUCTORS
VL_UNCOPYABLE(VerilatedVcd);
protected:
//=========================================================================
// Implementation of VerilatedTrace interface
// Called when the trace moves forward to a new time point
void emitTimeChange(uint64_t timeui) override;
// Hooks called from VerilatedTrace
bool preFullDump() override { return isOpen(); }
bool preChangeDump() override;
// Trace buffer management
Buffer* getTraceBuffer() override;
void commitTraceBuffer(Buffer*) override;
// Configure sub-class
void configure(const VerilatedTraceConfig&) override{};
public:
//=========================================================================
// External interface to client code
// CONSTRUCTOR
explicit VerilatedVcd(VerilatedVcdFile* filep = nullptr);
~VerilatedVcd();
// ACCESSORS
// 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
void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
// Open next data-only file
void openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex);
// Close the file
void close() VL_MT_SAFE_EXCLUDES(m_mutex);
// Flush any remaining data to this file
void flush() VL_MT_SAFE_EXCLUDES(m_mutex);
// Return if file is open
bool isOpen() const VL_MT_SAFE { return m_isOpen; }
//=========================================================================
// Internal interface to Verilator generated code
void declBit(uint32_t code, const char* name, bool array, int arraynum);
void declBus(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declQuad(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declArray(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declDouble(uint32_t code, const char* name, bool array, int arraynum);
};
#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);
#endif // DOXYGEN
//=============================================================================
// VerilatedVcdBuffer
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;
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;
#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
void adjustGrowp() {
m_growp = (m_bufp + m_size) - (2 * m_maxSignalBytes);
assert(m_growp >= m_bufp + m_maxSignalBytes);
}
#endif
void finishLine(uint32_t code, char* writep);
// CONSTRUCTOR
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 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);
};
//=============================================================================
// VerilatedFile
@@ -52,285 +241,6 @@ public:
virtual ssize_t write(const char* bufp, ssize_t len) VL_MT_UNSAFE;
};
//=============================================================================
// VerilatedVcd
// Base class to create a Verilator VCD dump
// This is an internally used class - see VerilatedVcdC for what to call from applications
class VerilatedVcd VL_NOT_FINAL : public VerilatedTrace<VerilatedVcd> {
private:
// Give the superclass access to private bits (to avoid virtual functions)
friend class VerilatedTrace<VerilatedVcd>;
//=========================================================================
// VCD specific internals
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)
vluint64_t m_rolloverMB = 0; // MB of file size to rollover at
int m_modDepth = 0; // Depth of module hierarchy
char* m_wrBufp; // Output buffer
const char* m_wrFlushp; // Output buffer flush trigger location
char* m_writep; // Write pointer into output buffer
vluint64_t m_wrChunkSize; // Output buffer size
vluint64_t m_wroteBytes = 0; // Number of bytes written to this file
std::vector<char> m_suffixes; // VCD line end string codes + metadata
using NameMap = std::map<const std::string, const std::string>;
NameMap* m_namemapp = nullptr; // List of names for the header
void bufferResize(vluint64_t minsize);
void bufferFlush() VL_MT_UNSAFE_ONE;
inline 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();
}
void openNextImp(bool incFilename);
void closePrev();
void closeErr();
void makeNameMap();
void deleteNameMap();
void printIndent(int level_change);
void printStr(const char* str);
void printQuad(vluint64_t n);
void printTime(vluint64_t timeui);
void declare(vluint32_t code, const char* name, const char* wirep, bool array, int arraynum,
bool tri, bool bussed, int msb, int lsb);
void dumpHeader();
static char* writeCode(char* writep, vluint32_t code);
void finishLine(vluint32_t code, char* writep);
// CONSTRUCTORS
VL_UNCOPYABLE(VerilatedVcd);
protected:
//=========================================================================
// Implementation of VerilatedTrace interface
// Implementations of protected virtual methods for VerilatedTrace
virtual void emitTimeChange(vluint64_t timeui) override;
// Hooks called from VerilatedTrace
virtual bool preFullDump() override { return isOpen(); }
virtual bool preChangeDump() override;
// Implementations of duck-typed methods for VerilatedTrace. These are
// called from only one place (namely full*) so always inline them.
inline void emitBit(vluint32_t code, CData newval);
inline void emitCData(vluint32_t code, CData newval, int bits);
inline void emitSData(vluint32_t code, SData newval, int bits);
inline void emitIData(vluint32_t code, IData newval, int bits);
inline void emitQData(vluint32_t code, QData newval, int bits);
inline void emitWData(vluint32_t code, const WData* newvalp, int bits);
inline void emitDouble(vluint32_t code, double newval);
public:
//=========================================================================
// External interface to client code
explicit VerilatedVcd(VerilatedVcdFile* filep = nullptr);
~VerilatedVcd();
// ACCESSORS
// Set size in megabytes after which new file should be created
void rolloverMB(vluint64_t rolloverMB) { m_rolloverMB = rolloverMB; }
// METHODS
// Open the file; call isOpen() to see if errors
void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
// Open next data-only file
void openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex);
// Close the file
void close() VL_MT_SAFE_EXCLUDES(m_mutex);
// Flush any remaining data to this file
void flush() VL_MT_SAFE_EXCLUDES(m_mutex);
// Return if file is open
bool isOpen() const VL_MT_SAFE { return m_isOpen; }
//=========================================================================
// Internal interface to Verilator generated code
void declBit(vluint32_t code, const char* name, bool array, int arraynum);
void declBus(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declArray(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declDouble(vluint32_t code, const char* name, bool array, int arraynum);
#ifdef VL_TRACE_VCD_OLD_API
//=========================================================================
// Note: These are only for testing for backward compatibility with foreign
// code and is not used by Verilator. Do not use these as there is no
// guarantee of functionality.
void declTriBit(vluint32_t code, const char* name, bool array, int arraynum);
void declTriBus(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
void declTriQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb);
void declTriArray(vluint32_t code, const char* name, bool array, int arraynum, int msb,
int lsb);
void fullBit(vluint32_t* oldp, CData newval) { fullBit(oldp - this->oldp(0), newval); }
void fullCData(vluint32_t* oldp, CData newval, int bits) {
fullBus(oldp - this->oldp(0), newval, bits);
}
void fullSData(vluint32_t* oldp, SData newval, int bits) {
fullBus(oldp - this->oldp(0), newval, bits);
}
void fullIData(vluint32_t* oldp, IData newval, int bits) {
fullBus(oldp - this->oldp(0), newval, bits);
}
void fullQData(vluint32_t* oldp, QData newval, int bits) {
fullQuad(oldp - this->oldp(0), newval, bits);
}
void fullWData(vluint32_t* oldp, const WData* newvalp, int bits) {
fullArray(oldp - this->oldp(0), newvalp, bits);
}
void fullDouble(vluint32_t* oldp, double newval) { fullDouble(oldp - this->oldp(0), newval); }
inline void chgBit(vluint32_t* oldp, CData newval) { chgBit(oldp - this->oldp(0), newval); }
inline void chgCData(vluint32_t* oldp, CData newval, int bits) {
chgBus(oldp - this->oldp(0), newval, bits);
}
inline void chgSData(vluint32_t* oldp, SData newval, int bits) {
chgBus(oldp - this->oldp(0), newval, bits);
}
inline void chgIData(vluint32_t* oldp, IData newval, int bits) {
chgBus(oldp - this->oldp(0), newval, bits);
}
inline void chgQData(vluint32_t* oldp, QData newval, int bits) {
chgQuad(oldp - this->oldp(0), newval, bits);
}
inline void chgWData(vluint32_t* oldp, const WData* newvalp, int bits) {
chgArray(oldp - this->oldp(0), newvalp, bits);
}
inline void chgDouble(vluint32_t* oldp, double newval) {
chgDouble(oldp - this->oldp(0), newval);
}
// Inside dumping routines, dump one signal, faster when not inlined
// due to code size reduction.
void fullBit(vluint32_t code, const vluint32_t newval);
void fullBus(vluint32_t code, const vluint32_t newval, int bits);
void fullQuad(vluint32_t code, const vluint64_t newval, int bits);
void fullArray(vluint32_t code, const vluint32_t* newvalp, int bits);
void fullArray(vluint32_t code, const vluint64_t* newvalp, int bits);
void fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri);
void fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits);
void fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint64_t newtri, int bits);
void fullTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip,
int bits);
void fullDouble(vluint32_t code, const double newval);
// Inside dumping routines, dump one signal if it has changed.
// We do want to inline these to avoid calls when the value did not change.
inline void chgBit(vluint32_t code, const vluint32_t newval) {
const vluint32_t diff = oldp(code)[0] ^ newval;
if (VL_UNLIKELY(diff)) fullBit(code, newval);
}
inline void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
const vluint32_t diff = oldp(code)[0] ^ newval;
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
fullBus(code, newval, bits);
}
}
}
inline void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
const vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval;
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
fullQuad(code, newval, bits);
}
}
}
inline void chgArray(vluint32_t code, const vluint32_t* newvalp, int bits) {
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
if (VL_UNLIKELY(oldp(code)[word] ^ newvalp[word])) {
fullArray(code, newvalp, bits);
return;
}
}
}
inline void chgArray(vluint32_t code, const vluint64_t* newvalp, int bits) {
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) {
if (VL_UNLIKELY(*(reinterpret_cast<vluint64_t*>(oldp(code + 2 * word)))
^ newvalp[word])) {
fullArray(code, newvalp, bits);
return;
}
}
}
inline void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
const vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
if (VL_UNLIKELY(diff)) {
// Verilator 3.510 and newer provide clean input, so the below
// is only for back compatibility
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
fullTriBit(code, newval, newtri);
}
}
}
inline void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri,
int bits) {
const vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
fullTriBus(code, newval, newtri, bits);
}
}
}
inline void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint64_t newtri,
int bits) {
const vluint64_t diff = (((*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval)
| ((*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) ^ newtri));
if (VL_UNLIKELY(diff)) {
if (VL_UNLIKELY(bits == 64 || (diff & ((1ULL << bits) - 1)))) {
fullTriQuad(code, newval, newtri, bits);
}
}
}
inline void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip,
int bits) {
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
if (VL_UNLIKELY((oldp(code)[word * 2] ^ newvalp[word])
| (oldp(code)[word * 2 + 1] ^ newtrip[word]))) {
fullTriArray(code, newvalp, newtrip, bits);
return;
}
}
}
inline void chgDouble(vluint32_t code, const double newval) {
// cppcheck-suppress invalidPointerCast
if (VL_UNLIKELY((*(reinterpret_cast<double*>(oldp(code)))) != newval)) {
fullDouble(code, newval);
}
}
// METHODS
// Old/standalone API only
void evcd(bool flag) { m_evcd = flag; }
#endif // VL_TRACE_VCD_OLD_API
};
#ifndef DOXYGEN
// Declare specializations here they are used in VerilatedVcdC just below
template <> void VerilatedTrace<VerilatedVcd>::dump(vluint64_t timeui);
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const char* unitp);
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const std::string& unit);
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const char* unitp);
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const std::string& unit);
template <> void VerilatedTrace<VerilatedVcd>::dumpvars(int level, const std::string& hier);
#endif // DOXYGEN
//=============================================================================
// VerilatedVcdC
/// Class representing a VCD dump file in C standalone (no SystemC)
@@ -349,7 +259,6 @@ public:
/// Destruct, flush, and close the dump
virtual ~VerilatedVcdC() { close(); }
public:
// METHODS - User called
/// Return if file is open
@@ -362,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
@@ -371,12 +284,12 @@ public:
/// Write one cycle of dump data
/// Call with the current context's time just after eval'ed,
/// e.g. ->dump(contextp->time())
void dump(vluint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
void dump(uint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
/// Write one cycle of dump data - backward compatible and to reduce
/// conversion warnings. It's better to use a vluint64_t time instead.
void dump(double timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
/// conversion warnings. It's better to use a uint64_t time instead.
void dump(double timestamp) { dump(static_cast<uint64_t>(timestamp)); }
void dump(uint32_t timestamp) { dump(static_cast<uint64_t>(timestamp)); }
void dump(int timestamp) { dump(static_cast<uint64_t>(timestamp)); }
// METHODS - Internal/backward compatible
// \protectedsection
@@ -400,17 +313,7 @@ public:
}
// Internal class access
inline VerilatedVcd* spTrace() { return &m_sptrace; }
#ifdef VL_TRACE_VCD_OLD_API
//=========================================================================
// Note: These are only for testing for backward compatibility with foreign
// code and is not used by Verilator. Do not use these as there is no
// guarantee of functionality.
// Use evcd format
void evcd(bool flag) VL_MT_UNSAFE_ONE { m_sptrace.evcd(flag); }
#endif
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 )
+103 -101
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>
@@ -66,11 +67,11 @@ class VerilatedVpio VL_NOT_FINAL {
// CONSTANTS
// Magic value stored in front of object to detect double free etc
// Must be odd, as aligned pointer can never be odd
static constexpr vluint32_t activeMagic() { return 0xfeed100f; }
static constexpr uint32_t activeMagic() { return 0xfeed100f; }
// MEM MANGLEMENT
// Internal note: Globals may multi-construct, see verilated.cpp top.
static VL_THREAD_LOCAL vluint8_t* t_freeHead;
static VL_THREAD_LOCAL uint8_t* t_freeHead;
public:
// CONSTRUCTORS
@@ -81,22 +82,23 @@ public:
// To simplify our free list, we use a size large enough for all derived types
// We reserve word zero for the next pointer, as that's safer in case a
// dangling reference to the original remains around.
static const size_t chunk = 96;
if (VL_UNCOVERABLE(size > chunk)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase chunk");
static constexpr size_t CHUNK_SIZE = 96;
if (VL_UNCOVERABLE(size > CHUNK_SIZE))
VL_FATAL_MT(__FILE__, __LINE__, "", "increase CHUNK_SIZE");
if (VL_LIKELY(t_freeHead)) {
vluint8_t* const newp = t_freeHead;
t_freeHead = *(reinterpret_cast<vluint8_t**>(newp));
*(reinterpret_cast<vluint32_t*>(newp)) = activeMagic();
uint8_t* const newp = t_freeHead;
t_freeHead = *(reinterpret_cast<uint8_t**>(newp));
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
return newp + 8;
}
// +8: 8 bytes for next
vluint8_t* newp = reinterpret_cast<vluint8_t*>(::operator new(chunk + 8));
*(reinterpret_cast<vluint32_t*>(newp)) = activeMagic();
uint8_t* newp = reinterpret_cast<uint8_t*>(::operator new(CHUNK_SIZE + 8));
*(reinterpret_cast<uint32_t*>(newp)) = activeMagic();
return newp + 8;
}
static void operator delete(void* obj, size_t /*size*/)VL_MT_SAFE {
vluint8_t* const oldp = (static_cast<vluint8_t*>(obj)) - 8;
if (VL_UNLIKELY(*(reinterpret_cast<vluint32_t*>(oldp)) != activeMagic())) {
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__, "",
"vpi_release_handle() called on same object twice, or on non-Verilator "
"VPI object");
@@ -112,13 +114,13 @@ 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>"; }
virtual const char* defname() const { return "<null>"; }
virtual vluint32_t type() const { return 0; }
virtual vluint32_t size() const { return 0; }
virtual uint32_t type() const { return 0; }
virtual uint32_t size() const { return 0; }
virtual const VerilatedRange* rangep() const { return nullptr; }
virtual vpiHandle dovpi_scan() { return nullptr; }
virtual PLI_INT32 dovpi_remove_cb() { return 0; }
@@ -127,52 +129,52 @@ public:
class VerilatedVpioTimedCb final : public VerilatedVpio {
// A handle to a timed callback created with vpi_register_cb
// User can call vpi_remove_cb or vpi_release_handle on it
const vluint64_t m_id; // Unique id/sequence number to find schedule's event
const uint64_t m_id; // Unique id/sequence number to find schedule's event
const QData m_time;
public:
VerilatedVpioTimedCb(vluint64_t id, QData time)
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 vluint32_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 {
// A handle to a non-timed callback created with vpi_register_cb
// User can call vpi_remove_cb or vpi_release_handle on it
const vluint64_t m_id; // Unique id/sequence number to find schedule's event
const uint64_t m_id; // Unique id/sequence number to find schedule's event
const PLI_INT32 m_reason; // VPI callback reason code
public:
// cppcheck-suppress uninitVar // m_value
VerilatedVpioReasonCb(vluint64_t id, PLI_INT32 reason)
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 vluint32_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 {
const vlsint32_t m_num;
const int32_t m_num;
public:
explicit VerilatedVpioConst(vlsint32_t num)
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 vluint32_t type() const override { return vpiConstant; }
vlsint32_t num() const { return m_num; }
uint32_t type() const override { return vpiConstant; }
int32_t num() const { return m_num; }
};
class VerilatedVpioVarBase VL_NOT_FINAL : public VerilatedVpio {
@@ -199,10 +201,10 @@ public:
}
const VerilatedVar* varp() const { return m_varp; }
const VerilatedScope* scopep() const { return m_scopep; }
virtual vluint32_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();
@@ -213,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 vluint32_t type() const override { return vpiParameter; }
uint32_t type() const override { return vpiParameter; }
void* varDatap() const { return m_varp->datap(); }
};
@@ -228,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 vluint32_t type() const override { return vpiRange; }
virtual vluint32_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 {
@@ -245,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 vluint32_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;
@@ -267,26 +269,26 @@ 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 vluint32_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 {
vluint8_t* m_prevDatap = nullptr; // Previous value of data, for cbValueChange
uint8_t* m_prevDatap = nullptr; // Previous value of data, for cbValueChange
union {
vluint8_t u8[4];
vluint32_t u32;
uint8_t u8[4];
uint32_t u32;
} m_mask; // memoized variable mask
vluint32_t m_entSize = 0; // memoized variable size
uint32_t m_entSize = 0; // memoized variable size
protected:
void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries
vlsint32_t m_index = 0;
int32_t m_index = 0;
public:
VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
@@ -307,24 +309,24 @@ 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) {
return dynamic_cast<VerilatedVpioVar*>(reinterpret_cast<VerilatedVpio*>(h));
}
vluint32_t mask() const { return m_mask.u32; }
vluint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
vluint32_t entSize() const { return m_entSize; }
vluint32_t index() const { return m_index; }
virtual vluint32_t type() const override {
uint32_t mask() const { return m_mask.u32; }
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; }
uint32_t type() const override {
return (varp()->dims() > 1) ? vpiMemory : vpiReg; // but might be wire, logic
}
void* prevDatap() const { return m_prevDatap; }
void* varDatap() const { return m_varDatap; }
void createPrevDatap() {
if (VL_UNLIKELY(!m_prevDatap)) {
m_prevDatap = new vluint8_t[entSize()];
m_prevDatap = new uint8_t[entSize()];
std::memcpy(prevDatap(), varp()->datap(), entSize());
}
}
@@ -332,20 +334,20 @@ public:
class VerilatedVpioMemoryWord final : public VerilatedVpioVar {
public:
VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep,
vlsint32_t index, int offset)
VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep, int32_t index,
int offset)
: VerilatedVpioVar{varp, scopep} {
m_index = index;
m_varDatap = (static_cast<vluint8_t*>(varp->datap())) + entSize() * offset;
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 vluint32_t type() const override { return vpiMemoryWord; }
virtual vluint32_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];
@@ -363,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 vluint32_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)) {
@@ -394,8 +396,8 @@ public:
class VerilatedVpioMemoryWordIter final : public VerilatedVpio {
const vpiHandle m_handle;
const VerilatedVar* const m_varp;
vlsint32_t m_iteration;
const vlsint32_t m_direction;
int32_t m_iteration;
const int32_t m_direction;
bool m_done = false;
public:
@@ -404,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 vluint32_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;
@@ -437,9 +439,9 @@ public:
static VerilatedVpioModule* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModule*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_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 {
@@ -451,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 vluint32_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;
@@ -472,14 +474,14 @@ using VerilatedPliCb = PLI_INT32 (*)(struct t_cb_data*);
class VerilatedVpiCbHolder final {
// Holds information needed to call a callback
vluint64_t m_id;
uint64_t m_id;
s_cb_data m_cbData;
s_vpi_value m_value;
VerilatedVpioVar m_varo; // If a cbValueChange callback, the object we will return
public:
// cppcheck-suppress uninitVar // m_value
VerilatedVpiCbHolder(vluint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
VerilatedVpiCbHolder(uint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
: m_id{id}
, m_cbData{*cbDatap}
, m_varo{varop} {
@@ -495,15 +497,15 @@ public:
~VerilatedVpiCbHolder() = default;
VerilatedPliCb cb_rtnp() const { return m_cbData.cb_rtn; }
s_cb_data* cb_datap() { return &m_cbData; }
vluint64_t id() const { return m_id; }
uint64_t id() const { return m_id; }
bool invalid() const { return !m_id; }
void invalidate() { m_id = 0; }
};
struct VerilatedVpiTimedCbsCmp {
// Ordering sets keyed by time, then callback unique id
bool operator()(const std::pair<QData, vluint64_t>& a,
const std::pair<QData, vluint64_t>& b) const {
bool operator()(const std::pair<QData, uint64_t>& a,
const std::pair<QData, uint64_t>& b) const {
if (a.first < b.first) return true;
if (a.first > b.first) return false;
return a.second < b.second;
@@ -515,14 +517,14 @@ class VerilatedVpiError;
class VerilatedVpiImp final {
enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 }; // Maxium callback reason
using VpioCbList = std::list<VerilatedVpiCbHolder>;
using VpioTimedCbs = std::map<std::pair<QData, vluint64_t>, VerilatedVpiCbHolder>;
using VpioTimedCbs = std::map<std::pair<QData, uint64_t>, VerilatedVpiCbHolder>;
// All only medium-speed, so use singleton function
VpioCbList m_cbObjLists[CB_ENUM_MAX_VALUE]; // Callbacks for each supported reason
VpioTimedCbs m_timedCbs; // Time based callbacks
VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
vluint64_t m_nextCallbackId = 1; // Id to identify callback
uint64_t m_nextCallbackId = 1; // Id to identify callback
static VerilatedVpiImp& s() { // Singleton
static VerilatedVpiImp s_s;
@@ -531,9 +533,9 @@ class VerilatedVpiImp final {
public:
static void assertOneCheck() { s().m_assertOne.check(); }
static vluint64_t nextCallbackId() { return ++s().m_nextCallbackId; }
static uint64_t nextCallbackId() { return ++s().m_nextCallbackId; }
static void cbReasonAdd(vluint64_t id, const s_cb_data* cb_data_p) {
static void cbReasonAdd(uint64_t id, const s_cb_data* cb_data_p) {
// The passed cb_data_p was property of the user, so need to recreate
if (VL_UNCOVERABLE(cb_data_p->reason >= CB_ENUM_MAX_VALUE)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "vpi bb reason too large");
@@ -544,7 +546,7 @@ public:
if (cb_data_p->reason == cbValueChange) varop = VerilatedVpioVar::castp(cb_data_p->obj);
s().m_cbObjLists[cb_data_p->reason].emplace_back(id, cb_data_p, varop);
}
static void cbTimedAdd(vluint64_t id, const s_cb_data* cb_data_p, QData time) {
static void cbTimedAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
// The passed cb_data_p was property of the user, so need to recreate
VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" PRId64
" delay=%" PRIu64 "\n",
@@ -553,7 +555,7 @@ public:
std::forward_as_tuple(std::make_pair(time, id)),
std::forward_as_tuple(id, cb_data_p, nullptr));
}
static void cbReasonRemove(vluint64_t id, vluint32_t reason) {
static void cbReasonRemove(uint64_t id, uint32_t reason) {
// Id might no longer exist, if already removed due to call after event, or teardown
VpioCbList& cbObjList = s().m_cbObjLists[reason];
// We do not remove it now as we may be iterating the list,
@@ -562,7 +564,7 @@ public:
if (ir.id() == id) ir.invalidate();
}
}
static void cbTimedRemove(vluint64_t id, QData time) {
static void cbTimedRemove(uint64_t id, QData time) {
// Id might no longer exist, if already removed due to call after event, or teardown
const auto it = s().m_timedCbs.find(std::make_pair(time, id));
if (VL_LIKELY(it != s().m_timedCbs.end())) it->second.invalidate();
@@ -592,7 +594,7 @@ public:
if (VL_LIKELY(it != s().m_timedCbs.cend())) return it->first.first;
return ~0ULL; // maxquad
}
static bool callCbs(const vluint32_t reason) VL_MT_UNSAFE_ONE {
static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE {
VpioCbList& cbObjList = s().m_cbObjLists[reason];
bool called = false;
if (cbObjList.empty()) return called;
@@ -664,7 +666,7 @@ public:
// Statics
// Internal note: Globals may multi-construct, see verilated.cpp top.
VL_THREAD_LOCAL vluint8_t* VerilatedVpio::t_freeHead = nullptr;
VL_THREAD_LOCAL uint8_t* VerilatedVpio::t_freeHead = nullptr;
//======================================================================
// VerilatedVpiError
@@ -744,7 +746,7 @@ void VerilatedVpi::callTimedCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::callTimedC
bool VerilatedVpi::callValueCbs() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::callValueCbs(); }
bool VerilatedVpi::callCbs(vluint32_t reason) VL_MT_UNSAFE_ONE {
bool VerilatedVpi::callCbs(uint32_t reason) VL_MT_UNSAFE_ONE {
return VerilatedVpiImp::callCbs(reason);
}
@@ -1309,7 +1311,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
QData time = 0;
if (cb_data_p->time) time = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
const QData abstime = VL_TIME_Q() + time;
const vluint64_t id = VerilatedVpiImp::nextCallbackId();
const uint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioTimedCb* const vop = new VerilatedVpioTimedCb{id, abstime};
VerilatedVpiImp::cbTimedAdd(id, cb_data_p, abstime);
return vop->castVpiHandle();
@@ -1324,7 +1326,7 @@ vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
case cbEnterInteractive: // FALLTHRU // NOP, but need to return handle, so make object
case cbExitInteractive: // FALLTHRU // NOP, but need to return handle, so make object
case cbInteractiveScopeChange: { // FALLTHRU // NOP, but need to return handle, so make object
const vluint64_t id = VerilatedVpiImp::nextCallbackId();
const uint64_t id = VerilatedVpiImp::nextCallbackId();
VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, cb_data_p->reason};
VerilatedVpiImp::cbReasonAdd(id, cb_data_p);
return vop->castVpiHandle();
@@ -1474,7 +1476,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
case vpiIndex: {
const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
if (VL_UNLIKELY(!vop)) return nullptr;
const vlsint32_t val = vop->index();
const int32_t val = vop->index();
return (new VerilatedVpioConst{val})->castVpiHandle();
}
case vpiScope: {
@@ -1996,7 +1998,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_
for (int i = 0; i < chars; ++i) {
union {
char byte[2];
vluint16_t half;
uint16_t half;
} val;
idx = div(i * 3, 8);
if (i < len) {
+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 vluint32_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;
+77 -33
View File
@@ -39,7 +39,8 @@
#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))
# define VL_ATTR_NORETURN __attribute__((noreturn))
@@ -82,6 +83,9 @@
#ifndef VL_ATTR_ALWINLINE
# define VL_ATTR_ALWINLINE ///< Attribute to inline, even when not optimizing
#endif
#ifndef VL_ATTR_NOINLINE
# define VL_ATTR_NOINLINE ///< Attribute to never inline, even when optimizing
#endif
#ifndef VL_ATTR_COLD
# define VL_ATTR_COLD ///< Attribute that function is rarely executed
#endif
@@ -214,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
@@ -241,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
//=========================================================================
@@ -282,6 +294,7 @@ void __gcov_flush(); // gcc sources gcc/gcov-io.h has the prototype
#include <cstdint>
#include <cinttypes>
#ifndef VL_NO_LEGACY
using vluint8_t = uint8_t; ///< 8-bit unsigned type (backward compatibility)
using vluint16_t = uint16_t; ///< 16-bit unsigned type (backward compatibility)
using vluint32_t = uint32_t; ///< 32-bit unsigned type (backward compatibility)
@@ -290,6 +303,7 @@ using vlsint8_t = int8_t; ///< 8-bit signed type (backward compatibility)
using vlsint16_t = int16_t; ///< 16-bit signed type (backward compatibility)
using vlsint32_t = int32_t; ///< 32-bit signed type (backward compatibility)
using vlsint64_t = int64_t; ///< 64-bit signed type (backward compatibility)
#endif
#if defined(__CYGWIN__)
@@ -320,12 +334,12 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// Deprecated, favor C++11's PRIx64, etc, instead
#ifndef VL_NO_LEGACY
# ifdef _MSC_VER
# define VL_PRI64 "I64" ///< print a vluint64_t (backward compatibility)
# define VL_PRI64 "I64" ///< print a uint64_t (backward compatibility)
# else // use standard C99 format specifiers
# if defined(__WORDSIZE) && (__WORDSIZE == 64)
# define VL_PRI64 "l" ///< print a vluint64_t (backward compatibility)
# define VL_PRI64 "l" ///< print a uint64_t (backward compatibility)
# else
# define VL_PRI64 "ll" ///< print a vluint64_t (backward compatibility)
# define VL_PRI64 "ll" ///< print a uint64_t (backward compatibility)
# endif
# endif
#endif
@@ -436,24 +450,25 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// Performance counters
#if defined(__i386__) || defined(__x86_64__)
// The vluint64_t argument is loaded with a high-performance counter for profiling
// The uint64_t argument is loaded with a high-performance counter for profiling
// or 0x0 if not implemented on this platform
#define VL_RDTSC(val) \
#define VL_GET_CPU_TICK(val) \
{ \
vluint32_t hi, lo; \
uint32_t hi; \
uint32_t lo; \
asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); \
(val) = ((vluint64_t)lo) | (((vluint64_t)hi) << 32); \
(val) = ((uint64_t)lo) | (((uint64_t)hi) << 32); \
}
#elif defined(__aarch64__)
// 1 GHz virtual system timer on SBSA level 5 compliant systems, else often 100 MHz
# define VL_RDTSC(val) \
# define VL_GET_CPU_TICK(val) \
{ \
asm volatile("isb" : : : "memory"); \
asm volatile("mrs %[rt],CNTVCT_EL0" : [rt] "=r"(val)); \
}
#else
// We just silently ignore unknown OSes, as only leads to missing statistics
# define VL_RDTSC(val) (val) = 0;
# define VL_GET_CPU_TICK(val) (val) = 0;
#endif
//=========================================================================
@@ -475,6 +490,9 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
# define VL_CPU_RELAX() asm volatile("yield" ::: "memory")
# elif defined(__powerpc64__)
# define VL_CPU_RELAX() asm volatile("or 1, 1, 1; or 2, 2, 2;" ::: "memory")
# elif defined(__loongarch__)
// LoongArch does not currently have a yield/pause instruction
# define VL_CPU_RELAX() asm volatile("nop" ::: "memory")
# else
# error "Missing VL_CPU_RELAX() definition. Or, don't use VL_THREADED"
# endif
@@ -489,10 +507,8 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
# define VL_STRCASECMP strcasecmp
#endif
#ifdef __MINGW32__
#if defined(__MINGW32__) || defined(_MSC_VER)
# define VL_LOCALTIME_R(timep, tmp) localtime_s((tmp), (timep))
#elif defined(_MSC_VER)
# define VL_LOCALTIME_R(timep, tmp) localtime_c((tmp), (timep))
#else
# define VL_LOCALTIME_R(timep, tmp) localtime_r((timep), (tmp))
#endif
@@ -514,12 +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 {
const T& m_v;
explicit reverse_wrapper(const T& a_v)
: m_v(a_v) {}
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);
}
// 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\(\)')
+1 -1
View File
@@ -610,7 +610,7 @@ def getGcovCoverage(args):
coverage_files = getFilteredCoverageFiles(coverage_files, args.excludepre)
logging.info("Found {} coverage files after filtering".format(len(coverage_files)))
# We "zero" the "counters" by simply deleting all gcda files
# We "zero" the "counters" by deleting all gcda files
if args.zerocounters:
removeFiles(coverage_files)
logging.info("Removed {} .gcda files".format(len(coverage_files)))
+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
+105 -110
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,88 +313,83 @@ public:
iterate(nodep);
m_graph.latchCheck(nodep, kwd == VAlwaysKwd::ALWAYS_LATCH);
}
virtual ~ActiveLatchCheckVisitor() {}
~ActiveLatchCheckVisitor() override = default;
};
//######################################################################
// Active AssignDly replacement functions
// 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_COMBO, CT_INITIAL, CT_LATCH };
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL };
private:
const CheckType m_check; // Combo logic or other
const AstNode* const m_alwaysp; // Always we're under
const AstNode* m_assignp = nullptr; // In assign
// MEMBERS
const CheckType m_check; // Process type we are checking
// VISITORS
virtual void visit(AstAssignDly* nodep) override {
if (m_check != CT_SEQ) {
// Convert to a non-delayed assignment
UINFO(5, " ASSIGNDLY " << nodep << endl);
if (m_check == CT_INITIAL) {
nodep->v3warn(INITIALDLY, "Delayed assignments (<=) in initial or final block\n"
<< nodep->warnMore()
<< "... Suggest blocking assignments (=)");
} else if (m_check == CT_LATCH) {
// Suppress. Shouldn't matter that the interior of the latch races
} else if (!(VN_IS(nodep->lhsp(), VarRef)
&& VN_AS(nodep->lhsp(), VarRef)->varp()->isLatched())) {
nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked"
" (non flop or latch) block\n"
<< nodep->warnMore()
<< "... Suggest blocking assignments (=)");
// Conversely, we could also suggest latches use delayed assignments, as
// recommended by Cliff Cummings?
}
AstNode* const newp = new AstAssign(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack());
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
void visit(AstAssignDly* nodep) override {
// Non-blocking assignments are OK in sequential processes
if (m_check == CT_SEQ) return;
// Issue appropriate warning
if (m_check == CT_INITIAL) {
nodep->v3warn(INITIALDLY,
"Non-blocking assignment '<=' in initial/final block\n"
<< nodep->warnMore()
<< "... This will be executed as a blocking assignment '='!");
} else {
nodep->v3warn(COMBDLY,
"Non-blocking assignment '<=' in combinational logic process\n"
<< nodep->warnMore()
<< "... This will be executed as a blocking assignment '='!");
}
// Convert to blocking assignment
nodep->replaceWith(new AstAssign{nodep->fileline(), //
nodep->lhsp()->unlinkFrBack(), //
nodep->rhsp()->unlinkFrBack()});
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
virtual void visit(AstAssign* nodep) override {
if (m_check == CT_SEQ) {
VL_RESTORER(m_assignp);
m_assignp = nodep;
iterateAndNextNull(nodep->lhsp());
}
}
virtual void visit(AstVarRef* nodep) override {
const AstVar* const varp = nodep->varp();
if (m_check == CT_SEQ && m_assignp && !varp->isUsedLoopIdx() // Ignore loop indices
&& !varp->isTemp()) {
// Allow turning off warnings on the always, or the variable also
if (!m_alwaysp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
&& !m_assignp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
&& !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)) {
m_assignp->v3warn(BLKSEQ,
"Blocking assignments (=) in sequential (flop or latch) block\n"
<< m_assignp->warnMore()
<< "... Suggest delayed assignments (<=)");
m_alwaysp->fileline()->modifyWarnOff(
V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always
varp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
}
}
void visit(AstAssign* nodep) override {
// Blocking assignments are always OK in combinational (and initial/final) processes
if (m_check != CT_SEQ) return;
const bool ignore = nodep->lhsp()->forall<AstVarRef>([&](const AstVarRef* refp) {
// Ignore reads (e.g.: index expressions)
if (refp->access().isReadOnly()) return true;
const AstVar* const varp = refp->varp();
// Ignore ...
return varp->isUsedLoopIdx() // ... loop indices
|| varp->isTemp() // ... temporaries
|| varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ); // ... user said so
});
if (ignore) return;
nodep->v3warn(BLKSEQ,
"Blocking assignment '=' in sequential logic process\n"
<< nodep->warnMore() //
<< "... Suggest using delayed assignment '<='");
}
//--------------------
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
ActiveDlyVisitor(AstNode* nodep, CheckType check)
: m_check{check}
, m_alwaysp{nodep} {
: 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
@@ -411,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
@@ -419,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(AstInitial* 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};
@@ -430,31 +421,39 @@ private:
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
virtual void visit(AstAssignAlias* 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};
AstActive* const wantactivep = m_namer.getIActive(nodep->fileline());
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);
}
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;
}
@@ -467,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);
}
@@ -527,43 +526,39 @@ private:
// Warn and/or convert any delayed assignments
if (combo && !sequent) {
{ ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB}; }
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd};
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_LATCH};
} else {
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMBO};
}
} 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()) {
@@ -592,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;
};
//######################################################################
@@ -608,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);
}
+35 -34
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());
@@ -98,7 +101,7 @@ private:
? static_cast<AstNode*>(
new AstCMath(fl, "vlSymsp->_vm_contextp__->assertOn()", 1))
: static_cast<AstNode*>(new AstConst(fl, AstConst::BitFalse())))),
nodep, nullptr);
nodep);
newp->user1(true); // Don't assert/cover this if
return newp;
}
@@ -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);
@@ -154,7 +157,7 @@ private:
}
if (bodysp && passsp) bodysp = bodysp->addNext(passsp);
ifp = new AstIf(nodep->fileline(), propp, bodysp, nullptr);
ifp = new AstIf(nodep->fileline(), propp, bodysp);
bodysp = ifp;
} else if (VN_IS(nodep, Assert) || VN_IS(nodep, AssertIntrinsic)) {
if (nodep->immediate()) {
@@ -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;
@@ -262,7 +265,7 @@ private:
if (itemp->isDefault()) has_default = true;
}
if (nodep->fullPragma() || nodep->priorityPragma()) {
// Simply need to add a default if there isn't one already
// Need to add a default if there isn't one already
++m_statAsFull;
if (!has_default) {
nodep->addItemsp(new AstCaseItem(
@@ -313,8 +316,7 @@ private:
AstIf* const ifp = new AstIf(
nodep->fileline(), new AstLogNot(nodep->fileline(), ohot),
newFireAssert(nodep,
"synthesis parallel_case, but multiple matches found"),
nullptr);
"synthesis parallel_case, but multiple matches found"));
ifp->branchPred(VBranchPred::BP_UNLIKELY);
nodep->addNotParallelp(ifp);
}
@@ -323,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()) {
@@ -338,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) {
@@ -384,10 +386,10 @@ private:
new AstLogAnd{fl, new AstLogNot{fl, newMonitorOffVarRefp(nodep, VAccess::READ)},
new AstEq{fl, new AstConst{fl, monNum},
newMonitorNumVarRefp(nodep, VAccess::READ)}},
stmtsp, nullptr};
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
@@ -395,48 +397,47 @@ 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{}}};
nodep->replaceWith(newsetp);
// Add "always_comb if (__Vstrobe) begin $display(...); __Vstrobe = '0; end"
AstNode* const stmtsp = nodep;
AstIf* const ifp
= new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp, nullptr};
AstIf* const ifp = new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp};
ifp->branchPred(VBranchPred::BP_UNLIKELY);
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);
@@ -447,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);
@@ -457,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);
@@ -476,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"
//============================================================================
+24 -24
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,23 +162,21 @@ 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)
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Only one PSL clock allowed per assertion");
// Block is the new expression to evaluate
AstNode* blockp = nodep->propp()->unlinkFrBack();
if (nodep->disablep()) {
m_disablep = nodep->disablep()->cloneTree(false);
if (AstNode* const disablep = nodep->disablep()) {
m_disablep = disablep->cloneTree(false);
if (VN_IS(nodep->backp(), Cover)) {
blockp = new AstAnd(
nodep->disablep()->fileline(),
new AstNot(nodep->disablep()->fileline(), nodep->disablep()->unlinkFrBack()),
blockp);
blockp = new AstAnd(disablep->fileline(),
new AstNot(disablep->fileline(), disablep->unlinkFrBack()),
blockp);
} else {
blockp = new AstOr(nodep->disablep()->fileline(),
nodep->disablep()->unlinkFrBack(), blockp);
blockp = new AstOr(disablep->fileline(), disablep->unlinkFrBack(), blockp);
}
}
// Unlink and just keep a pointer to it, convert to sentree as needed
@@ -184,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
@@ -198,7 +198,7 @@ public:
// Process
iterate(nodep);
}
virtual ~AssertPreVisitor() override = default;
~AssertPreVisitor() override = default;
};
//######################################################################
@@ -207,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);
}
+108 -103
View File
@@ -18,20 +18,23 @@
#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
vluint64_t AstNode::s_editCntLast = 0;
vluint64_t AstNode::s_editCntGbl = 0; // Hot cache line
uint64_t AstNode::s_editCntLast = 0;
uint64_t AstNode::s_editCntGbl = 0; // Hot cache line
// To allow for fast clearing of all user pointers, we keep a "timestamp"
// along with each userp, and thus by bumping this count we can make it look
@@ -114,7 +117,7 @@ string AstNode::encodeName(const string& namein) {
return vname.hashedName();
}
string AstNode::encodeNumber(vlsint64_t num) {
string AstNode::encodeNumber(int64_t num) {
if (num < 0) {
return "__02D" + cvtToStr(-num); // 2D=-
} else {
@@ -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) {
@@ -758,7 +801,7 @@ void AstNode::deleteTree() {
#ifdef VL_LEAK_CHECKS
void* AstNode::operator new(size_t size) {
// Optimization note: Aligning to cache line is a loss, due to lost packing
const AstNode* const objp = static_cast<AstNode*>(::operator new(size));
AstNode* const objp = static_cast<AstNode*>(::operator new(size));
V3Broken::addNewed(objp);
return objp;
}
@@ -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
@@ -1135,49 +1147,33 @@ void AstNode::v3errorEndFatal(std::ostringstream& str) const {
VL_UNREACHABLE
}
string AstNode::locationStr() const {
string str = "... In instance ";
const AstNode* backp = this;
int itmax = 10000; // Max iterations before giving up on location search
while (backp) {
if (VL_UNCOVERABLE(--itmax < 0)) {
// Likely some circular back link, and V3Ast is trying to report a low-level error
UINFO(1, "Ran out of iterations finding locationStr on " << backp << endl);
return ""; // LCOV_EXCL_LINE
}
const AstScope* scopep;
if ((scopep = VN_CAST(backp, Scope))) {
// The design is flattened and there are no useful scopes
// This is probably because of inlining
if (scopep->isTop()) break;
string AstNode::instanceStr() const {
// Max iterations before giving up on location search,
// in case we have some circular reference bug.
constexpr unsigned maxIterations = 10000;
unsigned iterCount = 0;
str += scopep->prettyName();
return str;
for (const AstNode* backp = this; backp; backp = backp->backp(), ++iterCount) {
if (VL_UNCOVERABLE(iterCount >= maxIterations)) return ""; // LCOV_EXCL_LINE
// Prefer the enclosing scope, if there is one. This is always under the enclosing module,
// so just pick it up when encountered
if (const AstScope* const scopep = VN_CAST(backp, Scope)) {
return scopep->isTop() ? "" : "... In instance " + scopep->prettyName();
}
backp = backp->backp();
}
backp = this;
while (backp) {
const AstModule* modp;
const AstNodeVarRef* nvrp;
if ((modp = VN_CAST(backp, Module)) && !modp->hierName().empty()) {
str += modp->hierName();
return str;
} else if ((nvrp = VN_CAST(backp, NodeVarRef))) {
const string prettyName = nvrp->prettyName();
// VarRefs have not been flattened yet and do not contain location information
if (prettyName != nvrp->name()) {
str += prettyName;
return str;
}
// If scopes don't exist, report an example instance of the enclosing module
if (const AstModule* const modp = VN_CAST(backp, Module)) {
const string instanceName = modp->someInstanceName();
return instanceName.empty() ? "" : "... In instance " + instanceName;
}
backp = backp->backp();
}
return "";
}
void AstNode::v3errorEnd(std::ostringstream& str) const {
if (!m_fileline) {
V3Error::v3errorEnd(str, locationStr());
V3Error::v3errorEnd(str, instanceStr());
} else {
std::ostringstream nsstr;
nsstr << str.str();
@@ -1187,7 +1183,11 @@ void AstNode::v3errorEnd(std::ostringstream& str) const {
const_cast<AstNode*>(this)->dump(nsstr);
nsstr << endl;
}
m_fileline->v3errorEnd(nsstr, locationStr());
// 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());
}
}
@@ -1264,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
+672 -1430
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
+343 -55
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;
@@ -225,9 +270,10 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
}
}
AstExecGraph::AstExecGraph(FileLine* fileline)
AstExecGraph::AstExecGraph(FileLine* fileline, const string& name)
: ASTGEN_SUPER_ExecGraph(fileline)
, m_depGraphp{new V3Graph} {}
, m_depGraphp{new V3Graph}
, m_name{name} {}
AstExecGraph::~AstExecGraph() { VL_DO_DANGLING(delete m_depGraphp, m_depGraphp); }
@@ -463,7 +509,7 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
} else if (widthMin() <= VL_IDATASIZE) {
arg += "uint32_t";
} else if (widthMin() <= VL_QUADSIZE) {
arg += "vluint64_t";
arg += "uint64_t";
} else {
arg += "uint32_t"; // []'s added later
}
@@ -480,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;
}
@@ -488,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 {
@@ -518,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)
@@ -554,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)
@@ -600,7 +646,7 @@ string AstVar::scType() const {
return "uint32_t";
}
} else {
return "vluint64_t";
return "uint64_t";
}
}
@@ -614,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
@@ -658,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);
}
@@ -672,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 + ">";
@@ -1019,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) {
@@ -1029,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;
}
@@ -1071,8 +1117,8 @@ static bool sameInit(const AstInitArray* ap, const AstInitArray* bp) {
if (!aDTypep->rangep()->sameTree(bDTypep->rangep())) return false;
// Compare initializer arrays by value. Note this is only called when they hash the same,
// so they likely run at most once per call to 'AstConstPool::findTable'.
const vluint64_t size = aDTypep->elementsConst();
for (vluint64_t n = 0; n < size; ++n) {
const uint64_t size = aDTypep->elementsConst();
for (uint64_t n = 0; n < size; ++n) {
const AstNode* const valAp = ap->getIndexDefaultedValuep(n);
const AstNode* const valBp = bp->getIndexDefaultedValuep(n);
if (!valAp->sameTree(valBp)) return false;
@@ -1177,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 {
@@ -1194,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 {
@@ -1220,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());
@@ -1293,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());
@@ -1308,7 +1362,8 @@ void AstClassPackage::cloneRelink() {
}
void AstClass::insertCache(AstNode* nodep) {
const bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto()));
|| (VN_IS(nodep, NodeFTask) && !VN_AS(nodep, NodeFTask)->isExternProto())
|| VN_IS(nodep, CFunc));
if (doit) {
if (m_members.find(nodep->name()) != m_members.end()) {
nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ());
@@ -1319,7 +1374,15 @@ void AstClass::insertCache(AstNode* nodep) {
}
void AstClass::repairCache() {
clearCache();
for (AstNode* itemp = membersp(); itemp; itemp = itemp->nextp()) { insertCache(itemp); }
for (auto* itemp = membersp(); itemp; itemp = itemp->nextp()) {
if (const auto* const scopep = VN_CAST(itemp, Scope)) {
for (auto* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
insertCache(blockp);
}
} else {
insertCache(itemp);
}
}
}
bool AstClass::isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp) {
// TAIL RECURSIVE
@@ -1333,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.'
@@ -1342,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 << " -> ";
@@ -1355,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]";
@@ -1372,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();
@@ -1391,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;
@@ -1403,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 << " -> ";
@@ -1412,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 << " -> ";
@@ -1422,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 {
@@ -1434,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()) {
@@ -1455,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();
@@ -1465,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()) {
@@ -1475,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();
@@ -1520,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]";
}
@@ -1590,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();
@@ -1607,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); }
@@ -1637,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);
}
@@ -1674,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";
@@ -1725,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]";
@@ -1757,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]";
}
@@ -1769,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() << "]";
@@ -1783,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 << " [::]";
@@ -1796,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());
@@ -1824,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]";
@@ -1850,9 +2126,13 @@ 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 {
this->AstNodeStmt::dump(str);
if (code()) str << " [code=" << code() << "]";
}
void AstTraceInc::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
str << " -> ";
@@ -1895,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() << "]";
-9399
View File
File diff suppressed because it is too large Load Diff
+24 -26
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) {
@@ -106,7 +104,7 @@ public:
}
// Get a reference to the user data
T_Data& operator()(const T_Node* nodep) {
T_Data& operator()(const T_Node* nodep) const {
T_Data* const userp = getUserp(nodep);
UASSERT_OBJ(userp, nodep, "Missing User data on const AstNode");
return *userp;
@@ -117,7 +115,7 @@ public:
};
// User pointer allocator classes. T_Node is the type of node the allocator should be applied to
// and is simply there for a bit of extra type safety. T_Data is the type of the data structure
// and is there for a bit of extra type safety. T_Data is the type of the data structure
// managed by the allocator.
template <class T_Node, class T_Data>
class AstUser1Allocator final : public AstUserAllocatorBase<T_Node, T_Data, 1> {};
+56 -53
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,13 +72,39 @@ 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;
return a + "__DOT__" + b;
}
void dotNames(const AstNodeBlock* const nodep, const char* const blockName) {
UINFO(8, "nname " << m_namedScope << endl);
if (nodep->name() != "") { // Else unneeded unnamed block
// Create data for dotted variable resolution
string dottedname = nodep->name() + "__DOT__"; // So always found
string::size_type pos;
while ((pos = dottedname.find("__DOT__")) != string::npos) {
const string ident = dottedname.substr(0, pos);
dottedname = dottedname.substr(pos + std::strlen("__DOT__"));
if (nodep->name() != "") {
m_displayScope = dot(m_displayScope, ident);
m_namedScope = dot(m_namedScope, ident);
}
m_unnamedScope = dot(m_unnamedScope, ident);
// Create CellInline for dotted var resolution
if (!m_ftaskp) {
AstCellInline* const inlinep = new AstCellInline(
nodep->fileline(), m_unnamedScope, blockName, m_modp->timeunit());
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
}
}
}
// Remap var names and replace lower Begins
iterateAndNextNull(nodep->stmtsp());
}
void liftNode(AstNode* nodep) {
nodep->unlinkFrBack();
if (m_ftaskp) {
@@ -87,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 != "") {
@@ -134,48 +163,22 @@ 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);
VL_RESTORER(m_namedScope);
VL_RESTORER(m_unnamedScope);
{
UINFO(8, "nname " << m_namedScope << endl);
if (nodep->name() != "") { // Else unneeded unnamed block
// Create data for dotted variable resolution
string dottedname = nodep->name() + "__DOT__"; // So always found
string::size_type pos;
while ((pos = dottedname.find("__DOT__")) != string::npos) {
const string ident = dottedname.substr(0, pos);
dottedname = dottedname.substr(pos + strlen("__DOT__"));
if (nodep->name() != "") {
m_displayScope = dot(m_displayScope, ident);
m_namedScope = dot(m_namedScope, ident);
}
m_unnamedScope = dot(m_unnamedScope, ident);
// Create CellInline for dotted var resolution
if (!m_ftaskp) {
AstCellInline* const inlinep = new AstCellInline(
nodep->fileline(), m_unnamedScope, "__BEGIN__", m_modp->timeunit());
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
}
}
}
dotNames(nodep, "__BEGIN__");
// Remap var names and replace lower Begins
iterateAndNextNull(nodep->stmtsp());
UASSERT_OBJ(!nodep->genforp(), nodep, "GENFORs should have been expanded earlier");
// Cleanup
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);
@@ -185,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()));
@@ -194,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()));
@@ -203,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);
@@ -212,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
@@ -231,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
@@ -258,7 +261,7 @@ private:
}
iterateChildren(nodep);
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
@@ -266,7 +269,7 @@ public:
: m_statep{statep} {
iterate(nodep);
}
virtual ~BeginVisitor() override = default;
~BeginVisitor() override = default;
};
//######################################################################
@@ -279,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);
@@ -302,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;
};
//######################################################################
@@ -320,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);
}
}
}

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