Compare commits

...
113 Commits
Author SHA1 Message Date
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
747 changed files with 29554 additions and 26544 deletions
+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:
+63 -9
View File
@@ -8,6 +8,60 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
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]
* 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 +74,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 +139,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 +163,7 @@ Verilator 4.214 2021-10-17
**Major:**
* Add profile-guided optmization of mtasks (#3150).
* Add profile-guided optimization of mtasks (#3150).
**Minor:**
@@ -175,7 +229,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]
@@ -214,10 +268,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 +309,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]
@@ -402,7 +456,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 +482,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.
+12 -11
View File
@@ -21,7 +21,7 @@ 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
@@ -57,23 +57,24 @@ 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.
are then compiled by a C++ compiler (gcc/clang/MSVC++). Executing the
resulting executable performs the design simulation. Verilator also
supports linking Verilated generated libraries, optionally encrypted, into
other simulators.
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 Incisive, ModelSim/Questa, VCS or another commercial
Verilog simulator, or if you are looking for a behavioral Verilog simulator
e.g. for a quick class project (we recommend `Icarus Verilog`_ for this.)
However, if you are looking for a path to migrate SystemVerilog to C++ or
SystemC, or your team is comfortable writing just a touch of C++ code,
Verilator is the tool for you.
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
+9 -8
View File
@@ -319,6 +319,7 @@ 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
--gdb Run Verilator under GDB interactively
--gdbbt Run Verilator under GDB for backtrace
@@ -344,11 +345,10 @@ detailed descriptions of these arguments.
--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
--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-pins64 Don't use uint64_t's for 33-64 bit sigs
--no-skip-identical Disable skipping identical output
+notimingchecks Ignored
-O0 Disable optimizations
@@ -370,7 +370,8 @@ detailed descriptions of these arguments.
--prefix <topname> Name of top level class
--prof-c Compile C++ code with profiling
--prof-cfuncs Name functions for profiling
--prof-threads Enable generating gantt chart data for threads
--prof-exec Enable generating execution profile for gantt chart
--prof-pgo Enable generating profiling data for PGO
--protect-key <key> Key for symbol protection
--protect-ids Hash identifier names for obscurity
--protect-lib <name> Create a DPI protected library
@@ -404,7 +405,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
@@ -445,10 +446,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
+164 -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,12 @@ 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 +183,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 +394,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 +496,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()
+7 -3
View File
@@ -54,8 +54,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 +89,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
+10 -6
View File
@@ -7,14 +7,14 @@
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.220 2022-03-12],
AC_INIT([Verilator],[4.224 2022-06-19],
[https://verilator.org],
[verilator],[https://verilator.org])
# When releasing, also update header of Changes file
# and commit using "devel release" or "Version bump" message
# Then 'make maintainer-dist'
AC_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
*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
+6
View File
@@ -7,6 +7,7 @@ Adrien Le Masle
Ahmed El-Mahmoudy
Alex Chadwick
Àlex Torregrosa
Aliaksei Chapyzhenka
Ameya Vikram Singh
Andreas Kuster
Chris Randall
@@ -34,6 +35,7 @@ Guokai Chen
Harald Heckmann
Howard Su
Huang Rui
Huanghuang Zhou
HungMingWu
HyungKi Jeong
Iru Cai
@@ -54,6 +56,7 @@ Josh Redford
Julie Schwartz
Julien Margetts
Kaleb Barrett
Kamil Rakoczy
Kanad Kanhere
Keith Colbert
Kevin Kiningham
@@ -69,6 +72,7 @@ Markus Krause
Marlon James
Marshal Qiao
Martin Schmidt
Martin Stadler
Matthew Ballance
Michael Killough
Michaël Lefebvre
@@ -111,6 +115,8 @@ Vassilis Papaefstathiou
Veripool API Bot
Victor Besyakov
Wilson Snyder
Xi Zhang
Yoda Lee
Yossi Nivin
Yuri Victorovich
Yutetsu TAKATSUKASA
+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)
+11 -6
View File
@@ -96,7 +96,7 @@ model. Here is a simple example:
Vtop *top; // Instantiation of model
vluint64_t main_time = 0; // Current simulation time
uint64_t main_time = 0; // Current simulation time
// This is a 64-bit integer to reduce wrap over issues and
// allow modulus. This is in units of the timeprecision
// used in Verilog (or from --timescale-override)
@@ -110,6 +110,9 @@ model. Here is a simple example:
Verilated::commandArgs(argc, argv); // Remember args
top = new Vtop; // Create model
// Do not instead make Vtop as a file-scope static
// variable, as the "C++ static initialization order fiasco"
// may cause a crash
top->reset_l = 0; // Set some inputs
@@ -150,7 +153,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 +203,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 +260,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 +432,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() {
@@ -506,7 +511,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.
+21 -9
View File
@@ -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`.
+102 -31
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.
@@ -431,6 +431,54 @@ 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:: -G<name>=<value>
Overwrites the given parameter of the toplevel module. The value is
@@ -648,13 +696,6 @@ 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
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:: --mod-prefix <topname>
Specifies the name to prepend to all lower level classes. Defaults to
@@ -703,9 +744,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>
@@ -768,7 +809,7 @@ Summary:
.. 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
@@ -845,10 +886,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`.
Deprecated. Same as --prof-exec and --prof-pgo together.
.. option:: --protect-key <key>
@@ -1032,7 +1082,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 +1161,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 +1216,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 +1690,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 +1719,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
+5 -5
View File
@@ -120,7 +120,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 +155,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
+13 -5
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:
@@ -118,7 +125,8 @@ 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 yapf3
sudo pip3 install sphinx sphinx_rtd_theme sphinxcontrib-spelling breathe
cpan install Pod::Perldoc
cpan install Parallel::Forker
+5 -5
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.
+20 -20
View File
@@ -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
+9 -9
View File
@@ -268,13 +268,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 +301,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.
@@ -817,7 +817,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
@@ -1186,7 +1186,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)
+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
@@ -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
+4 -4
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
+135 -90
View File
@@ -55,6 +55,7 @@
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <cstdlib>
#include <sstream>
#include <sys/stat.h> // mkdir
#include <list>
@@ -73,10 +74,10 @@ 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 +244,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 +296,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 +309,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 +325,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 +410,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 +424,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 +434,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 +454,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 +466,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 +483,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 +614,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);
@@ -687,7 +688,7 @@ 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;
@@ -711,7 +712,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 +879,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])) {
@@ -1183,7 +1184,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 +1198,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);
@@ -1642,7 +1643,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 +1678,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 +1768,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 +1776,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 +1823,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];
@@ -2026,9 +2027,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);
@@ -2234,8 +2235,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 +2280,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,7 +2289,17 @@ 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
@@ -2347,21 +2358,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};
@@ -2506,13 +2517,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 +2534,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 +2559,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 +2576,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 +2608,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 +2667,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 +2675,7 @@ VerilatedSyms::VerilatedSyms(VerilatedContext* contextp)
}
VerilatedSyms::~VerilatedSyms() {
VerilatedContext::checkMagic(_vm_contextp__);
#ifdef VL_THREADED
delete __Vm_evalMsgQp;
#endif
@@ -2821,8 +2866,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 +2890,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 +2911,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.
+45 -37
View File
@@ -101,11 +101,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 +115,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 +143,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 +226,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.
@@ -320,10 +320,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 +344,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;
@@ -374,6 +374,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);
@@ -468,16 +472,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;
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
@@ -518,13 +522,13 @@ public: // But for internal use only
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 +539,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 +567,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,21 +580,21 @@ 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; }
int8_t timeunit() const { return m_timeunit; }
inline 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; }
@@ -636,9 +644,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
@@ -760,11 +768,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(); }
@@ -850,8 +858,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; }
+5
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
@@ -193,6 +194,10 @@ VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
# 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
+8 -12
View File
@@ -59,7 +59,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;
};
@@ -76,7 +76,7 @@ private:
public:
// METHODS
// cppcheck-suppress truncLongCastReturn
virtual vluint64_t count() const override { return *m_countp; }
virtual uint64_t count() const override { return *m_countp; }
virtual void zero() const override { *m_countp = 0; }
// CONSTRUCTORS
// cppcheck-suppress noExplicitConstructor
@@ -103,7 +103,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
@@ -123,10 +123,6 @@ public:
protected:
friend class VerilatedCovContext;
virtual ~VerilatedCovImp() override { clearGuts(); }
static VerilatedCovImp& imp() VL_MT_SAFE {
static VerilatedCovImp s_singleton;
return s_singleton;
}
private:
// PRIVATE METHODS
@@ -372,7 +368,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 +435,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);
+5 -5
View File
@@ -72,7 +72,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;
@@ -126,8 +126,8 @@ public: // But Internal use only
// 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 +157,8 @@ protected:
friend class VerilatedCovImp;
// CONSTRUCTORS
// Internal: Only made as part of VerilatedCovImp
VerilatedCovContext() {}
virtual ~VerilatedCovContext() {}
VerilatedCovContext() = default;
virtual ~VerilatedCovContext() = default;
// METHODS
// Internal: access to implementation class
+1 -1
View File
@@ -417,7 +417,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);
+49 -23
View File
@@ -83,9 +83,11 @@ 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
@@ -111,7 +113,7 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
m_curScope.clear();
VerilatedTrace<VerilatedFst>::traceInit();
Super::traceInit();
// Clear the scope stack
auto it = m_curScope.begin();
@@ -133,23 +135,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 +159,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 +226,63 @@ 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
VerilatedFstBuffer* VerilatedFst::getTraceBuffer() { return new VerilatedFstBuffer{*this}; }
void VerilatedFst::commitTraceBuffer(VerilatedFstBuffer* bufp) {
#ifdef VL_TRACE_OFFLOAD
if (bufp->m_offloadBufferWritep) {
m_offloadBufferWritep = bufp->m_offloadBufferWritep;
return; // Buffer will be deleted by the offload thread
}
#endif
delete bufp;
}
//=============================================================================
// VerilatedFstBuffer implementation
VerilatedFstBuffer::VerilatedFstBuffer(VerilatedFst& owner)
: VerilatedTraceBuffer<VerilatedFst, VerilatedFstBuffer>{owner} {}
//=============================================================================
// 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 +290,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 +298,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 +306,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 +314,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 +330,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);
}
+69 -38
View File
@@ -31,21 +31,25 @@
#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 Buffer; // Give the buffer access to the private bits
//=========================================================================
// FST specific internals
void* m_fst;
std::map<vluint32_t, fstHandle> m_code2symbol;
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
@@ -53,38 +57,33 @@ private:
// 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
virtual void emitTimeChange(uint64_t timeui) override;
// Hooks called from VerilatedTrace
virtual bool preFullDump() override { return isOpen(); }
virtual 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
virtual VerilatedFstBuffer* getTraceBuffer() override;
virtual void commitTraceBuffer(VerilatedFstBuffer*) 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 +96,65 @@ 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 final : public VerilatedTraceBuffer<VerilatedFst, VerilatedFstBuffer> {
// Give the trace file access to the private bits
friend VerilatedFst;
friend VerilatedFst::Super;
// 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;
public:
// CONSTRUCTOR
explicit VerilatedFstBuffer(VerilatedFst& owner);
~VerilatedFstBuffer() = default;
//=========================================================================
// Implementation of VerilatedTraceBuffer interface
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
// called from only one place (the full* methods), so always inline them.
VL_ATTR_ALWINLINE inline void emitBit(uint32_t code, CData newval);
VL_ATTR_ALWINLINE inline void emitCData(uint32_t code, CData newval, int bits);
VL_ATTR_ALWINLINE inline void emitSData(uint32_t code, SData newval, int bits);
VL_ATTR_ALWINLINE inline void emitIData(uint32_t code, IData newval, int bits);
VL_ATTR_ALWINLINE inline void emitQData(uint32_t code, QData newval, int bits);
VL_ATTR_ALWINLINE inline void emitWData(uint32_t code, const WData* newvalp, int bits);
VL_ATTR_ALWINLINE inline void emitDouble(uint32_t code, double newval);
};
//=============================================================================
// VerilatedFstC
/// Create a FST dump file in C standalone (no SystemC) simulations.
@@ -155,12 +186,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
+47 -58
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;
@@ -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)
@@ -290,28 +279,28 @@ extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE;
#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 {
inline 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 +325,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
@@ -882,46 +871,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 +1004,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 +1056,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 +1410,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 +1430,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 +1929,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 +1950,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) {
+13 -8
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
@@ -244,8 +245,8 @@ 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 {
@@ -394,7 +395,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);
};
@@ -484,7 +489,7 @@ public:
public: // 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) {
+191
View File
@@ -0,0 +1,191 @@
// -*- 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() {
// Setup profiling on main thread
setupThread(0);
}
void VlExecutionProfiler::configure(const VerilatedContext& context) {
if (VL_UNLIKELY(m_enabled)) {
--m_windowCount;
if (VL_UNLIKELY(m_windowCount == context.profExecWindow())) {
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start collection\n"););
clear(); // Clear the profile after the cache warm-up cycles.
m_tickBegin = VL_CPU_TICK();
} else if (VL_UNLIKELY(m_windowCount == 0)) {
const uint64_t tickEnd = VL_CPU_TICK();
VL_DEBUG_IF(VL_DBG_MSGF("+ profile end\n"););
const std::string& fileName = context.profExecFilename();
dump(fileName.c_str(), tickEnd);
m_enabled = false;
}
return;
}
const uint64_t startReq = context.profExecStart() + 1; // + 1, so we can start at time 0
if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= context.profExecStart())) {
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start warmup\n"););
VL_DEBUG_IF(assert(m_windowCount == 0););
m_enabled = true;
m_windowCount = context.profExecWindow() * 2;
m_lastStartReq = startReq;
}
}
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
{
const VerilatedLockGuard lock{m_mutex};
bool exists = !m_traceps.emplace(threadId, &t_trace).second;
assert(!exists);
}
}
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);
}
+184 -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,204 @@
#define VERILATOR_VERILATED_PROFILER_H_
#include "verilatedos.h"
#include "verilated.h" // for VerilatedMutex and clang annotations
#include <deque>
#ifndef VL_PROFILER
#error "verilated_profiler.h/cpp expects VL_PROFILER (from --prof-{exec, pgo}"
#endif
#include "verilated.h"
#include <array>
#include <atomic>
#include <cassert>
#include <string>
#include <type_traits>
#include <vector>
class VlExecutionProfiler;
//=============================================================================
// Macros to simplify generated code
#define VL_EXEC_TRACE_ADD_RECORD(vlSymsp) \
if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfiler.enabled())) \
(vlSymsp)->__Vm_executionProfiler.addRecord()
//=============================================================================
// Return high-precision counter for profiling, or 0x0 if not available
VL_ATTR_ALWINLINE
inline 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 {
// 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
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
VlExecutionProfiler();
// METHODS
// Is profiling enabled
inline bool enabled() const { return m_enabled; }
// Append a trace record to the trace buffer of the current thread
inline VlExecutionRecord& addRecord() {
t_trace.emplace_back();
return t_trace.back();
}
// Configure profiler (called in beginning of 'eval')
void configure(const VerilatedContext&);
// 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);
};
//=============================================================================
// 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 +228,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 +245,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);
+5 -5
View File
@@ -63,8 +63,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 +192,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 +219,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
+25 -25
View File
@@ -40,8 +40,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 +58,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 +77,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 = (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 +111,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 +131,7 @@ protected:
public:
/// Construct
VerilatedDeserialize() {
m_bufp = new vluint8_t[bufferSize()];
m_bufp = new uint8_t[bufferSize()];
m_cp = m_bufp;
}
/// Destruct
@@ -150,12 +150,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 +165,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));
}
@@ -236,28 +236,28 @@ public:
//=============================================================================
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,12 +279,12 @@ 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);
@@ -295,7 +295,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 +307,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 -3
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
@@ -142,7 +142,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); }
+24 -127
View File
@@ -24,8 +24,11 @@
#include "verilatedos.h"
#include "verilated_threads.h"
#ifdef VL_PROFILER
#include "verilated_profiler.h"
#endif
#include <cstdio>
#include <fstream>
#include <memory>
#include <string>
@@ -34,14 +37,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,12 +51,11 @@ VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
//=============================================================================
// VlWorkerThread
VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling)
VlWorkerThread::VlWorkerThread(uint32_t threadId, VerilatedContext* contextp,
VlExecutionProfiler* profilerp)
: 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_cthread{startWorker, this, threadId, profilerp}
, m_contextp{contextp} {}
VlWorkerThread::~VlWorkerThread() {
@@ -66,8 +66,6 @@ VlWorkerThread::~VlWorkerThread() {
}
void VlWorkerThread::workerLoop() {
if (VL_UNLIKELY(m_profiling)) m_poolp->setupProfilingClientThread();
ExecRec work;
work.m_fnp = nullptr;
@@ -82,143 +80,42 @@ void VlWorkerThread::workerLoop() {
work.m_fnp = nullptr;
}
}
if (VL_UNLIKELY(m_profiling)) m_poolp->tearDownProfilingClientThread();
}
void VlWorkerThread::startWorker(VlWorkerThread* workerp) {
void VlWorkerThread::startWorker(VlWorkerThread* workerp, uint32_t threadId,
VlExecutionProfiler* profilerp) {
Verilated::threadContextp(workerp->m_contextp);
#ifdef VL_PROFILER
// Note: setupThread is not defined without VL_PROFILER, hence the #ifdef. Still, we might
// not be profiling execution (e.g.: PGO only), so profilerp might still be nullptr.
if (profilerp) profilerp->setupThread(threadId);
#endif
workerp->workerLoop();
}
//=============================================================================
// VlThreadPool
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads, bool profiling)
: m_profiling{profiling} {
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads,
VlExecutionProfiler* profiler) {
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
++nThreads;
const unsigned cpus = std::thread::hardware_concurrency();
if (cpus < nThreads + 1) {
if (cpus < nThreads) {
static int warnedOnce = 0;
if (!warnedOnce++) {
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
" --threads %d; may run slow.\n",
cpus, nThreads + 1);
cpus, nThreads);
}
}
// Create'em
for (int i = 0; i < nThreads; ++i) {
m_workers.push_back(new VlWorkerThread{this, contextp, profiling});
// Create worker threads
for (uint32_t threadId = 1; threadId < nThreads; ++threadId) {
m_workers.push_back(new VlWorkerThread{threadId, contextp, profiler});
}
// 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() {
// 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);
}
+17 -99
View File
@@ -35,8 +35,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
@@ -58,7 +60,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 +78,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 +87,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();
@@ -99,19 +101,19 @@ public:
// false while it's still waiting on more dependencies.
inline 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;
const uint32_t target = evenCycle ? m_upstreamDepCount : 0;
return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
}
inline void waitUntilUpstreamDone(bool evenCycle) const {
@@ -127,64 +129,7 @@ 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 VlExecutionProfiler;
class VlThreadPool;
class VlWorkerThread final {
@@ -205,7 +150,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,9 +162,6 @@ 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
@@ -228,7 +170,8 @@ private:
public:
// CONSTRUCTORS
explicit VlWorkerThread(VlThreadPool* poolp, VerilatedContext* contextp, bool profiling);
explicit VlWorkerThread(uint32_t threadId, VerilatedContext* contextp,
VlExecutionProfiler* profilerp);
~VlWorkerThread();
// METHODS
@@ -265,34 +208,20 @@ public:
if (notify) m_cv.notify_one();
}
void workerLoop();
static void startWorker(VlWorkerThread* workerp);
static void startWorker(VlWorkerThread* workerp, uint32_t threadId,
VlExecutionProfiler* profilerp);
};
class VlThreadPool final {
// TYPES
using ProfileTrace = std::vector<VlProfileRec>;
// 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(VerilatedContext* contextp, int nThreads, VlExecutionProfiler* profilerp);
~VlThreadPool();
// METHODS
@@ -302,17 +231,6 @@ public:
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);
+255 -137
View File
@@ -24,30 +24,49 @@
// clang-format off
// In FST mode, VL_TRACE_THREADED enables offloading, but only if we also have
// the FST writer thread. This means with --trace-threads 1, we get the FST
// writer thread only, and with --trace-threads 2 we get offloading as well
#if defined(VL_TRACE_FST_WRITER_THREAD) && defined(VL_TRACE_THREADED)
# define VL_TRACE_OFFLOAD
#endif
// VCD tracing can happen fully in parallel
#if defined(VM_TRACE_VCD) && VM_TRACE_VCD && defined(VL_TRACE_THREADED)
# define VL_TRACE_PARALLEL
#endif
#if defined(VL_TRACE_PARALLEL) && defined(VL_TRACE_OFFLOAD)
# error "Cannot have VL_TRACE_PARALLEL and VL_TRACE_OFFLOAD together"
#endif
#include "verilated.h"
#include "verilated_trace_defs.h"
#include <bitset>
#include <condition_variable>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#ifdef VL_TRACE_THREADED
# include <condition_variable>
#ifdef VL_TRACE_OFFLOAD
# include <deque>
# include <thread>
#endif
// clang-format on
#ifdef VL_TRACE_THREADED
class VlThreadPool;
template <class T_Trace, class T_Buffer> class VerilatedTraceBuffer;
#ifdef VL_TRACE_OFFLOAD
//=============================================================================
// Threaded tracing
// Offloaded tracing
// A simple synchronized first in first out queue
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 +107,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,7 +121,8 @@ 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
};
@@ -112,16 +132,22 @@ public:
//=============================================================================
// 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 subclass of VerilatedTraceBuffer.
template <class T_Trace, class T_Buffer> class VerilatedTrace VL_NOT_FINAL {
// Give the buffer (both base and derived) access to the private bits
friend VerilatedTraceBuffer<T_Trace, T_Buffer>;
friend T_Buffer;
public:
using Buffer = 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 cleanupCb_t = void (*)(void*, T_Trace*); // Type of cleanup callbacks
private:
struct CallbackRecord {
@@ -129,9 +155,10 @@ private:
// (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;
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 +167,103 @@ private:
CallbackRecord(dumpCb_t cb, void* userp)
: m_dumpCb{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
#ifdef VL_TRACE_PARALLEL
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
using ParallelCallbackMap = std::unordered_map<VlThreadPool*, std::vector<CallbackRecord>>;
protected:
uint32_t* m_sigs_oldvalp = nullptr; // Previous value store
EData* m_sigs_enabledp = nullptr; // Bit vector of enabled codes (nullptr = all on)
private:
uint64_t m_timeLastDump = 0; // Last time we did a dump
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
std::vector<CallbackRecord> m_initCbs; // Routines to initialize traciong
std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump
std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump
std::vector<CallbackRecord> m_initCbs; // Routines to initialize tracing
ParallelCallbackMap m_fullCbs; // Routines to perform full dump
ParallelCallbackMap m_chgCbs; // Routines to perform incremental dump
std::vector<CallbackRecord> m_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
std::vector<VlThreadPool*> m_threadPoolps; // All thread pools, in insertion order
bool m_fullDump = true; // Whether a full dump is required on the next call to 'dump'
uint32_t m_nextCode = 0; // Next code number to assign
uint32_t m_numSignals = 0; // Number of distinct signals
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)
void addThreadPool(VlThreadPool* threadPoolp) VL_MT_SAFE_EXCLUDES(m_mutex);
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 runParallelCallbacks(const ParallelCallbackMap& cbMap);
// 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_TRACE_OFFLOAD
// 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 +273,13 @@ 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; }
uint64_t timeLastDump() { return m_timeLastDump; }
double timeRes() const { return m_timeRes; }
double timeUnit() const { return m_timeUnit; }
@@ -223,7 +288,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; }
@@ -239,13 +304,17 @@ protected:
// 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;
public:
//=========================================================================
// External interface to client code
@@ -264,21 +333,57 @@ 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 addInitCb(initCb_t cb, void* userp) VL_MT_SAFE;
void addFullCb(dumpCb_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 addFullCb(dumpCb_t cb, void* userp, VlThreadPool* = nullptr) VL_MT_SAFE;
void addChgCb(dumpCb_t cb, void* userp, VlThreadPool* = nullptr) 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_Trace is the format specific subclass of VerilatedTrace.
// T_Buffer is the format specific subclass of VerilatedTraceBuffer.
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
template <class T_Trace, class T_Buffer> class VerilatedTraceBuffer VL_NOT_FINAL {
friend T_Trace; // Give the trace file access to the private bits
protected:
T_Trace& m_owner; // The VerilatedTrace subclass that owns this buffer
// Previous value store
uint32_t* const m_sigs_oldvalp = m_owner.m_sigs_oldvalp;
// Bit vector of enabled codes (nullptr = all on)
EData* const m_sigs_enabledp = m_owner.m_sigs_enabledp;
#ifdef VL_TRACE_OFFLOAD
// Write pointer into current buffer
uint32_t* m_offloadBufferWritep = m_owner.m_offloadBufferWritep;
// End of offload buffer
uint32_t* const m_offloadBufferEndp = m_owner.m_offloadBufferEndp;
#endif
// Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->'
// to access duck-typed functions to avoid a virtual function call.
inline T_Buffer* self() { return static_cast<T_Buffer*>(this); }
explicit VerilatedTraceBuffer(T_Trace& owner);
virtual ~VerilatedTraceBuffer() = default;
public:
//=========================================================================
// Hot path internal interface to Verilator generated code
@@ -288,108 +393,112 @@ 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 inline 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););
#ifdef VL_TRACE_OFFLOAD
// Offloaded tracing. Just dump everything in the offload buffer
inline void chgBit(uint32_t code, CData newval) {
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep += 2;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgCData(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 chgCData(uint32_t code, CData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_CDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgSData(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 chgSData(uint32_t code, SData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_SDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgIData(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 chgIData(uint32_t code, IData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_IDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep[2] = newval;
m_offloadBufferWritep += 3;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgQData(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 chgQData(uint32_t code, QData newval, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_QDATA;
m_offloadBufferWritep[1] = code;
*reinterpret_cast<QData*>(m_offloadBufferWritep + 2) = newval;
m_offloadBufferWritep += 4;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgWData(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 chgWData(uint32_t code, const WData* newvalp, int bits) {
m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
m_offloadBufferWritep[1] = code;
m_offloadBufferWritep += 2;
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_offloadBufferWritep++ = newvalp[i]; }
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
inline void chgDouble(vluint32_t code, double newval) {
m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_DOUBLE;
m_traceBufferWritep[1] = code;
inline void chgDouble(uint32_t code, double newval) {
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_DOUBLE;
m_offloadBufferWritep[1] = code;
// cppcheck-suppress invalidPointerCast
*reinterpret_cast<double*>(m_traceBufferWritep + 2) = newval;
m_traceBufferWritep += 4;
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
*reinterpret_cast<double*>(m_offloadBufferWritep + 2) = newval;
m_offloadBufferWritep += 4;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
#define CHG(name) chg##name##Impl
#else
#define CHG(name) chg##name
#define chgBit chgBitImpl
#define chgCData chgCDataImpl
#define chgSData chgSDataImpl
#define chgIData chgIDataImpl
#define chgQData chgQDataImpl
#define chgWData chgWDataImpl
#define chgDouble chgDoubleImpl
#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 inline 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 inline 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 inline 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 inline 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 inline 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 inline 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 +506,20 @@ public:
}
}
}
inline void CHG(Double)(vluint32_t* oldp, double newval) {
VL_ATTR_ALWINLINE inline 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_TRACE_OFFLOAD
#undef chgBit
#undef chgCData
#undef chgSData
#undef chgIData
#undef chgQData
#undef chgWData
#undef chgDouble
#endif
};
#endif // guard
@@ -10,32 +10,32 @@
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//=============================================================================
///
/// \file
/// \brief Verilated common-format tracing implementation code
///
/// This file must be compiled and linked against all Verilated objects
/// that use --trace.
///
/// Use "verilator --trace" to add this to the Makefile for the linker.
///
//
// Verilated tracing implementation code template common to all formats.
// This file is included by the format specific implementations and
// should not be used otherwise.
//
//=============================================================================
// clang-format off
#ifndef VL_CPPCHECK
#ifndef VL_DERIVED_T
#if !defined(VL_SUB_T) || !defined(VL_BUF_T)
# error "This file should be included in trace format implementations"
#endif
#include "verilated_intrinsics.h"
#include "verilated_trace.h"
#ifdef VL_TRACE_PARALLEL
# include "verilated_threads.h"
# include <list>
#endif
#if 0
# include <iostream>
# define VL_TRACE_THREAD_DEBUG(msg) std::cout << "TRACE THREAD: " << msg << std::endl
# define VL_TRACE_OFFLOAD_DEBUG(msg) std::cout << "TRACE OFFLOAD THREAD: " << msg << std::endl
#else
# define VL_TRACE_THREAD_DEBUG(msg)
# define VL_TRACE_OFFLOAD_DEBUG(msg)
#endif
// clang-format on
@@ -78,37 +78,37 @@ static std::string doubleToTimescale(double value) {
return valuestr; // Gets converted to string, so no ref to stack
}
#ifdef VL_TRACE_THREADED
#ifdef VL_TRACE_OFFLOAD
//=========================================================================
// Buffer management
template <> vluint32_t* VerilatedTrace<VL_DERIVED_T>::getTraceBuffer() {
vluint32_t* bufferp;
// Some jitter is expected, so some number of alternative trace buffers are
template <> uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
uint32_t* bufferp;
// Some jitter is expected, so some number of alternative offlaod buffers are
// required, but don't allocate more than 8 buffers.
if (m_numTraceBuffers < 8) {
if (m_numOffloadBuffers < 8) {
// Allocate a new buffer if none is available
if (!m_buffersFromWorker.tryGet(bufferp)) {
++m_numTraceBuffers;
if (!m_offloadBuffersFromWorker.tryGet(bufferp)) {
++m_numOffloadBuffers;
// Note: over allocate a bit so pointer comparison is well defined
// if we overflow only by a small amount
bufferp = new vluint32_t[m_traceBufferSize + 16];
bufferp = new uint32_t[m_offloadBufferSize + 16];
}
} else {
// Block until a buffer becomes available
bufferp = m_buffersFromWorker.get();
bufferp = m_offloadBuffersFromWorker.get();
}
return bufferp;
}
template <> void VerilatedTrace<VL_DERIVED_T>::waitForBuffer(const vluint32_t* buffp) {
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const uint32_t* buffp) {
// Slow path code only called on flush/shutdown, so use a simple algorithm.
// Collect buffers from worker and stash them until we get the one we want.
std::deque<vluint32_t*> stash;
do { stash.push_back(m_buffersFromWorker.get()); } while (stash.back() != buffp);
std::deque<uint32_t*> stash;
do { stash.push_back(m_offloadBuffersFromWorker.get()); } while (stash.back() != buffp);
// Now put them back in the queue, in the original order.
while (!stash.empty()) {
m_buffersFromWorker.put_front(stash.back());
m_offloadBuffersFromWorker.put_front(stash.back());
stash.pop_back();
}
}
@@ -116,22 +116,24 @@ template <> void VerilatedTrace<VL_DERIVED_T>::waitForBuffer(const vluint32_t* b
//=========================================================================
// Worker thread
template <> void VerilatedTrace<VL_DERIVED_T>::workerThreadMain() {
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
bool shutdown = false;
do {
vluint32_t* const bufferp = m_buffersToWorker.get();
uint32_t* const bufferp = m_offloadBuffersToWorker.get();
VL_TRACE_THREAD_DEBUG("");
VL_TRACE_THREAD_DEBUG("Got buffer: " << bufferp);
VL_TRACE_OFFLOAD_DEBUG("");
VL_TRACE_OFFLOAD_DEBUG("Got buffer: " << bufferp);
const vluint32_t* readp = bufferp;
const uint32_t* readp = bufferp;
std::unique_ptr<VL_BUF_T> traceBufp; // We own the passed tracebuffer
while (true) {
const vluint32_t cmd = readp[0];
const vluint32_t top = cmd >> 4;
const uint32_t cmd = readp[0];
const uint32_t top = cmd >> 4;
// Always set this up, as it is almost always needed
vluint32_t* const oldp = m_sigs_oldvalp + readp[1];
uint32_t* const oldp = m_sigs_oldvalp + readp[1];
// Note this increment needs to be undone on commands which do not
// actually contain a code, but those are the rare cases.
readp += 2;
@@ -139,71 +141,79 @@ template <> void VerilatedTrace<VL_DERIVED_T>::workerThreadMain() {
switch (cmd & 0xF) {
//===
// CHG_* commands
case VerilatedTraceCommand::CHG_BIT_0:
VL_TRACE_THREAD_DEBUG("Command CHG_BIT_0 " << top);
chgBitImpl(oldp, 0);
case VerilatedTraceOffloadCommand::CHG_BIT_0:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_0 " << top);
traceBufp->chgBitImpl(oldp, 0);
continue;
case VerilatedTraceCommand::CHG_BIT_1:
VL_TRACE_THREAD_DEBUG("Command CHG_BIT_1 " << top);
chgBitImpl(oldp, 1);
case VerilatedTraceOffloadCommand::CHG_BIT_1:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_1 " << top);
traceBufp->chgBitImpl(oldp, 1);
continue;
case VerilatedTraceCommand::CHG_CDATA:
VL_TRACE_THREAD_DEBUG("Command CHG_CDATA " << top);
case VerilatedTraceOffloadCommand::CHG_CDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_CDATA " << top);
// Bits stored in bottom byte of command
chgCDataImpl(oldp, *readp, top);
traceBufp->chgCDataImpl(oldp, *readp, top);
readp += 1;
continue;
case VerilatedTraceCommand::CHG_SDATA:
VL_TRACE_THREAD_DEBUG("Command CHG_SDATA " << top);
case VerilatedTraceOffloadCommand::CHG_SDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_SDATA " << top);
// Bits stored in bottom byte of command
chgSDataImpl(oldp, *readp, top);
traceBufp->chgSDataImpl(oldp, *readp, top);
readp += 1;
continue;
case VerilatedTraceCommand::CHG_IDATA:
VL_TRACE_THREAD_DEBUG("Command CHG_IDATA " << top);
case VerilatedTraceOffloadCommand::CHG_IDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_IDATA " << top);
// Bits stored in bottom byte of command
chgIDataImpl(oldp, *readp, top);
traceBufp->chgIDataImpl(oldp, *readp, top);
readp += 1;
continue;
case VerilatedTraceCommand::CHG_QDATA:
VL_TRACE_THREAD_DEBUG("Command CHG_QDATA " << top);
case VerilatedTraceOffloadCommand::CHG_QDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_QDATA " << top);
// Bits stored in bottom byte of command
chgQDataImpl(oldp, *reinterpret_cast<const QData*>(readp), top);
traceBufp->chgQDataImpl(oldp, *reinterpret_cast<const QData*>(readp), top);
readp += 2;
continue;
case VerilatedTraceCommand::CHG_WDATA:
VL_TRACE_THREAD_DEBUG("Command CHG_WDATA " << top);
chgWDataImpl(oldp, readp, top);
case VerilatedTraceOffloadCommand::CHG_WDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_WDATA " << top);
traceBufp->chgWDataImpl(oldp, readp, top);
readp += VL_WORDS_I(top);
continue;
case VerilatedTraceCommand::CHG_DOUBLE:
VL_TRACE_THREAD_DEBUG("Command CHG_DOUBLE " << top);
chgDoubleImpl(oldp, *reinterpret_cast<const double*>(readp));
case VerilatedTraceOffloadCommand::CHG_DOUBLE:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_DOUBLE " << top);
traceBufp->chgDoubleImpl(oldp, *reinterpret_cast<const double*>(readp));
readp += 2;
continue;
//===
// Rare commands
case VerilatedTraceCommand::TIME_CHANGE:
VL_TRACE_THREAD_DEBUG("Command TIME_CHANGE " << top);
case VerilatedTraceOffloadCommand::TIME_CHANGE:
VL_TRACE_OFFLOAD_DEBUG("Command TIME_CHANGE " << top);
readp -= 1; // No code in this command, undo increment
emitTimeChange(*reinterpret_cast<const vluint64_t*>(readp));
emitTimeChange(*reinterpret_cast<const uint64_t*>(readp));
readp += 2;
continue;
case VerilatedTraceOffloadCommand::TRACE_BUFFER:
VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
readp -= 1; // No code in this command, undo increment
traceBufp.reset(*reinterpret_cast<VL_BUF_T* const*>(readp));
readp += 2;
continue;
//===
// Commands ending this buffer
case VerilatedTraceCommand::END: VL_TRACE_THREAD_DEBUG("Command END"); break;
case VerilatedTraceCommand::SHUTDOWN:
VL_TRACE_THREAD_DEBUG("Command SHUTDOWN");
case VerilatedTraceOffloadCommand::END: //
VL_TRACE_OFFLOAD_DEBUG("Command END");
break;
case VerilatedTraceOffloadCommand::SHUTDOWN:
VL_TRACE_OFFLOAD_DEBUG("Command SHUTDOWN");
shutdown = true;
break;
//===
// Unknown command
default: { // LCOV_EXCL_START
VL_TRACE_THREAD_DEBUG("Command UNKNOWN");
VL_PRINTF_MT("Trace command: 0x%08x\n", cmd);
VL_TRACE_OFFLOAD_DEBUG("Command UNKNOWN " << cmd);
VL_FATAL_MT(__FILE__, __LINE__, "", "Unknown trace command");
break;
} // LCOV_EXCL_STOP
@@ -214,23 +224,23 @@ template <> void VerilatedTrace<VL_DERIVED_T>::workerThreadMain() {
break;
}
VL_TRACE_THREAD_DEBUG("Returning buffer");
VL_TRACE_OFFLOAD_DEBUG("Returning buffer");
// Return buffer
m_buffersFromWorker.put(bufferp);
m_offloadBuffersFromWorker.put(bufferp);
} while (VL_LIKELY(!shutdown));
}
template <> void VerilatedTrace<VL_DERIVED_T>::shutdownWorker() {
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
// If the worker thread is not running, done..
if (!m_workerThread) return;
// Hand an buffer with a shutdown command to the worker thread
vluint32_t* const bufferp = getTraceBuffer();
bufferp[0] = VerilatedTraceCommand::SHUTDOWN;
m_buffersToWorker.put(bufferp);
uint32_t* const bufferp = getOffloadBuffer();
bufferp[0] = VerilatedTraceOffloadCommand::SHUTDOWN;
m_offloadBuffersToWorker.put(bufferp);
// Wait for it to return
waitForBuffer(bufferp);
waitForOffloadBuffer(bufferp);
// Join the thread and delete it
m_workerThread->join();
m_workerThread.reset(nullptr);
@@ -241,72 +251,55 @@ template <> void VerilatedTrace<VL_DERIVED_T>::shutdownWorker() {
//=============================================================================
// Life cycle
template <> void VerilatedTrace<VL_DERIVED_T>::closeBase() {
#ifdef VL_TRACE_THREADED
shutdownWorker();
while (m_numTraceBuffers) {
delete[] m_buffersFromWorker.get();
--m_numTraceBuffers;
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {
#ifdef VL_TRACE_OFFLOAD
shutdownOffloadWorker();
while (m_numOffloadBuffers) {
delete[] m_offloadBuffersFromWorker.get();
--m_numOffloadBuffers;
}
#endif
}
template <> void VerilatedTrace<VL_DERIVED_T>::flushBase() {
#ifdef VL_TRACE_THREADED
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {
#ifdef VL_TRACE_OFFLOAD
// Hand an empty buffer to the worker thread
vluint32_t* const bufferp = getTraceBuffer();
*bufferp = VerilatedTraceCommand::END;
m_buffersToWorker.put(bufferp);
uint32_t* const bufferp = getOffloadBuffer();
*bufferp = VerilatedTraceOffloadCommand::END;
m_offloadBuffersToWorker.put(bufferp);
// Wait for it to be returned. As the processing is in-order,
// this ensures all previous buffers have been processed.
waitForBuffer(bufferp);
waitForOffloadBuffer(bufferp);
#endif
}
//=============================================================================
// Callbacks to run on global events
template <> void VerilatedTrace<VL_DERIVED_T>::onFlush(void* selfp) {
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush(void* selfp) {
// This calls 'flush' on the derived class (which must then get any mutex)
reinterpret_cast<VL_DERIVED_T*>(selfp)->flush();
reinterpret_cast<VL_SUB_T*>(selfp)->flush();
}
template <> void VerilatedTrace<VL_DERIVED_T>::onExit(void* selfp) {
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
// This calls 'close' on the derived class (which must then get any mutex)
reinterpret_cast<VL_DERIVED_T*>(selfp)->close();
reinterpret_cast<VL_SUB_T*>(selfp)->close();
}
//=============================================================================
// VerilatedTrace
template <>
VerilatedTrace<VL_DERIVED_T>::VerilatedTrace()
: m_sigs_oldvalp{nullptr}
, m_sigs_enabledp{nullptr}
, m_timeLastDump{0}
, m_fullDump{true}
, m_nextCode{0}
, m_numSignals{0}
, m_maxBits{0}
, m_scopeEscape{'.'}
, m_timeRes{1e-9}
, m_timeUnit {
1e-9
}
#ifdef VL_TRACE_THREADED
, m_numTraceBuffers { 0 }
#endif
{
template <> VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace() {
set_time_unit(Verilated::threadContextp()->timeunitString());
set_time_resolution(Verilated::threadContextp()->timeprecisionString());
}
template <> VerilatedTrace<VL_DERIVED_T>::~VerilatedTrace() {
template <> VerilatedTrace<VL_SUB_T, VL_BUF_T>::~VerilatedTrace() {
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = nullptr);
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
Verilated::removeFlushCb(VerilatedTrace<VL_DERIVED_T>::onFlush, this);
Verilated::removeExitCb(VerilatedTrace<VL_DERIVED_T>::onExit, this);
#ifdef VL_TRACE_THREADED
Verilated::removeFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
Verilated::removeExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
#ifdef VL_TRACE_OFFLOAD
closeBase();
#endif
}
@@ -314,11 +307,11 @@ template <> VerilatedTrace<VL_DERIVED_T>::~VerilatedTrace() {
//=========================================================================
// Internals available to format specific implementations
template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
// Note: It is possible to re-open a trace file (VCD in particular),
// so we must reset the next code here, but it must have the same number
// of codes on re-open
const vluint32_t expectedCodes = nextCode();
const uint32_t expectedCodes = nextCode();
m_nextCode = 1;
m_numSignals = 0;
m_maxBits = 0;
@@ -327,7 +320,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
// Call all initialize callbacks, which will:
// - Call decl* for each signal (these eventually call ::declCode)
// - Store the base code
for (vluint32_t i = 0; i < m_initCbs.size(); ++i) {
for (uint32_t i = 0; i < m_initCbs.size(); ++i) {
const CallbackRecord& cbr = m_initCbs[i];
cbr.m_initCb(cbr.m_userp, self(), nextCode());
}
@@ -339,7 +332,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
// Now that we know the number of codes, allocate space for the buffer
// holding previous signal values.
if (!m_sigs_oldvalp) m_sigs_oldvalp = new vluint32_t[nextCode()];
if (!m_sigs_oldvalp) m_sigs_oldvalp = new uint32_t[nextCode()];
// Apply enables
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
@@ -348,7 +341,7 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
// But it isn't, so alloc one bit for each code to indicate enablement
// We don't want to still use m_signs_enabledVec as std::vector<bool> is not
// guarenteed to be fast
m_sigs_enabledp = new vluint32_t[1 + VL_WORDS_I(nextCode())]{0};
m_sigs_enabledp = new uint32_t[1 + VL_WORDS_I(nextCode())]{0};
m_sigs_enabledVec.reserve(nextCode());
for (size_t code = 0; code < nextCode(); ++code) {
if (m_sigs_enabledVec[code]) {
@@ -359,25 +352,26 @@ template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
}
// Set callback so flush/abort will flush this file
Verilated::addFlushCb(VerilatedTrace<VL_DERIVED_T>::onFlush, this);
Verilated::addExitCb(VerilatedTrace<VL_DERIVED_T>::onExit, this);
Verilated::addFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
Verilated::addExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
#ifdef VL_TRACE_THREADED
// Compute trace buffer size. we need to be able to store a new value for
#ifdef VL_TRACE_OFFLOAD
// Compute offload buffer size. we need to be able to store a new value for
// each signal, which is 'nextCode()' entries after the init callbacks
// above have been run, plus up to 2 more words of metadata per signal,
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
// update.
m_traceBufferSize = nextCode() + numSignals() * 2 + 4;
m_offloadBufferSize = nextCode() + numSignals() * 2 + 4;
// Start the worker thread
m_workerThread.reset(new std::thread{&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this});
m_workerThread.reset(
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
#endif
}
template <>
bool VerilatedTrace<VL_DERIVED_T>::declCode(vluint32_t code, const char* namep, vluint32_t bits,
bool tri) {
bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const char* namep, uint32_t bits,
bool tri) {
if (VL_UNCOVERABLE(!code)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal");
}
@@ -421,28 +415,30 @@ bool VerilatedTrace<VL_DERIVED_T>::declCode(vluint32_t code, const char* namep,
//=========================================================================
// Internals available to format specific implementations
template <> std::string VerilatedTrace<VL_DERIVED_T>::timeResStr() const {
template <> std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
return doubleToTimescale(m_timeRes);
}
//=========================================================================
// External interface to client code
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
m_timeUnit = timescaleToDouble(unitp);
}
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const std::string& unit) VL_MT_SAFE {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const std::string& unit) VL_MT_SAFE {
set_time_unit(unit.c_str());
}
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const char* unitp) VL_MT_SAFE {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_resolution(const char* unitp) VL_MT_SAFE {
m_timeRes = timescaleToDouble(unitp);
}
template <>
void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const std::string& unit) VL_MT_SAFE {
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_resolution(const std::string& unit) VL_MT_SAFE {
set_time_resolution(unit.c_str());
}
template <>
void VerilatedTrace<VL_DERIVED_T>::dumpvars(int level, const std::string& hier) VL_MT_SAFE {
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dumpvars(int level, const std::string& hier) VL_MT_SAFE {
if (level == 0) {
m_dumpvars.clear(); // empty = everything on
} else {
@@ -455,8 +451,87 @@ void VerilatedTrace<VL_DERIVED_T>::dumpvars(int level, const std::string& hier)
}
}
#ifdef VL_TRACE_PARALLEL
template <> //
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::parallelWorkerTask(void* datap, bool) {
ParallelWorkerData* const wdp = reinterpret_cast<ParallelWorkerData*>(datap);
// Run the task
wdp->m_cb(wdp->m_userp, wdp->m_bufp);
// Mark buffer as ready
const VerilatedLockGuard lock{wdp->m_mutex};
wdp->m_ready.store(true);
if (wdp->m_waiting) wdp->m_cv.notify_one();
}
template <> VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wait() {
// Spin for a while, waiting for the buffer to become ready
for (int i = 0; i < VL_LOCK_SPINS; ++i) {
if (VL_LIKELY(m_ready.load(std::memory_order_relaxed))) return;
VL_CPU_RELAX();
}
// We have been spinning for a while, so yield the thread
VerilatedLockGuard lock{m_mutex};
m_waiting = true;
m_cv.wait(lock, [this] { return m_ready.load(std::memory_order_relaxed); });
m_waiting = false;
}
#endif
template <>
void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runParallelCallbacks(const ParallelCallbackMap& cbMap) {
for (VlThreadPool* threadPoolp : m_threadPoolps) {
#ifdef VL_TRACE_PARALLEL
// If tracing in parallel, dispatch to the thread pool (if exists)
if (threadPoolp && threadPoolp->numThreads()) {
// List of work items for thread (std::list, as ParallelWorkerData is not movable)
std::list<ParallelWorkerData> workerData;
// We use the whole pool + the main thread
const unsigned threads = threadPoolp->numThreads() + 1;
// Main thread executes all jobs with index % threads == 0
std::vector<ParallelWorkerData*> mainThreadWorkerData;
// The tracing callbacks to execute on this thread-pool
const auto& cbVec = cbMap.at(threadPoolp);
// Enuque all the jobs
for (unsigned i = 0; i < cbVec.size(); ++i) {
const CallbackRecord& cbr = cbVec[i];
// Always get the trace buffer on the main thread
Buffer* const bufp = getTraceBuffer();
// Create new work item
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
// Grab the new work item
ParallelWorkerData* const itemp = &workerData.back();
// Enqueue task to thread pool, or main thread
if (unsigned rem = i % threads) {
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp, false);
} else {
mainThreadWorkerData.push_back(itemp);
}
}
// Execute main thead jobs
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
parallelWorkerTask(itemp, false);
}
// Commit all trace buffers in order
for (ParallelWorkerData& item : workerData) {
// Wait until ready
item.wait();
// Commit the buffer
commitTraceBuffer(item.m_bufp);
}
continue;
}
#endif
// Fall back on sequential execution
for (const CallbackRecord& cbr : cbMap.at(threadPoolp)) {
Buffer* const traceBufferp = getTraceBuffer();
cbr.m_dumpCb(cbr.m_userp, traceBufferp);
commitTraceBuffer(traceBufferp);
}
}
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
// This does get the mutex, but if multiple threads are trying to dump
// chances are the data being dumped will have other problems
@@ -478,19 +553,19 @@ void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m
if (!preChangeDump()) return;
}
#ifdef VL_TRACE_THREADED
#ifdef VL_TRACE_OFFLOAD
// Currently only incremental dumps run on the worker thread
vluint32_t* bufferp = nullptr;
uint32_t* bufferp = nullptr;
if (VL_LIKELY(!m_fullDump)) {
// Get the trace buffer we are about to fill
bufferp = getTraceBuffer();
m_traceBufferWritep = bufferp;
m_traceBufferEndp = bufferp + m_traceBufferSize;
// Get the offload buffer we are about to fill
bufferp = getOffloadBuffer();
m_offloadBufferWritep = bufferp;
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
// Tell worker to update time point
m_traceBufferWritep[0] = VerilatedTraceCommand::TIME_CHANGE;
*reinterpret_cast<vluint64_t*>(m_traceBufferWritep + 1) = timeui;
m_traceBufferWritep += 3;
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
m_offloadBufferWritep += 3;
} else {
// Update time point
flushBase();
@@ -504,32 +579,26 @@ void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m
// Run the callbacks
if (VL_UNLIKELY(m_fullDump)) {
m_fullDump = false; // No more need for next dump to be full
for (vluint32_t i = 0; i < m_fullCbs.size(); ++i) {
const CallbackRecord& cbr = m_fullCbs[i];
cbr.m_dumpCb(cbr.m_userp, self());
}
runParallelCallbacks(m_fullCbs);
} else {
for (vluint32_t i = 0; i < m_chgCbs.size(); ++i) {
const CallbackRecord& cbr = m_chgCbs[i];
cbr.m_dumpCb(cbr.m_userp, self());
}
runParallelCallbacks(m_chgCbs);
}
for (vluint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
const CallbackRecord& cbr = m_cleanupCbs[i];
cbr.m_dumpCb(cbr.m_userp, self());
cbr.m_cleanupCb(cbr.m_userp, self());
}
#ifdef VL_TRACE_THREADED
#ifdef VL_TRACE_OFFLOAD
if (VL_LIKELY(bufferp)) {
// Mark end of the trace buffer we just filled
*m_traceBufferWritep++ = VerilatedTraceCommand::END;
// Mark end of the offload buffer we just filled
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::END;
// Assert no buffer overflow
assert(m_traceBufferWritep - bufferp <= m_traceBufferSize);
assert(m_offloadBufferWritep - bufferp <= m_offloadBufferSize);
// Pass it to the worker thread
m_buffersToWorker.put(bufferp);
m_offloadBuffersToWorker.put(bufferp);
}
#endif
}
@@ -538,8 +607,18 @@ void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) VL_MT_SAFE_EXCLUDES(m
// Non-hot path internal interface to Verilator generated code
template <>
void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
CallbackRecord& cbRec)
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addThreadPool(VlThreadPool* threadPoolp)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
for (VlThreadPool* const poolp : m_threadPoolps) {
if (poolp == threadPoolp) return;
}
m_threadPoolps.push_back(threadPoolp);
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
CallbackRecord& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
@@ -550,95 +629,40 @@ void VerilatedTrace<VL_DERIVED_T>::addCallbackRecord(std::vector<CallbackRecord>
cbVec.push_back(cbRec);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_initCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp,
VlThreadPool* threadPoolp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_fullCbs, cbr);
addThreadPool(threadPoolp);
addCallbackRecord(m_fullCbs[threadPoolp], cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp,
VlThreadPool* threadPoolp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_chgCbs, cbr);
addThreadPool(threadPoolp);
addCallbackRecord(m_chgCbs[threadPoolp], cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::addCleanupCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_cleanupCbs, cbr);
}
template <> void VerilatedTrace<VL_DERIVED_T>::pushNamePrefix(const std::string& prefix) {
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) {
m_namePrefixStack.push_back(m_namePrefixStack.back() + prefix);
}
template <> void VerilatedTrace<VL_DERIVED_T>::popNamePrefix(unsigned count) {
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::popNamePrefix(unsigned count) {
while (count--) m_namePrefixStack.pop_back();
assert(!m_namePrefixStack.empty());
}
//=========================================================================
// Hot path internal interface to Verilator generated code
// These functions must write the new value back into the old value store,
// and subsequently call the format specific emit* implementations. Note
// that this file must be included in the format specific implementation, so
// the emit* functions can be inlined for performance.
template <> void VerilatedTrace<VL_DERIVED_T>::fullBit(vluint32_t* oldp, CData newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitBit(code, newval);
}
template <>
void VerilatedTrace<VL_DERIVED_T>::fullCData(vluint32_t* oldp, CData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitCData(code, newval, bits);
}
template <>
void VerilatedTrace<VL_DERIVED_T>::fullSData(vluint32_t* oldp, SData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitSData(code, newval, bits);
}
template <>
void VerilatedTrace<VL_DERIVED_T>::fullIData(vluint32_t* oldp, IData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitIData(code, newval, bits);
}
template <>
void VerilatedTrace<VL_DERIVED_T>::fullQData(vluint32_t* oldp, QData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<QData*>(oldp) = newval;
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitQData(code, newval, bits);
}
template <>
void VerilatedTrace<VL_DERIVED_T>::fullWData(vluint32_t* oldp, const WData* newvalp, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitWData(code, newvalp, bits);
}
template <> void VerilatedTrace<VL_DERIVED_T>::fullDouble(vluint32_t* oldp, double newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<double*>(oldp) = newval;
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
// cppcheck-suppress invalidPointerCast
self()->emitDouble(code, newval);
}
//=========================================================================
// Primitives converting binary values to strings...
@@ -729,41 +753,86 @@ static inline void cvtQDataToStr(char* dstp, QData value) {
#define cvtEDataToStr cvtIDataToStr
//=============================================================================
//=========================================================================
// VerilatedTraceBuffer
#ifdef VERILATED_VCD_TEST
void verilated_trace_imp_selftest() {
#define SELF_CHECK(got, exp) \
do { \
if ((got) != (exp)) VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: selftest"); \
} while (0)
#define SELF_CHECK_TS(scale) \
SELF_CHECK(doubleToTimescale(timescaleToDouble(scale)), std::string{scale});
SELF_CHECK_TS("100s");
SELF_CHECK_TS("10s");
SELF_CHECK_TS("1s");
SELF_CHECK_TS("100ms");
SELF_CHECK_TS("10ms");
SELF_CHECK_TS("1ms");
SELF_CHECK_TS("100us");
SELF_CHECK_TS("10us");
SELF_CHECK_TS("1us");
SELF_CHECK_TS("100ns");
SELF_CHECK_TS("10ns");
SELF_CHECK_TS("1ns");
SELF_CHECK_TS("100ps");
SELF_CHECK_TS("10ps");
SELF_CHECK_TS("1ps");
SELF_CHECK_TS("100fs");
SELF_CHECK_TS("10fs");
SELF_CHECK_TS("1fs");
SELF_CHECK_TS("100as");
SELF_CHECK_TS("10as");
SELF_CHECK_TS("1as");
template <> //
VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::VerilatedTraceBuffer(VL_SUB_T& owner)
: m_owner{owner} {
#ifdef VL_TRACE_OFFLOAD
if (m_offloadBufferWritep) {
using This = VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>*;
// Tack on the buffer address
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
"This should be enough on all plafrorms");
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
*reinterpret_cast<This*>(m_offloadBufferWritep) = this;
m_offloadBufferWritep += 2;
}
#endif
}
#endif
// These functions must write the new value back into the old value store,
// and subsequently call the format specific emit* implementations. Note
// that this file must be included in the format specific implementation, so
// the emit* functions can be inlined for performance.
template <> //
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitBit(code, newval);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitCData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitSData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitIData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<QData*>(oldp) = newval;
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitQData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp,
int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitWData(code, newvalp, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<double*>(oldp) = newval;
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
// cppcheck-suppress invalidPointerCast
self()->emitDouble(code, newval);
}
#endif // VL_CPPCHECK
+13 -13
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:
@@ -241,7 +241,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 +288,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 +299,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 +309,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;
}
@@ -410,16 +410,16 @@ public:
}
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 {
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>{};
+147 -316
View File
@@ -62,12 +62,23 @@ 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
//=============================================================================
//=============================================================================
@@ -172,7 +183,7 @@ bool VerilatedVcd::preChangeDump() {
return isOpen();
}
void VerilatedVcd::emitTimeChange(vluint64_t timeui) {
void VerilatedVcd::emitTimeChange(uint64_t timeui) {
printStr("#");
printQuad(timeui);
printStr("\n");
@@ -183,7 +194,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 +229,17 @@ VerilatedVcd::~VerilatedVcd() {
if (m_wrBufp) VL_DO_CLEAR(delete[] m_wrBufp, m_wrBufp = nullptr);
deleteNameMap();
if (m_filep && m_fileNewed) VL_DO_CLEAR(delete m_filep, m_filep = nullptr);
#ifdef VL_TRACE_PARALLEL
assert(m_numBuffers == m_freeBuffers.size());
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
#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();
@@ -251,14 +266,14 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
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 +285,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 +307,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 +343,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 +474,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;
@@ -544,44 +561,91 @@ 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
VerilatedVcdBuffer* VerilatedVcd::getTraceBuffer() {
#ifdef VL_TRACE_PARALLEL
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// If no buffer available, allocate a new one
if (m_freeBuffers.empty()) {
constexpr size_t pageSize = 4096;
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
size_t startingSize = roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
++m_numBuffers;
}
// Grab a buffer
const auto pair = m_freeBuffers.back();
m_freeBuffers.pop_back();
// Return the buffer
return new VerilatedVcdBuffer{*this, pair.first, pair.second};
#else
return new VerilatedVcdBuffer{*this};
#endif
}
void VerilatedVcd::commitTraceBuffer(VerilatedVcdBuffer* bufp) {
#ifdef VL_TRACE_PARALLEL
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// Resize output buffer. Note, we use the full size of the trace buffer, as
// this is a lot more stable than the actual occupancy of the trace buffer.
// This helps us to avoid re-allocations due to small size changes.
bufferResize(bufp->m_size);
// Compute occupancy of buffer
const size_t usedSize = bufp->m_writep - bufp->m_bufp;
// Copy to output buffer
std::memcpy(m_writep, bufp->m_bufp, usedSize);
// Adjust write pointer
m_writep += usedSize;
// Flush if necessary
bufferCheck();
// Put buffer back on free list
m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
#else
// Needs adjusting for emitTimeChange
m_writep = bufp->m_writep;
#endif
delete bufp;
}
//=============================================================================
// VerilatedVcdBuffer implementation
#ifdef VL_TRACE_PARALLEL
VerilatedVcdBuffer::VerilatedVcdBuffer(VerilatedVcd& owner, char* bufp, size_t size)
: VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer>{owner}
, m_writep{bufp}
, m_bufp{bufp}
, m_size{size} {
adjustGrowp();
}
#else
VerilatedVcdBuffer::VerilatedVcdBuffer(VerilatedVcd& owner)
: VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer>{owner} {}
#endif
//=============================================================================
// Trace rendering primitives
static inline void
VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
@@ -593,7 +657,7 @@ static inline void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* s
#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 +670,55 @@ 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();
#ifdef VL_TRACE_PARALLEL
// Double the size of the buffer if necessary
if (VL_UNLIKELY(m_writep >= m_growp)) {
// Compute occupied size of current buffer
const size_t usedSize = m_writep - m_bufp;
// We are always doubling the size
m_size *= 2;
// Allocate the new buffer
char* const newBufp = new char[m_size];
// Copy from current buffer to new buffer
std::memcpy(newBufp, m_bufp, usedSize);
// Delete current buffer
delete[] m_bufp;
// Make new buffer the current buffer
m_bufp = newBufp;
// Adjust write pointer
m_writep = m_bufp + usedSize;
// Adjust resize limit
adjustGrowp();
}
#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;
}
#endif
}
//=============================================================================
// 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 +726,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 +734,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 +742,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 +750,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 +758,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 +775,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:
+188 -295
View File
@@ -28,7 +28,189 @@
#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 Buffer; // 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
bool m_evcd = false; // True for evcd format
std::string m_filename; // Filename we're writing to (if open)
uint64_t m_rolloverMB = 0; // MB of file size to rollover at
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_TRACE_PARALLEL
// 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;
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(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
virtual void emitTimeChange(uint64_t timeui) override;
// Hooks called from VerilatedTrace
virtual bool preFullDump() override { return isOpen(); }
virtual bool preChangeDump() override;
// Trace buffer management
virtual VerilatedVcdBuffer* getTraceBuffer() override;
virtual void commitTraceBuffer(VerilatedVcdBuffer*) override;
public:
//=========================================================================
// External interface to client code
// CONSTRUCTOR
explicit VerilatedVcd(VerilatedVcdFile* filep = nullptr);
~VerilatedVcd();
// ACCESSORS
// Set size in megabytes after which new file should be created
void rolloverMB(uint64_t rolloverMB) { m_rolloverMB = rolloverMB; }
// 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 final : public VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer> {
// Give the trace file access to the private bits
friend VerilatedVcd;
friend VerilatedVcd::Super;
#ifdef VL_TRACE_PARALLEL
char* m_writep; // Write pointer into m_bufp
char* m_bufp; // The beginning of the trace buffer
size_t m_size; // The size of the buffer at m_bufp
char* m_growp; // Resize limit pointer
#else
char* m_writep = m_owner.m_writep; // Write pointer into output buffer
char* const m_wrFlushp = m_owner.m_wrFlushp; // Output buffer flush trigger location
#endif
// VCD line end string codes + metadata
const char* const m_suffixes = m_owner.m_suffixes.data();
// The maximum number of bytes a single signal can emit
const size_t m_maxSignalBytes = m_owner.m_maxSignalBytes;
void finishLine(uint32_t code, char* writep);
#ifdef VL_TRACE_PARALLEL
void adjustGrowp() {
m_growp = (m_bufp + m_size) - (2 * m_maxSignalBytes);
assert(m_growp >= m_bufp + m_maxSignalBytes);
}
#endif
public:
// CONSTRUCTOR
#ifdef VL_TRACE_PARALLEL
explicit VerilatedVcdBuffer(VerilatedVcd& owner, char* bufp, size_t size);
#else
explicit VerilatedVcdBuffer(VerilatedVcd& owner);
#endif
~VerilatedVcdBuffer() = default;
//=========================================================================
// Implementation of VerilatedTraceBuffer interface
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
// called from only one place (the full* methods), so always inline them.
VL_ATTR_ALWINLINE inline void emitBit(uint32_t code, CData newval);
VL_ATTR_ALWINLINE inline void emitCData(uint32_t code, CData newval, int bits);
VL_ATTR_ALWINLINE inline void emitSData(uint32_t code, SData newval, int bits);
VL_ATTR_ALWINLINE inline void emitIData(uint32_t code, IData newval, int bits);
VL_ATTR_ALWINLINE inline void emitQData(uint32_t code, QData newval, int bits);
VL_ATTR_ALWINLINE inline void emitWData(uint32_t code, const WData* newvalp, int bits);
VL_ATTR_ALWINLINE inline void emitDouble(uint32_t code, double newval);
};
//=============================================================================
// VerilatedFile
@@ -52,285 +234,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)
@@ -371,12 +274,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
@@ -401,16 +304,6 @@ 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
};
#endif // guard
+71 -70
View File
@@ -66,11 +66,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 +81,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())) {
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");
@@ -117,8 +118,8 @@ public:
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 +128,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;
static VerilatedVpioTimedCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioTimedCb*>(reinterpret_cast<VerilatedVpioTimedCb*>(h));
}
virtual vluint32_t type() const override { return vpiCallback; }
virtual uint32_t type() const override { return vpiCallback; }
virtual 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;
static VerilatedVpioReasonCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioReasonCb*>(reinterpret_cast<VerilatedVpioReasonCb*>(h));
}
virtual vluint32_t type() const override { return vpiCallback; }
virtual uint32_t type() const override { return vpiCallback; }
virtual 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;
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; }
virtual uint32_t type() const override { return vpiConstant; }
int32_t num() const { return m_num; }
};
class VerilatedVpioVarBase VL_NOT_FINAL : public VerilatedVpio {
@@ -199,7 +200,7 @@ 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 uint32_t size() const override { return get_range().elements(); }
virtual const VerilatedRange* rangep() const override { return &get_range(); }
virtual const char* name() const override { return m_varp->name(); }
virtual const char* fullname() const override {
@@ -218,7 +219,7 @@ public:
static VerilatedVpioParam* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioParam*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiParameter; }
virtual uint32_t type() const override { return vpiParameter; }
void* varDatap() const { return m_varp->datap(); }
};
@@ -232,8 +233,8 @@ public:
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 uint32_t type() const override { return vpiRange; }
virtual uint32_t size() const override { return m_range->elements(); }
virtual const VerilatedRange* rangep() const override { return m_range; }
};
@@ -249,7 +250,7 @@ public:
static VerilatedVpioRangeIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioRangeIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiIterator; }
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
if (VL_UNLIKELY(m_done)) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
@@ -271,22 +272,22 @@ public:
static VerilatedVpioScope* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioScope*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiScope; }
virtual 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(); }
};
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)
@@ -313,18 +314,18 @@ public:
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; }
virtual 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,18 +333,18 @@ 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;
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 uint32_t type() const override { return vpiMemoryWord; }
virtual uint32_t size() const override { return varp()->packed().elements(); }
virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); }
virtual const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
@@ -367,7 +368,7 @@ public:
static VerilatedVpioVarIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioVarIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiIterator; }
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
if (VL_LIKELY(m_scopep->varsp())) {
const VerilatedVarNameMap* const varsp = m_scopep->varsp();
@@ -394,8 +395,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:
@@ -408,7 +409,7 @@ public:
static VerilatedVpioMemoryWordIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioMemoryWordIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiIterator; }
virtual uint32_t type() const override { return vpiIterator; }
void iterationInc() {
if (!(m_done = (m_iteration == m_varp->unpacked().left()))) m_iteration += m_direction;
}
@@ -437,7 +438,7 @@ public:
static VerilatedVpioModule* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModule*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiModule; }
virtual uint32_t type() const override { return vpiModule; }
virtual const char* name() const override { return m_name; }
virtual const char* fullname() const override { return m_fullname; }
};
@@ -455,7 +456,7 @@ public:
static VerilatedVpioModuleIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModuleIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual vluint32_t type() const override { return vpiIterator; }
virtual uint32_t type() const override { return vpiIterator; }
virtual 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
@@ -472,14 +473,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 +496,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 +516,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 +532,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 +545,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 +554,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 +563,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 +593,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 +665,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 +745,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 +1310,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 +1325,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 +1475,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 +1997,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) {
+1 -1
View File
@@ -47,7 +47,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(const 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;
+32 -12
View File
@@ -40,6 +40,7 @@
#ifdef __GNUC__
# define VL_ATTR_ALIGNED(alignment) __attribute__((aligned(alignment)))
# define VL_ATTR_ALWINLINE __attribute__((always_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
@@ -282,6 +286,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 +295,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 +326,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 +442,24 @@ 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, 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 +481,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 +498,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
@@ -518,6 +525,19 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// Conversions
namespace vlstd {
template <typename T> struct reverse_wrapper {
const T& m_v;
explicit reverse_wrapper(const T& a_v)
: m_v(a_v) {}
inline auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); }
inline auto end() -> decltype(m_v.rend()) { return m_v.rend(); }
};
// 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; }
}; // namespace vlstd
+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)))
+57 -58
View File
@@ -317,79 +317,74 @@ public:
iterate(nodep);
m_graph.latchCheck(nodep, kwd == VAlwaysKwd::ALWAYS_LATCH);
}
virtual ~ActiveLatchCheckVisitor() {}
virtual ~ActiveLatchCheckVisitor() = default;
};
//######################################################################
// Active AssignDly replacement functions
// Replace unsupported non-blocking assignments with blocking assignments
class ActiveDlyVisitor final : public ActiveBaseVisitor {
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);
// 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);
}
}
// 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); }
public:
// CONSTRUCTORS
ActiveDlyVisitor(AstNode* nodep, CheckType check)
: m_check{check}
, m_alwaysp{nodep} {
: m_check{check} {
iterate(nodep);
}
virtual ~ActiveDlyVisitor() override = default;
@@ -422,6 +417,14 @@ private:
virtual void visit(AstActive* nodep) override {
// Actives are being formed, so we can ignore any already made
}
virtual void visit(AstInitialStatic* 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);
}
virtual void visit(AstInitial* nodep) override {
// Relink to IACTIVE, unless already under it
UINFO(4, " INITIAL " << nodep << endl);
@@ -527,12 +530,8 @@ 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};
}
+6 -8
View File
@@ -98,7 +98,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;
}
@@ -154,7 +154,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()) {
@@ -262,7 +262,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 +313,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);
}
@@ -384,7 +383,7 @@ 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);
@@ -402,8 +401,7 @@ private:
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},
+6 -8
View File
@@ -167,16 +167,14 @@ private:
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
+24 -40
View File
@@ -30,8 +30,8 @@
//======================================================================
// 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 +114,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 {
@@ -758,7 +758,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;
}
@@ -1135,49 +1135,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 +1171,7 @@ void AstNode::v3errorEnd(std::ostringstream& str) const {
const_cast<AstNode*>(this)->dump(nsstr);
nsstr << endl;
}
m_fileline->v3errorEnd(nsstr, locationStr());
m_fileline->v3errorEnd(nsstr, instanceStr());
}
}
+120 -34
View File
@@ -83,7 +83,7 @@ using MTaskIdSet = std::set<int>; // Set of mtaskIds for Var sorting
#define VN_AS(nodep, nodetypename) (AstNode::privateAs<Ast##nodetypename, decltype(nodep)>(nodep))
// (V)erilator (N)ode deleted: Pointer to deleted AstNode (for assertions only)
#define VN_DELETED(nodep) VL_UNLIKELY((vluint64_t)(nodep) == 0x1)
#define VN_DELETED(nodep) VL_UNLIKELY((uint64_t)(nodep) == 0x1)
//######################################################################
@@ -1398,10 +1398,10 @@ class AstNode VL_NOT_FINAL {
AstNodeDType* m_dtypep = nullptr; // Data type of output or assignment (etc)
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list
FileLine* m_fileline; // Where it was declared
vluint64_t m_editCount; // When it was last edited
static vluint64_t s_editCntGbl; // Global edit counter
uint64_t m_editCount; // When it was last edited
static uint64_t s_editCntGbl; // Global edit counter
// Global edit counter, last value for printing * near node #s
static vluint64_t s_editCntLast;
static uint64_t s_editCntLast;
AstNode* m_clonep
= nullptr; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY)
@@ -1447,7 +1447,7 @@ private:
bool gateOnly);
void deleteTreeIter();
void deleteNode();
string locationStr() const;
string instanceStr() const;
public:
static void relinkOneLink(AstNode*& pointpr, AstNode* newp);
@@ -1559,7 +1559,7 @@ public:
}
static string
encodeName(const string& namein); // Encode user name into internal C representation
static string encodeNumber(vlsint64_t num); // Encode number into internal C representation
static string encodeNumber(int64_t num); // Encode number into internal C representation
static string vcdName(const string& namein); // Name for printing out to vcd files
string prettyName() const { return prettyName(name()); }
string prettyNameQ() const { return prettyNameQ(name()); }
@@ -1666,12 +1666,12 @@ public:
static void user5ClearTree() { VNUser5InUse::clear(); } // Clear userp()'s across the entire tree
// clang-format on
vluint64_t editCount() const { return m_editCount; }
uint64_t editCount() const { return m_editCount; }
void editCountInc() {
m_editCount = ++s_editCntGbl; // Preincrement, so can "watch AstNode::s_editCntGbl=##"
}
static vluint64_t editCountLast() { return s_editCntLast; }
static vluint64_t editCountGbl() { return s_editCntGbl; }
static uint64_t editCountLast() { return s_editCntLast; }
static uint64_t editCountGbl() { return s_editCntGbl; }
static void editCountSetLast() { s_editCntLast = editCountGbl(); }
// ACCESSORS for specific types
@@ -1972,47 +1972,122 @@ private:
} while (nodep);
}
public:
// Traverse subtree and call given function 'f' in pre-order on each node that has type 'T'.
// Prefer 'foreach' over simple VNVisitor that only needs to handle a single (or a few) node
// types, as it's easier to write, but more importantly, the dispatch to the operation function
// in 'foreach' should be completely predictable by branch target caches in modern CPUs,
// while it is basically unpredictable for VNVisitor.
template <typename T_Node> void foreach (std::function<void(T_Node*)> f) {
template <typename T_Arg, bool Default, bool VisitNext>
static bool predicateImpl(
// Using std::conditional for const correctness in the public 'foreach' functions
typename std::conditional<std::is_const<T_Arg>::value, const AstNode*, AstNode*>::type
nodep,
std::function<bool(T_Arg*)> p) {
// Note: Using a loop to iterate the nextp() chain, instead of tail recursion, because
// debug builds don't eliminate tail calls, causing stack overflow on long lists of nodes.
do {
// Prefetch children and next
ASTNODE_PREFETCH(nodep->op1p());
ASTNODE_PREFETCH(nodep->op2p());
ASTNODE_PREFETCH(nodep->op3p());
ASTNODE_PREFETCH(nodep->op4p());
if /* TODO: 'constexpr' in C++17 */ (VisitNext) ASTNODE_PREFETCH(nodep->nextp());
// Apply function in pre-order
if (privateTypeTest<typename std::remove_const<T_Arg>::type>(nodep)) {
if (p(static_cast<T_Arg*>(nodep)) != Default) return !Default;
}
// Traverse children (including their 'nextp()' chains), unless futile
if (mayBeUnder<typename std::remove_const<T_Arg>::type>(nodep)) {
if (AstNode* const op1p = nodep->op1p()) {
if (predicateImpl<T_Arg, Default, true>(op1p, p) != Default) return !Default;
}
if (AstNode* const op2p = nodep->op2p()) {
if (predicateImpl<T_Arg, Default, true>(op2p, p) != Default) return !Default;
}
if (AstNode* const op3p = nodep->op3p()) {
if (predicateImpl<T_Arg, Default, true>(op3p, p) != Default) return !Default;
}
if (AstNode* const op4p = nodep->op4p()) {
if (predicateImpl<T_Arg, Default, true>(op4p, p) != Default) return !Default;
}
}
// Traverse 'nextp()' chain if requested
if /* TODO: 'constexpr' in C++17 */ (VisitNext) {
nodep = nodep->nextp();
} else {
break;
}
} while (nodep);
return Default;
}
template <typename T_Node> constexpr static bool checkTypeParameter() {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
return true;
}
public:
// Traverse subtree and call given function 'f' in pre-order on each node that has type
// 'T_Node'. Prefer 'foreach' over simple VNVisitor that only needs to handle a single (or a
// few) node types, as it's easier to write, but more importantly, the dispatch to the
// operation function in 'foreach' should be completely predictable by branch target caches in
// modern CPUs, while it is basically unpredictable for VNVisitor.
template <typename T_Node> void foreach (std::function<void(T_Node*)> f) {
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
foreachImpl<T_Node, /* VisitNext: */ false>(this, f);
}
// Same as above, but for 'const' nodes
template <typename T_Node> void foreach (std::function<void(const T_Node*)> f) const {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
foreachImpl<const T_Node, /* VisitNext: */ false>(this, f);
}
// Same as 'foreach' but also follows 'this->nextp()'
template <typename T_Node> void foreachAndNext(std::function<void(T_Node*)> f) {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
foreachImpl<T_Node, /* VisitNext: */ true>(this, f);
}
// Same as 'foreach' but also follows 'this->nextp()'
template <typename T_Node> void foreachAndNext(std::function<void(const T_Node*)> f) const {
static_assert(!std::is_const<T_Node>::value,
"Type parameter 'T_Node' should not be const qualified");
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
foreachImpl<const T_Node, /* VisitNext: */ true>(this, f);
}
// Given a predicate function 'p' return true if and only if there exists a node of type
// 'T_Node' that satisfies the predicate 'p'. Returns false if no node of type 'T_Node' is
// present. Traversal is performed in some arbitrary order and is terminated as soon as the
// result can be determined.
template <typename T_Node> bool exists(std::function<bool(T_Node*)> p) {
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
return predicateImpl<T_Node, /* Default: */ false, /* VisitNext: */ false>(this, p);
}
// Same as above, but for 'const' nodes
template <typename T_Node> void exists(std::function<bool(const T_Node*)> p) const {
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
return predicateImpl<const T_Node, /* Default: */ false, /* VisitNext: */ false>(this, p);
}
// Given a predicate function 'p' return true if and only if all nodes of type
// 'T_Node' satisfy the predicate 'p'. Returns true if no node of type 'T_Node' is
// present. Traversal is performed in some arbitrary order and is terminated as soon as the
// result can be determined.
template <typename T_Node> bool forall(std::function<bool(T_Node*)> p) {
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
return predicateImpl<T_Node, /* Default: */ true, /* VisitNext: */ false>(this, p);
}
// Same as above, but for 'const' nodes
template <typename T_Node> void forall(std::function<bool(const T_Node*)> p) const {
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
return predicateImpl<const T_Node, /* Default: */ true, /* VisitNext: */ false>(this, p);
}
int nodeCount() const {
// TODO: this should really return size_t, but need to fix use sites
int count = 0;
@@ -2034,6 +2109,11 @@ template <> inline bool AstNode::privateMayBeUnder<AstNodeAssign>(const AstNode*
template <> inline bool AstNode::privateMayBeUnder<AstVarScope>(const AstNode* nodep) {
return !VN_IS(nodep, NodeStmt) && !VN_IS(nodep, NodeMath);
}
template <> inline bool AstNode::privateMayBeUnder<AstExecGraph>(const AstNode* nodep) {
if (VN_IS(nodep, ExecGraph)) return false; // Should not nest
if (VN_IS(nodep, NodeStmt)) return false; // Should be directly under CFunc
return true;
}
inline std::ostream& operator<<(std::ostream& os, const AstNode* rhs) {
if (!rhs) {
@@ -2071,7 +2151,7 @@ static_assert(sizeof(VNRef<AstNode>) == sizeof(std::reference_wrapper<AstNode>),
// without having to copy nodes into the collections.
// Forward declaration to avoid including V3Hasher.h which needs V3Ast.h (this file).
size_t V3HasherUncachedHash(AstNode&);
size_t V3HasherUncachedHash(const AstNode&);
// Specialization of std::hash for VNRef
template <typename T_Node> //
@@ -2409,10 +2489,12 @@ public:
class AstNodeAssign VL_NOT_FINAL : public AstNodeStmt {
protected:
AstNodeAssign(VNType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp)
AstNodeAssign(VNType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp,
AstNode* timingControlp = nullptr)
: AstNodeStmt{t, fl} {
setOp1p(rhsp);
setOp2p(lhsp);
addNOp3p(timingControlp);
dtypeFrom(lhsp);
}
@@ -2423,6 +2505,9 @@ public:
// So iteration hits the RHS which is "earlier" in execution order, it's op1, not op2
AstNode* rhsp() const { return op1p(); } // op1 = Assign from
AstNode* lhsp() const { return op2p(); } // op2 = Assign to
// op3 = Timing controls (delays, event controls)
AstNode* timingControlp() const { return op3p(); }
void addTimingControlp(AstNode* const np) { addNOp3p(np); }
void rhsp(AstNode* np) { setOp1p(np); }
void lhsp(AstNode* np) { setOp2p(np); }
virtual bool hasDType() const override { return true; }
@@ -2562,7 +2647,7 @@ private:
string m_text;
protected:
// Node that simply puts text into the output stream
// Node that puts text into the output stream
AstNodeText(VNType t, FileLine* fl, const string& textp)
: AstNode{t, fl} {
m_text = textp; // Copy it
@@ -3072,7 +3157,8 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
private:
string m_name; // Name of the module
const string m_origName; // Name of the module, ignoring name() changes, for dot lookup
string m_hierName; // Hierarchical name for errors, etc.
string m_someInstanceName; // Hierarchical name of some arbitrary instance of this module.
// Used for user messages only.
bool m_modPublic : 1; // Module has public references
bool m_modTrace : 1; // Tracing this module
bool m_inLibrary : 1; // From a library, no error if not used, never top level
@@ -3114,8 +3200,8 @@ public:
// ACCESSORS
virtual void name(const string& name) override { m_name = name; }
virtual string origName() const override { return m_origName; }
string hierName() const { return m_hierName; }
void hierName(const string& hierName) { m_hierName = hierName; }
string someInstanceName() const { return m_someInstanceName; }
void someInstanceName(const string& name) { m_someInstanceName = name; }
bool inLibrary() const { return m_inLibrary; }
void inLibrary(bool flag) { m_inLibrary = flag; }
void level(int level) { m_level = level; }
+21 -8
View File
@@ -225,9 +225,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 +464,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
}
@@ -600,7 +601,7 @@ string AstVar::scType() const {
return "uint32_t";
}
} else {
return "vluint64_t";
return "uint64_t";
}
}
@@ -1071,8 +1072,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;
@@ -1308,7 +1309,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 +1321,14 @@ 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* itemp = scopep->blocksp(); itemp; itemp = itemp->nextp())
insertCache(itemp);
} else {
insertCache(itemp);
}
}
}
bool AstClass::isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp) {
// TAIL RECURSIVE
@@ -1853,6 +1862,10 @@ void AstFork::dump(std::ostream& str) const {
this->AstNode::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 << " -> ";
+61 -38
View File
@@ -115,14 +115,14 @@ public:
dtypeSetLogicUnsized(32, m_num.widthMin(), VSigning::SIGNED);
}
class Unsized64 {}; // for creator type-overload selection
AstConst(FileLine* fl, Unsized64, vluint64_t num)
AstConst(FileLine* fl, Unsized64, uint64_t num)
: ASTGEN_SUPER_Const(fl)
, m_num(this, 64, 0) {
m_num.setQuad(num);
dtypeSetLogicSized(64, VSigning::UNSIGNED);
}
class SizedEData {}; // for creator type-overload selection
AstConst(FileLine* fl, SizedEData, vluint64_t num)
AstConst(FileLine* fl, SizedEData, uint64_t num)
: ASTGEN_SUPER_Const(fl)
, m_num(this, VL_EDATASIZE, 0) {
m_num.setQuad(num);
@@ -166,8 +166,8 @@ public:
const V3Number& num() const { return m_num; } // * = Value
V3Number& num() { return m_num; } // * = Value
uint32_t toUInt() const { return num().toUInt(); }
vlsint32_t toSInt() const { return num().toSInt(); }
vluint64_t toUQuad() const { return num().toUQuad(); }
int32_t toSInt() const { return num().toSInt(); }
uint64_t toUQuad() const { return num().toUQuad(); }
virtual string emitVerilog() override { V3ERROR_NA_RETURN(""); }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual bool cleanOut() const override { return true; }
@@ -1914,14 +1914,14 @@ private:
bool m_pure = false; // Pure optimizable
public:
AstCMethodHard(FileLine* fl, AstNode* fromp, VFlagChildDType, const string& name,
AstNode* pinsp)
AstNode* pinsp = nullptr)
: ASTGEN_SUPER_CMethodHard(fl, false)
, m_name{name} {
setOp1p(fromp);
dtypep(nullptr); // V3Width will resolve
addNOp2p(pinsp);
}
AstCMethodHard(FileLine* fl, AstNode* fromp, const string& name, AstNode* pinsp)
AstCMethodHard(FileLine* fl, AstNode* fromp, const string& name, AstNode* pinsp = nullptr)
: ASTGEN_SUPER_CMethodHard(fl, false)
, m_name{name} {
setOp1p(fromp);
@@ -2142,6 +2142,9 @@ public:
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
// op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_AS(op1p(), NodeDType); }
// op2 = Net delay
AstNode* delayp() const { return op2p(); }
void delayp(AstNode* const nodep) { setNOp2p(nodep); }
AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); }
// (Slow) recurse down to find basic data type (Note don't need virtual -
// AstVar isn't a NodeDType)
@@ -3407,7 +3410,7 @@ public:
};
class AstInitialAutomatic final : public AstNodeProcedure {
// initial for automatic variables
// Automatic variable initialization
// That is, it runs every function start, or class construction
public:
AstInitialAutomatic(FileLine* fl, AstNode* bodysp)
@@ -3415,6 +3418,15 @@ public:
ASTNODE_NODE_FUNCS(InitialAutomatic)
};
class AstInitialStatic final : public AstNodeProcedure {
// Static variable initialization
// That is, it runs at the beginning of simulation, before 'initial' blocks
public:
AstInitialStatic(FileLine* fl, AstNode* bodysp)
: ASTGEN_SUPER_InitialStatic(fl, bodysp) {}
ASTNODE_NODE_FUNCS(InitialStatic)
};
class AstAlways final : public AstNodeProcedure {
const VAlwaysKwd m_keyword;
@@ -3472,8 +3484,8 @@ public:
class AstAssign final : public AstNodeAssign {
public:
AstAssign(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
: ASTGEN_SUPER_Assign(fl, lhsp, rhsp) {
AstAssign(FileLine* fl, AstNode* lhsp, AstNode* rhsp, AstNode* timingControlp = nullptr)
: ASTGEN_SUPER_Assign(fl, lhsp, rhsp, timingControlp) {
dtypeFrom(lhsp);
}
ASTNODE_NODE_FUNCS(Assign)
@@ -3498,8 +3510,8 @@ public:
class AstAssignDly final : public AstNodeAssign {
public:
AstAssignDly(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
: ASTGEN_SUPER_AssignDly(fl, lhsp, rhsp) {}
AstAssignDly(FileLine* fl, AstNode* lhsp, AstNode* rhsp, AstNode* timingControlp = nullptr)
: ASTGEN_SUPER_AssignDly(fl, lhsp, rhsp, timingControlp) {}
ASTNODE_NODE_FUNCS(AssignDly)
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override {
return new AstAssignDly(this->fileline(), lhsp, rhsp);
@@ -3808,15 +3820,18 @@ public:
class AstDelay final : public AstNodeStmt {
// Delay statement
public:
AstDelay(FileLine* fl, AstNode* lhsp)
AstDelay(FileLine* fl, AstNode* lhsp, AstNode* stmtsp)
: ASTGEN_SUPER_Delay(fl) {
setOp1p(lhsp);
setNOp2p(stmtsp);
}
ASTNODE_NODE_FUNCS(Delay)
virtual bool same(const AstNode* samep) const override { return true; }
//
AstNode* lhsp() const { return op1p(); } // op2 = Statements to evaluate
AstNode* lhsp() const { return op1p(); } // op1 = delay value
void lhsp(AstNode* nodep) { setOp1p(nodep); }
void stmtsp(AstNode* nodep) { setOp2p(nodep); } // op2 = statements under delay
AstNode* stmtsp() const { return op2p(); }
};
class AstGenCase final : public AstNodeCase {
@@ -4610,7 +4625,7 @@ public:
class AstWhile final : public AstNodeStmt {
public:
AstWhile(FileLine* fl, AstNode* condp, AstNode* bodysp, AstNode* incsp = nullptr)
AstWhile(FileLine* fl, AstNode* condp, AstNode* bodysp = nullptr, AstNode* incsp = nullptr)
: ASTGEN_SUPER_While(fl) {
setOp2p(condp);
addNOp3p(bodysp);
@@ -4714,7 +4729,7 @@ private:
bool m_unique0Pragma; // unique0 case
bool m_priorityPragma; // priority case
public:
AstIf(FileLine* fl, AstNode* condp, AstNode* ifsp, AstNode* elsesp = nullptr)
AstIf(FileLine* fl, AstNode* condp, AstNode* ifsp = nullptr, AstNode* elsesp = nullptr)
: ASTGEN_SUPER_If(fl, condp, ifsp, elsesp) {
m_uniquePragma = false;
m_unique0Pragma = false;
@@ -4927,7 +4942,7 @@ private:
bool m_generate; // Underneath a generate
const bool m_implied; // Not inserted by user
public:
// Node that simply puts name into the output stream
// Node that puts name into the output stream
AstBegin(FileLine* fl, const string& name, AstNode* stmtsp, bool generate = false,
bool implied = false)
: ASTGEN_SUPER_Begin(fl, name, stmtsp)
@@ -4951,7 +4966,7 @@ class AstFork final : public AstNodeBlock {
private:
VJoinType m_joinType; // Join keyword type
public:
// Node that simply puts name into the output stream
// Node that puts name into the output stream
AstFork(FileLine* fl, const string& name, AstNode* stmtsp)
: ASTGEN_SUPER_Fork(fl, name, stmtsp) {}
ASTNODE_NODE_FUNCS(Fork)
@@ -5019,7 +5034,7 @@ class AstInitArray final : public AstNode {
// Parents: ASTVAR::init()
// Children: AstInitItem
public:
using KeyItemMap = std::map<vluint64_t, AstInitItem*>;
using KeyItemMap = std::map<uint64_t, AstInitItem*>;
private:
KeyItemMap m_map; // Node value for each array index
@@ -5054,7 +5069,7 @@ public:
AstNode* initsp() const { return op2p(); } // op2 = Initial value expressions
void addValuep(AstNode* newp) { addIndexValuep(m_map.size(), newp); }
const KeyItemMap& map() const { return m_map; }
AstNode* addIndexValuep(vluint64_t index, AstNode* newp) {
AstNode* addIndexValuep(uint64_t index, AstNode* newp) {
// Returns old value, caller must garbage collect
AstNode* oldp = nullptr;
const auto it = m_map.find(index);
@@ -5068,7 +5083,7 @@ public:
}
return oldp;
}
AstNode* getIndexValuep(vluint64_t index) const {
AstNode* getIndexValuep(uint64_t index) const {
const auto it = m_map.find(index);
if (it == m_map.end()) {
return nullptr;
@@ -5076,7 +5091,7 @@ public:
return it->second->valuep();
}
}
AstNode* getIndexDefaultedValuep(vluint64_t index) const {
AstNode* getIndexDefaultedValuep(uint64_t index) const {
AstNode* valuep = getIndexValuep(index);
if (!valuep) valuep = defaultp();
return valuep;
@@ -5234,15 +5249,15 @@ public:
}
};
class AstTimingControl final : public AstNodeStmt {
class AstEventControl final : public AstNodeStmt {
// Parents: stmtlist
public:
AstTimingControl(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp)
: ASTGEN_SUPER_TimingControl(fl) {
AstEventControl(FileLine* fl, AstSenTree* sensesp, AstNode* stmtsp)
: ASTGEN_SUPER_EventControl(fl) {
setNOp1p(sensesp);
setNOp2p(stmtsp);
}
ASTNODE_NODE_FUNCS(TimingControl)
ASTNODE_NODE_FUNCS(EventControl)
virtual string verilogKwd() const override { return "@(%l) %r"; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
@@ -5330,6 +5345,7 @@ public:
dtypeFrom(valuep);
addNOp1p(valuep);
}
virtual void dump(std::ostream& str) const override;
virtual int instrCount() const override { return 100; } // Large...
ASTNODE_NODE_FUNCS(TraceDecl)
virtual string name() const override { return m_showname; }
@@ -8522,6 +8538,7 @@ public:
AstNodeDType* childDTypep() const { return VN_AS(op1p(), NodeDType); }
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
AstNode* itemsp() const { return op2p(); } // op2 = AstPatReplicate, AstPatMember, etc
void addItemsp(AstNode* nodep) { addOp2p(nodep); }
};
class AstPatMember final : public AstNodeMath {
// Verilog '{a} or '{a{b}}
@@ -9199,27 +9216,30 @@ public:
class AstExecGraph final : public AstNode {
// For parallel execution, this node contains a dependency graph. Each
// node in the graph is an ExecMTask, which contains a body for the
// mtask, which contains a set of AstActive's, each of which calls a
// leaf AstCFunc. whew!
// vertex in the graph is an ExecMTask, which contains a body for the
// mtask (an AstMTaskBody), which contains sequentially executed statements.
//
// The mtask bodies are also children of this node, so we can visit
// them without traversing the graph (it's not always needed to
// traverse the graph.)
// The AstMTaskBody nodes are also children of this node, so we can visit
// them without traversing the graph.
private:
V3Graph* const m_depGraphp; // contains ExecMTask's
V3Graph* const m_depGraphp; // contains ExecMTask vertices
const string m_name; // Name of this AstExecGraph (for uniqueness at code generation)
public:
explicit AstExecGraph(FileLine* fl);
explicit AstExecGraph(FileLine* fl, const string& name);
ASTNODE_NODE_FUNCS_NO_DTOR(ExecGraph)
virtual ~AstExecGraph() override;
virtual const char* broken() const override {
BROKEN_RTN(!m_depGraphp);
return nullptr;
}
virtual string name() const override { return m_name; }
V3Graph* depGraphp() { return m_depGraphp; }
const V3Graph* depGraphp() const { return m_depGraphp; }
V3Graph* mutableDepGraphp() { return m_depGraphp; }
void addMTaskBody(AstMTaskBody* bodyp) { addOp1p(bodyp); }
// op1: The mtask bodies
AstMTaskBody* mTaskBodiesp() const { return VN_AS(op1p(), MTaskBody); }
void addMTaskBodyp(AstMTaskBody* bodyp) { addOp1p(bodyp); }
// op2: In later phases, the statements that start the parallel execution
void addStmtsp(AstNode* stmtp) { addOp2p(stmtp); }
};
@@ -9319,13 +9339,15 @@ private:
AstConstPool* const m_constPoolp; // Reference to constant pool, for faster lookup
AstPackage* m_dollarUnitPkgp = nullptr; // $unit
AstCFunc* m_evalp = nullptr; // The '_eval' function
AstExecGraph* m_execGraphp = nullptr; // Execution MTask graph for threads>1 mode
AstVarScope* m_dpiExportTriggerp = nullptr; // The DPI export trigger variable
AstTopScope* m_topScopep = nullptr; // The singleton AstTopScope under the top module
VTimescale m_timeunit; // Global time unit
VTimescale m_timeprecision; // Global time precision
bool m_changeRequest = false; // Have _change_request method
bool m_timescaleSpecified = false; // Input HDL specified timescale
uint32_t m_nextFreeMTaskID = 1; // Next unique MTask ID within netlist
// starts at 1 so 0 means no MTask ID
uint32_t m_nextFreeMTaskProfilingID = 0; // Next unique ID to use for PGO
public:
AstNetlist();
ASTNODE_NODE_FUNCS(Netlist)
@@ -9369,8 +9391,6 @@ public:
}
AstCFunc* evalp() const { return m_evalp; }
void evalp(AstCFunc* evalp) { m_evalp = evalp; }
AstExecGraph* execGraphp() const { return m_execGraphp; }
void execGraphp(AstExecGraph* graphp) { m_execGraphp = graphp; }
AstVarScope* dpiExportTriggerp() const { return m_dpiExportTriggerp; }
void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; }
AstTopScope* topScopep() const { return m_topScopep; }
@@ -9390,6 +9410,9 @@ public:
void timeprecisionMerge(FileLine*, const VTimescale& value);
void timescaleSpecified(bool specified) { m_timescaleSpecified = specified; }
bool timescaleSpecified() const { return m_timescaleSpecified; }
uint32_t allocNextMTaskID() { return m_nextFreeMTaskID++; }
uint32_t allocNextMTaskProfilingID() { return m_nextFreeMTaskProfilingID++; }
uint32_t usedMTaskProfilingIDs() const { return m_nextFreeMTaskProfilingID; }
};
//######################################################################
+2 -2
View File
@@ -106,7 +106,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 +117,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> {};
+28 -23
View File
@@ -76,6 +76,33 @@ private:
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 + 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) {
@@ -141,30 +168,8 @@ private:
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
+4 -5
View File
@@ -384,7 +384,7 @@ private:
VL_DANGLING(iconstp);
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
} else {
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
// Not a caseX mask, we can build CASEEQ(cexpr icond)
AstNode* const and1p = cexprp->cloneTree(false);
AstNode* const and2p = icondp;
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
@@ -427,8 +427,7 @@ private:
if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1;
if (depth == 1) { // First group or starting new group
itemnextp = nullptr;
AstIf* const newp
= new AstIf(itemp->fileline(), ifexprp->cloneTree(true), nullptr, nullptr);
AstIf* const newp = new AstIf(itemp->fileline(), ifexprp->cloneTree(true));
if (groupnextp) {
groupnextp->addElsesp(newp);
} else {
@@ -448,7 +447,7 @@ private:
VL_DO_DANGLING(itemexprp->deleteTree(), itemexprp);
itemexprp = new AstConst(itemp->fileline(), AstConst::BitTrue());
}
AstIf* const newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, nullptr);
AstIf* const newp = new AstIf(itemp->fileline(), itemexprp, istmtsp);
if (itemnextp) {
itemnextp->addElsesp(newp);
} else {
@@ -497,7 +496,7 @@ private:
V3Case::caseLint(nodep);
iterateChildren(nodep);
if (debug() >= 9) nodep->dumpTree(cout, " case_old: ");
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
if (isCaseTreeFast(nodep) && v3Global.opt.fCase()) {
// It's a simple priority encoder or complete statement
// we can make a tree of statements to avoid extra comparisons
++m_statCaseFast;
+1
View File
@@ -716,6 +716,7 @@ private:
// Ignores
virtual void visit(AstInitial*) override {}
virtual void visit(AstInitialAutomatic*) override {}
virtual void visit(AstInitialStatic*) override {}
virtual void visit(AstTraceDecl*) override {}
virtual void visit(AstCoverToggle*) override {}
virtual void visit(AstNodeDType*) override {}
+7 -1
View File
@@ -97,6 +97,7 @@ private:
m_prefix = nodep->name() + "__02e"; // .
iterateChildren(nodep);
}
nodep->repairCache();
}
virtual void visit(AstNodeModule* nodep) override {
// Visit for NodeModules that are not AstClass (AstClass is-a AstNodeModule)
@@ -153,6 +154,11 @@ private:
iterateChildren(nodep);
if (m_packageScopep) { m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep)); }
}
virtual void visit(AstInitialStatic* nodep) override {
// But not AstInitialAutomatic, which remains under the class
iterateChildren(nodep);
if (m_packageScopep) { m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep)); }
}
virtual void visit(AstNodeMath* nodep) override {} // Short circuit
virtual void visit(AstNodeStmt* nodep) override {} // Short circuit
@@ -173,7 +179,7 @@ public:
vscp->scopep(scopep);
vscp->unlinkFrBack();
scopep->addVarp(vscp);
} else if (VN_IS(nodep, Initial)) {
} else if (VN_IS(nodep, Initial) || VN_IS(nodep, InitialStatic)) {
nodep->unlinkFrBack();
scopep->addActivep(nodep);
} else {
+5 -4
View File
@@ -185,7 +185,7 @@ private:
AstIf* makeActiveIf(AstSenTree* sensesp) {
AstNode* const senEqnp = createSenseEquation(sensesp->sensesp());
UASSERT_OBJ(senEqnp, sensesp, "No sense equation, shouldn't be in sequent activation.");
AstIf* const newifp = new AstIf(sensesp->fileline(), senEqnp, nullptr, nullptr);
AstIf* const newifp = new AstIf(sensesp->fileline(), senEqnp);
return newifp;
}
void clearLastSen() {
@@ -310,8 +310,8 @@ private:
AstNode* const origp = nodep->origp()->unlinkFrBack();
AstNode* const changeWrp = nodep->changep()->unlinkFrBack();
AstNode* const changeRdp = ConvertWriteRefsToRead::main(changeWrp->cloneTree(false));
AstIf* const newp = new AstIf(
nodep->fileline(), new AstXor(nodep->fileline(), origp, changeRdp), incp, nullptr);
AstIf* const newp
= new AstIf(nodep->fileline(), new AstXor(nodep->fileline(), origp, changeRdp), incp);
// We could add another IF to detect posedges, and only increment if so.
// It's another whole branch though versus a potential memory miss.
// We'll go with the miss.
@@ -411,7 +411,8 @@ private:
}
}
virtual void visit(AstExecGraph* nodep) override {
for (m_mtaskBodyp = VN_AS(nodep->op1p(), MTaskBody); m_mtaskBodyp;
VL_RESTORER(m_mtaskBodyp);
for (m_mtaskBodyp = nodep->mTaskBodiesp(); m_mtaskBodyp;
m_mtaskBodyp = VN_AS(m_mtaskBodyp->nextp(), MTaskBody)) {
clearLastSen();
iterate(m_mtaskBodyp);
+121 -22
View File
@@ -44,13 +44,8 @@ public:
/// Update into maps from other
void update(const V3ConfigWildcardResolver& other) {
typename Map::const_iterator it;
for (it = other.m_mapResolved.begin(); it != other.m_mapResolved.end(); ++it) {
m_mapResolved[it->first].update(it->second);
}
for (it = other.m_mapWildcard.begin(); it != other.m_mapWildcard.end(); ++it) {
m_mapWildcard[it->first].update(it->second);
}
for (const auto& itr : other.m_mapResolved) m_mapResolved[itr.first].update(itr.second);
for (const auto& itr : other.m_mapWildcard) m_mapWildcard[itr.first].update(itr.second);
}
// Access and create a (wildcard) entity
@@ -68,12 +63,12 @@ public:
// Cannot be resolved, create if matched
// Update this entity with all matches in the wildcards
for (it = m_mapWildcard.begin(); it != m_mapWildcard.end(); ++it) {
if (VString::wildmatch(name, it->first)) {
for (const auto& wildent : m_mapWildcard) {
if (VString::wildmatch(name, wildent.first)) {
if (!newp) {
newp = &m_mapResolved[name]; // Emplace and get pointer
}
newp->update(it->second);
newp->update(wildent.second);
}
}
return newp;
@@ -198,8 +193,8 @@ public:
AstNode* const nodep = new AstPragma(modp->fileline(), type);
modp->addStmtp(nodep);
}
for (auto it = m_modPragmas.cbegin(); it != m_modPragmas.cend(); ++it) {
AstNode* const nodep = new AstPragma(modp->fileline(), *it);
for (const auto& itr : m_modPragmas) {
AstNode* const nodep = new AstPragma{modp->fileline(), itr};
modp->addStmtp(nodep);
}
}
@@ -344,32 +339,131 @@ public:
using V3ConfigFileResolver = V3ConfigWildcardResolver<V3ConfigFile>;
//######################################################################
// ScopeTrace tracking
class V3ConfigScopeTraceEntry final {
public:
const string m_scope; // Scope or regexp to match
const bool m_on = false; // True to enable message
int m_levels = 0; // # levels, 0 = all, 1 = only this, ...
// CONSTRUCTORS
V3ConfigScopeTraceEntry(const string& scope, bool on, int levels)
: m_scope{scope}
, m_on{on}
, m_levels{levels} {}
~V3ConfigScopeTraceEntry() = default;
bool operator<(const V3ConfigScopeTraceEntry& other) const {
if (m_on < other.m_on) return true;
if (m_on > other.m_on) return false;
if (m_levels < other.m_levels) return true;
if (m_levels > other.m_levels) return false;
return m_scope < other.m_scope;
}
};
// Tracks what matches are known to hit against V3ConfigScopeTraceEntries
class V3ConfigScopeTraceEntryMatch final {
public:
const V3ConfigScopeTraceEntry* m_entryp;
const string m_scopepart;
V3ConfigScopeTraceEntryMatch(const V3ConfigScopeTraceEntry* entryp, const string& scopepart)
: m_entryp{entryp}
, m_scopepart{scopepart} {}
bool operator<(const V3ConfigScopeTraceEntryMatch& other) const {
if (m_entryp < other.m_entryp) return true;
if (m_entryp > other.m_entryp) return false;
return m_scopepart < other.m_scopepart;
}
};
class V3ConfigScopeTraceResolver final {
std::vector<V3ConfigScopeTraceEntry> m_entries; // User specified on/offs and levels
std::map<V3ConfigScopeTraceEntryMatch, bool> m_matchCache; // Matching entries for speed
public:
void addScopeTraceOn(bool on, const string& scope, int levels) {
UINFO(9, "addScopeTraceOn " << on << " '" << scope << "' "
<< " levels=" << levels << endl);
m_entries.emplace_back(V3ConfigScopeTraceEntry{scope, on, levels});
m_matchCache.clear();
}
bool getEntryMatch(const V3ConfigScopeTraceEntry* entp, const string& scopepart) {
// Return if a entry matches the scopepart, with memoization
const auto& key = V3ConfigScopeTraceEntryMatch{entp, scopepart};
const auto& it = m_matchCache.find(key);
if (it != m_matchCache.end()) return it->second; // Cached
const bool matched = VString::wildmatch(scopepart, entp->m_scope);
m_matchCache.emplace(key, matched);
return matched;
}
bool getScopeTraceOn(const string& scope) {
// Apply in the order the user provided them, so they can choose on/off preferencing
int maxLevel = 1;
for (const auto& ch : scope) {
if (ch == '.') ++maxLevel;
}
UINFO(9, "getScopeTraceOn " << scope << " maxLevel=" << maxLevel << endl);
bool enabled = true;
for (const auto& ent : m_entries) {
// We apply shortest match first for each rule component
// (Otherwise the levels would be useless as "--scope top* --levels 1" would
// always match at every scopepart, and we wound't know how to count levels)
int partLevel = 1;
for (string::size_type partEnd = 0; true;) {
partEnd = scope.find('.', partEnd + 1);
if (partEnd == string::npos) partEnd = scope.length();
const std::string scopepart = scope.substr(0, partEnd);
if (getEntryMatch(&ent, scopepart)) {
const bool levelMatch
= !ent.m_levels || (ent.m_levels >= maxLevel - partLevel);
if (levelMatch) enabled = ent.m_on;
UINFO(9, "getScopeTraceOn-part " << scope << " enabled=" << enabled
<< " @ lev=" << partLevel
<< (levelMatch ? "[match]" : "[miss]")
<< " from scopepart=" << scopepart << endl);
break;
}
if (partEnd == scope.length()) break;
++partLevel;
}
}
return enabled;
}
};
//######################################################################
// Resolve modules and files in the design
class V3ConfigResolver final {
V3ConfigModuleResolver m_modules; // Access to module names (with wildcards)
V3ConfigFileResolver m_files; // Access to file names (with wildcards)
std::unordered_map<string, std::unordered_map<string, vluint64_t>>
V3ConfigScopeTraceResolver m_scopeTraces; // Regexp to trace enables
std::unordered_map<string, std::unordered_map<string, uint64_t>>
m_profileData; // Access to profile_data records
FileLine* m_profileFileLine = nullptr;
static V3ConfigResolver s_singleton; // Singleton (not via local static, as that's slow)
V3ConfigResolver() = default;
~V3ConfigResolver() = default;
public:
static V3ConfigResolver& s() { return s_singleton; }
static V3ConfigResolver& s() {
static V3ConfigResolver s_singleton;
return s_singleton;
}
V3ConfigModuleResolver& modules() { return m_modules; }
V3ConfigFileResolver& files() { return m_files; }
V3ConfigScopeTraceResolver& scopeTraces() { return m_scopeTraces; }
void addProfileData(FileLine* fl, const string& model, const string& key, vluint64_t cost) {
void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost) {
if (!m_profileFileLine) m_profileFileLine = fl;
if (cost == 0) cost = 1; // Cost 0 means delete (or no data)
m_profileData[model][key] += cost;
}
vluint64_t getProfileData(const string& model, const string& key) const {
uint64_t getProfileData(const string& model, const string& key) const {
const auto mit = m_profileData.find(model);
if (mit == m_profileData.cend()) return 0;
const auto it = mit->second.find(key);
@@ -379,8 +473,6 @@ public:
FileLine* getProfileDataFileLine() const { return m_profileFileLine; } // Maybe null
};
V3ConfigResolver V3ConfigResolver::s_singleton;
//######################################################################
// V3Config
@@ -430,10 +522,14 @@ void V3Config::addModulePragma(const string& module, VPragmaType pragma) {
}
void V3Config::addProfileData(FileLine* fl, const string& model, const string& key,
vluint64_t cost) {
uint64_t cost) {
V3ConfigResolver::s().addProfileData(fl, model, key, cost);
}
void V3Config::addScopeTraceOn(bool on, const string& scope, int levels) {
V3ConfigResolver::s().scopeTraces().addScopeTraceOn(on, scope, levels);
}
void V3Config::addVarAttr(FileLine* fl, const string& module, const string& ftask,
const string& var, VAttrType attr, AstSenTree* sensep) {
// Semantics: sensep only if public_flat_rw
@@ -534,12 +630,15 @@ void V3Config::applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar
if (vp) vp->apply(varp);
}
vluint64_t V3Config::getProfileData(const string& model, const string& key) {
uint64_t V3Config::getProfileData(const string& model, const string& key) {
return V3ConfigResolver::s().getProfileData(model, key);
}
FileLine* V3Config::getProfileDataFileLine() {
return V3ConfigResolver::s().getProfileDataFileLine();
}
bool V3Config::getScopeTraceOn(const string& scope) {
return V3ConfigResolver::s().scopeTraces().getScopeTraceOn(scope);
}
bool V3Config::waive(FileLine* filelinep, V3ErrorCode code, const string& message) {
V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filelinep->filename());
+6 -4
View File
@@ -36,20 +36,22 @@ public:
static void addInline(FileLine* fl, const string& module, const string& ftask, bool on);
static void addModulePragma(const string& module, VPragmaType pragma);
static void addProfileData(FileLine* fl, const string& model, const string& key,
vluint64_t cost);
static void addWaiver(V3ErrorCode code, const string& filename, const string& message);
uint64_t cost);
static void addScopeTraceOn(bool on, const string& scope, int levels);
static void addVarAttr(FileLine* fl, const string& module, const string& ftask,
const string& signal, VAttrType type, AstSenTree* nodep);
static void addWaiver(V3ErrorCode code, const string& filename, const string& message);
static void applyCase(AstCase* nodep);
static void applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep);
static void applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp);
static void applyIgnores(FileLine* filelinep);
static void applyModule(AstNodeModule* modulep);
static void applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp);
static void applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp);
static vluint64_t getProfileData(const string& model, const string& key);
static uint64_t getProfileData(const string& model, const string& key);
static FileLine* getProfileDataFileLine();
static bool getScopeTraceOn(const string& scope);
static bool waive(FileLine* filelinep, V3ErrorCode code, const string& message);
};
+66 -21
View File
@@ -33,6 +33,7 @@
#include "V3UniqueNames.h"
#include <algorithm>
#include <memory>
#include <type_traits>
//######################################################################
@@ -110,6 +111,15 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
BitPolarityEntry() = default;
};
struct FrozenNodeInfo final { // Context when a frozen node is found
bool m_polarity;
int m_lsb;
bool operator<(const FrozenNodeInfo& other) const {
if (m_lsb != other.m_lsb) return m_lsb < other.m_lsb;
return m_polarity < other.m_polarity;
}
};
class Restorer final { // Restore the original state unless disableRestore() is called
ConstBitOpTreeVisitor& m_visitor;
const size_t m_polaritiesSize;
@@ -298,7 +308,8 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
LeafInfo* m_leafp = nullptr; // AstConst or AstVarRef that currently looking for
const AstNode* const m_rootp; // Root of this AST subtree
std::vector<AstNode*> m_frozenNodes; // Nodes that cannot be optimized
std::vector<std::pair<AstNode*, FrozenNodeInfo>>
m_frozenNodes; // Nodes that cannot be optimized
std::vector<BitPolarityEntry> m_bitPolarities; // Polarity of bits found during iterate()
std::vector<std::unique_ptr<VarInfo>> m_varInfos; // VarInfo for each variable, [0] is nullptr
@@ -486,7 +497,7 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
restorer.restoreNow();
// Reach past a cast then add to frozen nodes to be added to final reduction
if (const AstCCast* const castp = VN_CAST(opp, CCast)) opp = castp->lhsp();
m_frozenNodes.push_back(opp);
m_frozenNodes.emplace_back(opp, FrozenNodeInfo{m_polarity, m_lsb});
m_failed = origFailed;
continue;
}
@@ -651,17 +662,21 @@ public:
}
}
std::map<FrozenNodeInfo, std::vector<AstNode*>> frozenNodes; // Group by FrozenNodeInfo
// Check if frozen terms are clean or not
for (AstNode* const termp : visitor.m_frozenNodes) {
for (const auto& frozenInfo : visitor.m_frozenNodes) {
AstNode* const termp = frozenInfo.first;
// Comparison operators are clean
if (VN_IS(termp, Eq) || VN_IS(termp, Neq) || VN_IS(termp, Lt) || VN_IS(termp, Lte)
|| VN_IS(termp, Gt) || VN_IS(termp, Gte)) {
if ((VN_IS(termp, Eq) || VN_IS(termp, Neq) || VN_IS(termp, Lt) || VN_IS(termp, Lte)
|| VN_IS(termp, Gt) || VN_IS(termp, Gte))
&& frozenInfo.second.m_lsb == 0) {
hasCleanTerm = true;
} else {
// Otherwise, conservatively assume the frozen term is dirty
hasDirtyTerm = true;
UINFO(9, "Dirty frozen term: " << termp << endl);
}
frozenNodes[frozenInfo.second].push_back(termp);
}
// Figure out if a final negation is required
@@ -671,7 +686,12 @@ public:
const bool needsCleaning = visitor.isAndTree() ? !hasCleanTerm : hasDirtyTerm;
// Add size of reduction tree to op count
resultOps += termps.size() + visitor.m_frozenNodes.size() - 1;
resultOps += termps.size() - 1;
for (const auto& lsbAndNodes : frozenNodes) {
if (lsbAndNodes.first.m_lsb > 0) ++resultOps; // Needs AstShiftR
if (!lsbAndNodes.first.m_polarity) ++resultOps; // Needs AstNot
resultOps += lsbAndNodes.second.size();
}
// Add final polarity flip in Xor tree
if (needsFlip) ++resultOps;
// Add final cleaning AND
@@ -680,7 +700,10 @@ public:
if (debug() >= 9) { // LCOV_EXCL_START
cout << "Bitop tree considered: " << endl;
for (AstNode* const termp : termps) termp->dumpTree("Reduced term: ");
for (AstNode* const termp : visitor.m_frozenNodes) termp->dumpTree("Frozen term: ");
for (const std::pair<AstNode*, FrozenNodeInfo>& termp : visitor.m_frozenNodes)
termp.first->dumpTree("Frozen term with lsb " + std::to_string(termp.second.m_lsb)
+ " polarity " + std::to_string(termp.second.m_polarity)
+ ": ");
cout << "Needs flipping: " << needsFlip << endl;
cout << "Needs cleaning: " << needsCleaning << endl;
cout << "Size: " << resultOps << " input size: " << visitor.m_ops << endl;
@@ -723,8 +746,25 @@ public:
resultp = reduce(resultp, termp);
}
// Add any frozen terms to the reduction
for (AstNode* const frozenp : visitor.m_frozenNodes) {
resultp = reduce(resultp, frozenp->unlinkFrBack());
for (auto&& nodes : frozenNodes) {
// nodes.second has same lsb and polarity
AstNode* termp = nullptr;
for (AstNode* const itemp : nodes.second) {
termp = reduce(termp, itemp->unlinkFrBack());
}
if (nodes.first.m_lsb > 0) { // LSB is not 0, so shiftR
AstNodeDType* const dtypep = termp->dtypep();
termp = new AstShiftR{termp->fileline(), termp,
new AstConst(termp->fileline(), AstConst::WidthedValue{},
termp->width(), nodes.first.m_lsb)};
termp->dtypep(dtypep);
}
if (!nodes.first.m_polarity) { // Polarity is inverted, so append Not
AstNodeDType* const dtypep = termp->dtypep();
termp = new AstNot{termp->fileline(), termp};
termp->dtypep(dtypep);
}
resultp = reduce(resultp, termp);
}
// Set width of masks to expected result width. This is required to prevent later removal
@@ -768,6 +808,9 @@ public:
class ConstVisitor final : public VNVisitor {
private:
// CONSTANTS
static constexpr unsigned CONCAT_MERGABLE_MAX_DEPTH = 10; // Limit alg recursion
// NODE STATE
// ** only when m_warn/m_doExpensive is set. If state is needed other times,
// ** must track down everywhere V3Const is called and make sure no overlaps.
@@ -1047,7 +1090,7 @@ private:
bool matchBitOpTree(AstNode* nodep) {
if (nodep->widthMin() != 1) return false;
if (!v3Global.opt.oConstBitOpTree()) return false;
if (!v3Global.opt.fConstBitOpTree()) return false;
string debugPrefix;
if (debug() >= 9) { // LCOV_EXCL_START
@@ -1369,7 +1412,7 @@ private:
return (VN_IS(nodep, And) || VN_IS(nodep, Or) || VN_IS(nodep, Xor));
}
bool ifAdjacentSel(const AstSel* lhsp, const AstSel* rhsp) {
if (!v3Global.opt.oAssemble()) return false; // opt disabled
if (!v3Global.opt.fAssemble()) return false; // opt disabled
if (!lhsp || !rhsp) return false;
const AstNode* const lfromp = lhsp->fromp();
const AstNode* const rfromp = rhsp->fromp();
@@ -1384,7 +1427,7 @@ private:
}
bool ifMergeAdjacent(AstNode* lhsp, AstNode* rhsp) {
// called by concatmergeable to determine if {lhsp, rhsp} make sense
if (!v3Global.opt.oAssemble()) return false; // opt disabled
if (!v3Global.opt.fAssemble()) return false; // opt disabled
// two same varref
if (operandsSame(lhsp, rhsp)) return true;
const AstSel* lselp = VN_CAST(lhsp, Sel);
@@ -1419,11 +1462,12 @@ private:
if (rend == rfromp->width() && lstart->toSInt() == 0) return true;
return false;
}
bool concatMergeable(const AstNode* lhsp, const AstNode* rhsp) {
bool concatMergeable(const AstNode* lhsp, const AstNode* rhsp, unsigned depth) {
// determine if {a OP b, c OP d} => {a, c} OP {b, d} is advantageous
if (!v3Global.opt.oAssemble()) return false; // opt disabled
if (!v3Global.opt.fAssemble()) return false; // opt disabled
if (lhsp->type() != rhsp->type()) return false;
if (!ifConcatMergeableBiop(lhsp)) return false;
if (depth > CONCAT_MERGABLE_MAX_DEPTH) return false; // As worse case O(n^2) algorithm
const AstNodeBiop* const lp = VN_CAST(lhsp, NodeBiop);
const AstNodeBiop* const rp = VN_CAST(rhsp, NodeBiop);
@@ -1433,11 +1477,12 @@ private:
const bool rad = ifMergeAdjacent(lp->rhsp(), rp->rhsp());
if (lad && rad) return true;
// {a[] & b[]&c[], a[] & b[]&c[]}
if (lad && concatMergeable(lp->rhsp(), rp->rhsp())) return true;
if (lad && concatMergeable(lp->rhsp(), rp->rhsp(), depth + 1)) return true;
// {a[]&b[] & c[], a[]&b[] & c[]}
if (rad && concatMergeable(lp->lhsp(), rp->lhsp())) return true;
if (rad && concatMergeable(lp->lhsp(), rp->lhsp(), depth + 1)) return true;
// {(a[]&b[])&(c[]&d[]), (a[]&b[])&(c[]&d[])}
if (concatMergeable(lp->lhsp(), rp->lhsp()) && concatMergeable(lp->rhsp(), rp->rhsp())) {
if (concatMergeable(lp->lhsp(), rp->lhsp(), depth + 1)
&& concatMergeable(lp->rhsp(), rp->rhsp(), depth + 1)) {
return true;
}
return false;
@@ -1697,7 +1742,7 @@ private:
AstNode* const lrp = lp->rhsp()->cloneTree(false);
AstNode* const rlp = rp->lhsp()->cloneTree(false);
AstNode* const rrp = rp->rhsp()->cloneTree(false);
if (concatMergeable(lp, rp)) {
if (concatMergeable(lp, rp, 0)) {
AstConcat* const newlp = new AstConcat(rlp->fileline(), llp, rlp);
AstConcat* const newrp = new AstConcat(rrp->fileline(), lrp, rrp);
// use the lhs to replace the parent concat
@@ -2505,7 +2550,7 @@ private:
if (nodep->access().isReadOnly()
&& ((!m_params // Can reduce constant wires into equations
&& m_doNConst
&& v3Global.opt.oConst()
&& v3Global.opt.fConst()
// Default value, not a "known" constant for this usage
&& !nodep->varp()->isClassMember()
&& !(nodep->varp()->isFuncLocal() && nodep->varp()->isNonOutput())
@@ -2658,7 +2703,7 @@ private:
// SENTREE(... SENITEM(x), SENGATE(SENITEM(x),*) ...) => SENITEM(x)
// Do we need the SENITEM's to be identical? No because we're
// ORing between them; we just need to ensure that the result is at
// least as frequently activating. So we simply
// least as frequently activating. So we
// SENGATE(SENITEM(x)) -> SENITEM(x), then let it collapse with the
// other SENITEM(x).
{
@@ -3367,7 +3412,7 @@ private:
TREEOPV("AstConcat{$lhsp.isZero, $rhsp}", "replaceExtend(nodep, nodep->rhsp())");
// CONCAT(a[1],a[0]) -> a[1:0]
TREEOPV("AstConcat{$lhsp.castSel, $rhsp.castSel, ifAdjacentSel(VN_AS($lhsp,,Sel),,VN_AS($rhsp,,Sel))}", "replaceConcatSel(nodep)");
TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp)}", "replaceConcatMerge(nodep)");
TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp,,0)}", "replaceConcatMerge(nodep)");
// Common two-level operations that can be simplified
TREEOP ("AstAnd {$lhsp.castConst,matchAndCond(nodep)}", "DONE");
TREEOP ("AstAnd {$lhsp.castConst, $rhsp.castOr, matchMaskedOr(nodep)}", "DONE");
+10 -2
View File
@@ -94,6 +94,7 @@ private:
bool m_inDly = false; // True in delayed assignments
bool m_inLoop = false; // True in for loops
bool m_inInitial = false; // True in initial blocks
bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK
using VarMap = std::map<const std::pair<AstNodeModule*, std::string>, AstVar*>;
VarMap m_modVarMap; // Table of new var names created under module
VDouble0 m_statSharedSet; // Statistic tracking
@@ -105,6 +106,7 @@ private:
void markVarUsage(AstNodeVarRef* nodep, bool blocking) {
// Ignore if warning is disabled on this reference (used by V3Force).
if (nodep->fileline()->warnIsOff(V3ErrorCode::BLKANDNBLK)) return;
if (m_ignoreBlkAndNBlk) return;
if (blocking) nodep->user5(true);
AstVarScope* const vscp = nodep->varScopep();
// UINFO(4, " MVU " << blocking << " " << nodep << endl);
@@ -351,8 +353,7 @@ private:
"Delayed assignment misoptimized; prev var found w/o associated IF");
} else {
postLogicp = new AstIf(nodep->fileline(),
new AstVarRef(nodep->fileline(), setvscp, VAccess::READ),
nullptr, nullptr);
new AstVarRef(nodep->fileline(), setvscp, VAccess::READ));
UINFO(9, " Created " << postLogicp << endl);
finalp->addStmtp(postLogicp);
finalp->user3p(setvscp); // Remember IF's vset variable
@@ -486,6 +487,13 @@ private:
}
}
virtual void visit(AstNodeReadWriteMem* nodep) override {
VL_RESTORER(m_ignoreBlkAndNBlk);
m_ignoreBlkAndNBlk = true; // $readmem/$writemem often used in mem models
// so we will suppress BLKANDNBLK warnings
iterateChildren(nodep);
}
virtual void visit(AstNodeFor* nodep) override { // LCOV_EXCL_LINE
nodep->v3fatalSrc(
"For statements should have been converted to while statements in V3Begin");
+2 -2
View File
@@ -161,7 +161,7 @@ private:
new AstCMath(funcp->fileline(),
string("&(") + funcp->scopep()->nameVlSym() + ")",
64)),
returnp, nullptr);
returnp);
newfuncp->addStmtsp(ifp);
} else {
newfuncp->addStmtsp(returnp);
@@ -176,7 +176,7 @@ private:
// newfuncp->addStmtsp(new AstStop(newfuncp->fileline()));
if (debug() >= 9) newfuncp->dumpTree(cout, " newfunc: ");
} else {
// Only a single function under this name, we can simply rename it
// Only a single function under this name, we can rename it
UINFO(6, " Wrapping " << name << " just one " << topFuncp << endl);
topFuncp->name(name);
}
+2 -2
View File
@@ -73,7 +73,7 @@ protected:
ofp()->putsNoTracking("}");
} else if (const AstUnpackArrayDType* const dtypep
= VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType)) {
const vluint64_t size = dtypep->elementsConst();
const uint64_t size = dtypep->elementsConst();
const uint32_t tabMod = tabModulus(dtypep->subDTypep());
// Note the double {{ initializer. The first { starts the initializer of the
// VlUnpacked, and the second starts the initializer of m_storage within the
@@ -81,7 +81,7 @@ protected:
puts("{");
ofp()->putsNoTracking("{");
puts("\n");
for (vluint64_t n = 0; n < size; ++n) {
for (uint64_t n = 0; n < size; ++n) {
m_unpackedWord = n;
if (n) puts((n % tabMod) ? ", " : ",\n");
iterate(nodep->getIndexDefaultedValuep(n));
+6 -5
View File
@@ -308,7 +308,8 @@ void EmitCFunc::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const
}
emitDispState.pushFormat(pfmt);
if (!ignore) {
if (argp->dtypep()->basicp()->keyword() == VBasicDTypeKwd::STRING) {
if (argp->dtypep()->basicp()
&& argp->dtypep()->basicp()->keyword() == VBasicDTypeKwd::STRING) {
// string in SystemVerilog is std::string in C++ which is not POD
emitDispState.pushArg(' ', nullptr, "-1");
} else {
@@ -543,7 +544,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
}
for (int word = VL_WORDS_I(upWidth) - 1; word >= 0; word--) {
// Only 32 bits - llx + long long here just to appease CPP format warning
ofp()->printf(",0x%08" PRIx64, static_cast<vluint64_t>(nodep->num().edataWord(
ofp()->printf(",0x%08" PRIx64, static_cast<uint64_t>(nodep->num().edataWord(
word + chunks * EMITC_NUM_CONSTW)));
}
puts(")");
@@ -565,7 +566,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
}
for (int word = EMITC_NUM_CONSTW - 1; word >= 0; word--) {
// Only 32 bits - llx + long long here just to appease CPP format warning
ofp()->printf(",0x%08" PRIx64, static_cast<vluint64_t>(nodep->num().edataWord(
ofp()->printf(",0x%08" PRIx64, static_cast<uint64_t>(nodep->num().edataWord(
word + chunks * EMITC_NUM_CONSTW)));
}
puts(")");
@@ -580,7 +581,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
ofp()->printf("%.17e", nodep->num().toDouble());
}
} else if (nodep->isQuad()) {
const vluint64_t num = nodep->toUQuad();
const uint64_t num = nodep->toUQuad();
if (num < 10) {
ofp()->printf("%" PRIu64 "ULL", num);
} else {
@@ -592,7 +593,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
if (num < 10) {
puts(cvtToStr(num));
} else {
ofp()->printf("0x%" PRIx64, static_cast<vluint64_t>(num));
ofp()->printf("0x%" PRIx64, static_cast<uint64_t>(num));
}
// If signed, we'll do our own functions
// But must be here, or <= comparisons etc may end up signed
+5 -7
View File
@@ -406,7 +406,7 @@ public:
virtual void visit(AstCMethodHard* nodep) override {
iterate(nodep->fromp());
puts(".");
puts(nodep->nameProtect());
puts(nodep->name());
puts("(");
bool comma = false;
for (AstNode* subnodep = nodep->pinsp(); subnodep; subnodep = subnodep->nextp()) {
@@ -1137,7 +1137,7 @@ public:
} else if (nodep->isWide()) {
UASSERT_OBJ(m_wideTempRefp, nodep, "Wide Constant w/ no temp");
emitConstant(nodep, m_wideTempRefp, "");
m_wideTempRefp = nullptr; // We used it, barf if set it a second time
m_wideTempRefp = nullptr; // We used it, fail if set it a second time
} else {
emitConstant(nodep, nullptr, "");
}
@@ -1202,11 +1202,9 @@ public:
emitVarReset(varp);
}
virtual void visit(AstExecGraph* nodep) override {
UASSERT_OBJ(nodep == v3Global.rootp()->execGraphp(), nodep,
"ExecGraph should be a singleton!");
// The location of the AstExecGraph within the containing _eval()
// function is where we want to invoke the graph and wait for it to
// complete. Emitting the children does just that.
// The location of the AstExecGraph within the containing AstCFunc is where we want to
// invoke the graph and wait for it to complete. Emitting the children does just that.
UASSERT_OBJ(!nodep->mTaskBodiesp(), nodep, "These should have been lowered");
iterateChildrenConst(nodep);
}
virtual void visit(AstChangeDet* nodep) override { //
+3 -4
View File
@@ -118,7 +118,7 @@ class EmitCHeader final : public EmitCConstInit {
void emitInternalVarDecls(const AstNodeModule* modp) {
if (!VN_IS(modp, Class)) {
putsDecoration("\n// INTERNAL VARIABLES\n");
puts(symClassName() + "* vlSymsp; // Symbol table\n");
puts(symClassName() + "* const vlSymsp;\n");
}
}
void emitParamDecls(const AstNodeModule* modp) {
@@ -146,7 +146,7 @@ class EmitCHeader final : public EmitCConstInit {
if (!VN_IS(modp, Class)) { // Classes use CFuncs with isConstructor/isDestructor
const string& name = prefixNameProtect(modp);
putsDecoration("\n// CONSTRUCTORS\n");
puts(name + "(const char* name);\n");
puts(name + "(" + symClassName() + "* symsp, const char* name);\n");
puts("~" + name + "();\n");
puts("VL_UNCOPYABLE(" + name + ");\n");
}
@@ -157,8 +157,7 @@ class EmitCHeader final : public EmitCConstInit {
if (!VN_IS(modp, Class)) {
decorateFirst(first, section);
puts("void " + protect("__Vconfigure") + "(" + symClassName()
+ "* symsp, bool first);\n");
puts("void " + protect("__Vconfigure") + "(bool first);\n");
}
if (v3Global.opt.coverage()) {
+17 -20
View File
@@ -63,7 +63,7 @@ class EmitCGatherDependencies final : VNVisitor {
UASSERT_OBJ(selfPointer.find("vlSymsp") != string::npos, nodep,
"Unknown self pointer: '" << selfPointer << "'");
// Dereferencing vlSymsp, so we need it's definition...
m_dependencies.insert(EmitCBaseVisitor::symClassName());
addSymsDependency();
}
}
@@ -117,9 +117,7 @@ class EmitCGatherDependencies final : VNVisitor {
iterateChildrenConst(nodep);
}
virtual void visit(AstNodeSimpleText* nodep) override {
if (nodep->text().find("vlSymsp") != string::npos) {
m_dependencies.insert(EmitCBaseVisitor::symClassName());
}
if (nodep->text().find("vlSymsp") != string::npos) addSymsDependency();
iterateChildrenConst(nodep);
}
virtual void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
@@ -236,8 +234,8 @@ class EmitCImp final : EmitCFunc {
"(" + modName + "* vlSelf);");
puts("\n");
puts(modName + "::" + modName + "(const char* _vcname__)\n");
puts(" : VerilatedModule(_vcname__)\n");
puts(modName + "::" + modName + "(" + symClassName() + "* symsp, const char* name)\n");
puts(" : VerilatedModule{name}\n");
ofp()->indentInc();
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
@@ -260,6 +258,7 @@ class EmitCImp final : EmitCFunc {
}
}
}
puts(", vlSymsp{symsp}\n");
ofp()->indentDec();
puts(" {\n");
@@ -279,10 +278,8 @@ class EmitCImp final : EmitCFunc {
"(" + modName + "* vlSelf, bool first);");
}
puts("\nvoid " + modName + "::" + protect("__Vconfigure") + "(" + symClassName()
+ "* _vlSymsp, bool first) {\n");
puts("\nvoid " + modName + "::" + protect("__Vconfigure") + "(bool first) {\n");
puts("if (false && first) {} // Prevent unused\n");
puts("this->vlSymsp = _vlSymsp;\n"); // First, as later stuff needs it.
if (v3Global.opt.coverage()) {
puts(modName + "__" + protect("_configure_coverage") + "(this, first);\n");
}
@@ -353,8 +350,8 @@ class EmitCImp final : EmitCFunc {
hash.insert(varp->dtypep()->width());
}
}
ofp()->printf("vluint64_t __Vcheckval = 0x%" PRIx64 "ULL;\n",
static_cast<vluint64_t>(hash.digestUInt64()));
ofp()->printf("uint64_t __Vcheckval = 0x%" PRIx64 "ULL;\n",
static_cast<uint64_t>(hash.digestUInt64()));
if (de) {
puts("os.readAssert(__Vcheckval);\n");
} else {
@@ -754,26 +751,26 @@ class EmitCTrace final : EmitCFunc {
const string func = nodep->full() ? "full" : "chg";
bool emitWidth = true;
if (nodep->dtypep()->basicp()->isDouble()) {
puts("tracep->" + func + "Double");
puts("bufp->" + func + "Double");
emitWidth = false;
} else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) {
puts("tracep->" + func + "WData");
puts("bufp->" + func + "WData");
} else if (nodep->isQuad()) {
puts("tracep->" + func + "QData");
puts("bufp->" + func + "QData");
} else if (nodep->declp()->widthMin() > 16) {
puts("tracep->" + func + "IData");
puts("bufp->" + func + "IData");
} else if (nodep->declp()->widthMin() > 8) {
puts("tracep->" + func + "SData");
puts("bufp->" + func + "SData");
} else if (nodep->declp()->widthMin() > 1) {
puts("tracep->" + func + "CData");
puts("bufp->" + func + "CData");
} else {
puts("tracep->" + func + "Bit");
puts("bufp->" + func + "Bit");
emitWidth = false;
}
const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords());
const uint32_t code = nodep->declp()->code() + offset;
puts(v3Global.opt.trueTraceThreads() && !nodep->full() ? "(base+" : "(oldp+");
puts(v3Global.opt.useTraceOffload() && !nodep->full() ? "(base+" : "(oldp+");
puts(cvtToStr(code - nodep->baseCode()));
puts(",");
emitTraceValue(nodep, arrayindex);
@@ -786,7 +783,7 @@ class EmitCTrace final : EmitCFunc {
AstVar* const varp = varrefp->varp();
puts("(");
if (emitTraceIsScBigUint(nodep)) {
puts("(vluint32_t*)");
puts("(uint32_t*)");
} else if (emitTraceIsScBv(nodep)) {
puts("VL_SC_BV_DATAP(");
}
+10 -9
View File
@@ -113,20 +113,18 @@ class CMakeEmitter final {
cmake_set_raw(*of, name + "_COVERAGE", v3Global.opt.coverage() ? "1" : "0");
*of << "# Threaded output mode? 0/1/N threads (from --threads)\n";
cmake_set_raw(*of, name + "_THREADS", cvtToStr(v3Global.opt.threads()));
*of << "# Threaded tracing output mode? 0/1/N threads (from --trace-threads)\n";
cmake_set_raw(*of, name + "_TRACE_THREADS", cvtToStr(v3Global.opt.traceThreads()));
*of << "# Threaded tracing output mode? 0/1/N threads (from --threads/--trace-threads)\n";
cmake_set_raw(*of, name + "_TRACE_THREADS", cvtToStr(v3Global.opt.vmTraceThreads()));
cmake_set_raw(*of, name + "_TRACE_FST_WRITER_THREAD",
v3Global.opt.traceThreads() && v3Global.opt.traceFormat().fst() ? "1" : "0");
*of << "# Struct output mode? 0/1 (from --trace-structs)\n";
cmake_set_raw(*of, name + "_TRACE_STRUCTS", cvtToStr(v3Global.opt.traceStructs()));
*of << "# VCD Tracing output mode? 0/1 (from --trace)\n";
cmake_set_raw(*of, name + "_TRACE_VCD",
(v3Global.opt.trace() && (v3Global.opt.traceFormat() == TraceFormat::VCD))
? "1"
: "0");
*of << "# FST Tracing output mode? 0/1 (from --fst-trace)\n";
(v3Global.opt.trace() && v3Global.opt.traceFormat().vcd()) ? "1" : "0");
*of << "# FST Tracing output mode? 0/1 (from --trace-fst)\n";
cmake_set_raw(*of, name + "_TRACE_FST",
(v3Global.opt.trace() && (v3Global.opt.traceFormat() != TraceFormat::VCD))
? "1"
: "0");
(v3Global.opt.trace() && v3Global.opt.traceFormat().fst()) ? "1" : "0");
*of << "\n### Sources...\n";
std::vector<string> classes_fast;
@@ -178,6 +176,9 @@ class CMakeEmitter final {
if (v3Global.opt.mtasks()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_threads.cpp");
}
if (v3Global.opt.usesProfiler()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_profiler.cpp");
}
if (!v3Global.opt.libCreate().empty()) {
global.emplace_back(v3Global.opt.makeDir() + "/" + v3Global.opt.libCreate() + ".cpp");
}
+8 -70
View File
@@ -330,21 +330,14 @@ class EmitCModel final : public EmitCFunc {
if (initial)
puts(topModNameProtected + "__" + protect("_eval_settle") + "(&(vlSymsp->TOP));\n");
const string recName = "__Vprfloop";
if (v3Global.opt.profThreads() && !initial) {
puts("VlProfileRec* " + recName + " = nullptr;\n");
// Leave this if() here, as don't want to call VL_RDTSC_Q unless profiling
puts("if (VL_UNLIKELY(vlSymsp->__Vm_profile_cycle_start)) {\n");
// Eval start
puts(/**/ recName + " = vlSymsp->__Vm_threadPoolp->profileAppend();\n");
puts(/**/ recName + "->startEvalLoop(VL_RDTSC_Q());\n");
puts("}\n");
if (v3Global.opt.profExec() && !initial) {
puts("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).evalLoopBegin();\n");
}
puts(topModNameProtected + "__" + protect("_eval") + "(&(vlSymsp->TOP));\n");
if (v3Global.opt.profThreads() && !initial) {
puts("if (VL_UNLIKELY(" + recName + ")) " + recName + "->endRecord(VL_RDTSC_Q());\n");
if (v3Global.opt.profExec() && !initial) {
puts("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).evalLoopEnd();\n");
}
if (v3Global.rootp()->changeRequest()) {
@@ -434,61 +427,9 @@ class EmitCModel final : public EmitCFunc {
puts("Verilated::mtaskId(" + cvtToStr(mtaskId) + ");\n");
}
if (v3Global.opt.profThreads()) {
puts("if (VL_UNLIKELY((vlSymsp->_vm_contextp__->profThreadsStart() != "
"vlSymsp->__Vm_profile_time_finished)\n");
puts(" && (VL_TIME_Q() > vlSymsp->_vm_contextp__->profThreadsStart())\n");
puts(" && (vlSymsp->_vm_contextp__->profThreadsWindow() >= 1))) {\n");
// Within a profile (either starting, middle, or end)
puts(/**/ "if (vlSymsp->__Vm_profile_window_ct == 0) {\n"); // Opening file?
puts(/**/ "VL_DEBUG_IF(VL_DBG_MSGF(\"+ profile start warmup\\n\"););\n");
// Start profile on this cycle. We'll capture a window worth, then
// only analyze the next window worth. The idea is that the first window
// capture will hit some cache-cold stuff (eg printf) but it'll be warm
// by the time we hit the second window, we hope.
puts(/****/ "vlSymsp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
// "* 2" as first half is warmup, second half is collection
puts(/****/ "vlSymsp->__Vm_profile_window_ct"
" = vlSymsp->_vm_contextp__->profThreadsWindow()"
" * 2 + 1;\n");
puts(/**/ "}\n");
puts(/**/ "--(vlSymsp->__Vm_profile_window_ct);\n");
puts(/**/ "if (vlSymsp->__Vm_profile_window_ct"
" == vlSymsp->_vm_contextp__->profThreadsWindow()) {\n");
// This barrier record in every threads' profile demarcates the
// cache-warm-up cycles before the barrier from the actual profile
// cycles afterward.
puts(/****/ "vlSymsp->__Vm_threadPoolp->profileAppendAll(");
puts(/****/ "VlProfileRec{VlProfileRec::Barrier{}});\n");
puts(/****/ "vlSymsp->__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
puts(/**/ "}\n");
// Ending trace file?
puts(/**/ "else if (vlSymsp->__Vm_profile_window_ct == 0) {\n");
puts(/****/ "vluint64_t tick_end = VL_RDTSC_Q();\n");
puts(/****/ "VL_DEBUG_IF(VL_DBG_MSGF(\"+ profile end\\n\"););\n");
puts(/****/ "vlSymsp->__Vm_threadPoolp->profileDump("
"vlSymsp->_vm_contextp__->profThreadsFilename().c_str(), "
"vlSymsp->__Vm_profile_cycle_start, "
"tick_end);\n");
// This turns off the test to enter the profiling code, but still
// allows the user to collect another profile by changing
// profThreadsStart
puts(/****/ "vlSymsp->__Vm_profile_time_finished = "
"vlSymsp->_vm_contextp__->profThreadsStart();\n");
puts(/****/ "vlSymsp->__Vm_profile_cycle_start = 0;\n");
puts(/**/ "}\n");
puts("}\n");
}
const string recName = "__Vprfeval";
if (v3Global.opt.profThreads()) {
puts("VlProfileRec* " + recName + " = nullptr;\n");
// Leave this if() here, as don't want to call VL_RDTSC_Q unless profiling
puts("if (VL_UNLIKELY(vlSymsp->__Vm_profile_cycle_start)) {\n");
// Eval start
puts(/**/ recName + " = vlSymsp->__Vm_threadPoolp->profileAppend();\n");
puts(/**/ recName + "->startEval(VL_RDTSC_Q());\n");
puts("}\n");
if (v3Global.opt.profExec()) {
puts("vlSymsp->__Vm_executionProfiler.configure(*(vlSymsp->_vm_contextp__));\n");
puts("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).evalBegin();\n");
}
emitSettleLoop(modp, /* initial: */ false);
@@ -499,10 +440,7 @@ class EmitCModel final : public EmitCFunc {
}
if (v3Global.opt.threads()) puts("Verilated::endOfEval(vlSymsp->__Vm_evalMsgQp);\n");
if (v3Global.opt.profThreads()) {
// End eval record
puts("if (VL_UNLIKELY(" + recName + ")) " + recName + "->endRecord(VL_RDTSC_Q());\n");
}
if (v3Global.opt.profExec()) puts("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).evalEnd();\n");
puts("}\n");
}
+32 -41
View File
@@ -395,7 +395,7 @@ void EmitCSyms::emitSymHdr() {
if (v3Global.needTraceDumper()) {
puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n");
}
if (v3Global.opt.profThreads()) puts("#include \"verilated_profiler.h\"\n");
if (v3Global.opt.usesProfiler()) puts("#include \"verilated_profiler.h\"\n");
puts("\n// INCLUDE MODEL CLASS\n");
puts("\n#include \"" + topClassName() + ".h\"\n");
@@ -445,18 +445,15 @@ void EmitCSyms::emitSymHdr() {
}
puts("bool __Vm_didInit = false;\n");
if (v3Global.opt.profExec()) {
puts("\n// EXECUTION PROFILING\n");
puts("VlExecutionProfiler __Vm_executionProfiler;\n");
}
if (v3Global.opt.mtasks()) {
puts("\n// MULTI-THREADING\n");
puts("VlThreadPool* const __Vm_threadPoolp;\n");
puts("bool __Vm_even_cycle = false;\n");
if (v3Global.opt.profThreads()) {
// rdtsc() at current cycle start
puts("vluint64_t __Vm_profile_cycle_start = 0;\n");
// Time we finished analysis
puts("vluint64_t __Vm_profile_time_finished = 0;\n");
// Track our position in the cache warmup and actual profile window
puts("vluint32_t __Vm_profile_window_ct = 0;\n");
}
}
puts("\n// MODULE INSTANCE STATE\n");
@@ -477,20 +474,10 @@ void EmitCSyms::emitSymHdr() {
puts("];\n");
}
if (v3Global.opt.profThreads()) {
puts("\n// PROFILING\n");
vluint64_t maxProfilerId = 0;
if (v3Global.opt.mtasks()) {
for (const V3GraphVertex* vxp
= v3Global.rootp()->execGraphp()->depGraphp()->verticesBeginp();
vxp; vxp = vxp->verticesNextp()) {
const ExecMTask* const mtp
= dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
if (maxProfilerId < mtp->profilerId()) maxProfilerId = mtp->profilerId();
}
}
++maxProfilerId; // As size must include 0
puts("VerilatedProfiler<" + cvtToStr(maxProfilerId) + "> _vm_profiler;\n");
if (v3Global.opt.profPgo()) {
puts("\n// PGO PROFILING\n");
const uint32_t usedMTaskProfilingIDs = v3Global.rootp()->usedMTaskProfilingIDs();
puts("VlPgoProfiler<" + cvtToStr(usedMTaskProfilingIDs) + "> _vm_pgoProfiler;\n");
}
if (!m_scopeNames.empty()) { // Scope names
@@ -682,8 +669,8 @@ void EmitCSyms::emitSymImp() {
puts("if (__Vm_dumping) _traceDumpClose();\n");
puts("#endif // VM_TRACE\n");
}
if (v3Global.opt.profThreads()) {
puts("_vm_profiler.write(\"" + topClassName()
if (v3Global.opt.profPgo()) {
puts("_vm_pgoProfiler.write(\"" + topClassName()
+ "\", _vm_contextp__->profVltFilename());\n");
}
if (v3Global.opt.mtasks()) puts("delete __Vm_threadPoolp;\n");
@@ -719,8 +706,8 @@ void EmitCSyms::emitSymImp() {
// that calls eval() becomes the final Nth thread for the
// duration of the eval call.
puts(" , __Vm_threadPoolp{new VlThreadPool{_vm_contextp__, "
+ cvtToStr(v3Global.opt.threads() - 1) + ", " + cvtToStr(v3Global.opt.profThreads())
+ "}}\n");
+ cvtToStr(v3Global.opt.threads() - 1) + ", "
+ (v3Global.opt.profExec() ? "&__Vm_executionProfiler" : "nullptr") + "}}\n");
}
puts(" // Setup module instances\n");
@@ -729,28 +716,32 @@ void EmitCSyms::emitSymImp() {
const AstNodeModule* const modp = i.second;
puts(" , ");
puts(protect(scopep->nameDotless()));
puts("{this");
if (modp->isTop()) {
puts("(namep)\n");
puts(", namep");
} else {
// The "." is added by catName
puts("(Verilated::catName(namep, ");
puts(", Verilated::catName(namep, ");
putsQuoted(protectWordsIf(scopep->prettyName(), scopep->protect()));
puts("))\n");
puts(")");
}
puts("}\n");
++m_numStmts;
}
puts("{\n");
if (v3Global.opt.profThreads()) {
puts("// Configure profiling\n");
if (v3Global.opt.profPgo()) {
puts("// Configure profiling for PGO\n");
if (v3Global.opt.mtasks()) {
for (const V3GraphVertex* vxp
= v3Global.rootp()->execGraphp()->depGraphp()->verticesBeginp();
vxp; vxp = vxp->verticesNextp()) {
ExecMTask* const mtp = dynamic_cast<ExecMTask*>(const_cast<V3GraphVertex*>(vxp));
puts("_vm_profiler.addCounter(" + cvtToStr(mtp->profilerId()) + ", \""
+ mtp->hashName() + "\");\n");
}
v3Global.rootp()->topModulep()->foreach<AstExecGraph>(
[&](const AstExecGraph* execGraphp) {
for (const V3GraphVertex* vxp = execGraphp->depGraphp()->verticesBeginp(); vxp;
vxp = vxp->verticesNextp()) {
const ExecMTask* const mtp = static_cast<const ExecMTask*>(vxp);
puts("_vm_pgoProfiler.addCounter(" + cvtToStr(mtp->profilerId()) + ", \""
+ mtp->hashName() + "\");\n");
}
});
}
}
@@ -796,7 +787,7 @@ void EmitCSyms::emitSymImp() {
const bool first = !modp->user1();
modp->user1(true);
puts(protectIf(scopep->nameDotless(), scopep->protect()) + "." + protect("__Vconfigure")
+ "(this, " + (first ? "true" : "false") + ");\n");
+ "(" + (first ? "true" : "false") + ");\n");
++m_numStmts;
}
+15 -6
View File
@@ -65,13 +65,18 @@ public:
of.puts("VM_TRACE = ");
of.puts(v3Global.opt.trace() ? "1" : "0");
of.puts("\n");
of.puts("# Tracing output mode in VCD format? 0/1 (from --trace)\n");
of.puts("VM_TRACE_VCD = ");
of.puts(v3Global.opt.trace() && v3Global.opt.traceFormat().vcd() ? "1" : "0");
of.puts("\n");
of.puts("# Tracing output mode in FST format? 0/1 (from --trace-fst)\n");
of.puts("VM_TRACE_FST = ");
of.puts(v3Global.opt.trace() && v3Global.opt.traceFormat().fst() ? "1" : "0");
of.puts("\n");
of.puts("# Tracing threaded output mode? 0/1/N threads (from --trace-thread)\n");
of.puts(
"# Tracing threaded output mode? 0/1/N threads (from --threads/--trace-thread)\n");
of.puts("VM_TRACE_THREADS = ");
of.puts(cvtToStr(v3Global.opt.trueTraceThreads()));
of.puts(cvtToStr(v3Global.opt.vmTraceThreads()));
of.puts("\n");
of.puts("# Separate FST writer thread? 0/1 (from --trace-fst with --trace-thread > 0)\n");
of.puts("VM_TRACE_FST_WRITER_THREAD = ");
@@ -112,6 +117,9 @@ public:
}
}
if (v3Global.opt.mtasks()) putMakeClassEntry(of, "verilated_threads.cpp");
if (v3Global.opt.usesProfiler()) {
putMakeClassEntry(of, "verilated_profiler.cpp");
}
} else if (support == 2 && slow) {
} else {
for (AstNodeFile* nodep = v3Global.rootp()->filesp(); nodep;
@@ -189,6 +197,7 @@ public:
of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n");
of.puts("VM_USER_CFLAGS = \\\n");
if (!v3Global.opt.libCreate().empty()) of.puts("\t-fPIC \\\n");
if (v3Global.opt.usesProfiler()) of.puts("\t-DVL_PROFILER \\\n");
const V3StringList& cFlags = v3Global.opt.cFlags();
for (const string& i : cFlags) of.puts("\t" + i + " \\\n");
of.puts("\n");
@@ -244,8 +253,8 @@ public:
}
if (!v3Global.opt.libCreate().empty()) {
const string libCreateDeps = "$(VK_OBJS) $(VK_GLOBAL_OBJS) " + v3Global.opt.libCreate()
+ ".o $(VM_HIER_LIBS)";
const string libCreateDeps = "$(VK_OBJS) $(VK_USER_OBJS) $(VK_GLOBAL_OBJS) "
+ v3Global.opt.libCreate() + ".o $(VM_HIER_LIBS)";
of.puts("\n### Library rules from --lib-create\n");
// The rule to create .a is defined in verilated.mk, so just define dependency here.
of.puts(v3Global.opt.libCreateName(false) + ": " + libCreateDeps + "\n");
@@ -329,8 +338,8 @@ class EmitMkHierVerilation final {
const V3HierBlockPlan::HierVector blocks
= m_planp->hierBlocksSorted(); // leaf comes first
// List in order of leaf-last order so that linker can resolve dependency
for (auto it = blocks.rbegin(); it != blocks.rend(); ++it) {
of.puts("\t" + (*it)->hierLib(true) + " \\\n");
for (auto& block : vlstd::reverse_view(blocks)) {
of.puts("\t" + block->hierLib(true) + " \\\n");
}
of.puts("\n");
+102 -97
View File
@@ -52,10 +52,10 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
}
// VISITORS
virtual void visit(AstNetlist* nodep) override { iterateAndNextNull(nodep->modulesp()); }
virtual void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); }
virtual void visit(AstNodeModule* nodep) override {
putfs(nodep, nodep->verilogKwd() + " " + prefixNameProtect(nodep) + ";\n");
iterateChildren(nodep);
iterateChildrenConst(nodep);
putqs(nodep, "end" + nodep->verilogKwd() + "\n");
}
virtual void visit(AstPort* nodep) override {}
@@ -65,7 +65,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
puts(nodep->prettyName());
puts(";\n");
// Only putfs the first time for each visitor; later for same node is putqs
iterateAndNextNull(nodep->stmtsp());
iterateAndNextConstNull(nodep->stmtsp());
putfs(nodep, nodep->isFunction() ? "endfunction\n" : "endtask\n");
}
@@ -75,7 +75,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
} else {
putbs("begin : " + nodep->name() + "\n");
}
iterateChildren(nodep);
iterateChildrenConst(nodep);
puts("end\n");
}
virtual void visit(AstFork* nodep) override {
@@ -84,75 +84,76 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
} else {
putbs("fork : " + nodep->name() + "\n");
}
iterateChildren(nodep);
iterateChildrenConst(nodep);
puts(nodep->joinType().verilogKwd());
puts("\n");
}
virtual void visit(AstFinal* nodep) override {
putfs(nodep, "final begin\n");
iterateChildren(nodep);
iterateChildrenConst(nodep);
putqs(nodep, "end\n");
}
virtual void visit(AstInitial* nodep) override {
putfs(nodep, "initial begin\n");
iterateChildren(nodep);
iterateChildrenConst(nodep);
putqs(nodep, "end\n");
}
virtual void visit(AstInitialAutomatic* nodep) override { iterateChildren(nodep); }
virtual void visit(AstInitialAutomatic* nodep) override { iterateChildrenConst(nodep); }
virtual void visit(AstInitialStatic* nodep) override { iterateChildrenConst(nodep); }
virtual void visit(AstAlways* nodep) override {
putfs(nodep, "always ");
if (m_sensesp) {
iterateAndNextNull(m_sensesp);
iterateAndNextConstNull(m_sensesp);
} // In active
else {
iterateAndNextNull(nodep->sensesp());
iterateAndNextConstNull(nodep->sensesp());
}
putbs(" begin\n");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
putqs(nodep, "end\n");
}
virtual void visit(AstAlwaysPublic* nodep) override {
putfs(nodep, "/*verilator public_flat_rw ");
if (m_sensesp) {
iterateAndNextNull(m_sensesp);
iterateAndNextConstNull(m_sensesp);
} // In active
else {
iterateAndNextNull(nodep->sensesp());
iterateAndNextConstNull(nodep->sensesp());
}
putqs(nodep, " ");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
putqs(nodep, "*/\n");
}
virtual void visit(AstNodeAssign* nodep) override {
if (VN_IS(nodep, AssignForce)) puts("force ");
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
putfs(nodep, " " + nodep->verilogKwd() + " ");
iterateAndNextNull(nodep->rhsp());
iterateAndNextConstNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstAssignDly* nodep) override {
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
putfs(nodep, " <= ");
iterateAndNextNull(nodep->rhsp());
iterateAndNextConstNull(nodep->rhsp());
puts(";\n");
}
virtual void visit(AstAssignAlias* nodep) override {
putbs("alias ");
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
putfs(nodep, " = ");
iterateAndNextNull(nodep->rhsp());
iterateAndNextConstNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstAssignW* nodep) override {
putfs(nodep, "assign ");
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
putbs(" = ");
iterateAndNextNull(nodep->rhsp());
iterateAndNextConstNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstRelease* nodep) override {
puts("release ");
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstBreak*) override {
@@ -172,7 +173,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
putfs(nodep, "");
puts(nodep->edgeType().verilogKwd());
if (nodep->sensp()) puts(" ");
iterateChildren(nodep);
iterateChildrenConst(nodep);
}
virtual void visit(AstNodeCase* nodep) override {
putfs(nodep, "");
@@ -183,7 +184,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
}
puts(nodep->verilogKwd());
puts(" (");
iterateAndNextNull(nodep->exprp());
iterateAndNextConstNull(nodep->exprp());
puts(")\n");
if (const AstCase* const casep = VN_CAST(nodep, Case)) {
if (casep->fullPragma() || casep->parallelPragma()) {
@@ -192,22 +193,22 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
if (casep->parallelPragma()) puts(" parallel_case");
}
}
iterateAndNextNull(nodep->itemsp());
iterateAndNextConstNull(nodep->itemsp());
putqs(nodep, "endcase\n");
}
virtual void visit(AstCaseItem* nodep) override {
if (nodep->condsp()) {
iterateAndNextNull(nodep->condsp());
iterateAndNextConstNull(nodep->condsp());
} else {
putbs("default");
}
putfs(nodep, ": begin ");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
putqs(nodep, "end\n");
}
virtual void visit(AstComment* nodep) override {
puts(string("// ") + nodep->name() + "\n");
iterateChildren(nodep);
iterateChildrenConst(nodep);
}
virtual void visit(AstContinue*) override {
putbs("continue");
@@ -222,13 +223,13 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
putfs(nodep, nodep->verilogKwd());
putbs("(");
if (fileOrStrgp) {
iterateAndNextNull(fileOrStrgp);
iterateAndNextConstNull(fileOrStrgp);
putbs(", ");
}
putsQuoted(text);
for (AstNode* expp = exprsp; expp; expp = expp->nextp()) {
puts(", ");
iterateAndNextNull(expp);
iterateAndNextConstNull(expp);
}
puts(");\n");
}
@@ -254,32 +255,32 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
virtual void visit(AstFOpen* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
iterateAndNextNull(nodep->filenamep());
iterateAndNextConstNull(nodep->filenamep());
putbs(", ");
iterateAndNextNull(nodep->modep());
iterateAndNextConstNull(nodep->modep());
puts(");\n");
}
virtual void visit(AstFOpenMcd* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
iterateAndNextNull(nodep->filenamep());
iterateAndNextConstNull(nodep->filenamep());
puts(");\n");
}
virtual void visit(AstFClose* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
if (nodep->filep()) iterateAndNextNull(nodep->filep());
if (nodep->filep()) iterateAndNextConstNull(nodep->filep());
puts(");\n");
}
virtual void visit(AstFFlush* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
if (nodep->filep()) iterateAndNextNull(nodep->filep());
if (nodep->filep()) iterateAndNextConstNull(nodep->filep());
puts(");\n");
}
virtual void visit(AstJumpBlock* nodep) override {
putbs("begin : label" + cvtToStr(nodep->labelNum()) + "\n");
if (nodep->stmtsp()) iterateAndNextNull(nodep->stmtsp());
if (nodep->stmtsp()) iterateAndNextConstNull(nodep->stmtsp());
puts("end\n");
}
virtual void visit(AstJumpGo* nodep) override {
@@ -291,27 +292,27 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
virtual void visit(AstNodeReadWriteMem* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
if (nodep->filenamep()) iterateAndNextNull(nodep->filenamep());
if (nodep->filenamep()) iterateAndNextConstNull(nodep->filenamep());
putbs(", ");
if (nodep->memp()) iterateAndNextNull(nodep->memp());
if (nodep->memp()) iterateAndNextConstNull(nodep->memp());
if (nodep->lsbp()) {
putbs(", ");
iterateAndNextNull(nodep->lsbp());
iterateAndNextConstNull(nodep->lsbp());
}
if (nodep->msbp()) {
putbs(", ");
iterateAndNextNull(nodep->msbp());
iterateAndNextConstNull(nodep->msbp());
}
puts(");\n");
}
virtual void visit(AstSysFuncAsTask* nodep) override {
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
puts(";\n");
}
virtual void visit(AstSysIgnore* nodep) override {
putfs(nodep, nodep->verilogKwd());
putbs("(");
iterateAndNextNull(nodep->exprsp());
iterateAndNextConstNull(nodep->exprsp());
puts(");\n");
}
virtual void visit(AstNodeFor* nodep) override {
@@ -319,31 +320,31 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
{
VL_RESTORER(m_suppressSemi);
m_suppressSemi = true;
iterateAndNextNull(nodep->initsp());
iterateAndNextConstNull(nodep->initsp());
puts(";");
iterateAndNextNull(nodep->condp());
iterateAndNextConstNull(nodep->condp());
puts(";");
iterateAndNextNull(nodep->incsp());
iterateAndNextConstNull(nodep->incsp());
}
puts(") begin\n");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
putqs(nodep, "end\n");
}
virtual void visit(AstRepeat* nodep) override {
putfs(nodep, "repeat (");
iterateAndNextNull(nodep->countp());
iterateAndNextConstNull(nodep->countp());
puts(") begin\n");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
putfs(nodep, "end\n");
}
virtual void visit(AstWhile* nodep) override {
iterateAndNextNull(nodep->precondsp());
iterateAndNextConstNull(nodep->precondsp());
putfs(nodep, "while (");
iterateAndNextNull(nodep->condp());
iterateAndNextConstNull(nodep->condp());
puts(") begin\n");
iterateAndNextNull(nodep->bodysp());
iterateAndNextNull(nodep->incsp());
iterateAndNextNull(nodep->precondsp()); // Need to recompute before next loop
iterateAndNextConstNull(nodep->bodysp());
iterateAndNextConstNull(nodep->incsp());
iterateAndNextConstNull(nodep->precondsp()); // Need to recompute before next loop
putfs(nodep, "end\n");
}
virtual void visit(AstNodeIf* nodep) override {
@@ -354,28 +355,28 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
if (ifp->unique0Pragma()) puts("unique0 ");
}
puts("if (");
iterateAndNextNull(nodep->condp());
iterateAndNextConstNull(nodep->condp());
puts(") begin\n");
iterateAndNextNull(nodep->ifsp());
iterateAndNextConstNull(nodep->ifsp());
if (nodep->elsesp()) {
putqs(nodep, "end\n");
putqs(nodep, "else begin\n");
iterateAndNextNull(nodep->elsesp());
iterateAndNextConstNull(nodep->elsesp());
}
putqs(nodep, "end\n");
}
virtual void visit(AstPast* nodep) override {
putfs(nodep, "$past(");
iterateAndNextNull(nodep->exprp());
iterateAndNextConstNull(nodep->exprp());
if (nodep->ticksp()) {
puts(", ");
iterateAndNextNull(nodep->ticksp());
iterateAndNextConstNull(nodep->ticksp());
}
puts(")");
}
virtual void visit(AstReturn* nodep) override {
putfs(nodep, "return ");
iterateAndNextNull(nodep->lhsp());
iterateAndNextConstNull(nodep->lhsp());
puts(";\n");
}
virtual void visit(AstStop* nodep) override { putfs(nodep, "$stop;\n"); }
@@ -401,22 +402,22 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
virtual void visit(AstScopeName* nodep) override {}
virtual void visit(AstCStmt* nodep) override {
putfs(nodep, "$_CSTMT(");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
puts(");\n");
}
virtual void visit(AstCMath* nodep) override {
putfs(nodep, "$_CMATH(");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
puts(");\n");
}
virtual void visit(AstUCStmt* nodep) override {
putfs(nodep, "$c(");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
puts(");\n");
}
virtual void visit(AstUCFunc* nodep) override {
putfs(nodep, "$c(");
iterateAndNextNull(nodep->bodysp());
iterateAndNextConstNull(nodep->bodysp());
puts(")");
}
@@ -450,27 +451,27 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
case 'k': putbs(""); break;
case 'l': {
UASSERT_OBJ(lhsp, nodep, "emitVerilog() references undef node");
iterateAndNextNull(lhsp);
iterateAndNextConstNull(lhsp);
break;
}
case 'r': {
UASSERT_OBJ(rhsp, nodep, "emitVerilog() references undef node");
iterateAndNextNull(rhsp);
iterateAndNextConstNull(rhsp);
break;
}
case 't': {
UASSERT_OBJ(thsp, nodep, "emitVerilog() references undef node");
iterateAndNextNull(thsp);
iterateAndNextConstNull(thsp);
break;
}
case 'o': {
UASSERT_OBJ(thsp, nodep, "emitVerilog() references undef node");
iterateAndNextNull(fhsp);
iterateAndNextConstNull(fhsp);
break;
}
case 'd': {
UASSERT_OBJ(nodep->dtypep(), nodep, "emitVerilog() references undef node");
iterateAndNextNull(nodep->dtypep());
iterateAndNextConstNull(nodep->dtypep());
break;
}
default: nodep->v3fatalSrc("Unknown emitVerilog format code: %" << c); break;
@@ -494,10 +495,10 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
}
virtual void visit(AstAttrOf* nodep) override {
putfs(nodep, "$_ATTROF(");
iterateAndNextNull(nodep->fromp());
iterateAndNextConstNull(nodep->fromp());
if (nodep->dimp()) {
putbs(", ");
iterateAndNextNull(nodep->dimp());
iterateAndNextConstNull(nodep->dimp());
}
puts(")");
}
@@ -516,11 +517,11 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
}
virtual void visit(AstNodeCond* nodep) override {
putbs("(");
iterateAndNextNull(nodep->condp());
iterateAndNextConstNull(nodep->condp());
putfs(nodep, " ? ");
iterateAndNextNull(nodep->expr1p());
iterateAndNextConstNull(nodep->expr1p());
putbs(" : ");
iterateAndNextNull(nodep->expr2p());
iterateAndNextConstNull(nodep->expr2p());
puts(")");
}
virtual void visit(AstRange* nodep) override {
@@ -532,21 +533,21 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
puts(cvtToStr(nodep->rightConst()));
puts("]");
} else {
iterateAndNextNull(nodep->leftp());
iterateAndNextConstNull(nodep->leftp());
puts(":");
iterateAndNextNull(nodep->rightp());
iterateAndNextConstNull(nodep->rightp());
puts("]");
}
}
virtual void visit(AstSel* nodep) override {
iterateAndNextNull(nodep->fromp());
iterateAndNextConstNull(nodep->fromp());
puts("[");
if (VN_IS(nodep->lsbp(), Const)) {
if (nodep->widthp()->isOne()) {
if (VN_IS(nodep->lsbp(), Const)) {
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt()));
} else {
iterateAndNextNull(nodep->lsbp());
iterateAndNextConstNull(nodep->lsbp());
}
} else {
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt()
@@ -555,20 +556,20 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt()));
}
} else {
iterateAndNextNull(nodep->lsbp());
iterateAndNextConstNull(nodep->lsbp());
putfs(nodep, "+:");
iterateAndNextNull(nodep->widthp());
iterateAndNextConstNull(nodep->widthp());
puts("]");
}
puts("]");
}
virtual void visit(AstSliceSel* nodep) override {
iterateAndNextNull(nodep->fromp());
iterateAndNextConstNull(nodep->fromp());
puts(cvtToStr(nodep->declRange()));
}
virtual void visit(AstTypedef* nodep) override {
putfs(nodep, "typedef ");
iterateAndNextNull(nodep->dtypep());
iterateAndNextConstNull(nodep->dtypep());
puts(" ");
puts(nodep->prettyName());
puts(";\n");
@@ -578,7 +579,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
putfs(nodep, nodep->prettyName());
if (nodep->rangep()) {
puts(" ");
iterateAndNextNull(nodep->rangep());
iterateAndNextConstNull(nodep->rangep());
puts(" ");
} else if (nodep->isRanged()) {
puts(" [");
@@ -592,14 +593,14 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
}
virtual void visit(AstNodeArrayDType* nodep) override {
iterate(nodep->subDTypep());
iterateAndNextNull(nodep->rangep());
iterateAndNextConstNull(nodep->rangep());
}
virtual void visit(AstNodeUOrStructDType* nodep) override {
puts(nodep->verilogKwd() + " ");
if (nodep->packed()) puts("packed ");
puts("\n");
puts("{");
iterateAndNextNull(nodep->membersp());
iterateAndNextConstNull(nodep->membersp());
puts("}");
}
virtual void visit(AstMemberDType* nodep) override {
@@ -616,10 +617,10 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
putfs(nodep, nodep->prettyName());
}
puts("(");
iterateAndNextNull(nodep->pinsp());
iterateAndNextConstNull(nodep->pinsp());
puts(")");
}
virtual void visit(AstArg* nodep) override { iterateAndNextNull(nodep->exprp()); }
virtual void visit(AstArg* nodep) override { iterateAndNextConstNull(nodep->exprp()); }
virtual void visit(AstPrintTimeScale* nodep) override {
puts(nodep->verilogKwd());
puts(";\n");
@@ -654,8 +655,8 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
virtual void visit(AstConst* nodep) override { putfs(nodep, nodep->num().ascii(true, true)); }
// Just iterate
virtual void visit(AstTopScope* nodep) override { iterateChildren(nodep); }
virtual void visit(AstScope* nodep) override { iterateChildren(nodep); }
virtual void visit(AstTopScope* nodep) override { iterateChildrenConst(nodep); }
virtual void visit(AstScope* nodep) override { iterateChildrenConst(nodep); }
virtual void visit(AstVar* nodep) override {
if (nodep->isIO()) {
putfs(nodep, nodep->verilogKwd());
@@ -686,7 +687,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
}
virtual void visit(AstActive* nodep) override {
m_sensesp = nodep->sensesp();
iterateAndNextNull(nodep->stmtsp());
iterateAndNextConstNull(nodep->stmtsp());
m_sensesp = nullptr;
}
virtual void visit(AstParseRef* nodep) override { puts(nodep->prettyName()); }
@@ -700,7 +701,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
// Default
virtual void visit(AstNode* nodep) override {
puts(string("\n???? // ") + nodep->prettyTypeName() + "\n");
iterateChildren(nodep);
iterateChildrenConst(nodep);
// Not v3fatalSrc so we keep processing
if (!m_suppressUnknown) {
nodep->v3error(
@@ -754,10 +755,10 @@ class EmitVStreamVisitor final : public EmitVBaseVisitor {
virtual void putqs(AstNode*, const string& str) override { putbs(str); }
public:
EmitVStreamVisitor(AstNode* nodep, std::ostream& os)
EmitVStreamVisitor(const AstNode* nodep, std::ostream& os)
: EmitVBaseVisitor{false, nullptr}
, m_os(os) { // Need () or GCC 4.8 false warning
iterate(nodep);
iterate(const_cast<AstNode*>(nodep));
}
virtual ~EmitVStreamVisitor() override = default;
};
@@ -789,6 +790,10 @@ class EmitVPrefixedFormatter final : public V3OutFormatter {
}
}
virtual void putsOutput(const char* strg) override {
for (const char* cp = strg; *cp; cp++) putcOutput(*cp);
}
public:
void prefixFl(FileLine* fl) { m_prefixFl = fl; }
FileLine* prefixFl() const { return m_prefixFl; }
@@ -828,12 +833,12 @@ class EmitVPrefixedVisitor final : public EmitVBaseVisitor {
}
public:
EmitVPrefixedVisitor(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth,
EmitVPrefixedVisitor(const AstNode* nodep, std::ostream& os, const string& prefix, int flWidth,
AstSenTree* domainp, bool user3mark)
: EmitVBaseVisitor{false, domainp}
, m_formatter{os, prefix, flWidth} {
if (user3mark) VNUser3InUse::check();
iterate(nodep);
iterate(const_cast<AstNode*>(nodep));
}
virtual ~EmitVPrefixedVisitor() override = default;
};
@@ -841,11 +846,11 @@ public:
//######################################################################
// EmitV class functions
void V3EmitV::verilogForTree(AstNode* nodep, std::ostream& os) {
void V3EmitV::verilogForTree(const AstNode* nodep, std::ostream& os) {
{ EmitVStreamVisitor{nodep, os}; }
}
void V3EmitV::verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix,
void V3EmitV::verilogPrefixedTree(const AstNode* nodep, std::ostream& os, const string& prefix,
int flWidth, AstSenTree* domainp, bool user3mark) {
{ EmitVPrefixedVisitor{nodep, os, prefix, flWidth, domainp, user3mark}; }
}
+2 -2
View File
@@ -27,8 +27,8 @@ class AstSenTree;
class V3EmitV final {
public:
static void verilogForTree(AstNode* nodep, std::ostream& os = std::cout);
static void verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix,
static void verilogForTree(const AstNode* nodep, std::ostream& os = std::cout);
static void verilogPrefixedTree(const AstNode* nodep, std::ostream& os, const string& prefix,
int flWidth, AstSenTree* domainp, bool user3mark);
static void emitvFiles();
static void debugEmitV(const string& filename);
+4 -4
View File
@@ -183,7 +183,7 @@ void V3Error::suppressThisWarning() {
string V3Error::warnMore() { return string(msgPrefix().size(), ' '); }
void V3Error::v3errorEnd(std::ostringstream& sstr, const string& locationStr) {
void V3Error::v3errorEnd(std::ostringstream& sstr, const string& extra) {
#if defined(__COVERITY__) || defined(__cppcheck__)
if (s_errorCode == V3ErrorCode::EC_FATAL) __coverity_panic__(x);
#endif
@@ -209,10 +209,10 @@ void V3Error::v3errorEnd(std::ostringstream& sstr, const string& locationStr) {
// Suppress duplicate messages
if (s_messages.find(msg) != s_messages.end()) return;
s_messages.insert(msg);
if (!locationStr.empty()) {
const string locationMsg = warnMore() + locationStr + "\n";
if (!extra.empty()) {
const string extraMsg = warnMore() + extra + "\n";
const size_t pos = msg.find('\n');
msg.insert(pos + 1, locationMsg);
msg.insert(pos + 1, extraMsg);
}
// Output
if (
+1 -1
View File
@@ -313,7 +313,7 @@ public:
static void vlAbortOrExit();
static void vlAbort();
// static, but often overridden in classes.
static void v3errorEnd(std::ostringstream& sstr, const string& locationStr = "");
static void v3errorEnd(std::ostringstream& sstr, const string& extra = "");
};
// Global versions, so that if the class doesn't define a operator, we get the functions anyways.
+15 -7
View File
@@ -700,6 +700,10 @@ int V3OutFormatter::endLevels(const char* strg) {
}
void V3OutFormatter::puts(const char* strg) {
if (!v3Global.opt.decoration()) {
putsOutput(strg);
return;
}
if (m_prependIndent && strg[0] != '\n') {
putsNoTracking(indentSpaces(endLevels(strg)));
m_prependIndent = false;
@@ -759,13 +763,8 @@ void V3OutFormatter::puts(const char* strg) {
break;
case '(':
indentInc();
if (v3Global.opt.decoration()) {
// Line up continuation with open paren, plus one indent
m_parenVec.push(m_column);
} else {
// Line up continuation with block+1
m_parenVec.push(m_indentLevel * m_blockIndent);
}
// Line up continuation with open paren, plus one indent
m_parenVec.push(m_column);
break;
case ')':
if (!m_parenVec.empty()) m_parenVec.pop();
@@ -806,6 +805,7 @@ void V3OutFormatter::putBreakExpr() {
// Add a line break if too wide
void V3OutFormatter::putBreak() {
if (!v3Global.opt.decoration()) return;
if (!m_nobreak) {
// char s[1000]; sprintf(s, "{%d,%d}", m_column, m_parenVec.top()); putsNoTracking(s);
if (exceededWidth()) {
@@ -824,11 +824,19 @@ void V3OutFormatter::putsQuoted(const string& strg) {
putcNoTracking('"');
}
void V3OutFormatter::putsNoTracking(const string& strg) {
if (!v3Global.opt.decoration()) {
putsOutput(strg.c_str());
return;
}
// Don't track {}'s, probably because it's a $display format string
for (const char c : strg) putcNoTracking(c);
}
void V3OutFormatter::putcNoTracking(char chr) {
if (!v3Global.opt.decoration()) {
putcOutput(chr);
return;
}
switch (chr) {
case '\n':
m_lineno++;
+2
View File
@@ -176,6 +176,7 @@ public:
// CALLBACKS - MUST OVERRIDE
virtual void putcOutput(char chr) = 0;
virtual void putsOutput(const char* str) = 0;
};
//============================================================================
@@ -193,6 +194,7 @@ public:
private:
// CALLBACKS
virtual void putcOutput(char chr) override { fputc(chr, m_fp); }
virtual void putsOutput(const char* str) override { fputs(str, m_fp); }
};
class V3OutCFile VL_NOT_FINAL : public V3OutFile {
+4 -4
View File
@@ -155,7 +155,7 @@ FileLine::FileLine(FileLine::EmptySecret) {
}
void FileLine::newContent() {
m_contentp = new VFileContent;
m_contentp = std::make_shared<VFileContent>();
m_contentLineno = 1;
}
@@ -340,15 +340,15 @@ void FileLine::modifyStateInherit(const FileLine* fromp) {
}
}
void FileLine::v3errorEnd(std::ostringstream& sstr, const string& locationStr) {
void FileLine::v3errorEnd(std::ostringstream& sstr, const string& extra) {
std::ostringstream nsstr;
if (lastLineno()) nsstr << this;
nsstr << sstr.str();
nsstr << endl;
std::ostringstream lstr;
if (!locationStr.empty()) {
if (!extra.empty()) {
lstr << std::setw(ascii().length()) << " "
<< ": " << locationStr;
<< ": " << extra;
}
m_waive = V3Config::waive(this, V3Error::errorCode(), sstr.str());
if (warnIsOff(V3Error::errorCode()) || m_waive) {
+4 -3
View File
@@ -26,6 +26,7 @@
#include <sstream>
#include <bitset>
#include <map>
#include <memory>
#include <set>
#include <deque>
@@ -97,7 +98,7 @@ class FileLine final {
int m_lastColumn = 0; // `line corrected token's last column number
int m_filenameno; // `line corrected filename number
int m_contentLineno = 0; // Line number within source stream
VFileContent* m_contentp = nullptr; // Source text contents line is within
std::shared_ptr<VFileContent> m_contentp = nullptr; // Source text contents line is within
FileLine* m_parent = nullptr; // Parent line that included this line
std::bitset<V3ErrorCode::_ENUM_MAX> m_warnOn;
bool m_waive = false; // Waive warning
@@ -180,7 +181,7 @@ public:
int firstColumn() const { return m_firstColumn; }
int lastLineno() const { return m_lastLineno; }
int lastColumn() const { return m_lastColumn; }
VFileContent* contentp() const { return m_contentp; }
std::shared_ptr<VFileContent> contentp() const { return m_contentp; }
// If not otherwise more specific, use last lineno for errors etc,
// as the parser errors etc generally make more sense pointing at the last parse point
int lineno() const { return m_lastLineno; }
@@ -245,7 +246,7 @@ public:
void modifyWarnOff(V3ErrorCode code, bool flag) { warnOff(code, flag); }
// OPERATORS
void v3errorEnd(std::ostringstream& str, const string& locationStr = "");
void v3errorEnd(std::ostringstream& str, const string& extra = "");
void v3errorEndFatal(std::ostringstream& str);
/// When building an error, prefix for printing continuation lines
/// e.g. information referring to the same FileLine as before
+2 -2
View File
@@ -397,11 +397,11 @@ private:
// Then propagate more complicated equations
optimizeSignals(true);
// Remove redundant logic
if (v3Global.opt.oDedupe()) {
if (v3Global.opt.fDedupe()) {
dedupe();
if (debug() >= 6) m_graph.dumpDotFilePrefixed("gate_dedup");
}
if (v3Global.opt.oAssemble()) {
if (v3Global.opt.fAssemble()) {
mergeAssigns();
if (debug() >= 6) m_graph.dumpDotFilePrefixed("gate_assm");
}
+32 -1
View File
@@ -23,6 +23,8 @@
#include <map>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
int V3Graph::s_debug = 0;
@@ -325,11 +327,19 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
*logp << "\t\t rankdir=" << dotRankDir() << "];\n";
// List of all possible subgraphs
// Collections of explicit ranks
std::unordered_set<std::string> ranks;
std::unordered_multimap<std::string, V3GraphVertex*> rankSets;
std::multimap<std::string, V3GraphVertex*> subgraphs;
for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) {
const string vertexSubgraph
= (colorAsSubgraph && vertexp->color()) ? cvtToStr(vertexp->color()) : "";
subgraphs.emplace(vertexSubgraph, vertexp);
const string& dotRank = vertexp->dotRank();
if (!dotRank.empty()) {
ranks.emplace(dotRank);
rankSets.emplace(dotRank, vertexp);
}
}
// We use a map here, as we don't want to corrupt anything (userp) in the graph,
@@ -346,7 +356,10 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
if (subgr != vertexSubgraph) {
if (subgr != "") *logp << "\t};\n";
subgr = vertexSubgraph;
if (subgr != "") *logp << "\tsubgraph cluster_" << subgr << " {\n";
if (subgr != "") {
*logp << "\tsubgraph cluster_" << subgr << " {\n";
*logp << "\tlabel=\"" << subgr << "\"\n";
}
}
if (subgr != "") *logp << "\t";
*logp << "\tn" << vertexp->dotName() << (n++) << "\t[fontsize=8 "
@@ -382,6 +395,24 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const {
}
}
}
// Print ranks
for (auto dotRank : ranks) {
*logp << "\t{ rank=";
if (dotRank != "sink" && dotRank != "source" && dotRank != "min" && dotRank != "max") {
*logp << "same";
} else {
*logp << dotRank;
}
*logp << "; ";
auto bounds = rankSets.equal_range(dotRank);
for (auto it{bounds.first}; it != bounds.second; ++it) {
if (it != bounds.first) *logp << ", ";
*logp << 'n' << numMap[it->second] << "";
}
*logp << " }\n";
}
// Vertex::m_user end, now unused
// Trailer
+1
View File
@@ -218,6 +218,7 @@ public:
virtual string dotShape() const { return ""; }
virtual string dotStyle() const { return ""; }
virtual string dotName() const { return ""; }
virtual string dotRank() const { return ""; }
virtual uint32_t rankAdder() const { return 1; }
virtual FileLine* fileline() const { return nullptr; } // nullptr for unknown
virtual int sortCmp(const V3GraphVertex* rhsp) const {
+1 -1
View File
@@ -254,7 +254,7 @@ void GraphAcyc::simplify(bool allowCut) {
if (allowCut) {
// The main algorithm works without these, though slower
// So if changing the main algorithm, comment these out for a test run
if (v3Global.opt.oAcycSimp()) {
if (v3Global.opt.fAcycSimp()) {
cutBasic(vertexp);
cutBackward(vertexp);
}
+2 -2
View File
@@ -32,13 +32,13 @@ struct GraphPCNode {
//
// Unlike the LogicMTasks's, we have no cost info for the generic graph
// accepted by GraphPathChecker, so assume each node has unit cost.
std::array<vluint32_t, GraphWay::NUM_WAYS> m_cp;
std::array<uint32_t, GraphWay::NUM_WAYS> m_cp;
// Detect if we've seen this node before in a given recursive
// operation. We'll use this in pathExistsInternal() to avoid checking
// the same node twice, and again in updateHalfCriticalPath() to assert
// there are no cycles.
vluint64_t m_seenAtGeneration = 0;
uint64_t m_seenAtGeneration = 0;
// CONSTRUCTORS
GraphPCNode() {

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