Merge from master for release.

This commit is contained in:
Wilson Snyder 2022-08-31 19:47:07 -04:00
commit b154af87a1
420 changed files with 12986 additions and 4560 deletions

View File

@ -17,7 +17,7 @@ AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
@ -59,15 +59,27 @@ ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
# Include grouping/sorting
SortIncludes: true
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
- Regex: '"(config_build|verilated_config|verilatedos)\.h"'
Priority: -1 # Sepecials before main header
- Regex: '(<|")verilated.*'
Priority: 1 # Runtime headers
- Regex: '"V3.*__gen.*\.h"'
Priority: 3 # Generated internal headers separately
- Regex: '"V3.*"'
Priority: 2 # Internal header
- Regex: '".*"'
Priority: 4 # Other non-system headers
- Regex: '<[[:alnum:]_.]+>'
Priority: 5 # Simple system headers next
- Regex: '<.*>'
Priority: 6 # Other system headers next
IncludeIsMainRegex: '$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 4
@ -91,7 +103,6 @@ PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true

43
Changes
View File

@ -8,6 +8,39 @@ The changes in each Verilator version are described below. The
contributors that suggested a given feature are shown in []. Thanks!
Verilator 4.226 2022-08-31
==========================
**Minor:**
* Add --future0 and --future1 options.
* Support class parameters (#2231) (#3541). [Arkadiusz Kozdra/Antmicro]
* Support wildcard index associative arrays (#3501). [Arkadiusz Kozdra/Antmicro]
* Support negated properties (#3572). [Aleksander Kiryk]
* Support $test$plusargs(expr) (#3489).
* Rename trace rolloverSize() (#3570).
* Improve Verilation speed with --threads on large designs. [Geza Lore]
* Fix struct pattern assignment (#2328) (#3517). [Mostafa Gamal]
* Fix public combo propagation issues (#2905). [Todd Strader]
* Fix incorrect tristate logic (#3399) [shareefj, Vighnesh Iyer]
* Fix incorrect bit op tree optimization (#3470). [algrobman]
* Fix bisonpre for MSYS2 (#3471).
* Fix max memory usage (#3483). [Kamil Rakoczy]
* Fix empty string arguments to display (#3484). [Grulfen]
* Fix table misoptimizing away display (#3488). [Stefan Post]
* Fix unique_ptr memory header for MinGW64 (#3493).
* Fix $dump systemtask with --output-split-cfuncs (#3495) (#3497). [Varun Koyyalagunta]
* Fix wrong bit op tree optimization (#3509). [Nathan Graybeal]
* Fix nested default assignment for struct pattern (#3511) (#3524). [Mostafa Gamal]
* Fix sformat string incorrectly cleared (#3515) (#3519). [Gustav Svensk]
* Fix segfault exporting non-existant package (#3535).
* Fix void-cast queue pop_front or pop_back (#3542) (#3364). [Drew Ranck]
* Fix case statement comparing string literal (#3544). [Gustav Svensk]
* Fix === with some tristate constants (#3551). [Ryszard Rozak]
* Fix converting subclasses to string (#3552). [Arkadiusz Kozdra/Antmicro]
* Fix --hierarchical with order-based pin connections (#3583) (#3585). [Kelin9298]
Verilator 4.224 2022-06-19
==========================
@ -26,7 +59,7 @@ Verilator 4.224 2022-06-19
* Improve conditional merging optimization (#3125). [Geza Lore, Shunyao CAD]
* Define VM_TRACE_VCD when tracing in VCD format. [Geza Lore, Shunyao CAD]
* Add assert when VerilatedContext is mis-deleted (#3121). [Rupert Swarbrick]
* Internal prep work towards timing control. [Krzysztof Bieganski]
* Internal prep work towards timing control. [Krzysztof Bieganski/Antmicro]
* Fix hang with large case statement optimization (#3405). [Mike Urbach]
* Fix UNOPTFLAT warning from initial static var (#3406). [Kamil Rakoczy]
* Fix compile error when enable VL_LEAK_CHECKS (#3411). [HungMingWu]
@ -193,7 +226,7 @@ Verilator 4.212 2021-09-01
* Fix re-evaluation of logic dependent on state set in DPI exports (#3091). [Geza Lore]
* Support unpacked array localparams in tasks/functions (#3078). [Geza Lore]
* Support timeunit/timeprecision in $unit.
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski]
* Support assignment patterns as children of pins (#3041). [Krzysztof Bieganski/Antmicro]
* Add --instr-count-dpi to tune assumed DPI import cost for multithreaded
model scheduling. Default value changed to 200 (#3068). [Yinan Xu]
* Output files are split based on the set of headers required
@ -258,7 +291,7 @@ Verilator 4.204 2021-06-12
* Prep work towards better ccache hashing/performance. [Geza Lore]
* Fix assertion failure in bitOpTree optimization (#2891) (#2899). [Raynard Qiao]
* Fix DPI functions not seen as vpiModule (#2893). [Todd Strader]
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski]
* Fix bounds check in VL_SEL_IWII (#2910). [Krzysztof Bieganski/Antmicro]
* Fix slowdown in elaboration (#2911). [Nathan Graybeal]
* Fix initialization of assoc in assoc array (#2914). [myftptoyman]
* Fix make support for gmake 3.x (#2920) (#2921). [Philipp Wagner]
@ -373,7 +406,7 @@ Verilator 4.108 2021-01-10
**Major:**
* Many VPI changes for IEEE compatibility, which may alter behavior from previous releases.
* Support randomize() class method and rand (#2607). [Krzysztof Bieganski]
* Support randomize() class method and rand (#2607). [Krzysztof Bieganski/Antmicro]
**Minor:**
@ -433,7 +466,7 @@ Verilator 4.104 2020-11-14
* Support queue and associative array 'with' statements (#2616).
* Support queue slicing (#2326).
* Support associative array pattern assignments and defaults.
* Support static methods and typedefs in classes (#2615). [Krzysztof Bieganski]
* Support static methods and typedefs in classes (#2615). [Krzysztof Bieganski/Antmicro]
* Add error on typedef referencing self (#2539). [Cody Piersall]
* With --debug, turn off address space layout randomization.
* Fix iteration over mutating list bug in VPI (#2588). [Kaleb Barrett]

View File

@ -322,7 +322,10 @@ cppcheck: $(CPPCHECK_DEP)
$(CPPCHECK) $(CPPCHECK_FLAGS) -DVL_DEBUG=1 -DVL_CPPCHECK=1 -DVL_THREADED=1 $(CPPCHECK_INC) $<
CLANGTIDY = clang-tidy
CLANGTIDY_FLAGS = -config='' -checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables'
CLANGTIDY_FLAGS = -config='' \
-header-filter='.*' \
-checks='-fuchsia-*,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-init-variables,-cppcoreguidelines-avoid-goto,-modernize-avoid-c-arrays,-readability-magic-numbers,-readability-simplify-boolean-expr,-cppcoreguidelines-macro-usage' \
CLANGTIDY_DEP = $(subst .h,.h.tidy,$(CPPCHECK_H)) \
$(subst .cpp,.cpp.tidy,$(CPPCHECK_CPP))
CLANGTIDY_DEFS = -DVL_DEBUG=1 -DVL_THREADED=1 -DVL_CPPCHECK=1
@ -331,7 +334,7 @@ clang-tidy: $(CLANGTIDY_DEP)
%.cpp.tidy: %.cpp
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) | 2>&1 tee $@
%.h.tidy: %.h
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) | 2>&1 tee $@
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) -x c++-header | 2>&1 tee $@
analyzer-src:
-rm -rf src/obj_dbg

View File

@ -284,33 +284,34 @@ detailed descriptions of these arguments.
--bbox-unsup Blackbox unsupported language features
--bin <filename> Override Verilator binary
--build Build model executable/library after Verilation
-CFLAGS <flags> C++ compiler arguments for makefile
--cc Create C++ output
--cdc Clock domain crossing analysis
-CFLAGS <flags> C++ compiler arguments for makefile
--clk <signal-name> Mark specified signal as clock
--make <build-tool> Generate scripts for specified build tool
--no-clk <signal-name> Prevent marking specified signal as clock
--compiler <compiler-name> Tune for specified C++ compiler
--converge-limit <loops> Tune convergence settle time
--coverage Enable all coverage
--coverage-line Enable line coverage
--coverage-max-width <width> Maximum array depth for coverage
--coverage-toggle Enable toggle coverage
--coverage-user Enable SVL user coverage
--coverage-underscore Enable coverage of _signals
--coverage-user Enable SVL user coverage
-D<var>[=<value>] Set preprocessor define
--debug Enable debugging
--debug-check Enable debugging assertions
--no-debug-leak Disable leaking memory in --debug mode
--debugi <level> Enable debugging at a specified level
--debugi-<srcfile> <level> Enable debugging a source file at a level
--no-decoration Disable comments and symbol decorations
--default-language <lang> Default language to parse
+define+<var>=<value> Set preprocessor define
--dpi-hdr-only Only produce the DPI header file
--dump-defines Show preprocessor defines with -E
--dump-tree Enable dumping .tree files
--dump-tree-addrids Use short identifiers instead of addresses
--dump-treei <level> Enable dumping .tree files at a level
--dump-treei-<srcfile> <level> Enable dumping .tree file at a source file at a level
--dump-tree-addrids Use short identifiers instead of addresses
-E Preprocess, but do not compile
--error-limit <value> Abort after this number of errors
--exe Link to create executable
@ -321,6 +322,7 @@ detailed descriptions of these arguments.
--flatten Force inlining of all modules, tasks and functions
-fno-<optimization> Disable internal optimization stage
-G<name>=<value> Overwrite top-level parameter
--gate-stmts <value> Tune gate optimizer depth
--gdb Run Verilator under GDB interactively
--gdbbt Run Verilator under GDB for backtrace
--generate-key Create random key for --protect-key
@ -328,54 +330,50 @@ detailed descriptions of these arguments.
--help Display this help
--hierarchical Enable hierarchical Verilation
-I<dir> Directory to search for includes
-j <jobs> Parallelism for --build
--gate-stmts <value> Tune gate optimizer depth
--if-depth <value> Tune IFDEPTH warning
+incdir+<dir> Directory to search for includes
--inline-mult <value> Tune module inlining
--instr-count-dpi <value> Assumed dynamic instruction count of DPI imports
-LDFLAGS <flags> Linker pre-object arguments for makefile
-j <jobs> Parallelism for --build
--l2-name <value> Verilog scope name of the top module
--language <lang> Default language standard to parse
-LDFLAGS <flags> Linker pre-object arguments for makefile
--lib-create <name> Create a DPI library
+libext+<ext>+[ext]... Extensions for finding modules
--lint-only Lint, but do not make output
--make <build-tool> Generate scripts for specified build tool
-MAKEFLAGS <flags> Arguments to pass to make during --build
--max-num-width <value> Maximum number width (default: 64K)
--MMD Create .d dependency files
--MP Create phony dependency targets
--Mdir <directory> Name of output object directory
--MMD Create .d dependency files
--mod-prefix <topname> Name to prepend to lower classes
--no-clk <signal-name> Prevent marking specified signal as clock
--no-decoration Disable comments and symbol decorations
--no-pins64 Don't use uint64_t's for 33-64 bit sigs
--no-skip-identical Disable skipping identical output
--MP Create phony dependency targets
+notimingchecks Ignored
-O0 Disable optimizations
-O3 High performance optimizations
-O<optimization-letter> Selectable optimizations
-o <executable> Name of final executable
--no-order-clock-delay Disable ordering clock enable assignments
--no-verilate Skip verilation and just compile previously Verilated code.
--output-split <statements> Split .cpp files into pieces
--output-split-cfuncs <statements> Split model functions
--output-split-ctrace <statements> Split tracing functions
-P Disable line numbers and blanks with -E
--pins-bv <bits> Specify types for top level ports
--pins-sc-uint Specify types for top level ports
--pins-sc-biguint Specify types for top level ports
--pins-sc-uint Specify types for top level ports
--pins-uint8 Specify types for top level ports
--no-pins64 Don't use uint64_t's for 33-64 bit sigs
--pipe-filter <command> Filter all input through a script
--pp-comments Show preprocessor comments with -E
--prefix <topname> Name of top level class
--private Debugging; see docs
--prof-c Compile C++ code with profiling
--prof-cfuncs Name functions for profiling
--prof-exec Enable generating execution profile for gantt chart
--prof-pgo Enable generating profiling data for PGO
--protect-key <key> Key for symbol protection
--protect-ids Hash identifier names for obscurity
--protect-key <key> Key for symbol protection
--protect-lib <name> Create a DPI protected library
--private Debugging; see docs
--public Debugging; see docs
--public-flat-rw Mark all variables, etc as public_flat_rw
-pvalue+<name>=<value> Overwrite toplevel parameter
@ -386,6 +384,7 @@ detailed descriptions of these arguments.
--rr Run Verilator and record with rr
--savable Enable model save-restore
--sc Create SystemC output
--no-skip-identical Disable skipping identical output
--stats Create statistics file
--stats-vars Provide statistics on variables
-sv Enable SystemVerilog parsing
@ -413,6 +412,7 @@ detailed descriptions of these arguments.
--unused-regexp <regexp> Tune UNUSED lint signals
-V Verbose version and config
-v <filename> Verilog library
--no-verilate Skip verilation and just compile previously Verilated code.
+verilog1995ext+<ext> Synonym for +1364-1995ext+<ext>
+verilog2001ext+<ext> Synonym for +1364-2001ext+<ext>
--version Displays program version and exits
@ -427,6 +427,9 @@ detailed descriptions of these arguments.
-Wno-lint Disable all lint warnings
-Wno-style Disable all style warnings
-Wpedantic Warn on compliance-test issues
-Wwarn-<message> Enable specified warning message
-Wwarn-lint Enable lint warning message
-Wwarn-style Enable style warning message
--x-assign <mode> Assign non-initial Xs to this value
--x-initial <mode> Assign initial Xs to this value
--x-initial-edge Enable initial X->0 and X->1 edge triggers

View File

@ -5,14 +5,14 @@
# General Public License Version 3 or the Perl Artistic License Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.224 2022-06-19],
[https://verilator.org],
[verilator],[https://verilator.org])
# When releasing, also update header of Changes file
# and commit using "devel release" or "Version bump" message
# Then 'make maintainer-dist'
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
#AC_INIT([Verilator],[#.### devel])
AC_INIT([Verilator],[4.226 2022-08-31],
[https://verilator.org],
[verilator],[https://verilator.org])
AC_CONFIG_HEADERS(src/config_build.h)
AC_CONFIG_FILES(Makefile src/Makefile src/Makefile_obj include/verilated.mk include/verilated_config.h verilator.pc verilator-config.cmake verilator-config-version.cmake)
@ -355,7 +355,7 @@ AC_SUBST(CFG_CXXFLAGS_PROFILE)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++20)
#_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++20)
case "$(which lsb_release 2>&1 > /dev/null && lsb_release -d)" in
*Ubuntu*22.04*)
*Arch*Linux* | *Ubuntu*22.04*)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=gnu++17)
_MY_CXX_CHECK_SET(CFG_CXXFLAGS_STD_NEWEST,-std=c++17)
;;

View File

@ -5,11 +5,13 @@ Please see the Verilator manual for 200+ additional contributors. Thanks to all.
Adrien Le Masle
Ahmed El-Mahmoudy
Aleksander Kiryk
Alex Chadwick
Àlex Torregrosa
Aliaksei Chapyzhenka
Ameya Vikram Singh
Andreas Kuster
Arkadiusz Kozdra
Chris Randall
Chuxuan Wang
Conor McCullough
@ -32,6 +34,7 @@ Gianfranco Costamagna
Glen Gibb
Graham Rushton
Guokai Chen
Gustav Svensk
Harald Heckmann
Howard Su
Huang Rui
@ -68,6 +71,7 @@ Lukasz Dalek
Maarten De Braekeleer
Maciej Sobkowski
Marco Widmer
Mariusz Glebocki
Markus Krause
Marlon James
Marshal Qiao
@ -79,6 +83,7 @@ Michaël Lefebvre
Mike Popoloski
Miodrag Milanović
Morten Borup Petersen
Mostafa Gamal
Nandu Raj
Nathan Kohagen
Nathan Myers
@ -95,6 +100,7 @@ Rafal Kapuscik
Raynard Qiao
Richard Myers
Rupert Swarbrick
Ryszard Rozak
Samuel Riedel
Sean Cross
Sebastien Van Cauwenberghe
@ -111,9 +117,11 @@ Tomasz Gorochowik
Tymoteusz Blazejczyk
Udi Finkelstein
Unai Martinez-Corral
Varun Koyyalagunta
Vassilis Papaefstathiou
Veripool API Bot
Victor Besyakov
William D. Jones
Wilson Snyder
Xi Zhang
Yoda Lee

View File

@ -86,61 +86,17 @@ Connecting to C++
In C++ output mode (:vlopt:`--cc`), the Verilator generated model class is a
simple C++ class. The user must write a C++ wrapper and main loop for the
simulation, which instantiates the model class, and link with the Verilated
model. Here is a simple example:
model.
.. code-block:: C++
Refer to ``examples/make_tracing_c`` in the distribution for a detailed
commented example.
#include <verilated.h> // Defines common routines
#include <iostream> // Need std::cout
#include "Vtop.h" // From Verilating "top.v"
Top level IO signals are read and written as members of the model. You
call the model's :code:`eval()` method to evaluate the model. When the
simulation is complete call the model's :code:`final()` method to execute
any SystemVerilog final blocks, and complete any assertions. See
:ref:`Evaluation Loop`.
Vtop *top; // Instantiation of model
uint64_t main_time = 0; // Current simulation time
// This is a 64-bit integer to reduce wrap over issues and
// allow modulus. This is in units of the timeprecision
// used in Verilog (or from --timescale-override)
double sc_time_stamp() { // Called by $time in Verilog
return main_time; // converts to double, to match
// what SystemC does
}
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv); // Remember args
top = new Vtop; // Create model
// Do not instead make Vtop as a file-scope static
// variable, as the "C++ static initialization order fiasco"
// may cause a crash
top->reset_l = 0; // Set some inputs
while (!Verilated::gotFinish()) {
if (main_time > 10) {
top->reset_l = 1; // Deassert reset
}
if ((main_time % 10) == 1) {
top->clk = 1; // Toggle clock
}
if ((main_time % 10) == 6) {
top->clk = 0;
}
top->eval(); // Evaluate model
cout << top->out << endl; // Read a output
main_time++; // Time passes...
}
top->final(); // Done simulating
// // (Though this example doesn't get here)
delete top;
}
Note top level IO signals are read and written as members of the model. You
call the :code:`eval()` method to evaluate the model. When the simulation is
complete call the :code:`final()` method to execute any SystemVerilog final
blocks, and complete any assertions. See :ref:`Evaluation Loop`.
Connecting to SystemC
@ -449,14 +405,15 @@ accesses the above signal "readme" would be:
int main(int argc, char** argv, char** env) {
Verilated::commandArgs(argc, argv);
Vour* top = new Vour;
Verilated::internalsDump(); // See scopes to help debug
while (!Verilated::gotFinish()) {
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
const std::unique_ptr<Vour> top{new Vour{contextp.get()}};
contextp->internalsDump(); // See scopes to help debug
while (!contextp->gotFinish()) {
top->eval();
VerilatedVpi::callValueCbs(); // For signal callbacks
read_and_check();
}
delete top;
return 0;
}
EOF

View File

@ -8,7 +8,7 @@ Simulation Runtime Arguments
The following are the arguments that may be passed to a Verilated
executable, provided that executable calls
:code:`Verilated::commandArgs()`.
:code:`VerilatedContext*->commandArgs(argc, argv)`.
All simulation runtime arguments begin with "+verilator", so that the
user's executable may skip over all "+verilator" arguments when parsing its
@ -96,7 +96,7 @@ Summary:
.. option:: +verilator+noassert
Disable assert checking per runtime argument. This is the same as
calling :code:`Verilated::assertOn(false)` in the model.
calling :code:`VerilatedContext*->assertOn(false)` in the model.
.. option:: +verilator+V

View File

@ -129,16 +129,6 @@ Summary:
is also used). Verilator manages the build itself, and for this --build
requires GNU Make to be available on the platform.
.. option:: -CFLAGS <flags>
Add specified C compiler argument to the generated makefiles. For
multiple flags either pass them as a single argument with space
separators quoted in the shell (:command:`-CFLAGS "-a -b"`), or use
multiple -CFLAGS options (:command:`-CFLAGS -a -CFLAGS -b`).
When make is run on the generated makefile these will be passed to the
C++ compiler (g++/clang++/msvc++).
.. option:: --cc
Specifies C++ without SystemC output mode; see also :vlopt:`--sc`
@ -156,6 +146,16 @@ Summary:
have interest in adding more traditional CDC checks, please contact the
authors.
.. option:: -CFLAGS <flags>
Add specified C compiler argument to the generated makefiles. For
multiple flags either pass them as a single argument with space
separators quoted in the shell (:command:`-CFLAGS "-a -b"`), or use
multiple -CFLAGS options (:command:`-CFLAGS -a -CFLAGS -b`).
When make is run on the generated makefile these will be passed to the
C++ compiler (g++/clang++/msvc++).
.. option:: --clk <signal-name>
With :vlopt:`--clk`, the specified signal-name is taken as a root clock
@ -179,6 +179,11 @@ Summary:
remove it from the combinatorial logic reevaluation checking code. This
may greatly improve performance.
.. option:: --no-clk <signal-name>
Prevent the specified signal from being marked as clock. See
:vlopt:`--clk`.
.. option:: --compiler <compiler-name>
Enables workarounds for the specified C++ compiler (list below).
@ -288,6 +293,13 @@ Summary:
<--debugi>`). Higher levels produce more detailed messages. See
:vlopt:`--debug` for other implications of enabling debug.
.. option:: --no-decoration
When creating output Verilated code, minimize comments, white space,
symbol names and other decorative items, at the cost of greatly reduced
readability. This may assist C++ compile times. This will not typically
change the ultimate model's performance, but may in some cases.
.. option:: --default-language <value>
Select the language to be used by default when first processing each
@ -479,6 +491,30 @@ Summary:
are typically used only when recommended by a maintainer to help debug
or work around an issue.
.. option:: -future0 <option>
Rarely needed. Suppress an unknown Verilator option for an option that
takes no additional arguments. This is used to allow scripts written
with pragmas for a later version of Verilator to run under a older
version. e.g. :code:`-future0 option --option` would on older versions
that do not understand :code:`--option` or :code:`+option` suppress what
would otherwise be an invalid option error, and on newer versions that
implement :code:`--option`, :code:`-future0 option --option` would have
the :code:`-future0 option` ignored and the :code:`--option` would
function appropriately.
.. option:: -future1 <option>
Rarely needed. Suppress an unknown Verilator option for an option that
takes an additional argument. This is used to allow scripts written
with pragmas for a later version of Verilator to run under a older
version. e.g. :code:`-future1 option --option arg` would on older
versions that do not understand :code:`--option arg` or :code:`+option
arg` suppress what would otherwise be an invalid option error, and on
newer versions that implement :code:`--option arg`, :code:`-future1
option --option arg` would have the :code:`-future1 option` ignored and
the :code:`--option arg` would function appropriately.
.. option:: -G<name>=<value>
Overwrites the given parameter of the toplevel module. The value is
@ -585,21 +621,6 @@ Summary:
to limit the number of parallel build jobs but attempt to execute all
independent build steps in parallel.
.. option:: -LDFLAGS <flags>
Add specified C linker arguments to the generated makefiles. For multiple
flags either pass them as a single argument with space separators quoted
in the shell (``-LDFLAGS "-a -b"``), or use multiple -LDFLAGS arguments
(``-LDFLAGS -a -LDFLAGS -b``).
When make is run on the generated makefile these will be passed to the
C++ linker (ld) **after** the primary file being linked. This flag is
called :vlopt:`-LDFLAGS` as that's the traditional name in simulators;
it's would have been better called LDLIBS as that's the Makefile
variable it controls. (In Make, LDFLAGS is before the first object,
LDLIBS after. -L libraries need to be in the Make variable LDLIBS, not
LDFLAGS.)
.. option:: --l2-name <value>
Instead of using the module name when showing Verilog scope, use the
@ -616,12 +637,20 @@ Summary:
A synonym for :vlopt:`--default-language`, for compatibility with other
tools and earlier versions of Verilator.
.. option:: +libext+<ext>[+<ext>][...]
.. option:: -LDFLAGS <flags>
Specify the extensions that should be used for finding modules. If for
example module "my" is referenced, look in :file:`my.<ext>`. Note
"+libext+" is fairly standard across Verilog tools. Defaults to
".v+.sv".
Add specified C linker arguments to the generated makefiles. For multiple
flags either pass them as a single argument with space separators quoted
in the shell (``-LDFLAGS "-a -b"``), or use multiple -LDFLAGS arguments
(``-LDFLAGS -a -LDFLAGS -b``).
When make is run on the generated makefile these will be passed to the
C++ linker (ld) **after** the primary file being linked. This flag is
called :vlopt:`-LDFLAGS` as that's the traditional name in simulators;
it's would have been better called LDLIBS as that's the Makefile
variable it controls. (In Make, LDFLAGS is before the first object,
LDLIBS after. -L libraries need to be in the Make variable LDLIBS, not
LDFLAGS.)
.. option:: --lib-create <name>
@ -640,6 +669,13 @@ Summary:
See also :vlopt:`--protect-lib`.
.. option:: +libext+<ext>[+<ext>][...]
Specify the extensions that should be used for finding modules. If for
example module "my" is referenced, look in :file:`my.<ext>`. Note
"+libext+" is fairly standard across Verilog tools. Defaults to
".v+.sv".
.. option:: --lint-only
Check the files for lint violations only, do not create any other
@ -678,17 +714,6 @@ Summary:
Set the maximum number literal width (e.g. in 1024'd22 this it the
1024). Defaults to 64K.
.. option:: --MMD =item --no-MMD
Enable/disable creation of .d dependency files, used for make dependency
detection, similar to gcc -MMD option. By default this option is
enabled for :vlopt:`--cc` or :vlopt:`--sc` modes.
.. option:: --MP
When creating .d dependency files with :vlopt:`--MMD` option, make phony
targets. Similar to :command:`gcc -MP` option.
.. option:: --Mdir <directory>
Specifies the name of the Make object directory. All generated files
@ -696,33 +721,23 @@ Summary:
The directory is created if it does not exist and the parent directories
exist; otherwise manually create the Mdir before calling Verilator.
.. option:: --MMD
.. option:: --no-MMD
Enable/disable creation of .d dependency files, used for make dependency
detection, similar to gcc -MMD option. By default this option is
enabled for :vlopt:`--cc` or :vlopt:`--sc` modes.
.. option:: --mod-prefix <topname>
Specifies the name to prepend to all lower level classes. Defaults to
the same as :vlopt:`--prefix`.
.. option:: --no-clk <signal-name>
.. option:: --MP
Prevent the specified signal from being marked as clock. See
:vlopt:`--clk`.
.. option:: --no-decoration
When creating output Verilated code, minimize comments, white space,
symbol names and other decorative items, at the cost of greatly reduced
readability. This may assist C++ compile times. This will not typically
change the ultimate model's performance, but may in some cases.
.. option:: --no-pins64
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
.. option:: --no-skip-identical =item --skip-identical
Rarely needed. Disables or enables skipping execution of Verilator if
all source files are identical, and all output files exist with newer
dates. By default this option is enabled for :vlopt:`--cc` or
:vlopt:`--sc` modes only.
When creating .d dependency files with :vlopt:`--MMD` option, make phony
targets. Similar to :command:`gcc -MP` option.
.. option:: +notimingchecks
@ -801,11 +816,6 @@ Summary:
With :vlopt:`-E`, disable generation of :code:`&96;line` markers and
blank lines, similar to :command:`gcc -P`.
.. option:: --pins64
Backward compatible alias for :vlopt:`--pins-bv 65 <--pins-bv>`. Note
that's a 65, not a 64.
.. option:: --pins-bv <width>
Specifies SystemC inputs/outputs of greater than or equal to <width>
@ -838,6 +848,15 @@ Summary:
of uint32_t. Likewise pins of width 9-16 will use uint16_t instead of
uint32_t.
.. option:: --pins64
Backward compatible alias for :vlopt:`--pins-bv 65 <--pins-bv>`. Note
that's a 65, not a 64.
.. option:: --no-pins64
Backward compatible alias for :vlopt:`--pins-bv 33 <--pins-bv>`.
.. option:: --pipe-filter <command>
Rarely needed. Verilator will spawn the specified command as a
@ -867,6 +886,11 @@ Summary:
prepended to the name of the :vlopt:`--top` option, or V prepended to
the first Verilog filename passed on the command line.
.. option:: --private
Opposite of :vlopt:`--public`. Is the default; this option exists for
backwards compatibility.
.. option:: --prof-c
When compiling the C++ code, enable the compiler's profiling flag
@ -900,23 +924,6 @@ Summary:
Deprecated. Same as --prof-exec and --prof-pgo together.
.. option:: --protect-key <key>
Specifies the private key for :vlopt:`--protect-ids`. For best security
this key should be 16 or more random bytes, a reasonable secure choice
is the output of :command:`verilator --generate-key` . Typically, a key
would be created by the user once for a given protected design library,
then every Verilator run for subsequent versions of that library would
be passed the same :vlopt:`--protect-key`. Thus, if the input Verilog is
similar between library versions (Verilator runs), the Verilated code
will likewise be mostly similar.
If :vlopt:`--protect-key` is not specified and a key is needed,
Verilator will generate a new key for every Verilator run. As the key is
not saved, this is best for security, but means every Verilator run will
give vastly different output even for identical input, perhaps harming
compile times (and certainly thrashing any "ccache").
.. option:: --protect-ids
Hash any private identifiers (variable, module, and assertion block
@ -937,6 +944,23 @@ Summary:
prototypes. Use of the VPI is not recommended as many design details
may be exposed, and an INSECURE warning will be issued.
.. option:: --protect-key <key>
Specifies the private key for :vlopt:`--protect-ids`. For best security
this key should be 16 or more random bytes, a reasonable secure choice
is the output of :command:`verilator --generate-key` . Typically, a key
would be created by the user once for a given protected design library,
then every Verilator run for subsequent versions of that library would
be passed the same :vlopt:`--protect-key`. Thus, if the input Verilog is
similar between library versions (Verilator runs), the Verilated code
will likewise be mostly similar.
If :vlopt:`--protect-key` is not specified and a key is needed,
Verilator will generate a new key for every Verilator run. As the key is
not saved, this is best for security, but means every Verilator run will
give vastly different output even for identical input, perhaps harming
compile times (and certainly thrashing any "ccache").
.. option:: --protect-lib <name>
Produces a DPI library similar to :vlopt:`--lib-create`, but hides
@ -948,11 +972,6 @@ Summary:
in the distribution for a demonstration of how to build and use the DPI
library.
.. option:: --private
Opposite of :vlopt:`--public`. Is the default; this option exists for
backwards compatibility.
.. option:: --public
This is only for historical debug use. Using it may result in
@ -1045,6 +1064,15 @@ Summary:
Specifies SystemC output mode; see also :vlopt:`--cc` option.
.. option:: --skip-identical
.. option:: --no-skip-identical
Rarely needed. Disables or enables skipping execution of Verilator if
all source files are identical, and all output files exist with newer
dates. By default this option is enabled for :vlopt:`--cc` or
:vlopt:`--sc` modes only.
.. option:: --stats
Creates a dump file with statistics on the design in

View File

@ -50,7 +50,7 @@ For --cc/--sc, it creates:
- Make include file with class names (from --make gmake)
* - *{prefix}*\ _hier.mk
- Make file for hierarchy blocks (from --make gmake)
* - *{prefix|*\ _hierMkArgs.f
* - *{prefix}*\ _hierMkArgs.f
- Arguments for hierarchical Verilation (from --make gmake)
* - *{prefix}*\ _hierCMakeArgs.f
- Arguments for hierarchical Verilation (from --make cmake)
@ -62,13 +62,17 @@ For --cc/--sc, it creates:
- Top level (SystemVerilog $root) internal header file
* - *{prefix}*\ ___024root.cpp
- Top level (SystemVerilog $root) internal C++ file
* - *{prefix}*___024root*{__n}*\ .cpp
- Additional top level internal C++ files (from --output-split)
* - *{prefix}*\ ___024root\ *{__n}*\ .cpp
- Additional top level internal C++ files
* - *{prefix}*\ ___024root\ *{__DepSet_hash__n}*\ .cpp
- Additional top level internal C++ files (hashed to reduce build times)
* - *{prefix}*\ ___024root__Slow\ *{__n}*\ .cpp
- Infrequent cold routines
* - *{prefix}*\ ___024root__Trace{__n}*\ .cpp
* - *{prefix}*\ ___024root\ *{__DepSet_hash__n}*\ .cpp
- Infrequent cold routines (hashed to reduce build times)
* - *{prefix}*\ ___024root__Trace\ *{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}*\ ___024root__Trace__Slow{__n}*\ .cpp
* - *{prefix}*\ ___024root__Trace__Slow\ *{__n}*\ .cpp
- Wave file generation code (from --trace)
* - *{prefix}*\ __Dpi.h
- DPI import and export declarations (from --dpi)
@ -87,7 +91,9 @@ For --cc/--sc, it creates:
* - *{prefix}{each_verilog_module}*\ .cpp
- Lower level internal C++ files
* - *{prefix}{each_verilog_module}{__n}*\ .cpp
- Additional lower C++ files (from --output-split)
- Additional lower C++ files
* - *{prefix}{each_verilog_module}{__DepSet_hash__n}*\ .cpp
- Additional lower C++ files (hased to reduce build times)
For --hierarchy mode, it creates:

View File

@ -489,7 +489,7 @@ $test$plusargs, $value$plusargs
.. code-block:: C++
Verilated::commandArgs(argc, argv);
{VerilatedContext*} ->commandArgs(argc, argv);
to register the command line before calling $test$plusargs or
$value$plusargs.

View File

@ -21,11 +21,14 @@ int main(int argc, char** argv, char** env) {
// Prevent unused variable warnings
if (false && argc && argv && env) {}
// Construct a VerilatedContext to hold simulation time, etc.
VerilatedContext* contextp = new VerilatedContext;
// Construct the Verilated model, from Vtop.h generated from Verilating "top.v"
Vtop* top = new Vtop;
Vtop* top = new Vtop{contextp};
// Simulate until $finish
while (!Verilated::gotFinish()) {
while (!contextp->gotFinish()) {
// Evaluate model
top->eval();

View File

@ -47,27 +47,34 @@
#define VERILATOR_VERILATED_CPP_
#include "verilatedos.h"
#include "verilated_imp.h"
#include "verilated_config.h"
#include "verilatedos.h"
#include "verilated_imp.h"
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <cstdlib>
#include <sstream>
#include <sys/stat.h> // mkdir
#include <list>
#include <limits>
#include <list>
#include <sstream>
#include <utility>
#include <sys/stat.h> // mkdir
// clang-format off
#if defined(_WIN32) || defined(__MINGW32__)
# include <direct.h> // mkdir
#endif
#ifdef VL_THREADED
# include "verilated_threads.h"
#endif
// clang-format on
#include "verilated_trace.h"
// Max characters in static char string for VL_VALUE_STRING
constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192;
@ -673,7 +680,10 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
if (std::numeric_limits<T>::is_integer) {
constexpr int b = 128;
constexpr int w = VL_WORDS_I(b);
VlWide<w> tmp0, tmp1, tmp2, tmp3;
VlWide<w> tmp0;
VlWide<w> tmp1;
VlWide<w> tmp2;
VlWide<w> tmp3;
WDataInP shifted = VL_EXTEND_WQ(b, 0, tmp0, static_cast<QData>(ld));
if (shift < 0) {
@ -691,7 +701,8 @@ std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t w
= VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits<uint64_t>::max()); // breaks shifted
if (VL_GT_W(w, integer, max64Bit)) {
WDataOutP v = VL_ASSIGN_W(b, tmp3, integer); // breaks fracDigitsPow10
VlWide<w> zero, ten;
VlWide<w> zero;
VlWide<w> ten;
VL_ZERO_W(b, zero);
VL_EXTEND_WI(b, 0, ten, 10);
char buf[128]; // 128B is obviously long enough to represent 128bit integer in decimal
@ -1453,11 +1464,12 @@ void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) VL_MT_SAFE {
void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp, ...) VL_MT_SAFE {
if (obits_ignored) {}
output = "";
std::string temp_output;
va_list ap;
va_start(ap, formatp);
_vl_vsformat(output, formatp, ap);
_vl_vsformat(temp_output, formatp, ap);
va_end(ap);
output = temp_output;
}
std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE {
@ -1606,8 +1618,8 @@ IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
return code >> 8; // Want exit status
}
IData VL_TESTPLUSARGS_I(const char* formatp) VL_MT_SAFE {
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(formatp);
IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE {
const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(format.c_str());
return match.empty() ? 0 : 1;
}
@ -1840,8 +1852,7 @@ VlReadMem::VlReadMem(bool hex, int bits, const std::string& filename, QData star
, m_bits{bits}
, m_filename(filename) // Need () or GCC 4.8 false warning
, m_end{end}
, m_addr{start}
, m_linenum{0} {
, m_addr{start} {
m_fp = std::fopen(filename.c_str(), "r");
if (VL_UNLIKELY(!m_fp)) {
// We don't report the Verilog source filename as it slow to have to pass it down
@ -1975,8 +1986,7 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
VlWriteMem::VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end)
: m_hex{hex}
, m_bits{bits}
, m_addr{0} {
, m_bits{bits} {
if (VL_UNLIKELY(start > end)) {
VL_FATAL_MT(filename.c_str(), 0, "", "$writemem invalid address range");
return;
@ -2158,29 +2168,6 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
//===========================================================================
// Timescale conversion
// Helper function for conversion of timescale strings
// Converts (1|10|100)(s|ms|us|ns|ps|fs) to power of then
int VL_TIME_STR_CONVERT(const char* strp) VL_PURE {
int scale = 0;
if (!strp) return 0;
if (*strp++ != '1') return 0;
while (*strp == '0') {
++scale;
++strp;
}
switch (*strp++) {
case 's': break;
case 'm': scale -= 3; break;
case 'u': scale -= 6; break;
case 'n': scale -= 9; break;
case 'p': scale -= 12; break;
case 'f': scale -= 15; break;
default: return 0;
}
if ((scale < 0) && (*strp++ != 's')) return 0;
if (*strp) return 0;
return scale;
}
static const char* vl_time_str(int scale) VL_PURE {
static const char* const names[]
= {"100s", "10s", "1s", "100ms", "10ms", "1ms", "100us", "10us", "1us",
@ -2302,8 +2289,9 @@ void VerilatedContext::checkMagic(const VerilatedContext* contextp) {
}
VerilatedContext::Serialized::Serialized() {
m_timeunit = VL_TIME_UNIT; // Initial value until overriden by _Vconfigure
m_timeprecision = VL_TIME_PRECISION; // Initial value until overriden by _Vconfigure
constexpr int8_t picosecond = -12;
m_timeunit = picosecond; // Initial value until overriden by _Vconfigure
m_timeprecision = picosecond; // Initial value until overriden by _Vconfigure
}
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
@ -2323,7 +2311,7 @@ std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMut
return m_dumpfile;
}
std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const std::string out = dumpfile();
std::string out = dumpfile();
if (VL_UNLIKELY(out.empty())) {
VL_PRINTF_MT("%%Warning: $dumpvar ignored as not proceeded by $dumpfile\n");
return "";
@ -2428,6 +2416,33 @@ const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
return vl_time_str(timeprecision());
}
void VerilatedContext::threads(unsigned n) {
if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: Simulation threads must be >= 1");
if (m_threadPool) {
VL_FATAL_MT(
__FILE__, __LINE__, "",
"%Error: Cannot set simulation threads after the thread pool has been created.");
}
#if VL_THREADED
if (m_threads == n) return; // To avoid unnecessary warnings
m_threads = n;
const unsigned hardwareThreadsAvailable = std::thread::hardware_concurrency();
if (m_threads > hardwareThreadsAvailable) {
VL_PRINTF_MT("%%Warning: System has %u hardware threads but simulation thread count set "
"to %u. This will likely cause significant slowdown.\n",
hardwareThreadsAvailable, m_threads);
}
#else
if (n > 1) {
VL_PRINTF_MT("%%Warning: Verilator run-time library built without VL_THREADS. Ignoring "
"call to 'VerilatedContext::threads' with argument %u.\n",
n);
}
#endif
}
void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) {
const VerilatedLockGuard lock{m_argMutex};
m_args.m_argVec.clear(); // Empty first, then add
@ -2458,6 +2473,33 @@ void VerilatedContext::internalsDump() const VL_MT_SAFE {
VerilatedImp::userDump();
}
void VerilatedContext::addModel(VerilatedModel* modelp) {
threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more
if (modelp->threads() > m_threads) {
std::ostringstream msg;
msg << "VerilatedContext has " << m_threads << " threads but model '"
<< modelp->modelName() << "' (instantiated as '" << modelp->hierName()
<< "') was Verilated with --threads " << modelp->threads() << ".\n";
const std::string str = msg.str();
VL_FATAL_MT(__FILE__, __LINE__, modelp->hierName(), str.c_str());
}
}
VerilatedVirtualBase* VerilatedContext::threadPoolp() {
if (m_threads == 1) return nullptr;
#if VL_THREADED
if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
#endif
return m_threadPool.get();
}
VerilatedVirtualBase*
VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) {
if (!m_executionProfiler) m_executionProfiler.reset(construct(*this));
return m_executionProfiler.get();
}
//======================================================================
// VerilatedContextImp:: Methods - command line
@ -2767,7 +2809,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE {
// When running internal code coverage (gcc --coverage, as opposed to
// verilator --coverage), dump coverage data to properly cover failing
// tests.
VL_GCOV_FLUSH();
VL_GCOV_DUMP();
}
void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE {
@ -2850,6 +2892,14 @@ void VerilatedImp::versionDump() VL_MT_SAFE {
VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
}
//===========================================================================
// VerilatedModel:: Methods
VerilatedModel::VerilatedModel(VerilatedContext& context)
: m_context{context} {}
std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { return nullptr; }
//===========================================================================
// VerilatedModule:: Methods

View File

@ -81,6 +81,7 @@
#endif
// clang-format on
class VerilatedContext;
class VerilatedContextImp;
class VerilatedContextImpData;
class VerilatedCovContext;
@ -90,6 +91,9 @@ class VerilatedFstC;
class VerilatedFstSc;
class VerilatedScope;
class VerilatedScopeNameMap;
template <class, class>
class VerilatedTrace;
class VerilatedTraceConfig;
class VerilatedVar;
class VerilatedVarNameMap;
class VerilatedVcd;
@ -252,6 +256,40 @@ public:
#endif
};
//=========================================================================
/// Base class of a Verilator generated (Verilated) model.
///
/// VerilatedModel is a base class of the user facing primary class generated
/// by Verilator.
class VerilatedModel VL_NOT_FINAL {
VL_UNCOPYABLE(VerilatedModel);
VerilatedContext& m_context; // The VerilatedContext this model is instantiated under
protected:
explicit VerilatedModel(VerilatedContext& context);
virtual ~VerilatedModel() = default;
public:
/// Returns the VerilatedContext this model is instantiated under
/// Used to get to e.g. simulation time via contextp()->time()
inline VerilatedContext* contextp() const { return &m_context; }
/// Returns the hierarchical name of this module instance.
virtual const char* hierName() const = 0;
/// Returns the name of this model (the name of the generated model class).
virtual const char* modelName() const = 0;
/// Returns the thread level parallelism, this model was Verilated with. Always 1 or higher.
virtual unsigned threads() const = 0;
private:
// The following are for use by Verilator internals only
template <class, class>
friend class VerilatedTrace;
// Run-time trace configuration requested by this model
virtual std::unique_ptr<VerilatedTraceConfig> traceConfig() const;
};
//=========================================================================
/// Base class for all Verilated module classes.
@ -266,10 +304,6 @@ public:
const char* name() const { return m_namep; } ///< Return name of module
};
/// Declare a module, ala SC_MODULE
#define VL_MODULE(modname) class modname VL_NOT_FINAL : public VerilatedModule
// Not class final in VL_MODULE, as users might be abstracting our models (--hierarchical)
//=========================================================================
// Functions overridable by user defines
// (Internals however must use VL_PRINTF_MT, which calls these.)
@ -362,6 +396,16 @@ protected:
// Implementation details
const std::unique_ptr<VerilatedContextImpData> m_impdatap;
// Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
#ifdef VL_THREADED
unsigned m_threads = std::thread::hardware_concurrency();
#else
const unsigned m_threads = 1;
#endif
// The thread pool shared by all models added to this context
std::unique_ptr<VerilatedVirtualBase> m_threadPool;
// The execution profiler shared by all models added to this context
std::unique_ptr<VerilatedVirtualBase> m_executionProfiler;
// Coverage access
std::unique_ptr<VerilatedVirtualBase> m_coveragep; // Pointer for coveragep()
@ -495,6 +539,12 @@ public:
/// Get time precision as IEEE-standard text
const char* timeprecisionString() const VL_MT_SAFE;
/// Get number of threads used for simulation (including the main thread)
unsigned threads() const { return m_threads; }
/// Set number of threads used for simulation (including the main thread)
/// Can only be called before the thread pool is created (before first model is added).
void threads(unsigned n);
/// Allow traces to at some point be enabled (disables some optimizations)
void traceEverOn(bool flag) VL_MT_SAFE {
if (flag) calcUnusedSigs(true);
@ -510,13 +560,20 @@ public:
/// releases - contact the authors before production use.
void scopesDump() const VL_MT_SAFE;
public: // But for internal use only
// METHODS - public but for internal use only
// Internal: access to implementation class
VerilatedContextImp* impp() { return reinterpret_cast<VerilatedContextImp*>(this); }
const VerilatedContextImp* impp() const {
return reinterpret_cast<const VerilatedContextImp*>(this);
}
void addModel(VerilatedModel*);
VerilatedVirtualBase* threadPoolp();
VerilatedVirtualBase*
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
// Internal: $dumpfile
void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
@ -830,7 +887,6 @@ public:
}
#endif
public:
// METHODS - INTERNAL USE ONLY (but public due to what uses it)
// Internal: Create a new module name by concatenating two strings
// Returns pointer to thread-local static data (overwritten on next call)

View File

@ -142,26 +142,6 @@ ifneq ($(VM_THREADS),0)
endif
endif
ifneq ($(VM_TRACE_THREADS),0)
ifneq ($(VM_TRACE_THREADS),)
ifeq ($(findstring -DVL_THREADED,$(CPPFLAGS)),)
$(error VM_TRACE_THREADS requires VM_THREADS)
endif
CPPFLAGS += -DVL_TRACE_THREADED
VK_C11=1
VK_LIBS_THREADED=1
endif
endif
ifneq ($(VM_TRACE_FST_WRITER_THREAD),0)
ifneq ($(VM_TRACE_FST_WRITER_THREAD),)
CPPFLAGS += -DVL_TRACE_FST_WRITER_THREAD
VK_C11=1
VK_LIBS_THREADED=1
endif
endif
ifneq ($(VK_C11),0)
ifneq ($(VK_C11),)
# Need C++11 at least, so always default to newest
@ -187,7 +167,7 @@ VM_SLOW += $(VM_CLASSES_SLOW) $(VM_SUPPORT_SLOW)
VK_FAST_OBJS = $(addsuffix .o, $(VM_FAST))
VK_SLOW_OBJS = $(addsuffix .o, $(VM_SLOW))
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
# Note VM_GLOBAL_FAST and VM_GLOBAL_SLOW holds the files required from the
# run-time library. In practice everything is actually in VM_GLOBAL_FAST,

View File

@ -22,8 +22,10 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_cov.h"
#include "verilated.h"
#include "verilated_cov_key.h"
#include <deque>
@ -69,22 +71,23 @@ public: // But only local to this file
// This isn't in the header file for auto-magic conversion because it
// inlines to too much code and makes compilation too slow.
template <class T> class VerilatedCoverItemSpec final : public VerilatedCovImpItem {
template <class T>
class VerilatedCoverItemSpec final : public VerilatedCovImpItem {
private:
// MEMBERS
T* m_countp; // Count value
public:
// METHODS
// cppcheck-suppress truncLongCastReturn
virtual uint64_t count() const override { return *m_countp; }
virtual void zero() const override { *m_countp = 0; }
uint64_t count() const override { return *m_countp; }
void zero() const override { *m_countp = 0; }
// CONSTRUCTORS
// cppcheck-suppress noExplicitConstructor
explicit VerilatedCoverItemSpec(T* countp)
: m_countp{countp} {
*m_countp = 0;
}
virtual ~VerilatedCoverItemSpec() override = default;
~VerilatedCoverItemSpec() override = default;
};
//=============================================================================
@ -122,7 +125,7 @@ public:
protected:
friend class VerilatedCovContext;
virtual ~VerilatedCovImp() override { clearGuts(); }
~VerilatedCovImp() override { clearGuts(); }
private:
// PRIVATE METHODS
@ -205,7 +208,7 @@ private:
// Forward to . so we have a whole word
const std::string suffix = *bpost ? std::string{bpost + 1} : "";
const std::string out = prefix + "*" + suffix;
std::string out = prefix + "*" + suffix;
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
// <<"\ncho="<<out<<endl;
@ -511,8 +514,10 @@ VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
static VerilatedMutex s_mutex;
// cppcheck-suppress identicalInnerCondition
if (VL_UNLIKELY(!m_coveragep)) {
const VerilatedLockGuard lock{s_mutex};
// cppcheck-suppress identicalInnerCondition
if (VL_LIKELY(!m_coveragep)) { // Not redundant, prevents race
m_coveragep.reset(new VerilatedCovImp);
}

View File

@ -26,6 +26,7 @@
#define VERILATOR_VERILATED_COV_H_
#include "verilatedos.h"
#include "verilated.h"
#include <iostream>
@ -88,7 +89,8 @@ class VerilatedCovImp;
//=============================================================================
// Convert VL_COVER_INSERT value arguments to strings, is \internal
template <class T> std::string vlCovCvtToStr(const T& t) VL_PURE {
template <class T>
std::string vlCovCvtToStr(const T& t) VL_PURE {
std::ostringstream os;
os << t;
return os.str();
@ -120,7 +122,8 @@ public:
/// Zero coverage points
void zero() VL_MT_SAFE;
public: // But Internal use only
// METHODS - public but Internal use only
// Insert a coverage item
// We accept from 1-30 key/value pairs, all as strings.
// Call _insert1, followed by _insert2 and _insert3
@ -158,7 +161,7 @@ protected:
// CONSTRUCTORS
// Internal: Only made as part of VerilatedCovImp
VerilatedCovContext() = default;
virtual ~VerilatedCovContext() = default;
~VerilatedCovContext() override = default;
// METHODS
// Internal: access to implementation class

View File

@ -28,7 +28,9 @@
#define VERILATOR_VERILATED_DPI_CPP_
#include "verilatedos.h"
#include "verilated_dpi.h"
#include "verilated_imp.h"
// On MSVC++ we need svdpi.h to declare exports, not imports

View File

@ -27,6 +27,7 @@
#define VERILATOR_VERILATED_DPI_H_
#include "verilatedos.h"
#include "verilated.h" // Also presumably included by caller
#include "verilated_sym_props.h"

View File

@ -28,7 +28,7 @@
#include "verilated_fst_c.h"
// GTKWave configuration
#ifdef VL_TRACE_FST_WRITER_THREAD
#ifdef VL_THREADED
# define HAVE_LIBPTHREAD
# define FST_WRITER_PARALLEL
#endif
@ -92,8 +92,7 @@ static_assert(static_cast<int>(FST_ST_VCD_PROGRAM) == static_cast<int>(VLT_TRACE
//=============================================================================
// VerilatedFst
VerilatedFst::VerilatedFst(void* fst)
: m_fst{fst} {}
VerilatedFst::VerilatedFst(void* /*fst*/) {}
VerilatedFst::~VerilatedFst() {
if (m_fst) fstWriterClose(m_fst);
@ -106,9 +105,7 @@ void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
m_fst = fstWriterCreate(filename, 1);
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
#ifdef VL_TRACE_FST_WRITER_THREAD
fstWriterSetParallelMode(m_fst, 1);
#endif
if (m_useFstWriterThread) fstWriterSetParallelMode(m_fst, 1);
fullDump(true); // First dump must be full for fst
m_curScope.clear();
@ -250,23 +247,36 @@ void VerilatedFst::declDouble(uint32_t code, const char* name, int dtypenum, fst
//=============================================================================
// Get/commit trace buffer
VerilatedFstBuffer* VerilatedFst::getTraceBuffer() { return new VerilatedFstBuffer{*this}; }
VerilatedFst::Buffer* VerilatedFst::getTraceBuffer() {
#ifdef VL_THREADED
if (offload()) return new OffloadBuffer{*this};
#endif
return new Buffer{*this};
}
void VerilatedFst::commitTraceBuffer(VerilatedFstBuffer* bufp) {
#ifdef VL_TRACE_OFFLOAD
if (bufp->m_offloadBufferWritep) {
m_offloadBufferWritep = bufp->m_offloadBufferWritep;
return; // Buffer will be deleted by the offload thread
void VerilatedFst::commitTraceBuffer(VerilatedFst::Buffer* bufp) {
#ifdef VL_THREADED
if (offload()) {
OffloadBuffer* const offloadBufferp = static_cast<OffloadBuffer*>(bufp);
if (offloadBufferp->m_offloadBufferWritep) {
m_offloadBufferWritep = offloadBufferp->m_offloadBufferWritep;
return; // Buffer will be deleted by the offload thread
}
}
#endif
delete bufp;
}
//=============================================================================
// VerilatedFstBuffer implementation
// Configure
VerilatedFstBuffer::VerilatedFstBuffer(VerilatedFst& owner)
: VerilatedTraceBuffer<VerilatedFst, VerilatedFstBuffer>{owner} {}
void VerilatedFst::configure(const VerilatedTraceConfig& config) {
// If at least one model requests the FST writer thread, then use it
m_useFstWriterThread |= config.m_useFstWriterThread;
}
//=============================================================================
// VerilatedFstBuffer implementation
//=============================================================================
// Trace rendering primitives

View File

@ -43,18 +43,20 @@ public:
using Super = VerilatedTrace<VerilatedFst, VerilatedFstBuffer>;
private:
friend Buffer; // Give the buffer access to the private bits
friend VerilatedFstBuffer; // Give the buffer access to the private bits
//=========================================================================
// FST specific internals
void* m_fst;
void* m_fst = nullptr;
std::map<uint32_t, fstHandle> m_code2symbol;
std::map<int, fstEnumHandle> m_local2fstdtype;
std::list<std::string> m_curScope;
fstHandle* m_symbolp = nullptr; // same as m_code2symbol, but as an array
char* m_strbuf = nullptr; // String buffer long enough to hold maxBits() chars
bool m_useFstWriterThread = false; // Whether to use the separate FST writer thread
// CONSTRUCTORS
VL_UNCOPYABLE(VerilatedFst);
void declare(uint32_t code, const char* name, int dtypenum, fstVarDir vardir,
@ -65,15 +67,18 @@ protected:
// Implementation of VerilatedTrace interface
// Called when the trace moves forward to a new time point
virtual void emitTimeChange(uint64_t timeui) override;
void emitTimeChange(uint64_t timeui) override;
// Hooks called from VerilatedTrace
virtual bool preFullDump() override { return isOpen(); }
virtual bool preChangeDump() override { return isOpen(); }
bool preFullDump() override { return isOpen(); }
bool preChangeDump() override { return isOpen(); }
// Trace buffer management
virtual VerilatedFstBuffer* getTraceBuffer() override;
virtual void commitTraceBuffer(VerilatedFstBuffer*) override;
Buffer* getTraceBuffer() override;
void commitTraceBuffer(Buffer*) override;
// Configure sub-class
void configure(const VerilatedTraceConfig&) override;
public:
//=========================================================================
@ -113,21 +118,31 @@ public:
#ifndef DOXYGEN
// Declare specialization here as it's used in VerilatedFstC just below
template <> void VerilatedFst::Super::dump(uint64_t time);
template <> void VerilatedFst::Super::set_time_unit(const char* unitp);
template <> void VerilatedFst::Super::set_time_unit(const std::string& unit);
template <> void VerilatedFst::Super::set_time_resolution(const char* unitp);
template <> void VerilatedFst::Super::set_time_resolution(const std::string& unit);
template <> void VerilatedFst::Super::dumpvars(int level, const std::string& hier);
template <>
void VerilatedFst::Super::dump(uint64_t time);
template <>
void VerilatedFst::Super::set_time_unit(const char* unitp);
template <>
void VerilatedFst::Super::set_time_unit(const std::string& unit);
template <>
void VerilatedFst::Super::set_time_resolution(const char* unitp);
template <>
void VerilatedFst::Super::set_time_resolution(const std::string& unit);
template <>
void VerilatedFst::Super::dumpvars(int level, const std::string& hier);
#endif
//=============================================================================
// VerilatedFstBuffer
class VerilatedFstBuffer final : public VerilatedTraceBuffer<VerilatedFst, VerilatedFstBuffer> {
class VerilatedFstBuffer VL_NOT_FINAL {
// Give the trace file access to the private bits
friend VerilatedFst;
friend VerilatedFst::Super;
friend VerilatedFst::Buffer;
friend VerilatedFst::OffloadBuffer;
VerilatedFst& m_owner; // Trace file owning this buffer. Required by subclasses.
// The FST file handle
void* const m_fst = m_owner.m_fst;
@ -136,10 +151,10 @@ class VerilatedFstBuffer final : public VerilatedTraceBuffer<VerilatedFst, Veril
// String buffer long enough to hold maxBits() chars
char* const m_strbuf = m_owner.m_strbuf;
public:
// CONSTRUCTOR
explicit VerilatedFstBuffer(VerilatedFst& owner);
~VerilatedFstBuffer() = default;
explicit VerilatedFstBuffer(VerilatedFst& owner)
: m_owner{owner} {}
virtual ~VerilatedFstBuffer() = default;
//=========================================================================
// Implementation of VerilatedTraceBuffer interface

View File

@ -22,6 +22,7 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_fst_sc.h"
//======================================================================
@ -30,7 +31,7 @@
void VerilatedFstSc::open(const char* filename) {
if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) {
vl_fatal(__FILE__, __LINE__, "VerilatedFstSc",
("%Error: VerilatedFstSc::open(\"" + std::string(filename)
("%Error: VerilatedFstSc::open(\"" + std::string{filename}
+ "\") is called before sc_core::sc_start(). "
"Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file.")
.c_str());
@ -63,9 +64,6 @@ void VerilatedFstSc::trace(const unsigned int&, const std::string&, const char**
DECL_TRACE_METHOD_B( unsigned short )
DECL_TRACE_METHOD_B( unsigned int )
DECL_TRACE_METHOD_B( unsigned long )
#ifdef SYSTEMC_64BIT_PATCHES
DECL_TRACE_METHOD_B( unsigned long long)
#endif
DECL_TRACE_METHOD_B( char )
DECL_TRACE_METHOD_B( short )
DECL_TRACE_METHOD_B( int )

View File

@ -23,8 +23,9 @@
#define _VERILATED_FST_SC_H_ 1
#include "verilatedos.h"
#include "verilated_sc.h"
#include "verilated_fst_c.h"
#include "verilated_sc.h"
#include <string>
@ -51,7 +52,7 @@ public:
}
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
}
virtual ~VerilatedFstSc() { close(); }
virtual ~VerilatedFstSc() /*override*/ { close(); }
// METHODS
/// Called by SystemC simulate()
@ -60,6 +61,7 @@ public:
}
// Override VerilatedFstC. Must be called after starting simulation.
// cppcheck-suppress missingOverride // GCC won't accept override
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
private:
@ -95,9 +97,6 @@ private:
DECL_TRACE_METHOD_B( unsigned short )
DECL_TRACE_METHOD_B( unsigned int )
DECL_TRACE_METHOD_B( unsigned long )
#ifdef SYSTEMC_64BIT_PATCHES
DECL_TRACE_METHOD_B( unsigned long long)
#endif
DECL_TRACE_METHOD_B( char )
DECL_TRACE_METHOD_B( short )
DECL_TRACE_METHOD_B( int )

View File

@ -146,7 +146,7 @@ extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp);
extern IData VL_SYSTEM_IQ(QData lhs);
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
extern IData VL_TESTPLUSARGS_I(const char* formatp);
extern IData VL_TESTPLUSARGS_I(const std::string& format);
extern const char* vl_mc_scan_plusargs(const char* prefixp); // PLIish
//=========================================================================
@ -256,25 +256,7 @@ extern void _vl_debug_print_w(int lbits, WDataInP const iwp);
//=========================================================================
// Pli macros
extern int VL_TIME_STR_CONVERT(const char* strp) VL_PURE;
// These are deprecated and used only to establish the default precision/units.
// Use Verilator timescale-override for better control.
// clang-format off
#ifndef VL_TIME_PRECISION
# ifdef VL_TIME_PRECISION_STR
# define VL_TIME_PRECISION VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR))
# else
# define VL_TIME_PRECISION (-12) ///< Timescale default units if not in Verilog - picoseconds
# endif
#endif
#ifndef VL_TIME_UNIT
# ifdef VL_TIME_UNIT_STR
# define VL_TIME_UNIT VL_TIME_STR_CONVERT(VL_STRINGIFY(VL_TIME_PRECISION_STR))
# else
# define VL_TIME_UNIT (-12) ///< Timescale default units if not in Verilog - picoseconds
# endif
#endif
#if defined(SYSTEMC_VERSION)
/// Return current simulation time

View File

@ -143,11 +143,10 @@ class VerilatedThreadMsgQueue final {
public:
// CONSTRUCTORS
VerilatedThreadMsgQueue() {}
~VerilatedThreadMsgQueue() {
// The only call of this with a non-empty queue is a fatal error.
// So this does not flush the queue, as the destination queue is not known to this class.
}
VerilatedThreadMsgQueue() = default;
~VerilatedThreadMsgQueue() = default;
// The only call of destructor with a non-empty queue is a fatal error.
// So this does not flush the queue, as the destination queue is not known to this class.
private:
VL_UNCOPYABLE(VerilatedThreadMsgQueue);
@ -188,11 +187,11 @@ class VerilatedFpList final {
public:
using const_iterator = FILE* const*;
explicit VerilatedFpList() {}
explicit VerilatedFpList() = default;
const_iterator begin() const { return m_fp; }
const_iterator end() const { return m_fp + m_sz; }
std::size_t size() const { return m_sz; }
std::size_t capacity() const { return 31; }
static std::size_t capacity() { return 31; }
void push_back(FILE* fd) {
if (VL_LIKELY(size() < capacity())) m_fp[m_sz++] = fd;
}
@ -236,12 +235,11 @@ class VerilatedContextImp final : VerilatedContext {
return s_s;
}
private:
public: // But only for verilated*.cpp
// CONSTRUCTORS - no data can live here, use only VerilatedContext
VerilatedContextImp() = delete;
~VerilatedContextImp() = delete;
public: // But only for verilated*.cpp
// METHODS - extending into VerilatedContext, call via impp()->
// Random seed handling
@ -272,13 +270,12 @@ public: // But only for verilated*.cpp
std::string argPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex);
std::pair<int, char**> argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex);
public: // But only for verilated*.cpp
// METHODS - scope name
// METHODS - scope name - INTERNAL only for verilated*.cpp
void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE;
void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE;
public: // But only for verilated*.cpp
// METHODS - file IO
// METHODS - file IO - INTERNAL only for verilated*.cpp
IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
const VerilatedLockGuard lock{m_fdMutex};
if (m_fdFreeMct.empty()) return 0;
@ -298,7 +295,7 @@ public: // But only for verilated*.cpp
const std::size_t start = std::max<std::size_t>(31UL + 1UL + 3UL, m_fdps.size());
const std::size_t excess = 10;
m_fdps.resize(start + excess);
std::fill(m_fdps.begin() + start, m_fdps.end(), (FILE*)0);
std::fill(m_fdps.begin() + start, m_fdps.end(), static_cast<FILE*>(nullptr));
m_fdFree.resize(excess);
for (std::size_t i = 0, id = start; i < m_fdFree.size(); ++i, ++id) {
m_fdFree[i] = id;
@ -344,7 +341,7 @@ public: // But only for verilated*.cpp
if (VL_UNLIKELY(idx <= 2)) return; // stdout/stdin/stderr
if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free
std::fclose(m_fdps[idx]);
m_fdps[idx] = (FILE*)0;
m_fdps[idx] = nullptr;
m_fdFree.push_back(idx);
} else {
// MCD case
@ -464,7 +461,6 @@ public:
// METHODS - debug
static void versionDump() VL_MT_SAFE;
public:
// METHODS - user scope tracking
// We implement this as a single large map instead of one map per scope.
// There's often many more scopes than userdata's and thus having a ~48byte
@ -486,7 +482,8 @@ public:
return it->second;
}
public: // But only for verilated.cpp
// METHODS - But only for verilated.cpp
// Symbol table destruction cleans up the entries for each scope.
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
// Slow ok - called once/scope on destruction, so we only iterate.
@ -512,8 +509,7 @@ public: // But only for verilated.cpp
}
}
public: // But only for verilated*.cpp
// METHODS - hierarchy
// METHODS - hierarchy - only for verilated*.cpp
static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
// Slow ok - called at construction for VPI accessible elements
const VerilatedLockGuard lock{s().m_hierMapMutex};
@ -534,8 +530,7 @@ public: // But only for verilated*.cpp
return &s().m_hierMap;
}
public: // But only for verilated*.cpp
// METHODS - export names
// METHODS - export names - only for verilated*.cpp
// Each function prototype is converted to a function number which we
// then use to index a 2D table also indexed by scope number, because we

View File

@ -17,6 +17,7 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_profiler.h"
#if VL_THREADED
@ -60,54 +61,87 @@ uint16_t VlExecutionRecord::getcpu() {
//=============================================================================
// VlExecutionProfiler implementation
template <size_t N> static size_t roundUptoMultipleOf(size_t value) {
template <size_t N>
static size_t roundUptoMultipleOf(size_t value) {
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
size_t mask = N - 1;
return (value + mask) & ~mask;
}
VlExecutionProfiler::VlExecutionProfiler() {
VlExecutionProfiler::VlExecutionProfiler(VerilatedContext& context)
: m_context{context} {
// Setup profiling on main thread
setupThread(0);
}
void VlExecutionProfiler::configure(const VerilatedContext& context) {
void VlExecutionProfiler::configure() {
if (VL_UNLIKELY(m_enabled)) {
--m_windowCount;
if (VL_UNLIKELY(m_windowCount == context.profExecWindow())) {
if (VL_UNLIKELY(m_windowCount == m_context.profExecWindow())) {
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start collection\n"););
clear(); // Clear the profile after the cache warm-up cycles.
m_tickBegin = VL_CPU_TICK();
} else if (VL_UNLIKELY(m_windowCount == 0)) {
const uint64_t tickEnd = VL_CPU_TICK();
VL_DEBUG_IF(VL_DBG_MSGF("+ profile end\n"););
const std::string& fileName = context.profExecFilename();
const std::string& fileName = m_context.profExecFilename();
dump(fileName.c_str(), tickEnd);
m_enabled = false;
}
return;
}
const uint64_t startReq = context.profExecStart() + 1; // + 1, so we can start at time 0
const uint64_t startReq = m_context.profExecStart() + 1; // + 1, so we can start at time 0
if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= context.profExecStart())) {
if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= m_context.profExecStart())) {
VL_DEBUG_IF(VL_DBG_MSGF("+ profile start warmup\n"););
VL_DEBUG_IF(assert(m_windowCount == 0););
m_enabled = true;
m_windowCount = context.profExecWindow() * 2;
m_windowCount = m_context.profExecWindow() * 2;
m_lastStartReq = startReq;
}
}
VerilatedVirtualBase* VlExecutionProfiler::construct(VerilatedContext& context) {
VlExecutionProfiler* const selfp = new VlExecutionProfiler{context};
#if VL_THREADED
if (VlThreadPool* const threadPoolp = static_cast<VlThreadPool*>(context.threadPoolp())) {
for (int i = 0; i < threadPoolp->numThreads(); ++i) {
// Data to pass to worker thread initialization
struct Data {
VlExecutionProfiler* const selfp;
const uint32_t threadId;
} data{selfp, static_cast<uint32_t>(i + 1)};
// Initialize worker thread
threadPoolp->workerp(i)->addTask(
[](void* userp, bool) {
Data* const datap = static_cast<Data*>(userp);
datap->selfp->setupThread(datap->threadId);
},
&data);
// Wait until initializationis complete
threadPoolp->workerp(i)->wait();
}
}
#endif
return selfp;
}
void VlExecutionProfiler::setupThread(uint32_t threadId) {
// Reserve some space in the thread-local profiling buffer, in order to try to avoid malloc
// while profiling.
t_trace.reserve(RESERVED_TRACE_CAPACITY);
// Register thread-local buffer in list of all buffers
bool exists;
{
const VerilatedLockGuard lock{m_mutex};
bool exists = !m_traceps.emplace(threadId, &t_trace).second;
assert(!exists);
exists = !m_traceps.emplace(threadId, &t_trace).second;
}
if (VL_UNLIKELY(exists)) {
VL_FATAL_MT(__FILE__, __LINE__, "", "multiple initialization of profiler on some thread");
}
}

View File

@ -24,10 +24,6 @@
#include "verilatedos.h"
#ifndef VL_PROFILER
#error "verilated_profiler.h/cpp expects VL_PROFILER (from --prof-{exec, pgo}"
#endif
#include "verilated.h"
#include <array>
@ -38,13 +34,14 @@
#include <vector>
class VlExecutionProfiler;
class VlThreadPool;
//=============================================================================
// Macros to simplify generated code
#define VL_EXEC_TRACE_ADD_RECORD(vlSymsp) \
if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfiler.enabled())) \
(vlSymsp)->__Vm_executionProfiler.addRecord()
if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfilerp->enabled())) \
(vlSymsp)->__Vm_executionProfilerp->addRecord()
//=============================================================================
// Return high-precision counter for profiling, or 0x0 if not available
@ -136,7 +133,7 @@ static_assert(std::is_trivially_destructible<VlExecutionRecord>::value,
//=============================================================================
// VlExecutionProfiler is for collecting profiling data about model execution
class VlExecutionProfiler final {
class VlExecutionProfiler final : public VerilatedVirtualBase {
// CONSTANTS
// In order to try to avoid dynamic memory allocations during the actual profiling phase,
@ -154,6 +151,7 @@ class VlExecutionProfiler final {
using ExecutionTrace = std::vector<VlExecutionRecord>;
// STATE
VerilatedContext& m_context; // The context this profiler is under
static VL_THREAD_LOCAL ExecutionTrace t_trace; // thread-local trace buffers
mutable VerilatedMutex m_mutex;
// Map from thread id to &t_trace of given thread
@ -167,31 +165,36 @@ class VlExecutionProfiler final {
public:
// CONSTRUCTOR
VlExecutionProfiler();
explicit VlExecutionProfiler(VerilatedContext& context);
~VlExecutionProfiler() override = default;
// METHODS
// Is profiling enabled
inline bool enabled() const { return m_enabled; }
// Append a trace record to the trace buffer of the current thread
inline VlExecutionRecord& addRecord() {
static inline VlExecutionRecord& addRecord() {
t_trace.emplace_back();
return t_trace.back();
}
// Configure profiler (called in beginning of 'eval')
void configure(const VerilatedContext&);
void configure();
// Setup profiling on a particular thread;
void setupThread(uint32_t threadId);
// Clear all profiling data
void clear() VL_MT_SAFE_EXCLUDES(m_mutex);
// Write profiling data into file
void dump(const char* filenamep, uint64_t tickEnd) VL_MT_SAFE_EXCLUDES(m_mutex);
// Passed to VerilatedContext to create the VlExecutionProfiler profiler instance
static VerilatedVirtualBase* construct(VerilatedContext& context);
};
//=============================================================================
// VlPgoProfiler is for collecting profiling data for PGO
template <std::size_t T_Entries> class VlPgoProfiler final {
template <std::size_t T_Entries>
class VlPgoProfiler final {
// TYPES
struct Record final {
const std::string m_name; // Hashed name of mtask/etc

View File

@ -24,8 +24,10 @@
#define VERILATOR_VERILATED_SAVE_CPP_
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_save.h"
#include "verilated.h"
#include "verilated_imp.h"
#include <cerrno>
@ -38,13 +40,13 @@
# include <unistd.h>
#endif
#ifndef O_LARGEFILE // For example on WIN32
#ifndef O_LARGEFILE // WIN32 headers omit this
# define O_LARGEFILE 0
#endif
#ifndef O_NONBLOCK
#ifndef O_NONBLOCK // WIN32 headers omit this
# define O_NONBLOCK 0
#endif
#ifndef O_CLOEXEC
#ifndef O_CLOEXEC // WIN32 headers omit this
# define O_CLOEXEC 0
#endif
// clang-format on

View File

@ -23,6 +23,7 @@
#define VERILATOR_VERILATED_SAVE_C_H_
#include "verilatedos.h"
#include "verilated.h"
#include <string>
@ -77,7 +78,7 @@ public:
virtual void flush() VL_MT_UNSAFE_ONE {}
/// Write data to stream
VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
const uint8_t* __restrict dp = (const uint8_t* __restrict)datap;
const uint8_t* __restrict dp = static_cast<const uint8_t* __restrict>(datap);
while (size) {
bufferCheck();
size_t blk = size;
@ -194,16 +195,16 @@ public:
/// Construct new object
VerilatedSave() = default;
/// Flush, close and destruct
virtual ~VerilatedSave() override { close(); }
~VerilatedSave() override { close(); }
// METHODS
/// Open the file; call isOpen() to see if errors
void open(const char* filenamep) VL_MT_UNSAFE_ONE;
/// Open the file; call isOpen() to see if errors
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
/// Flush and close the file
virtual void close() override VL_MT_UNSAFE_ONE;
void close() override VL_MT_UNSAFE_ONE;
/// Flush data to file
virtual void flush() override VL_MT_UNSAFE_ONE;
void flush() override VL_MT_UNSAFE_ONE;
};
//=============================================================================
@ -221,7 +222,7 @@ public:
/// Construct new object
VerilatedRestore() = default;
/// Flush, close and destruct
virtual ~VerilatedRestore() override { close(); }
~VerilatedRestore() override { close(); }
// METHODS
/// Open the file; call isOpen() to see if errors
@ -229,9 +230,9 @@ public:
/// Open the file; call isOpen() to see if errors
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
/// Close the file
virtual void close() override VL_MT_UNSAFE_ONE;
virtual void flush() override VL_MT_UNSAFE_ONE {}
virtual void fill() override VL_MT_UNSAFE_ONE;
void close() override VL_MT_UNSAFE_ONE;
void flush() override VL_MT_UNSAFE_ONE {}
void fill() override VL_MT_UNSAFE_ONE;
};
//=============================================================================
@ -287,7 +288,8 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& r
uint32_t len = 0;
os >> len;
rhs.resize(len);
return os.read((void*)rhs.data(), len);
// C cast is required below
return os.read((void*)(rhs.data()), len);
}
VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp);
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp);

View File

@ -134,7 +134,6 @@ public:
initUnpacked(ulims);
}
public:
~VerilatedVarProps() = default;
// METHODS
bool magicOk() const { return m_magic == MAGIC; }

View File

@ -30,6 +30,7 @@
#define VERILATOR_VERILATED_SYMS_H_
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_sym_props.h"

View File

@ -22,11 +22,8 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_threads.h"
#ifdef VL_PROFILER
#include "verilated_profiler.h"
#endif
#include "verilated_threads.h"
#include <cstdio>
#include <memory>
@ -51,68 +48,59 @@ VlMTaskVertex::VlMTaskVertex(uint32_t upstreamDepCount)
//=============================================================================
// VlWorkerThread
VlWorkerThread::VlWorkerThread(uint32_t threadId, VerilatedContext* contextp,
VlExecutionProfiler* profilerp)
VlWorkerThread::VlWorkerThread(VerilatedContext* contextp)
: m_ready_size{0}
, m_exiting{false}
, m_cthread{startWorker, this, threadId, profilerp}
, m_contextp{contextp} {}
, m_cthread{startWorker, this, contextp} {}
VlWorkerThread::~VlWorkerThread() {
m_exiting.store(true, std::memory_order_release);
wakeUp();
shutdown();
// The thread should exit; join it.
m_cthread.join();
}
static void shutdownTask(void*, bool) {
// Deliberately empty, we use the address of this function as a magic number
}
void VlWorkerThread::shutdown() { addTask(shutdownTask, nullptr); }
void VlWorkerThread::wait() {
// Enqueue a task that sets this flag. Execution is in-order so this ensures completion.
std::atomic<bool> flag{false};
addTask([](void* flagp, bool) { static_cast<std::atomic<bool>*>(flagp)->store(true); }, &flag);
// Spin wait
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
if (flag.load()) return;
VL_CPU_RELAX();
}
// Yield wait
while (!flag.load()) std::this_thread::yield();
}
void VlWorkerThread::workerLoop() {
ExecRec work;
work.m_fnp = nullptr;
// Wait for the first task without spinning, in case the thread is never actually used.
dequeWork</* SpinWait: */ false>(&work);
while (true) {
if (VL_LIKELY(!work.m_fnp)) dequeWork(&work);
// Do this here, not above, to avoid a race with the destructor.
if (VL_UNLIKELY(m_exiting.load(std::memory_order_acquire))) break;
if (VL_LIKELY(work.m_fnp)) {
work.m_fnp(work.m_selfp, work.m_evenCycle);
work.m_fnp = nullptr;
}
if (VL_UNLIKELY(work.m_fnp == shutdownTask)) break;
work.m_fnp(work.m_selfp, work.m_evenCycle);
// Wait for next task with spinning.
dequeWork</* SpinWait: */ true>(&work);
}
}
void VlWorkerThread::startWorker(VlWorkerThread* workerp, uint32_t threadId,
VlExecutionProfiler* profilerp) {
Verilated::threadContextp(workerp->m_contextp);
#ifdef VL_PROFILER
// Note: setupThread is not defined without VL_PROFILER, hence the #ifdef. Still, we might
// not be profiling execution (e.g.: PGO only), so profilerp might still be nullptr.
if (profilerp) profilerp->setupThread(threadId);
#endif
void VlWorkerThread::startWorker(VlWorkerThread* workerp, VerilatedContext* contextp) {
Verilated::threadContextp(contextp);
workerp->workerLoop();
}
//=============================================================================
// VlThreadPool
VlThreadPool::VlThreadPool(VerilatedContext* contextp, int nThreads,
VlExecutionProfiler* profiler) {
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
++nThreads;
const unsigned cpus = std::thread::hardware_concurrency();
if (cpus < nThreads) {
static int warnedOnce = 0;
if (!warnedOnce++) {
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
" --threads %d; may run slow.\n",
cpus, nThreads);
}
}
// Create worker threads
for (uint32_t threadId = 1; threadId < nThreads; ++threadId) {
m_workers.push_back(new VlWorkerThread{threadId, contextp, profiler});
}
VlThreadPool::VlThreadPool(VerilatedContext* contextp, unsigned nThreads) {
for (unsigned i = 0; i < nThreads; ++i) m_workers.push_back(new VlWorkerThread{contextp});
}
VlThreadPool::~VlThreadPool() {

View File

@ -24,6 +24,7 @@
#define VERILATOR_VERILATED_THREADS_H_
#include "verilatedos.h"
#include "verilated.h" // for VerilatedMutex and clang annotations
#ifndef VL_THREADED
@ -50,6 +51,9 @@
#endif
// clang-format on
class VlExecutionProfiler;
class VlThreadPool;
// VlMTaskVertex and VlThreadpool will work with multiple model class types.
// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
// as a void* here.
@ -129,20 +133,14 @@ public:
}
};
class VlExecutionProfiler;
class VlThreadPool;
class VlWorkerThread final {
private:
// TYPES
struct ExecRec {
VlExecFnp m_fnp; // Function to execute
VlSelfP m_selfp; // Symbol table to execute
bool m_evenCycle; // Even/odd for flag alternation
ExecRec()
: m_fnp{nullptr}
, m_selfp{nullptr}
, m_evenCycle{false} {}
VlExecFnp m_fnp = nullptr; // Function to execute
VlSelfP m_selfp = nullptr; // Symbol table to execute
bool m_evenCycle = false; // Even/odd for flag alternation
ExecRec() = default;
ExecRec(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
: m_fnp{fnp}
, m_selfp{selfp}
@ -162,26 +160,24 @@ private:
// Store the size atomically, so we can spin wait
std::atomic<size_t> m_ready_size;
std::atomic<bool> m_exiting; // Worker thread should exit
std::thread m_cthread; // Underlying C++ thread record
VerilatedContext* const m_contextp; // Context for spawned thread
VL_UNCOPYABLE(VlWorkerThread);
public:
// CONSTRUCTORS
explicit VlWorkerThread(uint32_t threadId, VerilatedContext* contextp,
VlExecutionProfiler* profilerp);
explicit VlWorkerThread(VerilatedContext* contextp);
~VlWorkerThread();
// METHODS
template <bool SpinWait>
inline void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Spin for a while, waiting for new data
for (int i = 0; i < VL_LOCK_SPINS; ++i) {
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) { //
break;
if VL_CONSTEXPR_CXX17 (SpinWait) {
for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) break;
VL_CPU_RELAX();
}
VL_CPU_RELAX();
}
VerilatedLockGuard lock{m_mutex};
while (m_ready.empty()) {
@ -195,8 +191,7 @@ public:
m_ready.erase(m_ready.begin());
m_ready_size.fetch_sub(1, std::memory_order_relaxed);
}
inline void wakeUp() { addTask(nullptr, nullptr, false); }
inline void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
inline void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle = false)
VL_MT_SAFE_EXCLUDES(m_mutex) {
bool notify;
{
@ -207,12 +202,15 @@ public:
}
if (notify) m_cv.notify_one();
}
void shutdown(); // Finish current tasks, then terminate thread
void wait(); // Blocks calling thread until all tasks complete in this thread
void workerLoop();
static void startWorker(VlWorkerThread* workerp, uint32_t threadId,
VlExecutionProfiler* profilerp);
static void startWorker(VlWorkerThread* workerp, VerilatedContext* contextp);
};
class VlThreadPool final {
class VlThreadPool final : public VerilatedVirtualBase {
// MEMBERS
std::vector<VlWorkerThread*> m_workers; // our workers
@ -221,8 +219,8 @@ public:
// Construct a thread pool with 'nThreads' dedicated threads. The thread
// pool will create these threads and make them available to execute tasks
// via this->workerp(index)->addTask(...)
VlThreadPool(VerilatedContext* contextp, int nThreads, VlExecutionProfiler* profilerp);
~VlThreadPool();
VlThreadPool(VerilatedContext* contextp, unsigned nThreads);
~VlThreadPool() override;
// METHODS
inline int numThreads() const { return m_workers.size(); }

View File

@ -24,21 +24,6 @@
// clang-format off
// In FST mode, VL_TRACE_THREADED enables offloading, but only if we also have
// the FST writer thread. This means with --trace-threads 1, we get the FST
// writer thread only, and with --trace-threads 2 we get offloading as well
#if defined(VL_TRACE_FST_WRITER_THREAD) && defined(VL_TRACE_THREADED)
# define VL_TRACE_OFFLOAD
#endif
// VCD tracing can happen fully in parallel
#if defined(VM_TRACE_VCD) && VM_TRACE_VCD && defined(VL_TRACE_THREADED)
# define VL_TRACE_PARALLEL
#endif
#if defined(VL_TRACE_PARALLEL) && defined(VL_TRACE_OFFLOAD)
# error "Cannot have VL_TRACE_PARALLEL and VL_TRACE_OFFLOAD together"
#endif
#include "verilated.h"
#include "verilated_trace_defs.h"
@ -47,9 +32,10 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#ifdef VL_TRACE_OFFLOAD
#ifdef VL_THREADED
# include <deque>
# include <thread>
#endif
@ -57,14 +43,18 @@
// clang-format on
class VlThreadPool;
template <class T_Trace, class T_Buffer> class VerilatedTraceBuffer;
template <class T_Buffer>
class VerilatedTraceBuffer;
template <class T_Buffer>
class VerilatedTraceOffloadBuffer;
#ifdef VL_TRACE_OFFLOAD
#ifdef VL_THREADED
//=============================================================================
// Offloaded tracing
// A simple synchronized first in first out queue
template <class T> class VerilatedThreadQueue final { // LCOV_EXCL_LINE // lcov bug
template <class T>
class VerilatedThreadQueue final { // LCOV_EXCL_LINE // lcov bug
private:
mutable VerilatedMutex m_mutex; // Protects m_queue
std::condition_variable_any m_cv;
@ -129,27 +119,47 @@ public:
};
#endif
//=============================================================================
// VerilatedTraceConfig
// Simple data representing trace configuration required by generated models.
class VerilatedTraceConfig final {
public:
const bool m_useParallel; // Use parallel tracing
const bool m_useOffloading; // Offloading trace rendering
const bool m_useFstWriterThread; // Use the separate FST writer thread
VerilatedTraceConfig(bool useParallel, bool useOffloading, bool useFstWriterThread)
: m_useParallel{useParallel}
, m_useOffloading{useOffloading}
, m_useFstWriterThread{useFstWriterThread} {}
};
//=============================================================================
// VerilatedTrace
// T_Trace is the format specific subclass of VerilatedTrace.
// T_Buffer is the format specific subclass of VerilatedTraceBuffer.
template <class T_Trace, class T_Buffer> class VerilatedTrace VL_NOT_FINAL {
// Give the buffer (both base and derived) access to the private bits
friend VerilatedTraceBuffer<T_Trace, T_Buffer>;
friend T_Buffer;
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
template <class T_Trace, class T_Buffer>
class VerilatedTrace VL_NOT_FINAL {
public:
using Buffer = T_Buffer;
using Buffer = VerilatedTraceBuffer<T_Buffer>;
using OffloadBuffer = VerilatedTraceOffloadBuffer<T_Buffer>;
//=========================================================================
// Generic tracing internals
using initCb_t = void (*)(void*, T_Trace*, uint32_t); // Type of init callbacks
using dumpCb_t = void (*)(void*, Buffer*); // Type of dump callbacks
using dumpOffloadCb_t = void (*)(void*, OffloadBuffer*); // Type of offload dump callbacks
using cleanupCb_t = void (*)(void*, T_Trace*); // Type of cleanup callbacks
private:
// Give the buffer (both base and derived) access to the private bits
friend T_Buffer;
friend Buffer;
friend OffloadBuffer;
struct CallbackRecord {
// Note: would make these fields const, but some old STL implementations
// (the one in Ubuntu 14.04 with GCC 4.8.4 in particular) use the
@ -158,6 +168,7 @@ private:
union { // The callback
initCb_t m_initCb;
dumpCb_t m_dumpCb;
dumpOffloadCb_t m_dumpOffloadCb;
cleanupCb_t m_cleanupCb;
};
void* m_userp; // The user pointer to pass to the callback (the symbol table)
@ -167,12 +178,18 @@ private:
CallbackRecord(dumpCb_t cb, void* userp)
: m_dumpCb{cb}
, m_userp{userp} {}
CallbackRecord(dumpOffloadCb_t cb, void* userp)
: m_dumpOffloadCb{cb}
, m_userp{userp} {}
CallbackRecord(cleanupCb_t cb, void* userp)
: m_cleanupCb{cb}
, m_userp{userp} {}
};
#ifdef VL_TRACE_PARALLEL
bool m_offload = false; // Use the offload thread (ignored if !VL_THREADED)
bool m_parallel = false; // Use parallel tracing (ignored if !VL_THREADED)
#ifdef VL_THREADED
struct ParallelWorkerData {
const dumpCb_t m_cb; // The callback
void* const m_userp; // The use pointer to pass to the callback
@ -194,19 +211,17 @@ private:
static void parallelWorkerTask(void*, bool);
#endif
using ParallelCallbackMap = std::unordered_map<VlThreadPool*, std::vector<CallbackRecord>>;
protected:
uint32_t* m_sigs_oldvalp = nullptr; // Previous value store
EData* m_sigs_enabledp = nullptr; // Bit vector of enabled codes (nullptr = all on)
private:
uint64_t m_timeLastDump = 0; // Last time we did a dump
std::vector<bool> m_sigs_enabledVec; // Staging for m_sigs_enabledp
std::vector<CallbackRecord> m_initCbs; // Routines to initialize tracing
ParallelCallbackMap m_fullCbs; // Routines to perform full dump
ParallelCallbackMap m_chgCbs; // Routines to perform incremental dump
std::vector<CallbackRecord> m_fullCbs; // Routines to perform full dump
std::vector<CallbackRecord> m_fullOffloadCbs; // Routines to perform offloaded full dump
std::vector<CallbackRecord> m_chgCbs; // Routines to perform incremental dump
std::vector<CallbackRecord> m_chgOffloadCbs; // Routines to perform offloaded incremental dump
std::vector<CallbackRecord> m_cleanupCbs; // Routines to call at the end of dump
std::vector<VlThreadPool*> m_threadPoolps; // All thread pools, in insertion order
bool m_fullDump = true; // Whether a full dump is required on the next call to 'dump'
uint32_t m_nextCode = 0; // Next code number to assign
uint32_t m_numSignals = 0; // Number of distinct signals
@ -216,24 +231,27 @@ private:
char m_scopeEscape = '.';
double m_timeRes = 1e-9; // Time resolution (ns/ms etc)
double m_timeUnit = 1e-0; // Time units (ns/ms etc)
uint64_t m_timeLastDump = 0; // Last time we did a dump
bool m_didSomeDump = false; // Did at least one dump (i.e.: m_timeLastDump is valid)
VerilatedContext* m_contextp = nullptr; // The context used by the traced models
std::unordered_set<const VerilatedModel*> m_models; // The collection of models being traced
void addThreadPool(VlThreadPool* threadPoolp) VL_MT_SAFE_EXCLUDES(m_mutex);
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord& cbRec)
void addCallbackRecord(std::vector<CallbackRecord>& cbVec, CallbackRecord&& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex);
// Equivalent to 'this' but is of the sub-type 'T_Trace*'. Use 'self()->'
// to access duck-typed functions to avoid a virtual function call.
T_Trace* self() { return static_cast<T_Trace*>(this); }
void runParallelCallbacks(const ParallelCallbackMap& cbMap);
void runCallbacks(const std::vector<CallbackRecord>& cbVec);
void runOffloadedCallbacks(const std::vector<CallbackRecord>& cbVec);
// Flush any remaining data for this file
static void onFlush(void* selfp) VL_MT_UNSAFE_ONE;
// Close the file on termination
static void onExit(void* selfp) VL_MT_UNSAFE_ONE;
#ifdef VL_TRACE_OFFLOAD
#ifdef VL_THREADED
// Number of total offload buffers that have been allocated
uint32_t m_numOffloadBuffers = 0;
// Size of offload buffers
@ -279,7 +297,6 @@ protected:
uint32_t numSignals() const { return m_numSignals; }
uint32_t maxBits() const { return m_maxBits; }
void fullDump(bool value) { m_fullDump = value; }
uint64_t timeLastDump() { return m_timeLastDump; }
double timeRes() const { return m_timeRes; }
double timeUnit() const { return m_timeUnit; }
@ -300,6 +317,14 @@ protected:
void closeBase();
void flushBase();
#ifdef VL_THREADED
inline bool offload() const { return m_offload; }
inline bool parallel() const { return m_parallel; }
#else
static constexpr bool offload() { return false; }
static constexpr bool parallel() { return false; }
#endif
//=========================================================================
// Virtual functions to be provided by the format specific implementation
@ -315,6 +340,9 @@ protected:
virtual Buffer* getTraceBuffer() = 0;
virtual void commitTraceBuffer(Buffer*) = 0;
// Configure sub-class
virtual void configure(const VerilatedTraceConfig&) = 0;
public:
//=========================================================================
// External interface to client code
@ -341,9 +369,12 @@ public:
//=========================================================================
// Non-hot path internal interface to Verilator generated code
void addModel(VerilatedModel*) VL_MT_SAFE_EXCLUDES(m_mutex);
void addInitCb(initCb_t cb, void* userp) VL_MT_SAFE;
void addFullCb(dumpCb_t cb, void* userp, VlThreadPool* = nullptr) VL_MT_SAFE;
void addChgCb(dumpCb_t cb, void* userp, VlThreadPool* = nullptr) VL_MT_SAFE;
void addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
void addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE;
void addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE;
void addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE;
void scopeEscape(char flag) { m_scopeEscape = flag; }
@ -355,33 +386,26 @@ public:
//=============================================================================
// VerilatedTraceBuffer
// T_Trace is the format specific subclass of VerilatedTrace.
// T_Buffer is the format specific subclass of VerilatedTraceBuffer.
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
template <class T_Trace, class T_Buffer> class VerilatedTraceBuffer VL_NOT_FINAL {
friend T_Trace; // Give the trace file access to the private bits
template <class T_Buffer>
class VerilatedTraceBuffer VL_NOT_FINAL : public T_Buffer {
protected:
T_Trace& m_owner; // The VerilatedTrace subclass that owns this buffer
// Type of the owner trace file
using Trace = typename std::remove_cv<
typename std::remove_reference<decltype(T_Buffer::m_owner)>::type>::type;
// Previous value store
uint32_t* const m_sigs_oldvalp = m_owner.m_sigs_oldvalp;
// Bit vector of enabled codes (nullptr = all on)
EData* const m_sigs_enabledp = m_owner.m_sigs_enabledp;
static_assert(std::has_virtual_destructor<T_Buffer>::value, "");
static_assert(std::is_base_of<VerilatedTrace<Trace, T_Buffer>, Trace>::value, "");
#ifdef VL_TRACE_OFFLOAD
// Write pointer into current buffer
uint32_t* m_offloadBufferWritep = m_owner.m_offloadBufferWritep;
// End of offload buffer
uint32_t* const m_offloadBufferEndp = m_owner.m_offloadBufferEndp;
#endif
friend Trace; // Give the trace file access to the private bits
friend std::default_delete<VerilatedTraceBuffer<T_Buffer>>;
// Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->'
// to access duck-typed functions to avoid a virtual function call.
inline T_Buffer* self() { return static_cast<T_Buffer*>(this); }
uint32_t* const m_sigs_oldvalp; // Previous value store
EData* const m_sigs_enabledp; // Bit vector of enabled codes (nullptr = all on)
explicit VerilatedTraceBuffer(T_Trace& owner);
virtual ~VerilatedTraceBuffer() = default;
explicit VerilatedTraceBuffer(Trace& owner);
~VerilatedTraceBuffer() override = default;
public:
//=========================================================================
@ -412,7 +436,67 @@ public:
void fullWData(uint32_t* oldp, const WData* newvalp, int bits);
void fullDouble(uint32_t* oldp, double newval);
#ifdef VL_TRACE_OFFLOAD
// In non-offload mode, these are called directly by the trace callbacks,
// and are called chg*. In offload mode, they are called by the worker
// thread and are called chg*Impl
// Check previous dumped value of signal. If changed, then emit trace entry
VL_ATTR_ALWINLINE inline void chgBit(uint32_t* oldp, CData newval) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
}
VL_ATTR_ALWINLINE inline void chgCData(uint32_t* oldp, CData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgSData(uint32_t* oldp, SData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgIData(uint32_t* oldp, IData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgQData(uint32_t* oldp, QData newval, int bits) {
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
for (int i = 0; i < (bits + 31) / 32; ++i) {
if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
fullWData(oldp, newvalp, bits);
return;
}
}
}
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);
}
};
#ifdef VL_THREADED
//=============================================================================
// VerilatedTraceOffloadBuffer
// T_Buffer is the format specific base class of VerilatedTraceBuffer.
// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
template <class T_Buffer>
class VerilatedTraceOffloadBuffer final : public VerilatedTraceBuffer<T_Buffer> {
using typename VerilatedTraceBuffer<T_Buffer>::Trace;
friend Trace; // Give the trace file access to the private bits
uint32_t* m_offloadBufferWritep; // Write pointer into current buffer
uint32_t* const m_offloadBufferEndp; // End of offload buffer
explicit VerilatedTraceOffloadBuffer(Trace& owner);
~VerilatedTraceOffloadBuffer() override = default;
public:
//=========================================================================
// Hot path internal interface to Verilator generated code
// Offloaded tracing. Just dump everything in the offload buffer
inline void chgBit(uint32_t code, CData newval) {
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
@ -463,63 +547,7 @@ public:
m_offloadBufferWritep += 4;
VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
}
#define chgBit chgBitImpl
#define chgCData chgCDataImpl
#define chgSData chgSDataImpl
#define chgIData chgIDataImpl
#define chgQData chgQDataImpl
#define chgWData chgWDataImpl
#define chgDouble chgDoubleImpl
#endif
// In non-offload mode, these are called directly by the trace callbacks,
// and are called chg*. In offload mode, they are called by the worker
// thread and are called chg*Impl
// Check previous dumped value of signal. If changed, then emit trace entry
VL_ATTR_ALWINLINE inline void chgBit(uint32_t* oldp, CData newval) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
}
VL_ATTR_ALWINLINE inline void chgCData(uint32_t* oldp, CData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgSData(uint32_t* oldp, SData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgIData(uint32_t* oldp, IData newval, int bits) {
const uint32_t diff = *oldp ^ newval;
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgQData(uint32_t* oldp, QData newval, int bits) {
const uint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
}
VL_ATTR_ALWINLINE inline void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
for (int i = 0; i < (bits + 31) / 32; ++i) {
if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
fullWData(oldp, newvalp, bits);
return;
}
}
}
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);
}
#ifdef VL_TRACE_OFFLOAD
#undef chgBit
#undef chgCData
#undef chgSData
#undef chgIData
#undef chgQData
#undef chgWData
#undef chgDouble
#endif
};
#endif
#endif // guard

View File

@ -26,7 +26,7 @@
#include "verilated_intrinsics.h"
#include "verilated_trace.h"
#ifdef VL_TRACE_PARALLEL
#ifdef VL_THREADED
# include "verilated_threads.h"
# include <list>
#endif
@ -78,11 +78,12 @@ static std::string doubleToTimescale(double value) {
return valuestr; // Gets converted to string, so no ref to stack
}
#ifdef VL_TRACE_OFFLOAD
#ifdef VL_THREADED
//=========================================================================
// Buffer management
template <> uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
template <>
uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
uint32_t* bufferp;
// Some jitter is expected, so some number of alternative offlaod buffers are
// required, but don't allocate more than 8 buffers.
@ -101,7 +102,8 @@ template <> uint32_t* VerilatedTrace<VL_SUB_T, VL_BUF_T>::getOffloadBuffer() {
return bufferp;
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const uint32_t* buffp) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const uint32_t* buffp) {
// Slow path code only called on flush/shutdown, so use a simple algorithm.
// Collect buffers from worker and stash them until we get the one we want.
std::deque<uint32_t*> stash;
@ -116,7 +118,8 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::waitForOffloadBuffer(const
//=========================================================================
// Worker thread
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
bool shutdown = false;
do {
@ -127,7 +130,7 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
const uint32_t* readp = bufferp;
std::unique_ptr<VL_BUF_T> traceBufp; // We own the passed tracebuffer
std::unique_ptr<Buffer> traceBufp; // We own the passed tracebuffer
while (true) {
const uint32_t cmd = readp[0];
@ -143,44 +146,44 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
// CHG_* commands
case VerilatedTraceOffloadCommand::CHG_BIT_0:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_0 " << top);
traceBufp->chgBitImpl(oldp, 0);
traceBufp->chgBit(oldp, 0);
continue;
case VerilatedTraceOffloadCommand::CHG_BIT_1:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_1 " << top);
traceBufp->chgBitImpl(oldp, 1);
traceBufp->chgBit(oldp, 1);
continue;
case VerilatedTraceOffloadCommand::CHG_CDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_CDATA " << top);
// Bits stored in bottom byte of command
traceBufp->chgCDataImpl(oldp, *readp, top);
traceBufp->chgCData(oldp, *readp, top);
readp += 1;
continue;
case VerilatedTraceOffloadCommand::CHG_SDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_SDATA " << top);
// Bits stored in bottom byte of command
traceBufp->chgSDataImpl(oldp, *readp, top);
traceBufp->chgSData(oldp, *readp, top);
readp += 1;
continue;
case VerilatedTraceOffloadCommand::CHG_IDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_IDATA " << top);
// Bits stored in bottom byte of command
traceBufp->chgIDataImpl(oldp, *readp, top);
traceBufp->chgIData(oldp, *readp, top);
readp += 1;
continue;
case VerilatedTraceOffloadCommand::CHG_QDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_QDATA " << top);
// Bits stored in bottom byte of command
traceBufp->chgQDataImpl(oldp, *reinterpret_cast<const QData*>(readp), top);
traceBufp->chgQData(oldp, *reinterpret_cast<const QData*>(readp), top);
readp += 2;
continue;
case VerilatedTraceOffloadCommand::CHG_WDATA:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_WDATA " << top);
traceBufp->chgWDataImpl(oldp, readp, top);
traceBufp->chgWData(oldp, readp, top);
readp += VL_WORDS_I(top);
continue;
case VerilatedTraceOffloadCommand::CHG_DOUBLE:
VL_TRACE_OFFLOAD_DEBUG("Command CHG_DOUBLE " << top);
traceBufp->chgDoubleImpl(oldp, *reinterpret_cast<const double*>(readp));
traceBufp->chgDouble(oldp, *reinterpret_cast<const double*>(readp));
readp += 2;
continue;
@ -196,7 +199,7 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
case VerilatedTraceOffloadCommand::TRACE_BUFFER:
VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
readp -= 1; // No code in this command, undo increment
traceBufp.reset(*reinterpret_cast<VL_BUF_T* const*>(readp));
traceBufp.reset(*reinterpret_cast<Buffer* const*>(readp));
readp += 2;
continue;
@ -231,7 +234,8 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain() {
} while (VL_LIKELY(!shutdown));
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
// If the worker thread is not running, done..
if (!m_workerThread) return;
@ -251,37 +255,45 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::shutdownOffloadWorker() {
//=============================================================================
// Life cycle
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {
#ifdef VL_TRACE_OFFLOAD
shutdownOffloadWorker();
while (m_numOffloadBuffers) {
delete[] m_offloadBuffersFromWorker.get();
--m_numOffloadBuffers;
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::closeBase() {
#ifdef VL_THREADED
if (offload()) {
shutdownOffloadWorker();
while (m_numOffloadBuffers) {
delete[] m_offloadBuffersFromWorker.get();
--m_numOffloadBuffers;
}
}
#endif
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {
#ifdef VL_TRACE_OFFLOAD
// Hand an empty buffer to the worker thread
uint32_t* const bufferp = getOffloadBuffer();
*bufferp = VerilatedTraceOffloadCommand::END;
m_offloadBuffersToWorker.put(bufferp);
// Wait for it to be returned. As the processing is in-order,
// this ensures all previous buffers have been processed.
waitForOffloadBuffer(bufferp);
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::flushBase() {
#ifdef VL_THREADED
if (offload()) {
// Hand an empty buffer to the worker thread
uint32_t* const bufferp = getOffloadBuffer();
*bufferp = VerilatedTraceOffloadCommand::END;
m_offloadBuffersToWorker.put(bufferp);
// Wait for it to be returned. As the processing is in-order,
// this ensures all previous buffers have been processed.
waitForOffloadBuffer(bufferp);
}
#endif
}
//=============================================================================
// Callbacks to run on global events
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush(void* selfp) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush(void* selfp) {
// This calls 'flush' on the derived class (which must then get any mutex)
reinterpret_cast<VL_SUB_T*>(selfp)->flush();
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
// This calls 'close' on the derived class (which must then get any mutex)
reinterpret_cast<VL_SUB_T*>(selfp)->close();
}
@ -289,25 +301,26 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit(void* selfp) {
//=============================================================================
// VerilatedTrace
template <> VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace() {
template <>
VerilatedTrace<VL_SUB_T, VL_BUF_T>::VerilatedTrace() {
set_time_unit(Verilated::threadContextp()->timeunitString());
set_time_resolution(Verilated::threadContextp()->timeprecisionString());
}
template <> VerilatedTrace<VL_SUB_T, VL_BUF_T>::~VerilatedTrace() {
template <>
VerilatedTrace<VL_SUB_T, VL_BUF_T>::~VerilatedTrace() {
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = nullptr);
if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
Verilated::removeFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
Verilated::removeExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
#ifdef VL_TRACE_OFFLOAD
closeBase();
#endif
if (offload()) closeBase();
}
//=========================================================================
// Internals available to format specific implementations
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
// Note: It is possible to re-open a trace file (VCD in particular),
// so we must reset the next code here, but it must have the same number
// of codes on re-open
@ -355,17 +368,19 @@ template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::traceInit() VL_MT_UNSAFE {
Verilated::addFlushCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onFlush, this);
Verilated::addExitCb(VerilatedTrace<VL_SUB_T, VL_BUF_T>::onExit, this);
#ifdef VL_TRACE_OFFLOAD
// Compute offload buffer size. we need to be able to store a new value for
// each signal, which is 'nextCode()' entries after the init callbacks
// above have been run, plus up to 2 more words of metadata per signal,
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
// update.
m_offloadBufferSize = nextCode() + numSignals() * 2 + 4;
#ifdef VL_THREADED
if (offload()) {
// Compute offload buffer size. we need to be able to store a new value for
// each signal, which is 'nextCode()' entries after the init callbacks
// above have been run, plus up to 2 more words of metadata per signal,
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
// update.
m_offloadBufferSize = nextCode() + numSignals() * 2 + 4;
// Start the worker thread
m_workerThread.reset(
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
// Start the worker thread
m_workerThread.reset(
new std::thread{&VerilatedTrace<VL_SUB_T, VL_BUF_T>::offloadWorkerThreadMain, this});
}
#endif
}
@ -415,14 +430,16 @@ bool VerilatedTrace<VL_SUB_T, VL_BUF_T>::declCode(uint32_t code, const char* nam
//=========================================================================
// Internals available to format specific implementations
template <> std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
template <>
std::string VerilatedTrace<VL_SUB_T, VL_BUF_T>::timeResStr() const {
return doubleToTimescale(m_timeRes);
}
//=========================================================================
// External interface to client code
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_unit(const char* unitp) VL_MT_SAFE {
m_timeUnit = timescaleToDouble(unitp);
}
template <>
@ -451,8 +468,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dumpvars(int level, const std::string&
}
}
#ifdef VL_TRACE_PARALLEL
template <> //
#ifdef VL_THREADED
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::parallelWorkerTask(void* datap, bool) {
ParallelWorkerData* const wdp = reinterpret_cast<ParallelWorkerData*>(datap);
// Run the task
@ -463,7 +480,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::parallelWorkerTask(void* datap, bool) {
if (wdp->m_waiting) wdp->m_cv.notify_one();
}
template <> VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wait() {
template <>
VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWorkerData::wait() {
// Spin for a while, waiting for the buffer to become ready
for (int i = 0; i < VL_LOCK_SPINS; ++i) {
if (VL_LIKELY(m_ready.load(std::memory_order_relaxed))) return;
@ -478,55 +496,69 @@ template <> VL_ATTR_NOINLINE void VerilatedTrace<VL_SUB_T, VL_BUF_T>::ParallelWo
#endif
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runParallelCallbacks(const ParallelCallbackMap& cbMap) {
for (VlThreadPool* threadPoolp : m_threadPoolps) {
#ifdef VL_TRACE_PARALLEL
// If tracing in parallel, dispatch to the thread pool (if exists)
if (threadPoolp && threadPoolp->numThreads()) {
// List of work items for thread (std::list, as ParallelWorkerData is not movable)
std::list<ParallelWorkerData> workerData;
// We use the whole pool + the main thread
const unsigned threads = threadPoolp->numThreads() + 1;
// Main thread executes all jobs with index % threads == 0
std::vector<ParallelWorkerData*> mainThreadWorkerData;
// The tracing callbacks to execute on this thread-pool
const auto& cbVec = cbMap.at(threadPoolp);
// Enuque all the jobs
for (unsigned i = 0; i < cbVec.size(); ++i) {
const CallbackRecord& cbr = cbVec[i];
// Always get the trace buffer on the main thread
Buffer* const bufp = getTraceBuffer();
// Create new work item
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
// Grab the new work item
ParallelWorkerData* const itemp = &workerData.back();
// Enqueue task to thread pool, or main thread
if (unsigned rem = i % threads) {
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp, false);
} else {
mainThreadWorkerData.push_back(itemp);
}
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runCallbacks(const std::vector<CallbackRecord>& cbVec) {
#ifdef VL_THREADED
if (parallel()) {
// If tracing in parallel, dispatch to the thread pool
VlThreadPool* threadPoolp = static_cast<VlThreadPool*>(m_contextp->threadPoolp());
// List of work items for thread (std::list, as ParallelWorkerData is not movable)
std::list<ParallelWorkerData> workerData;
// We use the whole pool + the main thread
const unsigned threads = threadPoolp->numThreads() + 1;
// Main thread executes all jobs with index % threads == 0
std::vector<ParallelWorkerData*> mainThreadWorkerData;
// Enuque all the jobs
for (unsigned i = 0; i < cbVec.size(); ++i) {
const CallbackRecord& cbr = cbVec[i];
// Always get the trace buffer on the main thread
Buffer* const bufp = getTraceBuffer();
// Create new work item
workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
// Grab the new work item
ParallelWorkerData* const itemp = &workerData.back();
// Enqueue task to thread pool, or main thread
if (unsigned rem = i % threads) {
threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp);
} else {
mainThreadWorkerData.push_back(itemp);
}
// Execute main thead jobs
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
parallelWorkerTask(itemp, false);
}
// Commit all trace buffers in order
for (ParallelWorkerData& item : workerData) {
// Wait until ready
item.wait();
// Commit the buffer
commitTraceBuffer(item.m_bufp);
}
continue;
}
// Execute main thead jobs
for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
parallelWorkerTask(itemp, false);
}
// Commit all trace buffers in order
for (ParallelWorkerData& item : workerData) {
// Wait until ready
item.wait();
// Commit the buffer
commitTraceBuffer(item.m_bufp);
}
// Done
return;
}
#endif
// Fall back on sequential execution
for (const CallbackRecord& cbr : cbVec) {
Buffer* const traceBufferp = getTraceBuffer();
cbr.m_dumpCb(cbr.m_userp, traceBufferp);
commitTraceBuffer(traceBufferp);
}
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::runOffloadedCallbacks(
const std::vector<CallbackRecord>& cbVec) {
// Fall back on sequential execution
for (const CallbackRecord& cbr : cbVec) {
#ifdef VL_THREADED
Buffer* traceBufferp = getTraceBuffer();
cbr.m_dumpOffloadCb(cbr.m_userp, static_cast<OffloadBuffer*>(traceBufferp));
commitTraceBuffer(traceBufferp);
#else
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#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);
}
}
}
@ -536,13 +568,14 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
// This does get the mutex, but if multiple threads are trying to dump
// chances are the data being dumped will have other problems
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(m_timeLastDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
if (VL_UNCOVERABLE(m_didSomeDump && timeui <= m_timeLastDump)) { // LCOV_EXCL_START
VL_PRINTF_MT("%%Warning: previous dump at t=%" PRIu64 ", requesting t=%" PRIu64
", dump call ignored\n",
m_timeLastDump, timeui);
return;
} // LCOV_EXCL_STOP
m_timeLastDump = timeui;
m_didSomeDump = true;
Verilated::quiesce();
@ -553,35 +586,47 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
if (!preChangeDump()) return;
}
#ifdef VL_TRACE_OFFLOAD
// Currently only incremental dumps run on the worker thread
uint32_t* bufferp = nullptr;
if (VL_LIKELY(!m_fullDump)) {
// Get the offload buffer we are about to fill
bufferp = getOffloadBuffer();
m_offloadBufferWritep = bufferp;
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
if (offload()) {
#ifdef VL_THREADED
// Currently only incremental dumps run on the worker thread
if (VL_LIKELY(!m_fullDump)) {
// Get the offload buffer we are about to fill
bufferp = getOffloadBuffer();
m_offloadBufferWritep = bufferp;
m_offloadBufferEndp = bufferp + m_offloadBufferSize;
// Tell worker to update time point
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
m_offloadBufferWritep += 3;
// Tell worker to update time point
m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
*reinterpret_cast<uint64_t*>(m_offloadBufferWritep + 1) = timeui;
m_offloadBufferWritep += 3;
} else {
// Update time point
flushBase();
emitTimeChange(timeui);
}
#else
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#endif
} else {
// Update time point
flushBase();
emitTimeChange(timeui);
}
#else
// Update time point
emitTimeChange(timeui);
#endif
// Run the callbacks
if (VL_UNLIKELY(m_fullDump)) {
m_fullDump = false; // No more need for next dump to be full
runParallelCallbacks(m_fullCbs);
if (offload()) {
runOffloadedCallbacks(m_fullOffloadCbs);
} else {
runCallbacks(m_fullCbs);
}
} else {
runParallelCallbacks(m_chgCbs);
if (offload()) {
runOffloadedCallbacks(m_chgOffloadCbs);
} else {
runCallbacks(m_chgCbs);
}
}
for (uint32_t i = 0; i < m_cleanupCbs.size(); ++i) {
@ -589,8 +634,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
cbr.m_cleanupCb(cbr.m_userp, self());
}
#ifdef VL_TRACE_OFFLOAD
if (VL_LIKELY(bufferp)) {
#ifdef VL_THREADED
if (offload() && VL_LIKELY(bufferp)) {
// Mark end of the offload buffer we just filled
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::END;
@ -607,58 +652,98 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD
// Non-hot path internal interface to Verilator generated code
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addThreadPool(VlThreadPool* threadPoolp)
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addModel(VerilatedModel* modelp)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
for (VlThreadPool* const poolp : m_threadPoolps) {
if (poolp == threadPoolp) return;
const bool firstModel = m_models.empty();
const bool newModel = m_models.insert(modelp).second;
VerilatedContext* const contextp = modelp->contextp();
// Validate
if (!newModel) { // LCOV_EXCL_START
VL_FATAL_MT(__FILE__, __LINE__, "",
"The same model has already been added to this trace file");
}
m_threadPoolps.push_back(threadPoolp);
if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"A trace file instance can only handle models from the same context");
}
if (VL_UNCOVERABLE(m_didSomeDump)) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"Cannot add models to a trace file if 'dump' has already been called");
} // LCOV_EXCL_STOP
// Keep hold of the context
m_contextp = contextp;
// Get the desired trace config from the model
const std::unique_ptr<VerilatedTraceConfig> configp = modelp->traceConfig();
#ifndef VL_THREADED
if (configp->m_useOffloading) {
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use trace offloading without VL_THREADED");
}
#endif
// Configure trace base class
if (!firstModel) {
if (m_offload != configp->m_useOffloading) {
VL_FATAL_MT(__FILE__, __LINE__, "",
"Either all or no models using the same trace file must use offloading");
}
}
m_offload = configp->m_useOffloading;
// If at least one model requests parallel tracing, then use it
m_parallel |= configp->m_useParallel;
if (VL_UNCOVERABLE(m_parallel && m_offload)) { // LCOV_EXCL_START
VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use parallel tracing with offloading");
} // LCOV_EXCL_STOP
// Configure format specific sub class
configure(*(configp.get()));
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCallbackRecord(std::vector<CallbackRecord>& cbVec,
CallbackRecord& cbRec)
CallbackRecord&& cbRec)
VL_MT_SAFE_EXCLUDES(m_mutex) {
const VerilatedLockGuard lock{m_mutex};
if (VL_UNCOVERABLE(timeLastDump() != 0)) { // LCOV_EXCL_START
const std::string msg = (std::string{"Internal: "} + __FILE__ + "::" + __FUNCTION__
+ " called with already open file");
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
} // LCOV_EXCL_STOP
cbVec.push_back(cbRec);
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addInitCb(initCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_initCbs, cbr);
addCallbackRecord(m_initCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp,
VlThreadPool* threadPoolp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addThreadPool(threadPoolp);
addCallbackRecord(m_fullCbs[threadPoolp], cbr);
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_fullCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp,
VlThreadPool* threadPoolp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addThreadPool(threadPoolp);
addCallbackRecord(m_chgCbs[threadPoolp], cbr);
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addFullCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_fullOffloadCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_chgCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addChgCb(dumpOffloadCb_t cb, void* userp) VL_MT_SAFE {
addCallbackRecord(m_chgOffloadCbs, CallbackRecord{cb, userp});
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE {
CallbackRecord cbr{cb, userp};
addCallbackRecord(m_cleanupCbs, cbr);
addCallbackRecord(m_cleanupCbs, CallbackRecord{cb, userp});
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::pushNamePrefix(const std::string& prefix) {
m_namePrefixStack.push_back(m_namePrefixStack.back() + prefix);
}
template <> void VerilatedTrace<VL_SUB_T, VL_BUF_T>::popNamePrefix(unsigned count) {
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::popNamePrefix(unsigned count) {
while (count--) m_namePrefixStack.pop_back();
assert(!m_namePrefixStack.empty());
}
@ -756,83 +841,93 @@ static inline void cvtQDataToStr(char* dstp, QData value) {
//=========================================================================
// VerilatedTraceBuffer
template <> //
VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::VerilatedTraceBuffer(VL_SUB_T& owner)
: m_owner{owner} {
#ifdef VL_TRACE_OFFLOAD
if (m_offloadBufferWritep) {
using This = VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>*;
// Tack on the buffer address
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
"This should be enough on all plafrorms");
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
*reinterpret_cast<This*>(m_offloadBufferWritep) = this;
m_offloadBufferWritep += 2;
}
#endif
}
template <>
VerilatedTraceBuffer<VL_BUF_T>::VerilatedTraceBuffer(Trace& owner)
: VL_BUF_T{owner}
, m_sigs_oldvalp{owner.m_sigs_oldvalp}
, m_sigs_enabledp{owner.m_sigs_enabledp} {}
// These functions must write the new value back into the old value store,
// and subsequently call the format specific emit* implementations. Note
// that this file must be included in the format specific implementation, so
// the emit* functions can be inlined for performance.
template <> //
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
template <>
void VerilatedTraceBuffer<VL_BUF_T>::fullBit(uint32_t* oldp, CData newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitBit(code, newval);
emitBit(code, newval);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullCData(uint32_t* oldp, CData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitCData(code, newval, bits);
emitCData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullSData(uint32_t* oldp, SData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitSData(code, newval, bits);
emitSData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullIData(uint32_t* oldp, IData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*oldp = newval; // Still copy even if not tracing so chg doesn't call full
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitIData(code, newval, bits);
emitIData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<QData*>(oldp) = newval;
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitQData(code, newval, bits);
emitQData(code, newval, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp,
int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp, int bits) {
const uint32_t code = oldp - m_sigs_oldvalp;
for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
self()->emitWData(code, newvalp, bits);
emitWData(code, newvalp, bits);
}
template <>
void VerilatedTraceBuffer<VL_SUB_T, VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
void VerilatedTraceBuffer<VL_BUF_T>::fullDouble(uint32_t* oldp, double newval) {
const uint32_t code = oldp - m_sigs_oldvalp;
*reinterpret_cast<double*>(oldp) = newval;
if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
// cppcheck-suppress invalidPointerCast
self()->emitDouble(code, newval);
emitDouble(code, newval);
}
#ifdef VL_THREADED
//=========================================================================
// VerilatedTraceOffloadBuffer
template <>
VerilatedTraceOffloadBuffer<VL_BUF_T>::VerilatedTraceOffloadBuffer(VL_SUB_T& owner)
: VerilatedTraceBuffer<VL_BUF_T>{owner}
, m_offloadBufferWritep{owner.m_offloadBufferWritep}
, m_offloadBufferEndp{owner.m_offloadBufferEndp} {
if (m_offloadBufferWritep) {
using This = VerilatedTraceBuffer<VL_BUF_T>*;
// Tack on the buffer address
static_assert(2 * sizeof(uint32_t) >= sizeof(This),
"This should be enough on all plafrorms");
*m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
*reinterpret_cast<This*>(m_offloadBufferWritep) = static_cast<This>(this);
m_offloadBufferWritep += 2;
}
}
#endif
#endif // VL_CPPCHECK

View File

@ -91,9 +91,9 @@ class VlReadMem final {
const int m_bits; // Bit width of values
const std::string& m_filename; // Filename
const QData m_end; // End address (as specified by user)
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to read
int m_linenum; // Line number last read from file
FILE* m_fp = nullptr; // File handle for filename
QData m_addr = 0; // Next address to read
int m_linenum = 0; // Line number last read from file
bool m_anyAddr = false; // Had address directive in the file
public:
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
@ -107,8 +107,8 @@ public:
class VlWriteMem final {
const bool m_hex; // Hex format
const int m_bits; // Bit width of values
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to write
FILE* m_fp = nullptr; // File handle for filename
QData m_addr = 0; // Next address to write
public:
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlWriteMem();
@ -131,7 +131,8 @@ public:
static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE;
template <std::size_t T_Words> struct VlWide final {
template <std::size_t T_Words>
struct VlWide final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
EData m_storage[T_Words]; // Contents of the packed array
@ -163,7 +164,8 @@ VlWide<T_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<T_Words>&) {
return *((VlWide<T_Words>*)inp);
}
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
template <std::size_t T_Words>
std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
return VL_TO_STRING_W(T_Words, obj.data());
}
@ -174,7 +176,8 @@ template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& o
//
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
// For dynamic arrays it is always zero
template <class T_Value, size_t T_MaxSize = 0> class VlQueue final {
template <class T_Value, size_t T_MaxSize = 0>
class VlQueue final {
private:
// TYPES
using Deque = std::deque<T_Value>;
@ -199,7 +202,8 @@ public:
// Standard copy constructor works. Verilog: assoca = assocb
// Also must allow conversion from a different T_MaxSize queue
template <size_t U_MaxSize = 0> VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
template <size_t U_MaxSize = 0>
VlQueue operator=(const VlQueue<T_Value, U_MaxSize>& rhs) {
m_deque = rhs.privateDeque();
if (VL_UNLIKELY(T_MaxSize && T_MaxSize < m_deque.size())) m_deque.resize(T_MaxSize - 1);
return *this;
@ -330,7 +334,8 @@ public:
// Methods
void sort() { std::sort(m_deque.begin(), m_deque.end()); }
template <typename Func> void sort(Func with_func) {
template <typename Func>
void sort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
@ -338,7 +343,8 @@ public:
});
}
void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
template <typename Func> void rsort(Func with_func) {
template <typename Func>
void rsort(Func with_func) {
// with_func returns arbitrary type to use for the sort comparison
std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
// index number is meaninless with sort, as it changes
@ -373,7 +379,8 @@ public:
}
return out;
}
template <typename Func> VlQueue find(Func with_func) const {
template <typename Func>
VlQueue find(Func with_func) const {
VlQueue out;
IData index = 0;
for (const auto& i : m_deque) {
@ -382,7 +389,8 @@ public:
}
return out;
}
template <typename Func> VlQueue<IData> find_index(Func with_func) const {
template <typename Func>
VlQueue<IData> find_index(Func with_func) const {
VlQueue<IData> out;
IData index = 0;
for (const auto& i : m_deque) {
@ -391,7 +399,8 @@ public:
}
return out;
}
template <typename Func> VlQueue find_first(Func with_func) const {
template <typename Func>
VlQueue find_first(Func with_func) const {
// Can't use std::find_if as need index number
IData index = 0;
for (const auto& i : m_deque) {
@ -400,7 +409,8 @@ public:
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_first_index(Func with_func) const {
template <typename Func>
VlQueue<IData> find_first_index(Func with_func) const {
IData index = 0;
for (const auto& i : m_deque) {
if (with_func(index, i)) return VlQueue<IData>::cons(index);
@ -408,7 +418,8 @@ public:
}
return VlQueue<IData>{};
}
template <typename Func> VlQueue find_last(Func with_func) const {
template <typename Func>
VlQueue find_last(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto& item : vlstd::reverse_view(m_deque)) {
if (with_func(index, item)) return VlQueue::cons(item);
@ -416,7 +427,8 @@ public:
}
return VlQueue{};
}
template <typename Func> VlQueue<IData> find_last_index(Func with_func) const {
template <typename Func>
VlQueue<IData> find_last_index(Func with_func) const {
IData index = m_deque.size() - 1;
for (auto& item : vlstd::reverse_view(m_deque)) {
if (with_func(index, item)) return VlQueue<IData>::cons(index);
@ -442,7 +454,8 @@ public:
for (const auto& i : m_deque) out += i;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
template <typename Func>
T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out += with_func(index++, i);
@ -456,7 +469,8 @@ public:
for (; it != m_deque.end(); ++it) out *= *it;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
template <typename Func>
T_Value r_product(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
@ -474,7 +488,8 @@ public:
for (; it != m_deque.end(); ++it) out &= *it;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
template <typename Func>
T_Value r_and(Func with_func) const {
if (m_deque.empty()) return T_Value(0);
auto it = m_deque.begin();
IData index = 0;
@ -489,7 +504,8 @@ public:
for (const auto& i : m_deque) out |= i;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
template <typename Func>
T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out |= with_func(index++, i);
@ -500,7 +516,8 @@ public:
for (const auto& i : m_deque) out ^= i;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
template <typename Func>
T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
IData index = 0;
for (const auto& i : m_deque) out ^= with_func(index++, i);
@ -520,7 +537,8 @@ public:
}
};
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
template <class T_Value>
std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
return obj.to_string();
}
@ -529,7 +547,8 @@ template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
template <class T_Key, class T_Value> class VlAssocArray final {
template <class T_Key, class T_Value>
class VlAssocArray final {
private:
// TYPES
using Map = std::map<T_Key, T_Value>;
@ -654,19 +673,22 @@ public:
}
return out;
}
template <typename Func> VlQueue<T_Value> find(Func with_func) const {
template <typename Func>
VlQueue<T_Value> find(Func with_func) const {
VlQueue<T_Value> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.second);
return out;
}
template <typename Func> VlQueue<T_Key> find_index(Func with_func) const {
template <typename Func>
VlQueue<T_Key> find_index(Func with_func) const {
VlQueue<T_Key> out;
for (const auto& i : m_map)
if (with_func(i.first, i.second)) out.push_back(i.first);
return out;
}
template <typename Func> VlQueue<T_Value> find_first(Func with_func) const {
template <typename Func>
VlQueue<T_Value> find_first(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
@ -674,7 +696,8 @@ public:
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_first_index(Func with_func) const {
template <typename Func>
VlQueue<T_Key> find_first_index(Func with_func) const {
const auto it
= std::find_if(m_map.begin(), m_map.end(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
@ -682,7 +705,8 @@ public:
if (it == m_map.end()) return VlQueue<T_Value>{};
return VlQueue<T_Key>::cons(it->first);
}
template <typename Func> VlQueue<T_Value> find_last(Func with_func) const {
template <typename Func>
VlQueue<T_Value> find_last(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
@ -690,7 +714,8 @@ public:
if (it == m_map.rend()) return VlQueue<T_Value>{};
return VlQueue<T_Value>::cons(it->second);
}
template <typename Func> VlQueue<T_Key> find_last_index(Func with_func) const {
template <typename Func>
VlQueue<T_Key> find_last_index(Func with_func) const {
const auto it
= std::find_if(m_map.rbegin(), m_map.rend(), [=](const std::pair<T_Key, T_Value>& i) {
return with_func(i.first, i.second);
@ -724,7 +749,8 @@ public:
for (const auto& i : m_map) out += i.second;
return out;
}
template <typename Func> T_Value r_sum(Func with_func) const {
template <typename Func>
T_Value r_sum(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out += with_func(i.first, i.second);
return out;
@ -737,7 +763,8 @@ public:
for (; it != m_map.end(); ++it) out *= it->second;
return out;
}
template <typename Func> T_Value r_product(Func with_func) const {
template <typename Func>
T_Value r_product(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
@ -753,7 +780,8 @@ public:
for (; it != m_map.end(); ++it) out &= it->second;
return out;
}
template <typename Func> T_Value r_and(Func with_func) const {
template <typename Func>
T_Value r_and(Func with_func) const {
if (m_map.empty()) return T_Value(0);
auto it = m_map.begin();
T_Value out{with_func(it->first, it->second)};
@ -766,7 +794,8 @@ public:
for (const auto& i : m_map) out |= i.second;
return out;
}
template <typename Func> T_Value r_or(Func with_func) const {
template <typename Func>
T_Value r_or(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out |= with_func(i.first, i.second);
return out;
@ -776,7 +805,8 @@ public:
for (const auto& i : m_map) out ^= i.second;
return out;
}
template <typename Func> T_Value r_xor(Func with_func) const {
template <typename Func>
T_Value r_xor(Func with_func) const {
T_Value out(0); // Type must have assignment operator
for (const auto& i : m_map) out ^= with_func(i.first, i.second);
return out;
@ -839,7 +869,8 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
/// This class may get exposed to a Verilated Model's top I/O, if the top
/// IO has an unpacked array.
template <class T_Value, std::size_t T_Depth> struct VlUnpacked final {
template <class T_Value, std::size_t T_Depth>
struct VlUnpacked final {
// MEMBERS
// This should be the only data member, otherwise generated static initializers need updating
T_Value m_storage[T_Depth]; // Contents of the unpacked array

View File

@ -38,13 +38,13 @@
# include <unistd.h>
#endif
#ifndef O_LARGEFILE // For example on WIN32
#ifndef O_LARGEFILE // WIN32 headers omit this
# define O_LARGEFILE 0
#endif
#ifndef O_NONBLOCK
#ifndef O_NONBLOCK // WIN32 headers omit this
# define O_NONBLOCK 0
#endif
#ifndef O_CLOEXEC
#ifndef O_CLOEXEC // WIN32 headers omit this
# define O_CLOEXEC 0
#endif
@ -65,7 +65,8 @@ constexpr unsigned VL_TRACE_SUFFIX_ENTRY_SIZE = 8; // Size of a suffix entry
//=============================================================================
// Utility functions: TODO: put these in a common place and share them.
template <size_t N> static size_t roundUpToMultipleOf(size_t value) {
template <size_t N>
static size_t roundUpToMultipleOf(size_t value) {
static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
size_t mask = N - 1;
return (value + mask) & ~mask;
@ -119,13 +120,13 @@ void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
// Set member variables
m_filename = filename; // "" is ok, as someone may overload open
openNextImp(m_rolloverMB != 0);
openNextImp(m_rolloverSize != 0);
if (!isOpen()) return;
dumpHeader();
// When using rollover, the first chunk contains the header only.
if (m_rolloverMB) openNextImp(true);
if (m_rolloverSize) openNextImp(true);
}
void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
@ -179,7 +180,7 @@ void VerilatedVcd::openNextImp(bool incFilename) {
}
bool VerilatedVcd::preChangeDump() {
if (VL_UNLIKELY(m_rolloverMB && m_wroteBytes > m_rolloverMB)) openNextImp(true);
if (VL_UNLIKELY(m_rolloverSize && m_wroteBytes > m_rolloverSize)) openNextImp(true);
return isOpen();
}
@ -229,9 +230,11 @@ VerilatedVcd::~VerilatedVcd() {
if (m_wrBufp) VL_DO_CLEAR(delete[] m_wrBufp, m_wrBufp = nullptr);
deleteNameMap();
if (m_filep && m_fileNewed) VL_DO_CLEAR(delete m_filep, m_filep = nullptr);
#ifdef VL_TRACE_PARALLEL
assert(m_numBuffers == m_freeBuffers.size());
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
#ifdef VL_THREADED
if (parallel()) {
assert(m_numBuffers == m_freeBuffers.size());
for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
}
#endif
}
@ -260,11 +263,6 @@ void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
// This function is on the flush() call path
const VerilatedLockGuard lock{m_mutex};
if (!isOpen()) return;
if (m_evcd) {
printStr("$vcdclose ");
printQuad(timeLastDump());
printStr(" $end\n");
}
closePrev();
// closePrev() called Super::flush(), so we just
// need to shut down the tracing thread here.
@ -514,38 +512,29 @@ void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, b
// Print reference
std::string decl = "$var ";
if (m_evcd) {
decl += "port";
} else {
decl += wirep; // usually "wire"
}
decl += wirep; // usually "wire"
constexpr size_t bufsize = 1000;
char buf[bufsize];
VL_SNPRINTF(buf, bufsize, " %2d ", bits);
decl += buf;
if (m_evcd) {
VL_SNPRINTF(buf, bufsize, "<%u", code);
decl += buf;
} else {
// Add string code to decl
char* const endp = writeCode(buf, code);
*endp = '\0';
decl += buf;
// Build suffix array entry
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
const size_t length = endp - buf;
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
// 1 bit values don't have a ' ' separator between value and string code
const bool isBit = bits == 1;
entryp[0] = ' '; // Separator
// Use memcpy as we checked size above, and strcpy is flagged unsafe
std::memcpy(entryp + !isBit, buf,
std::strlen(buf)); // Code (overwrite separator if isBit)
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
// Set length of suffix (used to increment write pointer)
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1;
}
// Add string code to decl
char* const endp = writeCode(buf, code);
*endp = '\0';
decl += buf;
// Build suffix array entry
char* const entryp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
const size_t length = endp - buf;
assert(length <= VL_TRACE_MAX_VCD_CODE_SIZE);
// 1 bit values don't have a ' ' separator between value and string code
const bool isBit = bits == 1;
entryp[0] = ' '; // Separator
// Use memcpy as we checked size above, and strcpy is flagged unsafe
std::memcpy(entryp + !isBit, buf,
std::strlen(buf)); // Code (overwrite separator if isBit)
entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n'
// Set length of suffix (used to increment write pointer)
entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1;
decl += " ";
decl += basename;
if (array) {
@ -583,67 +572,63 @@ void VerilatedVcd::declDouble(uint32_t code, const char* name, bool array, int a
//=============================================================================
// Get/commit trace buffer
VerilatedVcdBuffer* VerilatedVcd::getTraceBuffer() {
#ifdef VL_TRACE_PARALLEL
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// If no buffer available, allocate a new one
if (m_freeBuffers.empty()) {
constexpr size_t pageSize = 4096;
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
size_t startingSize = roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
++m_numBuffers;
VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer() {
VerilatedVcd::Buffer* const bufp = new Buffer{*this};
#ifdef VL_THREADED
if (parallel()) {
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// If no buffer available, allocate a new one
if (m_freeBuffers.empty()) {
constexpr size_t pageSize = 4096;
// 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
size_t startingSize = roundUpToMultipleOf<pageSize>(4 * m_maxSignalBytes);
m_freeBuffers.emplace_back(new char[startingSize], startingSize);
++m_numBuffers;
}
// Grab a buffer
const auto pair = m_freeBuffers.back();
m_freeBuffers.pop_back();
// Initialize
bufp->m_writep = bufp->m_bufp = pair.first;
bufp->m_size = pair.second;
bufp->adjustGrowp();
}
// Grab a buffer
const auto pair = m_freeBuffers.back();
m_freeBuffers.pop_back();
// Return the buffer
return new VerilatedVcdBuffer{*this, pair.first, pair.second};
#else
return new VerilatedVcdBuffer{*this};
#endif
// Return the buffer
return bufp;
}
void VerilatedVcd::commitTraceBuffer(VerilatedVcdBuffer* bufp) {
#ifdef VL_TRACE_PARALLEL
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// Resize output buffer. Note, we use the full size of the trace buffer, as
// this is a lot more stable than the actual occupancy of the trace buffer.
// This helps us to avoid re-allocations due to small size changes.
bufferResize(bufp->m_size);
// Compute occupancy of buffer
const size_t usedSize = bufp->m_writep - bufp->m_bufp;
// Copy to output buffer
std::memcpy(m_writep, bufp->m_bufp, usedSize);
// Adjust write pointer
m_writep += usedSize;
// Flush if necessary
bufferCheck();
// Put buffer back on free list
m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
void VerilatedVcd::commitTraceBuffer(VerilatedVcd::Buffer* bufp) {
if (parallel()) {
#if VL_THREADED
// Note: This is called from VeriltedVcd::dump, which already holds the lock
// Resize output buffer. Note, we use the full size of the trace buffer, as
// this is a lot more stable than the actual occupancy of the trace buffer.
// This helps us to avoid re-allocations due to small size changes.
bufferResize(bufp->m_size);
// Compute occupancy of buffer
const size_t usedSize = bufp->m_writep - bufp->m_bufp;
// Copy to output buffer
std::memcpy(m_writep, bufp->m_bufp, usedSize);
// Adjust write pointer
m_writep += usedSize;
// Flush if necessary
bufferCheck();
// Put buffer back on free list
m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
#else
// Needs adjusting for emitTimeChange
m_writep = bufp->m_writep;
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#endif
} else {
// Needs adjusting for emitTimeChange
m_writep = bufp->m_writep;
}
delete bufp;
}
//=============================================================================
// VerilatedVcdBuffer implementation
#ifdef VL_TRACE_PARALLEL
VerilatedVcdBuffer::VerilatedVcdBuffer(VerilatedVcd& owner, char* bufp, size_t size)
: VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer>{owner}
, m_writep{bufp}
, m_bufp{bufp}
, m_size{size} {
adjustGrowp();
}
#else
VerilatedVcdBuffer::VerilatedVcdBuffer(VerilatedVcd& owner)
: VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer>{owner} {}
#endif
//=============================================================================
// Trace rendering primitives
@ -679,35 +664,39 @@ void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) {
// suffix, which was stored in the last byte of the suffix buffer entry.
m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];
#ifdef VL_TRACE_PARALLEL
// Double the size of the buffer if necessary
if (VL_UNLIKELY(m_writep >= m_growp)) {
// Compute occupied size of current buffer
const size_t usedSize = m_writep - m_bufp;
// We are always doubling the size
m_size *= 2;
// Allocate the new buffer
char* const newBufp = new char[m_size];
// Copy from current buffer to new buffer
std::memcpy(newBufp, m_bufp, usedSize);
// Delete current buffer
delete[] m_bufp;
// Make new buffer the current buffer
m_bufp = newBufp;
// Adjust write pointer
m_writep = m_bufp + usedSize;
// Adjust resize limit
adjustGrowp();
}
if (m_owner.parallel()) {
#ifdef VL_THREADED
// Double the size of the buffer if necessary
if (VL_UNLIKELY(m_writep >= m_growp)) {
// Compute occupied size of current buffer
const size_t usedSize = m_writep - m_bufp;
// We are always doubling the size
m_size *= 2;
// Allocate the new buffer
char* const newBufp = new char[m_size];
// Copy from current buffer to new buffer
std::memcpy(newBufp, m_bufp, usedSize);
// Delete current buffer
delete[] m_bufp;
// Make new buffer the current buffer
m_bufp = newBufp;
// Adjust write pointer
m_writep = m_bufp + usedSize;
// Adjust resize limit
adjustGrowp();
}
#else
// Flush the write buffer if there's not enough space left for new information
// We only call this once per vector, so we need enough slop for a very wide "b###" line
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
m_owner.m_writep = m_writep;
m_owner.bufferFlush();
m_writep = m_owner.m_writep;
}
VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable");
#endif
} else {
// Flush the write buffer if there's not enough space left for new information
// We only call this once per vector, so we need enough slop for a very wide "b###" line
if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
m_owner.m_writep = m_writep;
m_owner.bufferFlush();
m_writep = m_owner.m_writep;
}
}
}
//=============================================================================

View File

@ -41,7 +41,7 @@ public:
using Super = VerilatedTrace<VerilatedVcd, VerilatedVcdBuffer>;
private:
friend Buffer; // Give the buffer access to the private bits
friend VerilatedVcdBuffer; // Give the buffer access to the private bits
//=========================================================================
// VCD specific internals
@ -49,9 +49,8 @@ private:
VerilatedVcdFile* m_filep; // File we're writing to
bool m_fileNewed; // m_filep needs destruction
bool m_isOpen = false; // True indicates open file
bool m_evcd = false; // True for evcd format
std::string m_filename; // Filename we're writing to (if open)
uint64_t m_rolloverMB = 0; // MB of file size to rollover at
uint64_t m_rolloverSize = 0; // File size to rollover at
int m_modDepth = 0; // Depth of module hierarchy
char* m_wrBufp; // Output buffer
@ -66,7 +65,7 @@ private:
using NameMap = std::map<const std::string, const std::string>;
NameMap* m_namemapp = nullptr; // List of names for the header
#ifdef VL_TRACE_PARALLEL
#ifdef VL_THREADED
// Vector of free trace buffers as (pointer, size) pairs.
std::vector<std::pair<char*, size_t>> m_freeBuffers;
size_t m_numBuffers = 0; // Number of trace buffers allocated
@ -103,15 +102,18 @@ protected:
// Implementation of VerilatedTrace interface
// Called when the trace moves forward to a new time point
virtual void emitTimeChange(uint64_t timeui) override;
void emitTimeChange(uint64_t timeui) override;
// Hooks called from VerilatedTrace
virtual bool preFullDump() override { return isOpen(); }
virtual bool preChangeDump() override;
bool preFullDump() override { return isOpen(); }
bool preChangeDump() override;
// Trace buffer management
virtual VerilatedVcdBuffer* getTraceBuffer() override;
virtual void commitTraceBuffer(VerilatedVcdBuffer*) override;
Buffer* getTraceBuffer() override;
void commitTraceBuffer(Buffer*) override;
// Configure sub-class
void configure(const VerilatedTraceConfig&) override{};
public:
//=========================================================================
@ -122,8 +124,8 @@ public:
~VerilatedVcd();
// ACCESSORS
// Set size in megabytes after which new file should be created
void rolloverMB(uint64_t rolloverMB) { m_rolloverMB = rolloverMB; }
// Set size in bytes after which new file should be created.
void rolloverSize(uint64_t size) { m_rolloverSize = size; }
// METHODS - All must be thread safe
// Open the file; call isOpen() to see if errors
@ -149,58 +151,63 @@ public:
#ifndef DOXYGEN
// Declare specialization here as it's used in VerilatedFstC just below
template <> void VerilatedVcd::Super::dump(uint64_t time);
template <> void VerilatedVcd::Super::set_time_unit(const char* unitp);
template <> void VerilatedVcd::Super::set_time_unit(const std::string& unit);
template <> void VerilatedVcd::Super::set_time_resolution(const char* unitp);
template <> void VerilatedVcd::Super::set_time_resolution(const std::string& unit);
template <> void VerilatedVcd::Super::dumpvars(int level, const std::string& hier);
template <>
void VerilatedVcd::Super::dump(uint64_t time);
template <>
void VerilatedVcd::Super::set_time_unit(const char* unitp);
template <>
void VerilatedVcd::Super::set_time_unit(const std::string& unit);
template <>
void VerilatedVcd::Super::set_time_resolution(const char* unitp);
template <>
void VerilatedVcd::Super::set_time_resolution(const std::string& unit);
template <>
void VerilatedVcd::Super::dumpvars(int level, const std::string& hier);
#endif // DOXYGEN
//=============================================================================
// VerilatedVcdBuffer
class VerilatedVcdBuffer final : public VerilatedTraceBuffer<VerilatedVcd, VerilatedVcdBuffer> {
// Give the trace file access to the private bits
class VerilatedVcdBuffer VL_NOT_FINAL {
// Give the trace file ans sub-classes access to the private bits
friend VerilatedVcd;
friend VerilatedVcd::Super;
friend VerilatedVcd::Buffer;
friend VerilatedVcd::OffloadBuffer;
#ifdef VL_TRACE_PARALLEL
char* m_writep; // Write pointer into m_bufp
char* m_bufp; // The beginning of the trace buffer
size_t m_size; // The size of the buffer at m_bufp
char* m_growp; // Resize limit pointer
#else
char* m_writep = m_owner.m_writep; // Write pointer into output buffer
char* const m_wrFlushp = m_owner.m_wrFlushp; // Output buffer flush trigger location
#endif
VerilatedVcd& m_owner; // Trace file owning this buffer. Required by subclasses.
// Write pointer into output buffer (in parallel mode, this is set up in 'getTraceBuffer')
char* m_writep = m_owner.parallel() ? nullptr : m_owner.m_writep;
// Output buffer flush trigger location (only used when not parallel)
char* const m_wrFlushp = m_owner.parallel() ? nullptr : m_owner.m_wrFlushp;
// VCD line end string codes + metadata
const char* const m_suffixes = m_owner.m_suffixes.data();
// The maximum number of bytes a single signal can emit
const size_t m_maxSignalBytes = m_owner.m_maxSignalBytes;
void finishLine(uint32_t code, char* writep);
#ifdef VL_THREADED
// Additional data for parallel tracing only
char* m_bufp = nullptr; // The beginning of the trace buffer
size_t m_size = 0; // The size of the buffer at m_bufp
char* m_growp = nullptr; // Resize limit pointer
#ifdef VL_TRACE_PARALLEL
void adjustGrowp() {
m_growp = (m_bufp + m_size) - (2 * m_maxSignalBytes);
assert(m_growp >= m_bufp + m_maxSignalBytes);
}
#endif
public:
void finishLine(uint32_t code, char* writep);
// CONSTRUCTOR
#ifdef VL_TRACE_PARALLEL
explicit VerilatedVcdBuffer(VerilatedVcd& owner, char* bufp, size_t size);
#else
explicit VerilatedVcdBuffer(VerilatedVcd& owner);
#endif
~VerilatedVcdBuffer() = default;
explicit VerilatedVcdBuffer(VerilatedVcd& owner)
: m_owner{owner} {}
virtual ~VerilatedVcdBuffer() = default;
//=========================================================================
// Implementation of VerilatedTraceBuffer interface
// Implementations of duck-typed methods for VerilatedTraceBuffer. These are
// called from only one place (the full* methods), so always inline them.
VL_ATTR_ALWINLINE inline void emitBit(uint32_t code, CData newval);
@ -252,7 +259,6 @@ public:
/// Destruct, flush, and close the dump
virtual ~VerilatedVcdC() { close(); }
public:
// METHODS - User called
/// Return if file is open
@ -265,8 +271,12 @@ public:
/// The header is only in the first file created, this allows
/// "cat" to be used to combine the header plus any number of data files.
void openNext(bool incFilename = true) VL_MT_SAFE { m_sptrace.openNext(incFilename); }
/// Set size in megabytes after which new file should be created
void rolloverMB(size_t rolloverMB) VL_MT_SAFE { m_sptrace.rolloverMB(rolloverMB); }
/// Set size in bytes after which new file should be created
/// This will create a header file, followed by each separate file
/// which might be larger than the given size (due to chunking and
/// alignment to a start of a given time's dump). Any file but the
/// first may be removed. Cat files together to create viewable vcd.
void rolloverSize(size_t size) VL_MT_SAFE { m_sptrace.rolloverSize(size); }
/// Close dump
void close() VL_MT_SAFE { m_sptrace.close(); }
/// Flush dump

View File

@ -22,6 +22,7 @@
//=============================================================================
#include "verilatedos.h"
#include "verilated_vcd_sc.h"
//======================================================================
@ -30,7 +31,7 @@
void VerilatedVcdSc::open(const char* filename) {
if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) {
vl_fatal(__FILE__, __LINE__, "VerilatedVcdSc",
("%Error: VerilatedVcdSc::open(\"" + std::string(filename)
("%Error: VerilatedVcdSc::open(\"" + std::string{filename}
+ "\") is called before sc_core::sc_start(). "
"Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file.")
.c_str());
@ -63,9 +64,6 @@ void VerilatedVcdSc::trace(const unsigned int&, const std::string&, const char**
DECL_TRACE_METHOD_B( unsigned short )
DECL_TRACE_METHOD_B( unsigned int )
DECL_TRACE_METHOD_B( unsigned long )
#ifdef SYSTEMC_64BIT_PATCHES
DECL_TRACE_METHOD_B( unsigned long long)
#endif
DECL_TRACE_METHOD_B( char )
DECL_TRACE_METHOD_B( short )
DECL_TRACE_METHOD_B( int )

View File

@ -23,6 +23,7 @@
#define VERILATOR_VERILATED_VCD_SC_H_
#include "verilatedos.h"
#include "verilated_sc.h"
#include "verilated_vcd_c.h"
@ -54,7 +55,7 @@ public:
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
}
/// Destruct, flush, and close the dump
virtual ~VerilatedVcdSc() { close(); }
virtual ~VerilatedVcdSc() /*override*/ { close(); }
// METHODS - for SC kernel
// Called by SystemC simulate()
@ -63,6 +64,7 @@ public:
}
// Override VerilatedVcdC. Must be called after starting simulation.
// cppcheck-suppress missingOverride // GCC won't accept override
virtual void open(const char* filename) /*override*/ VL_MT_SAFE;
private:
@ -98,9 +100,6 @@ private:
DECL_TRACE_METHOD_B( unsigned short )
DECL_TRACE_METHOD_B( unsigned int )
DECL_TRACE_METHOD_B( unsigned long )
#ifdef SYSTEMC_64BIT_PATCHES
DECL_TRACE_METHOD_B( unsigned long long)
#endif
DECL_TRACE_METHOD_B( char )
DECL_TRACE_METHOD_B( short )
DECL_TRACE_METHOD_B( int )

View File

@ -26,8 +26,9 @@
#define VERILATOR_VERILATED_VPI_CPP_
#include "verilated.h"
#include "verilated_vpi.h"
#include "verilated.h"
#include "verilated_imp.h"
#include <list>
@ -135,12 +136,12 @@ public:
VerilatedVpioTimedCb(uint64_t id, QData time)
: m_id{id}
, m_time{time} {}
virtual ~VerilatedVpioTimedCb() override = default;
~VerilatedVpioTimedCb() override = default;
static VerilatedVpioTimedCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioTimedCb*>(reinterpret_cast<VerilatedVpioTimedCb*>(h));
}
virtual uint32_t type() const override { return vpiCallback; }
virtual PLI_INT32 dovpi_remove_cb() override;
uint32_t type() const override { return vpiCallback; }
PLI_INT32 dovpi_remove_cb() override;
};
class VerilatedVpioReasonCb final : public VerilatedVpio {
@ -154,12 +155,12 @@ public:
VerilatedVpioReasonCb(uint64_t id, PLI_INT32 reason)
: m_id{id}
, m_reason{reason} {}
virtual ~VerilatedVpioReasonCb() override = default;
~VerilatedVpioReasonCb() override = default;
static VerilatedVpioReasonCb* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioReasonCb*>(reinterpret_cast<VerilatedVpioReasonCb*>(h));
}
virtual uint32_t type() const override { return vpiCallback; }
virtual PLI_INT32 dovpi_remove_cb() override;
uint32_t type() const override { return vpiCallback; }
PLI_INT32 dovpi_remove_cb() override;
};
class VerilatedVpioConst final : public VerilatedVpio {
@ -168,11 +169,11 @@ class VerilatedVpioConst final : public VerilatedVpio {
public:
explicit VerilatedVpioConst(int32_t num)
: m_num{num} {}
virtual ~VerilatedVpioConst() override = default;
~VerilatedVpioConst() override = default;
static VerilatedVpioConst* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioConst*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiConstant; }
uint32_t type() const override { return vpiConstant; }
int32_t num() const { return m_num; }
};
@ -200,10 +201,10 @@ public:
}
const VerilatedVar* varp() const { return m_varp; }
const VerilatedScope* scopep() const { return m_scopep; }
virtual uint32_t size() const override { return get_range().elements(); }
virtual const VerilatedRange* rangep() const override { return &get_range(); }
virtual const char* name() const override { return m_varp->name(); }
virtual const char* fullname() const override {
uint32_t size() const override { return get_range().elements(); }
const VerilatedRange* rangep() const override { return &get_range(); }
const char* name() const override { return m_varp->name(); }
const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
t_out = std::string{m_scopep->name()} + "." + name();
return t_out.c_str();
@ -214,12 +215,12 @@ class VerilatedVpioParam final : public VerilatedVpioVarBase {
public:
VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
: VerilatedVpioVarBase{varp, scopep} {}
virtual ~VerilatedVpioParam() override = default;
~VerilatedVpioParam() override = default;
static VerilatedVpioParam* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioParam*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiParameter; }
uint32_t type() const override { return vpiParameter; }
void* varDatap() const { return m_varp->datap(); }
};
@ -229,13 +230,13 @@ class VerilatedVpioRange final : public VerilatedVpio {
public:
explicit VerilatedVpioRange(const VerilatedRange* range)
: m_range{range} {}
virtual ~VerilatedVpioRange() override = default;
~VerilatedVpioRange() override = default;
static VerilatedVpioRange* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioRange*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiRange; }
virtual uint32_t size() const override { return m_range->elements(); }
virtual const VerilatedRange* rangep() const override { return m_range; }
uint32_t type() const override { return vpiRange; }
uint32_t size() const override { return m_range->elements(); }
const VerilatedRange* rangep() const override { return m_range; }
};
class VerilatedVpioRangeIter final : public VerilatedVpio {
@ -246,12 +247,12 @@ class VerilatedVpioRangeIter final : public VerilatedVpio {
public:
explicit VerilatedVpioRangeIter(const VerilatedRange* range)
: m_range{range} {}
virtual ~VerilatedVpioRangeIter() override = default;
~VerilatedVpioRangeIter() override = default;
static VerilatedVpioRangeIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioRangeIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
if (VL_UNLIKELY(m_done)) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
@ -268,14 +269,14 @@ protected:
public:
explicit VerilatedVpioScope(const VerilatedScope* scopep)
: m_scopep{scopep} {}
virtual ~VerilatedVpioScope() override = default;
~VerilatedVpioScope() override = default;
static VerilatedVpioScope* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioScope*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiScope; }
uint32_t type() const override { return vpiScope; }
const VerilatedScope* scopep() const { return m_scopep; }
virtual const char* name() const override { return m_scopep->name(); }
virtual const char* fullname() const override { return m_scopep->name(); }
const char* name() const override { return m_scopep->name(); }
const char* fullname() const override { return m_scopep->name(); }
};
class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase {
@ -308,7 +309,7 @@ public:
m_mask.u32 = 0;
}
}
virtual ~VerilatedVpioVar() override {
~VerilatedVpioVar() override {
if (m_prevDatap) VL_DO_CLEAR(delete[] m_prevDatap, m_prevDatap = nullptr);
}
static VerilatedVpioVar* castp(vpiHandle h) {
@ -318,7 +319,7 @@ public:
uint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
uint32_t entSize() const { return m_entSize; }
uint32_t index() const { return m_index; }
virtual uint32_t type() const override {
uint32_t type() const override {
return (varp()->dims() > 1) ? vpiMemory : vpiReg; // but might be wire, logic
}
void* prevDatap() const { return m_prevDatap; }
@ -339,14 +340,14 @@ public:
m_index = index;
m_varDatap = (static_cast<uint8_t*>(varp->datap())) + entSize() * offset;
}
virtual ~VerilatedVpioMemoryWord() override = default;
~VerilatedVpioMemoryWord() override = default;
static VerilatedVpioMemoryWord* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioMemoryWord*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiMemoryWord; }
virtual uint32_t size() const override { return varp()->packed().elements(); }
virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); }
virtual const char* fullname() const override {
uint32_t type() const override { return vpiMemoryWord; }
uint32_t size() const override { return varp()->packed().elements(); }
const VerilatedRange* rangep() const override { return &(varp()->packed()); }
const char* fullname() const override {
static VL_THREAD_LOCAL std::string t_out;
constexpr size_t LEN_MAX_INDEX = 25;
char num[LEN_MAX_INDEX];
@ -364,12 +365,12 @@ class VerilatedVpioVarIter final : public VerilatedVpio {
public:
explicit VerilatedVpioVarIter(const VerilatedScope* scopep)
: m_scopep{scopep} {}
virtual ~VerilatedVpioVarIter() override = default;
~VerilatedVpioVarIter() override = default;
static VerilatedVpioVarIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioVarIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
if (VL_LIKELY(m_scopep->varsp())) {
const VerilatedVarNameMap* const varsp = m_scopep->varsp();
if (VL_UNLIKELY(!m_started)) {
@ -405,15 +406,15 @@ public:
, m_varp{varp}
, m_iteration{varp->unpacked().right()}
, m_direction{VL_LIKELY(varp->unpacked().left() > varp->unpacked().right()) ? 1 : -1} {}
virtual ~VerilatedVpioMemoryWordIter() override = default;
~VerilatedVpioMemoryWordIter() override = default;
static VerilatedVpioMemoryWordIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioMemoryWordIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiIterator; }
uint32_t type() const override { return vpiIterator; }
void iterationInc() {
if (!(m_done = (m_iteration == m_varp->unpacked().left()))) m_iteration += m_direction;
}
virtual vpiHandle dovpi_scan() override {
vpiHandle dovpi_scan() override {
if (VL_UNLIKELY(m_done)) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;
@ -438,9 +439,9 @@ public:
static VerilatedVpioModule* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModule*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiModule; }
virtual const char* name() const override { return m_name; }
virtual const char* fullname() const override { return m_fullname; }
uint32_t type() const override { return vpiModule; }
const char* name() const override { return m_name; }
const char* fullname() const override { return m_fullname; }
};
class VerilatedVpioModuleIter final : public VerilatedVpio {
@ -452,12 +453,12 @@ public:
: m_vec{&vec} {
m_it = m_vec->begin();
}
virtual ~VerilatedVpioModuleIter() override = default;
~VerilatedVpioModuleIter() override = default;
static VerilatedVpioModuleIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModuleIter*>(reinterpret_cast<VerilatedVpio*>(h));
}
virtual uint32_t type() const override { return vpiIterator; }
virtual vpiHandle dovpi_scan() override {
uint32_t type() const override { return vpiIterator; }
vpiHandle dovpi_scan() override {
if (m_it == m_vec->end()) {
delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
return nullptr;

View File

@ -25,6 +25,7 @@
#define VERILATOR_VERILATED_VPI_H_
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_syms.h"
@ -47,7 +48,7 @@ public:
static bool callValueCbs() VL_MT_UNSAFE_ONE;
/// Call callbacks of arbitrary types.
/// User wrapper code should call this from their main loops.
static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE;
static bool callCbs(uint32_t reason) VL_MT_UNSAFE_ONE;
/// Returns time of the next registered VPI callback, or
/// ~(0ULL) if none are registered
static QData cbNextDeadline() VL_MT_UNSAFE_ONE;

View File

@ -218,22 +218,33 @@
// C++-2011
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(VL_CPPCHECK)
# ifndef VL_NO_LEGACY
// These are deprecated historical defines. We leave them in case users referenced them.
# define VL_EQ_DELETE = delete
# define vl_unique_ptr std::unique_ptr
# define vl_unordered_map std::unordered_map
# define vl_unordered_set std::unordered_set
# define VL_INCLUDE_UNORDERED_MAP <unordered_map>
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
# define VL_FINAL final
# define VL_MUTABLE mutable
# define VL_OVERRIDE override
# endif
#else
# error "Verilator requires a C++11 or newer compiler"
#endif
#ifndef VL_NO_LEGACY
// These are deprecated historical defines. We leave them in case users referenced them.
# define VL_EQ_DELETE = delete
# define vl_unique_ptr std::unique_ptr
# define vl_unordered_map std::unordered_map
# define vl_unordered_set std::unordered_set
# define VL_INCLUDE_UNORDERED_MAP <unordered_map>
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
# define VL_FINAL final
# define VL_MUTABLE mutable
# define VL_OVERRIDE override
#endif
//=========================================================================
// C++-2017
#if __cplusplus >= 201703L
# define VL_CONSTEXPR_CXX17 constexpr
#else
# define VL_CONSTEXPR_CXX17
#endif
//=========================================================================
// Optimization
@ -245,14 +256,11 @@
// Internal coverage
#ifdef VL_GCOV
extern "C" {
void __gcov_flush(); // gcc sources gcc/gcov-io.h has the prototype
}
// Flush internal code coverage data before e.g. std::abort()
# define VL_GCOV_FLUSH() \
__gcov_flush()
extern "C" void __gcov_dump();
// Dump internal code coverage data before e.g. std::abort()
# define VL_GCOV_DUMP() __gcov_dump()
#else
# define VL_GCOV_FLUSH()
# define VL_GCOV_DUMP()
#endif
//=========================================================================
@ -446,7 +454,8 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
// or 0x0 if not implemented on this platform
#define VL_GET_CPU_TICK(val) \
{ \
uint32_t hi, lo; \
uint32_t hi; \
uint32_t lo; \
asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); \
(val) = ((uint64_t)lo) | (((uint64_t)hi) << 32); \
}
@ -521,12 +530,20 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#define VL_STRINGIFY(x) VL_STRINGIFY2(x)
#define VL_STRINGIFY2(x) #x
//=========================================================================
// Offset of field in type
// Address zero can cause compiler problems
#define VL_OFFSETOF(type, field) \
(reinterpret_cast<size_t>(&(reinterpret_cast<type*>(0x10000000)->field)) - 0x10000000)
//=========================================================================
// Conversions
namespace vlstd {
template <typename T> struct reverse_wrapper {
template <typename T>
struct reverse_wrapper {
const T& m_v;
explicit reverse_wrapper(const T& a_v)
@ -536,10 +553,16 @@ template <typename T> struct reverse_wrapper {
};
// C++20's std::ranges::reverse_view
template <typename T> reverse_wrapper<T> reverse_view(const T& v) { return reverse_wrapper<T>(v); }
template <typename T>
reverse_wrapper<T> reverse_view(const T& v) {
return reverse_wrapper<T>(v);
}
// C++17's std::as_const
template <class T> T const& as_const(T& v) { return v; }
template <class T>
T const& as_const(T& v) {
return v;
}
}; // namespace vlstd
//=========================================================================

View File

@ -29,10 +29,11 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Active.h"
#include "V3Ast.h"
#include "V3Const.h"
#include "V3Global.h"
#include "V3Graph.h"
#include <unordered_map>
@ -75,7 +76,7 @@ public:
class LatchDetectGraph final : public V3Graph {
protected:
LatchDetectGraphVertex* m_curVertexp; // Current latch detection graph vertex
LatchDetectGraphVertex* m_curVertexp = nullptr; // Current latch detection graph vertex
std::vector<AstVarRef*> m_outputs; // Vector of lvalues encountered on this pass
VL_DEBUG_FUNC; // Declare debug()
@ -290,13 +291,13 @@ private:
// STATE
LatchDetectGraph m_graph; // Graph used to detect latches in combo always
// VISITORS
virtual void visit(AstVarRef* nodep) {
virtual void visit(AstVarRef* nodep) override {
const AstVar* const varp = nodep->varp();
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) {
m_graph.addAssignment(nodep);
}
}
virtual void visit(AstNodeIf* nodep) {
virtual void visit(AstNodeIf* nodep) override {
if (!nodep->isBoundsCheck()) {
LatchDetectGraphVertex* const parentp = m_graph.currentp();
LatchDetectGraphVertex* const branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
@ -308,7 +309,7 @@ private:
}
}
//--------------------
virtual void visit(AstNode* nodep) { iterateChildren(nodep); }
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
@ -317,7 +318,7 @@ public:
iterate(nodep);
m_graph.latchCheck(nodep, kwd == VAlwaysKwd::ALWAYS_LATCH);
}
virtual ~ActiveLatchCheckVisitor() = default;
~ActiveLatchCheckVisitor() override = default;
};
//######################################################################
@ -387,7 +388,7 @@ public:
: m_check{check} {
iterate(nodep);
}
virtual ~ActiveDlyVisitor() override = default;
~ActiveDlyVisitor() override = default;
};
//######################################################################
@ -530,7 +531,7 @@ private:
// Warn and/or convert any delayed assignments
if (combo && !sequent) {
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB};
{ ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB}; }
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd};
} else if (!combo && sequent) {
ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_SEQ};
@ -598,7 +599,7 @@ private:
public:
// CONSTRUCTORS
explicit ActiveVisitor(AstNetlist* nodep) { iterate(nodep); }
virtual ~ActiveVisitor() override = default;
~ActiveVisitor() override = default;
};
//######################################################################

View File

@ -26,11 +26,12 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3ActiveTop.h"
#include "V3Ast.h"
#include "V3SenTree.h"
#include "V3Const.h"
#include "V3Global.h"
#include "V3SenTree.h"
//######################################################################
// Active class functions

View File

@ -17,9 +17,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Assert.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3Stats.h"
//######################################################################

View File

@ -20,8 +20,8 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
#include "V3Error.h"
//============================================================================

View File

@ -20,9 +20,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3AssertPre.h"
#include "V3Ast.h"
#include "V3Global.h"
//######################################################################
// Assert class functions

View File

@ -18,10 +18,11 @@
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3Broken.h"
#include "V3EmitV.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3String.h"
#include <iomanip>
@ -205,7 +206,7 @@ string AstNode::prettyName(const string& namein) {
string AstNode::prettyTypeName() const {
if (name() == "") return typeName();
return string(typeName()) + " '" + prettyName() + "'";
return std::string{typeName()} + " '" + prettyName() + "'";
}
//######################################################################
@ -281,13 +282,13 @@ void AstNode::addNextHere(AstNode* newp) {
// This could be at head, tail, or both (single)
// New could be head of single node, or list
UASSERT(newp, "Null item passed to addNext");
UASSERT(!newp->backp(), "New node (back) already assigned?");
UASSERT_OBJ(!newp->backp(), newp, "New node (back) already assigned?");
debugTreeChange(this, "-addHereThs: ", __LINE__, false);
debugTreeChange(newp, "-addHereNew: ", __LINE__, true);
newp->editCountInc();
AstNode* const addlastp = newp->m_headtailp; // Last node in list to be added
UASSERT(!addlastp->m_nextp, "Headtailp tail isn't at the tail");
UASSERT_OBJ(!addlastp->m_nextp, addlastp, "Headtailp tail isn't at the tail");
// Forward links
AstNode* const oldnextp = this->m_nextp;
@ -437,7 +438,7 @@ void VNRelinker::dump(std::ostream& str) const {
AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) {
debugTreeChange(this, "-unlinkWNextThs: ", __LINE__, true);
AstNode* const oldp = this;
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
UASSERT_OBJ(oldp->m_backp, oldp, "Node has no back, already unlinked?");
oldp->editCountInc();
AstNode* const backp = oldp->m_backp;
if (linkerp) {
@ -497,7 +498,7 @@ AstNode* AstNode::unlinkFrBackWithNext(VNRelinker* linkerp) {
AstNode* AstNode::unlinkFrBack(VNRelinker* linkerp) {
debugTreeChange(this, "-unlinkFrBkThs: ", __LINE__, true);
AstNode* const oldp = this;
UASSERT(oldp->m_backp, "Node has no back, already unlinked?");
UASSERT_OBJ(oldp->m_backp, oldp, "Node has no back, already unlinked?");
oldp->editCountInc();
AstNode* const backp = oldp->m_backp;
if (linkerp) {
@ -565,7 +566,7 @@ void AstNode::relink(VNRelinker* linkerp) {
}
AstNode* const newp = this;
UASSERT(linkerp && linkerp->m_backp, "Need non-empty linker");
UASSERT(!newp->backp(), "New node already linked?");
UASSERT_OBJ(!newp->m_backp, newp, "New node already linked?");
newp->editCountInc();
if (debug() > 8) {
@ -631,11 +632,56 @@ void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set
}
void AstNode::addHereThisAsNext(AstNode* newp) {
// {old}->this->{next} becomes {old}->new->this->{next}
VNRelinker handle;
this->unlinkFrBackWithNext(&handle);
newp->addNext(this);
handle.relink(newp);
// {back}->this->{next} becomes {back}->new->this->{next}
UASSERT_OBJ(!newp->backp(), newp, "New node already linked?");
UASSERT_OBJ(this->m_backp, this, "'this' node has no back, already unlinked?");
UASSERT_OBJ(newp->m_headtailp, newp, "m_headtailp not set on new node");
//
AstNode* const backp = this->m_backp;
AstNode* const newLastp = newp->m_headtailp;
//
this->editCountInc();
// Common linkage
newLastp->m_nextp = this;
this->m_backp = newLastp;
newp->m_backp = backp;
// newLastp will not be the last node in the list as 'this' will follow it.
// If newLastp == newp, then below handles newp becoming head
newLastp->m_headtailp = nullptr;
// Linkage dependent on position
if (backp && backp->m_nextp == this) {
// If 'this' is not at the head of a list, then the new node will also not be at the head
// of a list, so we can just link in the new node in the middle.
backp->m_nextp = newp;
newp->m_headtailp = nullptr;
} else {
// If 'this' is at the head of a list, then the new node becomes the head of that list.
if (backp) {
if (backp->m_op1p == this) {
backp->m_op1p = newp;
} else if (backp->m_op2p == this) {
backp->m_op2p = newp;
} else if (backp->m_op3p == this) {
backp->m_op3p = newp;
} else {
UASSERT_OBJ(backp->m_op4p == this, this, "Don't know where newp should go");
backp->m_op4p = newp;
}
}
// We also need to update m_headtailp.
AstNode* const tailp = this->m_headtailp;
this->m_headtailp = nullptr;
newp->m_headtailp = tailp;
tailp->m_headtailp = newp;
}
// Iterator fixup
if (newLastp->m_iterpp) *(newLastp->m_iterpp) = this;
if (this->m_iterpp) {
*(this->m_iterpp) = newp;
this->m_iterpp = nullptr;
}
//
debugTreeChange(this, "-addHereThisAsNext: ", __LINE__, true);
}
void AstNode::swapWith(AstNode* bp) {
@ -1248,3 +1294,8 @@ void VNDeleter::doDeletes() {
for (AstNode* const nodep : m_deleteps) nodep->deleteTree();
m_deleteps.clear();
}
//######################################################################
// VNVisitor
#include "V3Ast__gen_visitor_defns.h" // From ./astgen

View File

@ -20,11 +20,13 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Broken.h"
#include "V3Error.h"
#include "V3FileLine.h"
#include "V3Number.h"
#include "V3Global.h"
#include "V3Broken.h"
#include "V3Number.h"
#include "V3Ast__gen_classes.h" // From ./astgen
#include <cmath>
#include <functional>
@ -34,8 +36,6 @@
#include <unordered_set>
#include <utility>
#include <vector>
#include "V3Ast__gen_classes.h" // From ./astgen
// Things like:
// class V3AstNode;
@ -95,7 +95,7 @@ public:
// const char* ascii() const {...};
enum en m_e;
// cppcheck-suppress uninitVar // responsibility of each subclass
inline VNType() {}
inline VNType() = default;
// cppcheck-suppress noExplicitConstructor
inline VNType(en _e)
: m_e{_e} {}
@ -997,7 +997,7 @@ public:
return false;
}
//
VNumRange() {}
VNumRange() = default;
VNumRange(int hi, int lo, bool littleEndian) { init(hi, lo, littleEndian); }
VNumRange(int left, int right)
: m_left{left}
@ -1123,7 +1123,7 @@ class VNUser final {
} m_u;
public:
VNUser() {}
VNUser() = default;
// non-explicit:
// cppcheck-suppress noExplicitConstructor
VNUser(int i) {
@ -1133,7 +1133,7 @@ public:
explicit VNUser(void* p) { m_u.up = p; }
~VNUser() = default;
// Casters
template <class T> //
template <class T>
typename std::enable_if<std::is_pointer<T>::value, T>::type to() const {
return reinterpret_cast<T>(m_u.up);
}
@ -1297,10 +1297,8 @@ public:
/// Return edited nodep; see comments in V3Ast.cpp
AstNode* iterateSubtreeReturnEdits(AstNode* nodep);
#include "V3Ast__gen_visitor.h" // From ./astgen
// Things like:
// virtual void visit(AstBreak* nodep) { visit((AstNodeStmt*)(nodep)); }
// virtual void visit(AstNodeStmt* nodep) { visit((AstNode*)(nodep)); }
virtual void visit(AstNode* nodep) = 0;
#include "V3Ast__gen_visitor_decls.h" // From ./astgen
};
//######################################################################
@ -1334,25 +1332,27 @@ inline std::ostream& operator<<(std::ostream& os, const VNRelinker& rhs) {
return os;
}
//######################################################################
// Callback base class to determine if node matches some formula
// ######################################################################
// Callback base class to determine if node matches some formula
class VNodeMatcher VL_NOT_FINAL {
public:
virtual bool nodeMatch(const AstNode* nodep) const { return true; }
};
//######################################################################
// AstNode -- Base type of all Ast types
// ######################################################################
// AstNode -- Base type of all Ast types
// Prefetch a node.
#define ASTNODE_PREFETCH_NON_NULL(nodep) \
do { \
VL_PREFETCH_RD(&((nodep)->m_nextp)); \
VL_PREFETCH_RD(&((nodep)->m_type)); \
} while (false)
// The if() makes it faster, even though prefetch won't fault on null pointers
#define ASTNODE_PREFETCH(nodep) \
do { \
if (nodep) { \
VL_PREFETCH_RD(&((nodep)->m_nextp)); \
VL_PREFETCH_RD(&((nodep)->m_type)); \
} \
if (nodep) ASTNODE_PREFETCH_NON_NULL(nodep); \
} while (false)
class AstNode VL_NOT_FINAL {
@ -1509,6 +1509,16 @@ public:
AstNode* firstAbovep() const { // Returns nullptr when second or later in list
return ((backp() && backp()->nextp() != this) ? backp() : nullptr);
}
// isFirstInMyListOfStatements(n) -- implemented by child classes:
// AstNodeBlock, AstCaseItem, AstNodeIf, AstNodeFTask, and possibly others.
virtual bool isFirstInMyListOfStatements(AstNode* n) const { return false; }
// isStandaloneBodyStmt == Do we need a ; on generated cpp for this node?
bool isStandaloneBodyStmt() {
return (!firstAbovep() // we're 2nd or later in the list, so yes need ;
// If we're first in the list, check what backp() thinks of us:
|| (backp() && backp()->isFirstInMyListOfStatements(this)));
}
uint8_t brokenState() const { return m_brokenState; }
void brokenState(uint8_t value) { m_brokenState = value; }
@ -1555,7 +1565,7 @@ public:
static string dedotName(const string& namein); // Name with dots removed
static string prettyName(const string& namein); // Name for printing out to the user
static string prettyNameQ(const string& namein) { // Quoted pretty name (for errors)
return string("'") + prettyName(namein) + "'";
return std::string{"'"} + prettyName(namein) + "'";
}
static string
encodeName(const string& namein); // Encode user name into internal C representation
@ -1717,13 +1727,13 @@ public:
void dtypeSetVoid() { dtypep(findVoidDType()); }
// Data type locators
AstNodeDType* findBitDType() { return findBasicDType(VBasicDTypeKwd::LOGIC); }
AstNodeDType* findDoubleDType() { return findBasicDType(VBasicDTypeKwd::DOUBLE); }
AstNodeDType* findStringDType() { return findBasicDType(VBasicDTypeKwd::STRING); }
AstNodeDType* findSigned32DType() { return findBasicDType(VBasicDTypeKwd::INTEGER); }
AstNodeDType* findUInt32DType() { return findBasicDType(VBasicDTypeKwd::UINT32); }
AstNodeDType* findUInt64DType() { return findBasicDType(VBasicDTypeKwd::UINT64); }
AstNodeDType* findCHandleDType() { return findBasicDType(VBasicDTypeKwd::CHANDLE); }
AstNodeDType* findBitDType() const { return findBasicDType(VBasicDTypeKwd::LOGIC); }
AstNodeDType* findDoubleDType() const { return findBasicDType(VBasicDTypeKwd::DOUBLE); }
AstNodeDType* findStringDType() const { return findBasicDType(VBasicDTypeKwd::STRING); }
AstNodeDType* findSigned32DType() const { return findBasicDType(VBasicDTypeKwd::INTEGER); }
AstNodeDType* findUInt32DType() const { return findBasicDType(VBasicDTypeKwd::UINT32); }
AstNodeDType* findUInt64DType() const { return findBasicDType(VBasicDTypeKwd::UINT64); }
AstNodeDType* findCHandleDType() const { return findBasicDType(VBasicDTypeKwd::CHANDLE); }
AstNodeDType* findEmptyQueueDType() const;
AstNodeDType* findVoidDType() const;
AstNodeDType* findQueueIndexDType() const;
@ -1787,7 +1797,6 @@ public:
void deleteTree(); // Always deletes the next link
void checkTree(); // User Interface version
void checkIter() const;
void clearIter() { m_iterpp = nullptr; }
void dumpPtrs(std::ostream& os = std::cout) const;
void dumpTree(std::ostream& os = std::cout, const string& indent = " ",
int maxDepth = 0) const;
@ -1858,16 +1867,12 @@ private:
// For internal use only.
// Note: specializations for particular node types are provided by 'astgen'
template <typename T> inline static bool privateTypeTest(const AstNode* nodep);
template <typename T>
inline static bool privateTypeTest(const AstNode* nodep);
// For internal use only.
// Note: specializations for particular node types are provided below
template <typename T_Node> inline static bool privateMayBeUnder(const AstNode* nodep) {
return true;
}
// For internal use only.
template <typename TargetType, typename DeclType> constexpr static bool uselessCast() {
template <typename TargetType, typename DeclType>
constexpr static bool uselessCast() {
using NonRef = typename std::remove_reference<DeclType>::type;
using NonPtr = typename std::remove_pointer<NonRef>::type;
using NonCV = typename std::remove_cv<NonPtr>::type;
@ -1875,7 +1880,8 @@ private:
}
// For internal use only.
template <typename TargetType, typename DeclType> constexpr static bool impossibleCast() {
template <typename TargetType, typename DeclType>
constexpr static bool impossibleCast() {
using NonRef = typename std::remove_reference<DeclType>::type;
using NonPtr = typename std::remove_pointer<NonRef>::type;
using NonCV = typename std::remove_cv<NonPtr>::type;
@ -1884,20 +1890,23 @@ private:
public:
// For use via the VN_IS macro only
template <typename T, typename E> inline static bool privateIs(const AstNode* nodep) {
template <typename T, typename E>
inline static bool privateIs(const AstNode* nodep) {
static_assert(!uselessCast<T, E>(), "Unnecessary VN_IS, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_IS, node cannot be this type.");
return nodep && privateTypeTest<T>(nodep);
}
// For use via the VN_CAST macro only
template <typename T, typename E> inline static T* privateCast(AstNode* nodep) {
template <typename T, typename E>
inline static T* privateCast(AstNode* nodep) {
static_assert(!uselessCast<T, E>(),
"Unnecessary VN_CAST, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_CAST, node cannot be this type.");
return nodep && privateTypeTest<T>(nodep) ? reinterpret_cast<T*>(nodep) : nullptr;
}
template <typename T, typename E> inline static const T* privateCast(const AstNode* nodep) {
template <typename T, typename E>
inline static const T* privateCast(const AstNode* nodep) {
static_assert(!uselessCast<T, E>(),
"Unnecessary VN_CAST, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_CAST, node cannot be this type.");
@ -1905,7 +1914,8 @@ public:
}
// For use via the VN_AS macro only
template <typename T, typename E> inline static T* privateAs(AstNode* nodep) {
template <typename T, typename E>
inline static T* privateAs(AstNode* nodep) {
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
@ -1913,7 +1923,8 @@ public:
<< "'");
return reinterpret_cast<T*>(nodep);
}
template <typename T, typename E> inline static const T* privateAs(const AstNode* nodep) {
template <typename T, typename E>
inline static const T* privateAs(const AstNode* nodep) {
static_assert(!uselessCast<T, E>(), "Unnecessary VN_AS, node known to have target type.");
static_assert(!impossibleCast<T, E>(), "Unnecessary VN_AS, node cannot be this type.");
UASSERT_OBJ(!nodep || privateTypeTest<T>(nodep), nodep,
@ -1924,104 +1935,44 @@ public:
// Predicate that returns true if the given 'nodep' might have a descendant of type 'T_Node'.
// This is conservative and is used to speed up traversals.
template <typename T_Node> inline static bool mayBeUnder(const AstNode* nodep) {
// Note: specializations for particular node types are provided below
template <typename T_Node>
static bool mayBeUnder(const AstNode* nodep) {
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 privateMayBeUnder<T_Node>(nodep);
return true;
}
// Predicate that is true for node subtypes 'T_Node' that do not have any children
// This is conservative and is used to speed up traversals.
// Note: specializations for particular node types are provided below
template <typename T_Node>
static constexpr bool isLeaf() {
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 false;
}
private:
template <typename T_Arg, bool VisitNext>
static void foreachImpl(
// 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<void(T_Arg*)> f) {
// Using std::conditional for const correctness in the public 'foreach' functions
template <typename T_Arg>
using ConstCorrectAstNode =
typename std::conditional<std::is_const<T_Arg>::value, const AstNode, AstNode>::type;
// 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());
template <typename T_Arg>
inline static void foreachImpl(ConstCorrectAstNode<T_Arg>* nodep,
const std::function<void(T_Arg*)>& f, bool visitNext);
// Apply function in pre-order
if (privateTypeTest<typename std::remove_const<T_Arg>::type>(nodep)) {
f(static_cast<T_Arg*>(nodep));
}
template <typename T_Arg, bool Default>
inline static bool predicateImpl(ConstCorrectAstNode<T_Arg>* nodep,
const std::function<bool(T_Arg*)>& p);
// Traverse children (including their 'nextp()' chains), unless futile
if (mayBeUnder<typename std::remove_const<T_Arg>::type>(nodep)) {
if (AstNode* const op1p = nodep->op1p()) foreachImpl<T_Arg, true>(op1p, f);
if (AstNode* const op2p = nodep->op2p()) foreachImpl<T_Arg, true>(op2p, f);
if (AstNode* const op3p = nodep->op3p()) foreachImpl<T_Arg, true>(op3p, f);
if (AstNode* const op4p = nodep->op4p()) foreachImpl<T_Arg, true>(op4p, f);
}
// Traverse 'nextp()' chain if requested
if /* TODO: 'constexpr' in C++17 */ (VisitNext) {
nodep = nodep->nextp();
} else {
break;
}
} while (nodep);
}
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() {
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,
@ -2031,61 +1982,70 @@ private:
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) {
// 'T_Node'. The node passd to the function 'f' can be removed or replaced, but other editing
// of the iterated tree is not safe. 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);
foreachImpl<T_Node>(this, f, /* visitNext: */ false);
}
// Same as above, but for 'const' nodes
template <typename T_Node> void foreach (std::function<void(const T_Node*)> f) const {
template <typename T_Node>
void foreach (std::function<void(const T_Node*)> f) const {
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
foreachImpl<const T_Node, /* VisitNext: */ false>(this, f);
foreachImpl<const T_Node>(this, f, /* visitNext: */ false);
}
// Same as 'foreach' but also follows 'this->nextp()'
template <typename T_Node> void foreachAndNext(std::function<void(T_Node*)> f) {
template <typename T_Node>
void foreachAndNext(std::function<void(T_Node*)> f) {
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
foreachImpl<T_Node, /* VisitNext: */ true>(this, f);
foreachImpl<T_Node>(this, f, /* visitNext: */ true);
}
// Same as 'foreach' but also follows 'this->nextp()'
template <typename T_Node> void foreachAndNext(std::function<void(const T_Node*)> f) const {
template <typename T_Node>
void foreachAndNext(std::function<void(const T_Node*)> f) const {
static_assert(checkTypeParameter<T_Node>(), "Invalid type parameter 'T_Node'");
foreachImpl<const T_Node, /* VisitNext: */ true>(this, f);
foreachImpl<const T_Node>(this, f, /* visitNext: */ true);
}
// 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) {
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);
return predicateImpl<T_Node, /* Default: */ false>(this, p);
}
// Same as above, but for 'const' nodes
template <typename T_Node> void exists(std::function<bool(const T_Node*)> p) const {
template <typename T_Node>
bool 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);
return predicateImpl<const T_Node, /* Default: */ 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) {
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);
return predicateImpl<T_Node, /* Default: */ true>(this, p);
}
// Same as above, but for 'const' nodes
template <typename T_Node> void forall(std::function<bool(const T_Node*)> p) const {
template <typename T_Node>
bool 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);
return predicateImpl<const T_Node, /* Default: */ true>(this, p);
}
int nodeCount() const {
@ -2099,22 +2059,214 @@ public:
// Specialisations of privateTypeTest
#include "V3Ast__gen_impl.h" // From ./astgen
// Specializations of privateMayBeUnder
template <> inline bool AstNode::privateMayBeUnder<AstCell>(const AstNode* nodep) {
// Specializations of AstNode::mayBeUnder
template <>
inline bool AstNode::mayBeUnder<AstCell>(const AstNode* nodep) {
return !VN_IS(nodep, NodeStmt) && !VN_IS(nodep, NodeMath);
}
template <> inline bool AstNode::privateMayBeUnder<AstNodeAssign>(const AstNode* nodep) {
template <>
inline bool AstNode::mayBeUnder<AstNodeAssign>(const AstNode* nodep) {
return !VN_IS(nodep, NodeMath);
}
template <> inline bool AstNode::privateMayBeUnder<AstVarScope>(const AstNode* nodep) {
return !VN_IS(nodep, NodeStmt) && !VN_IS(nodep, NodeMath);
template <>
inline bool AstNode::mayBeUnder<AstVarScope>(const AstNode* nodep) {
if (VN_IS(nodep, VarScope)) return false; // Should not nest
if (VN_IS(nodep, Var)) return false;
if (VN_IS(nodep, Active)) return false;
if (VN_IS(nodep, NodeStmt)) return false;
if (VN_IS(nodep, NodeMath)) return false;
return true;
}
template <> inline bool AstNode::privateMayBeUnder<AstExecGraph>(const AstNode* nodep) {
template <>
inline bool AstNode::mayBeUnder<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;
}
// Specializations of AstNode::isLeaf
template <>
constexpr bool AstNode::isLeaf<AstNodeVarRef>() {
return true;
}
template <>
constexpr bool AstNode::isLeaf<AstVarRef>() {
return true;
}
template <>
constexpr bool AstNode::isLeaf<AstVarXRef>() {
return true;
}
// foreach implementation
template <typename T_Arg>
void AstNode::foreachImpl(ConstCorrectAstNode<T_Arg>* nodep, const std::function<void(T_Arg*)>& f,
bool visitNext) {
// Checking the function is bound up front eliminates this check from the loop at invocation
if (!f) {
nodep->v3fatal("AstNode::foreach called with unbound function"); // LCOV_EXCL_LINE
} else {
// Pre-order traversal implemented directly (without recursion) for speed reasons. The very
// first iteration (the one that operates on the input nodep) is special, as we might or
// might not need to enqueue nodep->nextp() depending on VisitNext, while in all other
// iterations, we do want to enqueue nodep->nextp(). Duplicating code (via
// 'foreachImplVisit') for the initial iteration here to avoid an extra branch in the loop
using T_Arg_NonConst = typename std::remove_const<T_Arg>::type;
using Node = ConstCorrectAstNode<T_Arg>;
// Traversal stack
std::vector<Node*> stack; // Kept as a vector for easy resizing
Node** basep = nullptr; // Pointer to base of stack
Node** topp = nullptr; // Pointer to top of stack
Node** limp = nullptr; // Pointer to stack limit (when need growing)
// We prefetch this far into the stack
constexpr int prefetchDistance = 2;
// Grow stack to given size
const auto grow = [&](size_t size) VL_ATTR_ALWINLINE {
const ptrdiff_t occupancy = topp - basep;
stack.resize(size);
basep = stack.data() + prefetchDistance;
topp = basep + occupancy;
limp = basep + size - 5; // We push max 5 items per iteration
};
// Initial stack size
grow(32);
// We want some non-null pointers at the beginning. These will be prefetched, but not
// visited, so the root node will suffice. This eliminates needing branches in the loop.
for (int i = -prefetchDistance; i; ++i) basep[i] = nodep;
// Visit given node, enqueue children for traversal
const auto visit = [&](Node* currp) VL_ATTR_ALWINLINE {
// Type test this node
if (AstNode::privateTypeTest<T_Arg_NonConst>(currp)) {
// Call the client function
f(static_cast<T_Arg*>(currp));
// Short circuit if iterating leaf nodes
if VL_CONSTEXPR_CXX17 (isLeaf<T_Arg_NonConst>()) return;
}
// Enqueue children for traversal, unless futile
if (mayBeUnder<T_Arg_NonConst>(currp)) {
if (AstNode* const op4p = currp->op4p()) *topp++ = op4p;
if (AstNode* const op3p = currp->op3p()) *topp++ = op3p;
if (AstNode* const op2p = currp->op2p()) *topp++ = op2p;
if (AstNode* const op1p = currp->op1p()) *topp++ = op1p;
}
};
// Enqueue the next of the root node, if required
if (visitNext && nodep->nextp()) *topp++ = nodep->nextp();
// Visit the root node
visit(nodep);
// Visit the rest of the tree
while (VL_LIKELY(topp > basep)) {
// Pop next node in the traversal
Node* const headp = *--topp;
// Prefetch in case we are ascending the tree
ASTNODE_PREFETCH_NON_NULL(topp[-prefetchDistance]);
// Ensure we have stack space for nextp and the 4 children
if (VL_UNLIKELY(topp >= limp)) grow(stack.size() * 2);
// Enqueue the next node
if (headp->nextp()) *topp++ = headp->nextp();
// Visit the head node
visit(headp);
}
}
}
// predicate implementation
template <typename T_Arg, bool Default>
bool AstNode::predicateImpl(ConstCorrectAstNode<T_Arg>* nodep,
const std::function<bool(T_Arg*)>& p) {
// Implementation similar to foreach, but abort traversal as soon as result is determined.
if (!p) {
nodep->v3fatal("AstNode::foreach called with unbound function"); // LCOV_EXCL_LINE
} else {
using T_Arg_NonConst = typename std::remove_const<T_Arg>::type;
using Node = ConstCorrectAstNode<T_Arg>;
// Traversal stack
std::vector<Node*> stack; // Kept as a vector for easy resizing
Node** basep = nullptr; // Pointer to base of stack
Node** topp = nullptr; // Pointer to top of stack
Node** limp = nullptr; // Pointer to stack limit (when need growing)
// We prefetch this far into the stack
constexpr int prefetchDistance = 2;
// Grow stack to given size
const auto grow = [&](size_t size) VL_ATTR_ALWINLINE {
const ptrdiff_t occupancy = topp - basep;
stack.resize(size);
basep = stack.data() + prefetchDistance;
topp = basep + occupancy;
limp = basep + size - 5; // We push max 5 items per iteration
};
// Initial stack size
grow(32);
// We want some non-null pointers at the beginning. These will be prefetched, but not
// visited, so the root node will suffice. This eliminates needing branches in the loop.
for (int i = -prefetchDistance; i; ++i) basep[i] = nodep;
// Visit given node, enqueue children for traversal, return true if result determined.
const auto visit = [&](Node* currp) VL_ATTR_ALWINLINE {
// Type test this node
if (AstNode::privateTypeTest<T_Arg_NonConst>(currp)) {
// Call the client function
if (p(static_cast<T_Arg*>(currp)) != Default) return true;
// Short circuit if iterating leaf nodes
if VL_CONSTEXPR_CXX17 (isLeaf<T_Arg_NonConst>()) return false;
}
// Enqueue children for traversal, unless futile
if (mayBeUnder<T_Arg_NonConst>(currp)) {
if (AstNode* const op4p = currp->op4p()) *topp++ = op4p;
if (AstNode* const op3p = currp->op3p()) *topp++ = op3p;
if (AstNode* const op2p = currp->op2p()) *topp++ = op2p;
if (AstNode* const op1p = currp->op1p()) *topp++ = op1p;
}
return false;
};
// Visit the root node
if (visit(nodep)) return !Default;
// Visit the rest of the tree
while (VL_LIKELY(topp > basep)) {
// Pop next node in the traversal
Node* const headp = *--topp;
// Prefetch in case we are ascending the tree
ASTNODE_PREFETCH_NON_NULL(topp[-prefetchDistance]);
// Ensure we have stack space for nextp and the 4 children
if (VL_UNLIKELY(topp >= limp)) grow(stack.size() * 2);
// Enqueue the next node
if (headp->nextp()) *topp++ = headp->nextp();
// Visit the head node
if (visit(headp)) return !Default;
}
return Default;
}
}
inline std::ostream& operator<<(std::ostream& os, const AstNode* rhs) {
if (!rhs) {
os << "nullptr";
@ -2128,7 +2280,7 @@ inline void VNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
//######################################################################
// VNRef is std::reference_wrapper that can only hold AstNode subtypes
template <typename T_Node> //
template <typename T_Node>
class VNRef final : public std::reference_wrapper<T_Node> {
static_assert(std::is_base_of<AstNode, T_Node>::value,
"Type parameter 'T_Node' must be a subtype of AstNode");
@ -2154,13 +2306,13 @@ static_assert(sizeof(VNRef<AstNode>) == sizeof(std::reference_wrapper<AstNode>),
size_t V3HasherUncachedHash(const AstNode&);
// Specialization of std::hash for VNRef
template <typename T_Node> //
template <typename T_Node>
struct std::hash<VNRef<T_Node>> final {
size_t operator()(VNRef<T_Node> r) const { return V3HasherUncachedHash(r); }
};
// Specialization of std::equal_to for VNRef
template <typename T_Node> //
template <typename T_Node>
struct std::equal_to<VNRef<T_Node>> final {
size_t operator()(VNRef<T_Node> ra, VNRef<T_Node> rb) const {
return ra.get().sameTree(&(rb.get()));
@ -2424,6 +2576,7 @@ public:
AstNode* stmtsp() const { return op1p(); } // op1 = List of statements
void addStmtsp(AstNode* nodep) { addNOp1p(nodep); }
bool unnamed() const { return m_unnamed; }
bool isFirstInMyListOfStatements(AstNode* nodep) const override { return nodep == stmtsp(); }
};
class AstNodePreSel VL_NOT_FINAL : public AstNode {
@ -2445,7 +2598,7 @@ public:
void fromp(AstNode* nodep) { return setOp1p(nodep); }
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
void thsp(AstNode* nodep) { return setOp3p(nodep); }
void attrp(AstAttrOf* nodep) { return setOp4p((AstNode*)nodep); }
void attrp(AstAttrOf* nodep) { return setOp4p(reinterpret_cast<AstNode*>(nodep)); }
// METHODS
virtual bool same(const AstNode*) const override { return true; }
};
@ -2537,7 +2690,7 @@ public:
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
virtual bool isGateOptimizable() const override { return false; }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstNodeIf VL_NOT_FINAL : public AstNodeStmt {
@ -2564,11 +2717,14 @@ public:
virtual bool isGateOptimizable() const override { return false; }
virtual bool isGateDedupable() const override { return true; }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
void branchPred(VBranchPred flag) { m_branchPred = flag; }
VBranchPred branchPred() const { return m_branchPred; }
void isBoundsCheck(bool flag) { m_isBoundsCheck = flag; }
bool isBoundsCheck() const { return m_isBoundsCheck; }
bool isFirstInMyListOfStatements(AstNode* n) const override {
return n == ifsp() || n == elsesp();
}
};
class AstNodeCase VL_NOT_FINAL : public AstNodeStmt {
@ -2606,7 +2762,7 @@ protected:
: AstNodeMath{t, fl}
, m_access{access}
, m_name{name} {
this->varp(nullptr);
varp(nullptr);
}
AstNodeVarRef(VNType t, FileLine* fl, const string& name, AstVar* varp, const VAccess& access)
: AstNodeMath{t, fl}
@ -3095,6 +3251,7 @@ public:
bool isVirtual() const { return m_virtual; }
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
};
class AstNodeFTaskRef VL_NOT_FINAL : public AstNodeStmt {

View File

@ -481,7 +481,7 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
}
} else {
// Newer internal-compatible types
arg += dtypep()->cType((named ? name() : string{}), true, isRef);
arg += dtypep()->cType((named ? name() : std::string{}), true, isRef);
}
return arg;
}
@ -489,7 +489,7 @@ string AstVar::cPubArgType(bool named, bool forReturn) const {
class dpiTypesToStringConverter VL_NOT_FINAL {
public:
virtual string openArray(const AstVar*) const { return "const svOpenArrayHandle"; }
virtual string bitLogicVector(const AstVar* varp, bool isBit) const {
virtual string bitLogicVector(const AstVar* /*varp*/, bool isBit) const {
return isBit ? "svBitVecVal" : "svLogicVecVal";
}
virtual string primitive(const AstVar* varp) const {
@ -659,7 +659,7 @@ public:
}
};
string AstNodeDType::cType(const string& name, bool forFunc, bool isRef) const {
string AstNodeDType::cType(const string& name, bool /*forFunc*/, bool isRef) const {
const CTypeRecursed info = cTypeRecurse(false);
return info.render(name, isRef);
}
@ -673,6 +673,9 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true);
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true);
info.m_type = "VlAssocArray<" + key.m_type + ", " + val.m_type + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
info.m_type = "VlAssocArray<std::string, " + sub.m_type + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
info.m_type = "VlQueue<" + sub.m_type + ">";
@ -1323,8 +1326,9 @@ void AstClass::repairCache() {
clearCache();
for (auto* itemp = membersp(); itemp; itemp = itemp->nextp()) {
if (const auto* const scopep = VN_CAST(itemp, Scope)) {
for (auto* itemp = scopep->blocksp(); itemp; itemp = itemp->nextp())
insertCache(itemp);
for (auto* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
insertCache(blockp);
}
} else {
insertCache(itemp);
}
@ -1351,7 +1355,7 @@ AstClass* AstClassExtends::classp() const {
return refp->classp();
}
void AstClassRefDType::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeDType::dump(str);
if (classOrPackagep()) str << " cpkg=" << nodeAddr(classOrPackagep());
if (classp()) {
str << " -> ";
@ -1382,7 +1386,7 @@ void AstEnumItemRef::dump(std::ostream& str) const {
}
}
void AstIfaceRefDType::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeDType::dump(str);
if (cellName() != "") str << " cell=" << cellName();
if (ifaceName() != "") str << " if=" << ifaceName();
if (modportName() != "") str << " mp=" << modportName();
@ -1431,7 +1435,7 @@ void AstJumpLabel::dump(std::ostream& str) const {
}
}
void AstLogOr::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeMath::dump(str);
if (sideEffect()) str << " [SIDE]";
}
void AstMemberSel::dump(std::ostream& str) const {
@ -1444,7 +1448,7 @@ void AstMemberSel::dump(std::ostream& str) const {
}
}
void AstMethodCall::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
this->AstNodeFTaskRef::dump(str);
if (isStatement()) str << " [STMT]";
str << " -> ";
if (taskp()) {
@ -1530,7 +1534,7 @@ void AstRefDType::dump(std::ostream& str) const {
}
}
void AstNodeUOrStructDType::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeDType::dump(str);
if (packed()) str << " [PACKED]";
if (isFourstate()) str << " [4STATE]";
}
@ -1621,7 +1625,7 @@ void AstPackageImport::dump(std::ostream& str) const {
str << " -> " << packagep();
}
void AstPatMember::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeMath::dump(str);
if (isDefault()) str << " [DEFAULT]";
}
void AstNodeTriop::dump(std::ostream& str) const { this->AstNodeMath::dump(str); }
@ -1683,6 +1687,10 @@ string AstQueueDType::prettyDTypeName() const {
if (boundConst()) str += ":" + cvtToStr(boundConst());
return str + "]";
}
void AstWildcardArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[*]";
}
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[]";
@ -1766,7 +1774,7 @@ void AstScope::dump(std::ostream& str) const {
str << " [modp=" << reinterpret_cast<const void*>(modp()) << "]";
}
void AstScopeName::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeMath::dump(str);
if (dpiExport()) str << " [DPIEX]";
if (forFormat()) str << " [FMT]";
}
@ -1833,7 +1841,7 @@ void AstNodeBlock::dump(std::ostream& str) const {
if (unnamed()) str << " [UNNAMED]";
}
void AstBegin::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeBlock::dump(str);
if (generate()) str << " [GEN]";
if (genforp()) str << " [GENFOR]";
if (implied()) str << " [IMPLIED]";
@ -1859,7 +1867,7 @@ void AstCoverInc::dump(std::ostream& str) const {
}
}
void AstFork::dump(std::ostream& str) const {
this->AstNode::dump(str);
this->AstNodeBlock::dump(str);
if (!joinType().join()) str << " [" << joinType() << "]";
}
void AstTraceDecl::dump(std::ostream& str) const {

View File

@ -77,10 +77,11 @@ public:
, m_num(this, width, value) {
initWithNumber();
}
class DtypedValue {}; // for creator type-overload selection
AstConst(FileLine* fl, DtypedValue, AstNodeDType* nodedtypep, uint32_t value)
class DTyped {}; // for creator type-overload selection
// Zero/empty constant with a type matching nodetypep
AstConst(FileLine* fl, DTyped, const AstNodeDType* nodedtypep)
: ASTGEN_SUPER_Const(fl)
, m_num(this, nodedtypep->width(), value, nodedtypep->widthSized()) {
, m_num(this, nodedtypep) {
initWithNumber();
}
class StringToParse {}; // for creator type-overload selection
@ -186,20 +187,20 @@ public:
class AstEmpty final : public AstNode {
// Represents something missing, e.g. a missing argument in FOREACH
public:
AstEmpty(FileLine* fl)
explicit AstEmpty(FileLine* fl)
: ASTGEN_SUPER_Empty(fl) {}
ASTNODE_NODE_FUNCS(Empty)
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstEmptyQueue final : public AstNodeMath {
public:
AstEmptyQueue(FileLine* fl)
explicit AstEmptyQueue(FileLine* fl)
: ASTGEN_SUPER_EmptyQueue(fl) {}
ASTNODE_NODE_FUNCS(EmptyQueue)
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual string emitVerilog() override { return "{}"; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual bool cleanOut() const override { return true; }
};
@ -246,7 +247,7 @@ public:
bool littleEndian() const { return leftConst() < rightConst(); }
virtual void dump(std::ostream& str) const override;
virtual string emitC() { V3ERROR_NA_RETURN(""); }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstBracketRange final : public AstNodeRange {
@ -260,7 +261,7 @@ public:
ASTNODE_NODE_FUNCS(BracketRange)
virtual string emitC() { V3ERROR_NA_RETURN(""); }
virtual string emitVerilog() { V3ERROR_NA_RETURN(""); }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
// Will be removed in V3Width, which relies on this
// being a child not a dtype pointed node
virtual bool maybePointedTo() const override { return false; }
@ -275,7 +276,18 @@ public:
ASTNODE_NODE_FUNCS(UnsizedRange)
virtual string emitC() { V3ERROR_NA_RETURN(""); }
virtual string emitVerilog() { return "[]"; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstWildcardRange final : public AstNodeRange {
// Wildcard range specification, for wildcard index type associative arrays
public:
explicit AstWildcardRange(FileLine* fl)
: ASTGEN_SUPER_WildcardRange(fl) {}
ASTNODE_NODE_FUNCS(WildcardRange)
virtual string emitC() { V3ERROR_NA_RETURN(""); }
virtual string emitVerilog() { return "[*]"; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstGatePin final : public AstNodeMath {
@ -649,7 +661,7 @@ class AstDynArrayDType final : public AstNodeDType {
// Dynamic array data type, ie "[]"
// Children: DTYPE (moved to refDTypep() in V3Width)
private:
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
AstNodeDType* m_refDTypep = nullptr; // Elements of this type (after widthing)
public:
AstDynArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp)
: ASTGEN_SUPER_DynArrayDType(fl) {
@ -819,6 +831,62 @@ public:
virtual bool isCompound() const override { return true; }
};
class AstWildcardArrayDType final : public AstNodeDType {
// Wildcard index type associative array data type, ie "some_dtype var_name [*]"
// Children: DTYPE (moved to refDTypep() in V3Width)
private:
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
public:
AstWildcardArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp)
: ASTGEN_SUPER_WildcardArrayDType(fl) {
childDTypep(dtp); // Only for parser
refDTypep(nullptr);
dtypep(nullptr); // V3Width will resolve
}
ASTNODE_NODE_FUNCS(WildcardArrayDType)
virtual const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep())));
return nullptr;
}
virtual void cloneRelink() override {
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
}
virtual bool same(const AstNode* samep) const override {
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
if (!asamep->subDTypep()) return false;
return (subDTypep() == asamep->subDTypep());
}
virtual bool similarDType(AstNodeDType* samep) const override {
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
return type() == samep->type() && asamep->subDTypep()
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
}
virtual void dumpSmall(std::ostream& str) const override;
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
// op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_AS(op1p(), NodeDType); }
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
virtual AstNodeDType* subDTypep() const override {
return m_refDTypep ? m_refDTypep : childDTypep();
}
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
virtual AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
virtual void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
// METHODS
virtual AstBasicDType* basicp() const override { return subDTypep()->basicp(); }
virtual AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
virtual AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
virtual int widthAlignBytes() const override {
return sizeof(std::map<std::string, std::string>);
}
virtual int widthTotalBytes() const override {
return sizeof(std::map<std::string, std::string>);
}
virtual bool isCompound() const override { return true; }
};
class AstBasicDType final : public AstNodeDType {
// Builtin atomic/vectored data type
// Children: RANGE (converted to constant in V3Width)
@ -975,7 +1043,7 @@ class AstConstDType final : public AstNodeDType {
// ConstDType are removed in V3LinkLValue and become AstVar::isConst.
// When more generic types are supported AstConstDType will be propagated further.
private:
AstNodeDType* m_refDTypep; // Inherit from this base data type
AstNodeDType* m_refDTypep = nullptr; // Inherit from this base data type
public:
AstConstDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp)
: ASTGEN_SUPER_ConstDType(fl) {
@ -1134,7 +1202,7 @@ class AstQueueDType final : public AstNodeDType {
// Queue array data type, ie "[ $ ]"
// Children: DTYPE (moved to refDTypep() in V3Width)
private:
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
AstNodeDType* m_refDTypep = nullptr; // Elements of this type (after widthing)
public:
AstQueueDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstNode* boundp)
: ASTGEN_SUPER_QueueDType(fl) {
@ -1328,7 +1396,7 @@ class AstMemberDType final : public AstNodeDType {
// A member of a struct/union
// PARENT: AstNodeUOrStructDType
private:
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
AstNodeDType* m_refDTypep = nullptr; // Elements of this type (after widthing)
string m_name; // Name of variable
string m_tag; // Holds the string of the verilator tag -- used in XML output.
int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member
@ -1517,8 +1585,8 @@ class AstEnumDType final : public AstNodeDType {
// Children: ENUMVALUEs
private:
string m_name; // Name from upper typedef, if any
AstNodeDType* m_refDTypep; // Elements are of this type after V3Width
const int m_uniqueNum;
AstNodeDType* m_refDTypep = nullptr; // Elements are of this type after V3Width
const int m_uniqueNum = 0;
public:
AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstNode* itemsp)
@ -1641,7 +1709,7 @@ public:
return true;
} // esp for V3Const::ifSameAssign
virtual bool isPredictOptimizable() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual int instrCount() const override { return widthInstrs(); }
// Special operators
// Return base var (or const) nodep dereferences
@ -1682,7 +1750,45 @@ public:
return true;
} // esp for V3Const::ifSameAssign
virtual bool isPredictOptimizable() const override { return false; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual int instrCount() const override { return widthInstrs(); }
};
class AstWildcardSel final : public AstNodeSel {
// Parents: math|stmt
// Children: varref|arraysel, math
private:
void init(AstNode* fromp) {
if (fromp && VN_IS(fromp->dtypep()->skipRefp(), WildcardArrayDType)) {
// Strip off array to find what array references
dtypeFrom(VN_AS(fromp->dtypep()->skipRefp(), WildcardArrayDType)->subDTypep());
}
}
public:
AstWildcardSel(FileLine* fl, AstNode* fromp, AstNode* bitp)
: ASTGEN_SUPER_WildcardSel(fl, fromp, bitp) {
init(fromp);
}
ASTNODE_NODE_FUNCS(WildcardSel)
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override {
return new AstWildcardSel{this->fileline(), lhsp, rhsp};
}
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
V3ERROR_NA;
}
virtual string emitVerilog() override { return "%k(%l%f[%r])"; }
virtual string emitC() override { return "%li%k[%ri]"; }
virtual bool cleanOut() const override { return true; }
virtual bool cleanLhs() const override { return false; }
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool isGateOptimizable() const override {
return true;
} // esp for V3Const::ifSameAssign
virtual bool isPredictOptimizable() const override { return false; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual int instrCount() const override { return widthInstrs(); }
};
@ -1709,7 +1815,7 @@ public:
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstSelLoopVars final : public AstNode {
@ -1722,7 +1828,7 @@ public:
addNOp2p(elementsp);
}
ASTNODE_NODE_FUNCS(SelLoopVars)
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual bool maybePointedTo() const override { return false; }
AstNode* fromp() const { return op1p(); }
void fromp(AstNode* nodep) { setOp1p(nodep); }
@ -3129,8 +3235,7 @@ public:
AstNodeModule* classOrPackagep() const {
AstNode* foundp = m_classOrPackageNodep;
while (auto* const anodep = VN_CAST(foundp, Typedef)) foundp = anodep->subDTypep();
while (auto* const anodep = VN_CAST(foundp, ClassRefDType))
foundp = anodep->classOrPackagep();
if (auto* const anodep = VN_CAST(foundp, ClassRefDType)) foundp = anodep->classp();
return VN_CAST(foundp, NodeModule);
}
AstPackage* packagep() const { return VN_CAST(classOrPackageNodep(), Package); }
@ -3252,7 +3357,7 @@ public:
addNOp2p(exprp);
}
ASTNODE_NODE_FUNCS(WithParse)
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
//
AstNode* funcrefp() const { return op1p(); }
AstNode* exprp() const { return op2p(); }
@ -3272,7 +3377,7 @@ public:
, m_name{name}
, m_index(index) {}
ASTNODE_NODE_FUNCS(LambdaArgRef)
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual string emitVerilog() override { return name(); }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual bool cleanOut() const override { return true; }
@ -3299,7 +3404,7 @@ public:
addNOp3p(exprp);
}
ASTNODE_NODE_FUNCS(With)
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual bool hasDType() const override { return true; }
virtual const char* broken() const override {
BROKEN_RTN(!indexArgRefp()); // varp needed to know lambda's arg dtype
@ -3473,13 +3578,14 @@ public:
addNOp2p(bodysp);
}
ASTNODE_NODE_FUNCS(AlwaysPublic)
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
//
AstSenTree* sensesp() const { return VN_AS(op1p(), SenTree); } // op1 = Sensitivity list
AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate
void addStmtp(AstNode* nodep) { addOp2p(nodep); }
// Special accessors
bool isJustOneBodyStmt() const { return bodysp() && !bodysp()->nextp(); }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); }
};
class AstAssign final : public AstNodeAssign {
@ -3804,7 +3910,7 @@ public:
}
ASTNODE_NODE_FUNCS(CoverToggle)
virtual int instrCount() const override { return 3 + INSTR_COUNT_BRANCH + INSTR_COUNT_LD; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return true; }
virtual bool isOutputter() const override {
@ -3826,7 +3932,7 @@ public:
setNOp2p(stmtsp);
}
ASTNODE_NODE_FUNCS(Delay)
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
//
AstNode* lhsp() const { return op1p(); } // op1 = delay value
void lhsp(AstNode* nodep) { setOp1p(nodep); }
@ -3903,6 +4009,7 @@ public:
void condsp(AstNode* nodep) { setOp1p(nodep); }
void addBodysp(AstNode* newp) { addOp2p(newp); }
bool isDefault() const { return condsp() == nullptr; }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); }
};
class AstSFormatF final : public AstNode {
@ -4032,7 +4139,7 @@ public:
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual bool cleanOut() const { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
VDumpCtlType ctlType() const { return m_ctlType; }
AstNode* exprp() const { return op1p(); } // op2 = Expressions to output
void exprp(AstNode* nodep) { setOp1p(nodep); }
@ -4103,7 +4210,7 @@ public:
virtual bool isOutputter() const override { return false; }
virtual bool cleanOut() const { return false; }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter
AstSFormatF* fmtp() const { return VN_AS(op1p(), SFormatF); }
AstNode* lhsp() const { return op3p(); }
@ -4126,7 +4233,7 @@ public:
virtual bool isPure() const override { return true; }
virtual bool isOutputter() const override { return false; }
virtual int instrCount() const override { return 0; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* lhsp() const { return op1p(); } // op1 = Expressions to eval
void lhsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to eval
};
@ -4167,7 +4274,7 @@ public:
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual bool isUnlikely() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* filep() const { return op2p(); }
void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); }
};
@ -4188,7 +4295,7 @@ public:
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual bool isUnlikely() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* filep() const { return op1p(); }
AstNode* filenamep() const { return op2p(); }
AstNode* modep() const { return op3p(); }
@ -4209,7 +4316,7 @@ public:
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual bool isUnlikely() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* filep() const { return op1p(); }
AstNode* filenamep() const { return op2p(); }
};
@ -4229,7 +4336,7 @@ public:
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual bool isUnlikely() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* filep() const { return op2p(); }
void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); }
};
@ -4257,7 +4364,7 @@ public:
virtual bool isPure() const override { return false; } // SPECIAL: has 'visual' ordering
virtual bool isOutputter() const override { return true; } // SPECIAL: makes output
virtual bool cleanOut() const override { return false; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* memp() const { return op1p(); }
void memp(AstNode* nodep) { setOp1p(nodep); }
AstNode* filep() const { return op2p(); }
@ -4286,7 +4393,7 @@ public:
virtual bool isOutputter() const override { return true; }
virtual bool isUnlikely() const override { return true; }
virtual bool cleanOut() const override { return false; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* filep() const { return op2p(); }
void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); }
};
@ -4309,7 +4416,7 @@ public:
virtual bool isOutputter() const override { return true; }
virtual bool isUnlikely() const override { return true; }
virtual bool cleanOut() const override { return false; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* filep() const { return op2p(); }
void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); }
};
@ -4335,7 +4442,7 @@ public:
virtual bool isPure() const override { return false; } // SPECIAL: has 'visual' ordering
virtual bool isOutputter() const override { return true; } // SPECIAL: makes output
virtual bool cleanOut() const override { return false; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* filep() const { return op2p(); }
void filep(AstNode* nodep) { setOp2p(nodep); }
AstNode* offset() const { return op3p(); }
@ -4499,7 +4606,7 @@ public:
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual bool isUnlikely() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* lhsp() const { return op1p(); }
};
@ -4520,7 +4627,7 @@ public:
virtual bool isOutputter() const override { return true; }
virtual bool isUnlikely() const override { return true; }
virtual bool cleanOut() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* lhsp() const { return op1p(); }
};
@ -4541,7 +4648,7 @@ public:
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return !outp(); }
virtual bool cleanOut() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* searchp() const { return op1p(); } // op1 = Search expression
void searchp(AstNode* nodep) { setOp1p(nodep); }
AstNode* outp() const { return op2p(); } // op2 = Expressions to output
@ -4551,26 +4658,21 @@ public:
class AstTestPlusArgs final : public AstNodeMath {
// Parents: expr
// Child: variable to set. If nullptr then this is a $test$plusargs instead of $value$plusargs
private:
string m_text;
public:
AstTestPlusArgs(FileLine* fl, const string& text)
: ASTGEN_SUPER_TestPlusArgs(fl)
, m_text{text} {}
AstTestPlusArgs(FileLine* fl, AstNode* searchp)
: ASTGEN_SUPER_TestPlusArgs(fl) {
setOp1p(searchp);
}
ASTNODE_NODE_FUNCS(TestPlusArgs)
virtual string name() const override { return m_text; }
virtual string verilogKwd() const override { return "$test$plusargs"; }
virtual string emitVerilog() override { return verilogKwd(); }
virtual string emitC() override { return "VL_VALUEPLUSARGS_%nq(%lw, %P, nullptr)"; }
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool cleanOut() const override { return true; }
virtual bool same(const AstNode* samep) const override {
return text() == static_cast<const AstTestPlusArgs*>(samep)->text();
}
string text() const { return m_text; } // * = Text to display
void text(const string& text) { m_text = text; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstNode* searchp() const { return op1p(); } // op1 = Search expression
void searchp(AstNode* nodep) { setOp1p(nodep); }
};
class AstGenFor final : public AstNodeFor {
@ -4592,7 +4694,8 @@ public:
AstNode* bodysp() const { return op4p(); } // op4 = body of loop
virtual bool isGateOptimizable() const override { return false; }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); }
};
class AstRepeat final : public AstNodeStmt {
@ -4609,7 +4712,8 @@ public:
return false;
} // Not relevant - converted to FOR
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); }
};
class AstWait final : public AstNodeStmt {
@ -4621,6 +4725,7 @@ public:
}
ASTNODE_NODE_FUNCS(Wait)
AstNode* bodysp() const { return op3p(); } // op3 = body of loop
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); }
};
class AstWhile final : public AstNodeStmt {
@ -4642,11 +4747,12 @@ public:
void addIncsp(AstNode* newp) { addOp4p(newp); }
virtual bool isGateOptimizable() const override { return false; }
virtual int instrCount() const override { return INSTR_COUNT_BRANCH; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
// Stop statement searchback here
virtual void addBeforeStmt(AstNode* newp, AstNode* belowp) override;
// Stop statement searchback here
virtual void addNextStmt(AstNode* newp, AstNode* belowp) override;
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == bodysp(); }
};
class AstBreak final : public AstNodeStmt {
@ -4762,7 +4868,7 @@ public:
ASTNODE_NODE_FUNCS(JumpBlock)
virtual int instrCount() const override { return 0; }
virtual bool maybePointedTo() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
// op1 = Statements
AstNode* stmtsp() const { return op1p(); } // op1 = List of statements
void addStmtsp(AstNode* nodep) { addNOp1p(nodep); }
@ -4848,7 +4954,7 @@ public:
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return widthInstrs() * 2; } // xor, or/logor
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstConsAssoc final : public AstNodeMath {
@ -4867,7 +4973,7 @@ public:
virtual bool cleanOut() const override { return true; }
virtual int instrCount() const override { return widthInstrs(); }
AstNode* defaultp() const { return op1p(); }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstSetAssoc final : public AstNodeMath {
// Set an assoc array element and return object, '{}
@ -4889,7 +4995,48 @@ public:
AstNode* lhsp() const { return op1p(); }
AstNode* keyp() const { return op2p(); }
AstNode* valuep() const { return op3p(); }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstConsWildcard final : public AstNodeMath {
// Construct a wildcard assoc array and return object, '{}
// Parents: math
// Children: expression (elements or other queues)
public:
AstConsWildcard(FileLine* fl, AstNode* defaultp)
: ASTGEN_SUPER_ConsWildcard(fl) {
setNOp1p(defaultp);
}
ASTNODE_NODE_FUNCS(ConsWildcard)
virtual string emitVerilog() override { return "'{}"; }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
virtual bool cleanOut() const override { return true; }
virtual int instrCount() const override { return widthInstrs(); }
AstNode* defaultp() const { return op1p(); }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstSetWildcard final : public AstNodeMath {
// Set a wildcard assoc array element and return object, '{}
// Parents: math
// Children: expression (elements or other queues)
public:
AstSetWildcard(FileLine* fl, AstNode* lhsp, AstNode* keyp, AstNode* valuep)
: ASTGEN_SUPER_SetWildcard(fl) {
setOp1p(lhsp);
setNOp2p(keyp);
setOp3p(valuep);
}
ASTNODE_NODE_FUNCS(SetWildcard)
virtual string emitVerilog() override { return "'{}"; }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
virtual bool cleanOut() const override { return true; }
virtual int instrCount() const override { return widthInstrs(); }
AstNode* lhsp() const { return op1p(); }
AstNode* keyp() const { return op2p(); }
AstNode* valuep() const { return op3p(); }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstConsDynArray final : public AstNodeMath {
@ -4910,7 +5057,7 @@ public:
virtual int instrCount() const override { return widthInstrs(); }
AstNode* lhsp() const { return op1p(); } // op1 = expression
AstNode* rhsp() const { return op2p(); } // op2 = expression
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstConsQueue final : public AstNodeMath {
@ -4931,7 +5078,7 @@ public:
virtual int instrCount() const override { return widthInstrs(); }
AstNode* lhsp() const { return op1p(); } // op1 = expression
AstNode* rhsp() const { return op2p(); } // op2 = expression
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstBegin final : public AstNodeBlock {
@ -5108,7 +5255,7 @@ public:
: ASTGEN_SUPER_New(fl, false, "new", pinsp) {}
ASTNODE_NODE_FUNCS(New)
virtual bool cleanOut() const { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual bool hasDType() const override { return true; }
virtual int instrCount() const override { return widthInstrs(); }
};
@ -5127,7 +5274,7 @@ public:
virtual string emitVerilog() override { return "new"; }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual bool cleanOut() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual int instrCount() const override { return widthInstrs(); }
AstNode* rhsp() const { return op1p(); }
};
@ -5147,7 +5294,7 @@ public:
virtual string emitVerilog() override { return "new"; }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual bool cleanOut() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual int instrCount() const override { return widthInstrs(); }
AstNode* sizep() const { return op1p(); }
AstNode* rhsp() const { return op2p(); }
@ -5580,7 +5727,7 @@ public:
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
bool combinable(const AstRand* samep) const {
return !seedp() && !samep->seedp() && reset() == samep->reset()
&& urandom() == samep->urandom();
@ -5631,7 +5778,7 @@ public:
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return INSTR_COUNT_TIME; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual void dump(std::ostream& str = std::cout) const override;
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
@ -5652,7 +5799,7 @@ public:
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return INSTR_COUNT_TIME; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
virtual void dump(std::ostream& str = std::cout) const override;
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
@ -5677,7 +5824,7 @@ public:
virtual bool isSubstOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual int instrCount() const override { return INSTR_COUNT_PLI; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
//======================================================================
@ -5998,7 +6145,9 @@ public:
class AstCLog2 final : public AstNodeUniop {
public:
AstCLog2(FileLine* fl, AstNode* lhsp)
: ASTGEN_SUPER_CLog2(fl, lhsp) {}
: ASTGEN_SUPER_CLog2(fl, lhsp) {
dtypeSetSigned32();
}
ASTNODE_NODE_FUNCS(CLog2)
virtual void numberOperate(V3Number& out, const V3Number& lhs) override { out.opCLog2(lhs); }
virtual string emitVerilog() override { return "%f$clog2(%l)"; }
@ -6263,7 +6412,7 @@ public:
virtual bool cleanOut() const override { return true; }
virtual bool cleanLhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstFEof final : public AstNodeUniop {
@ -6305,7 +6454,7 @@ public:
AstNode* filep() const { return op1p(); }
void strp(AstNode* nodep) { setOp2p(nodep); }
AstNode* strp() const { return op2p(); }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstFGetC final : public AstNodeUniop {
@ -8430,7 +8579,7 @@ public:
AstNode* exprp() const { return op1p(); } // op1 = expression
AstSenTree* sentreep() const { return VN_AS(op2p(), SenTree); } // op2 = clock domain
void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstPast final : public AstNodeMath {
@ -8453,7 +8602,7 @@ public:
AstNode* ticksp() const { return op2p(); } // op2 = ticks or nullptr means 1
AstSenTree* sentreep() const { return VN_AS(op4p(), SenTree); } // op4 = clock domain
void sentreep(AstSenTree* sentreep) { addOp4p(sentreep); } // op4 = clock domain
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstRose final : public AstNodeMath {
@ -8474,7 +8623,7 @@ public:
AstNode* exprp() const { return op1p(); } // op1 = expression
AstSenTree* sentreep() const { return VN_AS(op2p(), SenTree); } // op2 = clock domain
void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstSampled final : public AstNodeMath {
@ -8493,7 +8642,7 @@ public:
virtual bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
virtual int instrCount() const override { return 0; }
AstNode* exprp() const { return op1p(); } // op1 = expression
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstStable final : public AstNodeMath {
@ -8514,7 +8663,7 @@ public:
AstNode* exprp() const { return op1p(); } // op1 = expression
AstSenTree* sentreep() const { return VN_AS(op2p(), SenTree); } // op2 = clock domain
void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstPattern final : public AstNodeMath {
@ -8589,7 +8738,7 @@ public:
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
AstSenTree* sentreep() const { return VN_AS(op4p(), SenTree); } // op4 = clock domain
void sentreep(AstSenTree* sentreep) { addOp4p(sentreep); } // op4 = clock domain
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
//======================================================================
@ -8810,7 +8959,7 @@ public:
virtual bool isPredictOptimizable() const override { return false; }
virtual bool isPure() const override { return false; }
virtual bool isOutputter() const override { return true; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
};
//======================================================================
@ -8830,7 +8979,7 @@ public:
ASTNODE_BASE_FUNCS(NodeFile)
virtual void dump(std::ostream& str) const override;
virtual string name() const override { return m_name; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
void tblockp(AstTextBlock* tblockp) { setOp1p(tblockp); }
AstTextBlock* tblockp() { return VN_AS(op1p(), TextBlock); }
};
@ -9098,7 +9247,7 @@ public:
}
ASTNODE_NODE_FUNCS(CReturn)
virtual int instrCount() const override { return widthInstrs(); }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
//
AstNode* lhsp() const { return op1p(); }
};
@ -9129,7 +9278,7 @@ public:
virtual bool cleanOut() const override { return m_cleanOut; }
virtual string emitVerilog() override { V3ERROR_NA_RETURN(""); }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
void addBodysp(AstNode* nodep) { addNOp1p(nodep); }
AstNode* bodysp() const { return op1p(); } // op1 = expressions to print
bool pure() const { return m_pure; }
@ -9146,7 +9295,7 @@ public:
ASTNODE_NODE_FUNCS(CReset)
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
AstVarRef* varrefp() const { return VN_AS(op1p(), VarRef); } // op1 = varref to reset
};
@ -9164,7 +9313,7 @@ public:
ASTNODE_NODE_FUNCS(CStmt)
virtual bool isGateOptimizable() const override { return false; }
virtual bool isPredictOptimizable() const override { return false; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual bool same(const AstNode* /*samep*/) const override { return true; }
void addBodysp(AstNode* nodep) { addNOp1p(nodep); }
AstNode* bodysp() const { return op1p(); } // op1 = expressions to print
};
@ -9260,7 +9409,7 @@ class AstTypeTable final : public AstNode {
AstEmptyQueueDType* m_emptyQueuep = nullptr;
AstQueueDType* m_queueIndexp = nullptr;
AstVoidDType* m_voidp = nullptr;
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX];
AstBasicDType* m_basicps[VBasicDTypeKwd::_ENUM_MAX]{};
//
using DetailedMap = std::map<VBasicTypeKey, AstBasicDType*>;
DetailedMap m_detailedMap;

View File

@ -27,7 +27,8 @@
#include <utility>
#include <vector>
template <class T_Node, class T_Data, int T_UserN> class AstUserAllocatorBase VL_NOT_FINAL {
template <class T_Node, class T_Data, int T_UserN>
class AstUserAllocatorBase VL_NOT_FINAL {
static_assert(1 <= T_UserN && T_UserN <= 5, "Wrong user pointer number");
static_assert(std::is_base_of<AstNode, T_Node>::value, "T_Node must be an AstNode type");
@ -35,17 +36,16 @@ private:
std::vector<T_Data*> m_allocated;
inline T_Data* getUserp(const T_Node* nodep) const {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
const VNUser user = nodep->user1u();
return user.to<T_Data*>();
} else if (T_UserN == 2) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
const VNUser user = nodep->user2u();
return user.to<T_Data*>();
} else if (T_UserN == 3) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
const VNUser user = nodep->user3u();
return user.to<T_Data*>();
} else if (T_UserN == 4) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
const VNUser user = nodep->user4u();
return user.to<T_Data*>();
} else {
@ -55,14 +55,13 @@ private:
}
inline void setUserp(T_Node* nodep, T_Data* userp) const {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
nodep->user1u(VNUser(userp));
} else if (T_UserN == 2) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
nodep->user2u(VNUser(userp));
} else if (T_UserN == 3) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
nodep->user3u(VNUser(userp));
} else if (T_UserN == 4) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
nodep->user4u(VNUser(userp));
} else {
nodep->user5u(VNUser(userp));
@ -71,14 +70,13 @@ private:
protected:
AstUserAllocatorBase() {
// This simplifies statically as T_UserN is constant. In C++17, use 'if constexpr'.
if (T_UserN == 1) {
if VL_CONSTEXPR_CXX17 (T_UserN == 1) {
VNUser1InUse::check();
} else if (T_UserN == 2) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 2) {
VNUser2InUse::check();
} else if (T_UserN == 3) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 3) {
VNUser3InUse::check();
} else if (T_UserN == 4) {
} else if VL_CONSTEXPR_CXX17 (T_UserN == 4) {
VNUser4InUse::check();
} else {
VNUser5InUse::check();
@ -94,7 +92,7 @@ protected:
public:
// Get a reference to the user data. If does not exist, construct it with given arguments.
template <typename... Args> //
template <typename... Args>
T_Data& operator()(T_Node* nodep, Args&&... args) {
T_Data* userp = getUserp(nodep);
if (!userp) {

View File

@ -28,9 +28,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Begin.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <algorithm>

View File

@ -26,9 +26,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Branch.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <map>

View File

@ -25,9 +25,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Broken.h"
#include "V3Ast.h"
#include "V3Global.h"
// This visitor does not edit nodes, and is called at error-exit, so should use constant iterators
#include "V3AstConstOnly.h"
@ -48,7 +49,7 @@ static class BrokenCntGlobal {
uint8_t m_count = MIN_VALUE;
public:
uint8_t get() {
uint8_t get() const {
UASSERT(MIN_VALUE <= m_count && m_count <= MAX_VALUE, "Invalid generation number");
return m_count;
}

View File

@ -27,10 +27,11 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitCBase.h"
#include "V3CCtors.h"
#include "V3EmitCBase.h"
#include "V3Global.h"
#include <algorithm>
#include <list>

View File

@ -25,9 +25,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3CUse.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <set>

View File

@ -37,9 +37,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Case.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3Stats.h"
#include <algorithm>

View File

@ -40,9 +40,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Cast.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <algorithm>

View File

@ -24,13 +24,14 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Cdc.h"
#include "V3Ast.h"
#include "V3Graph.h"
#include "V3Const.h"
#include "V3EmitV.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3Graph.h"
#include <algorithm>
#include <deque>

View File

@ -29,10 +29,11 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Ast.h"
#include "V3Changed.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <algorithm>
//######################################################################

View File

@ -23,9 +23,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Class.h"
#include "V3Ast.h"
#include "V3Global.h"
//######################################################################
@ -115,13 +116,13 @@ private:
// Move later, or we wouldn't keep interating the class
// We're really moving the VarScope but we might not
// have a pointer to it yet
m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep));
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
}
if (!m_ftaskp && nodep->lifetime().isStatic()) {
m_toPackageMoves.push_back(std::make_pair(nodep, m_classPackagep));
m_toPackageMoves.emplace_back(std::make_pair(nodep, m_classPackagep));
// We're really moving the VarScope but we might not
// have a pointer to it yet
m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep));
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
}
}
}
@ -137,7 +138,7 @@ private:
m_ftaskp = nodep;
iterateChildren(nodep);
if (m_packageScopep && nodep->lifetime().isStatic()) {
m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep));
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
}
}
}
@ -152,12 +153,16 @@ private:
virtual void visit(AstInitial* nodep) override {
// But not AstInitialAutomatic, which remains under the class
iterateChildren(nodep);
if (m_packageScopep) { m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep)); }
if (m_packageScopep) {
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
}
}
virtual void visit(AstInitialStatic* nodep) override {
// But not AstInitialAutomatic, which remains under the class
iterateChildren(nodep);
if (m_packageScopep) { m_toScopeMoves.push_back(std::make_pair(nodep, m_packageScopep)); }
if (m_packageScopep) {
m_toScopeMoves.emplace_back(std::make_pair(nodep, m_packageScopep));
}
}
virtual void visit(AstNodeMath* nodep) override {} // Short circuit

View File

@ -26,9 +26,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Clean.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <algorithm>
@ -88,6 +89,7 @@ private:
if (VN_IS(nodep, Var)
|| VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|| VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays
|| VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)

View File

@ -30,9 +30,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Clock.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <algorithm>

View File

@ -22,12 +22,13 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Combine.h"
#include "V3DupFinder.h"
#include "V3Stats.h"
#include "V3Ast.h"
#include "V3AstUserAllocator.h"
#include "V3DupFinder.h"
#include "V3Global.h"
#include "V3Stats.h"
#include <list>
#include <vector>
@ -220,7 +221,9 @@ class CombineVisitor final : VNVisitor {
// CONSTRUCTORS
explicit CombineVisitor(AstNetlist* nodep) { iterate(nodep); }
~CombineVisitor() { V3Stats::addStat("Optimizations, Combined CFuncs", m_cfuncsCombined); }
~CombineVisitor() override {
V3Stats::addStat("Optimizations, Combined CFuncs", m_cfuncsCombined);
}
public:
static void apply(AstNetlist* netlistp) { CombineVisitor{netlistp}; }

View File

@ -24,9 +24,10 @@
#include "verilatedos.h"
#include "V3Common.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3EmitCBase.h"
#include "V3Global.h"
//######################################################################
// Common component builders
@ -50,7 +51,7 @@ static void makeToString(AstClass* nodep) {
funcp->isStatic(false);
funcp->protect(false);
AstNode* const exprp
= new AstCMath{nodep->fileline(), R"(std::string("'{") + to_string_middle() + "}")", 0};
= new AstCMath{nodep->fileline(), R"(std::string{"'{"} + to_string_middle() + "}")", 0};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtp(funcp);
@ -85,11 +86,11 @@ static void makeToStringMiddle(AstClass* nodep) {
}
}
}
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
string stmt = "out += \"";
if (nodep->extendsp()) {
string stmt = "out += ";
if (!comma.empty()) stmt += "\", \"+ ";
// comma = ", "; // Nothing further so not needed
stmt += nodep->extendsp()->dtypep()->nameProtect();
stmt += EmitCBaseVisitor::prefixNameProtect(nodep->extendsp()->dtypep());
stmt += "::to_string_middle();\n";
nodep->user1(true); // So what we extend dumps this
funcp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
@ -103,13 +104,13 @@ static void makeToStringMiddle(AstClass* nodep) {
void V3Common::commonAll() {
UINFO(2, __FUNCTION__ << ": " << endl);
// NODE STATE
// Entire netlist:
// AstClass::user1() -> bool. True if class needs to_string dumper
const VNUser1InUse m_inuser1;
// Create common contents for each module
for (AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
if (AstClass* const classp = VN_CAST(nodep, Class)) {
// NODE STATE
// Entire netlist:
// AstClass::user1() -> bool. True if class needs to_string dumper
const VNUser1InUse m_inuser1;
// Create ToString methods
makeVlToString(classp);
makeToString(classp);

View File

@ -17,9 +17,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Config.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3Config.h"
#include <map>
#include <set>
@ -33,7 +34,8 @@
// as wildcards and are accessed by a resolved name. It rebuilds a name lookup
// cache of resolved entities. Entities stored in this container need an update
// function that takes a reference of this type to join multiple entities into one.
template <typename T> class V3ConfigWildcardResolver {
template <typename T>
class V3ConfigWildcardResolver final {
using Map = std::map<const std::string, T>;
Map m_mapWildcard; // Wildcard strings to entities

View File

@ -20,9 +20,9 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3Error.h"
#include "V3FileLine.h"
#include "V3Ast.h"
//######################################################################

View File

@ -23,14 +23,15 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3Const.h"
#include "V3Ast.h"
#include "V3Width.h"
#include "V3Global.h"
#include "V3Simulate.h"
#include "V3Stats.h"
#include "V3String.h"
#include "V3UniqueNames.h"
#include "V3Width.h"
#include <algorithm>
#include <memory>
@ -79,14 +80,50 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
// bool indicating if the term is clean (0/1 value, or if the top bits might be dirty)
using ResultTerm = std::tuple<AstNode*, unsigned, bool>;
struct LeafInfo final { // Leaf node (either AstConst or AstVarRef)
class LeafInfo final { // Leaf node (either AstConst or AstVarRef)
// MEMBERS
bool m_polarity = true;
int m_lsb = 0;
int m_lsb = 0; // LSB of actually used bit of m_refp->varp()
int m_msb = 0; // MSB of actually used bit of m_refp->varp()
int m_wordIdx = -1; // -1 means AstWordSel is not used.
AstVarRef* m_refp = nullptr;
const AstConst* m_constp = nullptr;
int width() const {
public:
// CONSTRUCTORS
LeafInfo() = default;
LeafInfo(const LeafInfo& other) = default;
explicit LeafInfo(int lsb)
: m_lsb{lsb} {}
// METHODS
void setLeaf(AstVarRef* refp) {
UASSERT(!m_refp && !m_constp, "Must be called just once");
m_refp = refp;
m_msb = refp->varp()->widthMin() - 1;
}
void setLeaf(const AstConst* constp) {
UASSERT(!m_refp && !m_constp, "Must be called just once");
m_constp = constp;
m_msb = constp->widthMin() - 1;
}
void updateBitRange(const AstCCast* castp) {
m_msb = std::min(m_msb, m_lsb + castp->width() - 1);
}
void updateBitRange(const AstShiftR* shiftp) {
m_lsb += VN_AS(shiftp->rhsp(), Const)->toUInt();
}
void wordIdx(int i) { m_wordIdx = i; }
void polarity(bool p) { m_polarity = p; }
AstVarRef* refp() const { return m_refp; }
const AstConst* constp() const { return m_constp; }
int wordIdx() const { return m_wordIdx; }
bool polarity() const { return m_polarity; }
int lsb() const { return m_lsb; }
int msb() const { return std::min(m_msb, varWidth() - 1); }
int varWidth() const {
UASSERT(m_refp, "m_refp should be set");
const int width = m_refp->varp()->widthMin();
if (!m_refp->isWide()) {
@ -289,7 +326,6 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
return ResultTerm{resultp, ops, clean};
}
public:
// CONSTRUCTORS
VarInfo(ConstBitOpTreeVisitor* parent, AstVarRef* refp, int width)
: m_parentp{parent}
@ -339,32 +375,32 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
UINFO(9, "Increment to " << m_ops << " " << nodep << " called from line " << line << "\n");
}
VarInfo& getVarInfo(const LeafInfo& ref) {
UASSERT_OBJ(ref.m_refp, m_rootp, "null varref in And/Or/Xor optimization");
AstNode* nodep = ref.m_refp->varScopep();
if (!nodep) nodep = ref.m_refp->varp(); // Not scoped
UASSERT_OBJ(ref.refp(), m_rootp, "null varref in And/Or/Xor optimization");
AstNode* nodep = ref.refp()->varScopep();
if (!nodep) nodep = ref.refp()->varp(); // Not scoped
int baseIdx = nodep->user4();
if (baseIdx == 0) { // Not set yet
baseIdx = m_varInfos.size();
const int numWords
= ref.m_refp->dtypep()->isWide() ? ref.m_refp->dtypep()->widthWords() : 1;
= ref.refp()->dtypep()->isWide() ? ref.refp()->dtypep()->widthWords() : 1;
m_varInfos.resize(m_varInfos.size() + numWords);
nodep->user4(baseIdx);
}
const size_t idx = baseIdx + std::max(0, ref.m_wordIdx);
const size_t idx = baseIdx + std::max(0, ref.wordIdx());
VarInfo* varInfop = m_varInfos[idx].get();
if (!varInfop) {
varInfop = new VarInfo{this, ref.m_refp, ref.width()};
varInfop = new VarInfo{this, ref.refp(), ref.varWidth()};
m_varInfos[idx].reset(varInfop);
} else {
if (!varInfop->sameVarAs(ref.m_refp))
CONST_BITOP_SET_FAILED("different var (scope?)", ref.m_refp);
if (!varInfop->sameVarAs(ref.refp()))
CONST_BITOP_SET_FAILED("different var (scope?)", ref.refp());
}
return *varInfop;
}
// Traverse down to see AstConst or AstVarRef
LeafInfo findLeaf(AstNode* nodep, bool expectConst) {
LeafInfo info;
LeafInfo info{m_lsb};
{
VL_RESTORER(m_leafp);
m_leafp = &info;
@ -373,9 +409,9 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
bool ok = !m_failed;
if (expectConst) {
ok &= !info.m_refp && info.m_constp;
ok &= !info.refp() && info.constp();
} else {
ok &= info.m_refp && !info.m_constp;
ok &= info.refp() && !info.constp();
}
return ok ? info : LeafInfo{};
}
@ -384,7 +420,10 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
virtual void visit(AstNode* nodep) override {
CONST_BITOP_SET_FAILED("Hit unexpected op", nodep);
}
virtual void visit(AstCCast* nodep) override { iterateChildren(nodep); }
virtual void visit(AstCCast* nodep) override {
iterateChildren(nodep);
if (m_leafp) m_leafp->updateBitRange(nodep);
}
virtual void visit(AstShiftR* nodep) override {
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
AstConst* const constp = VN_CAST(nodep->rhsp(), Const);
@ -392,12 +431,14 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
m_lsb += constp->toUInt();
incrOps(nodep, __LINE__);
iterate(nodep->lhsp());
m_leafp->updateBitRange(nodep);
m_lsb -= constp->toUInt();
}
virtual void visit(AstNot* nodep) override {
CONST_BITOP_RETURN_IF(nodep->widthMin() != 1, nodep);
AstNode* lhsp = nodep->lhsp();
if (AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp();
AstCCast* const castp = VN_CAST(lhsp, CCast);
if (castp) lhsp = castp->lhsp();
CONST_BITOP_RETURN_IF(!VN_IS(lhsp, VarRef) && !VN_IS(lhsp, Xor) && !VN_IS(lhsp, RedXor)
&& !VN_IS(lhsp, ShiftR),
lhsp);
@ -406,68 +447,68 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
iterateChildren(nodep);
// Don't restore m_polarity for Xor as it counts parity of the entire tree
if (!isXorTree()) m_polarity = !m_polarity;
if (m_leafp && castp) m_leafp->updateBitRange(castp);
}
virtual void visit(AstWordSel* nodep) override {
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
AstConst* const constp = VN_CAST(nodep->bitp(), Const);
CONST_BITOP_RETURN_IF(!constp, nodep->rhsp());
UASSERT_OBJ(m_leafp->m_wordIdx == -1, nodep, "Unexpected nested WordSel");
m_leafp->m_wordIdx = constp->toSInt();
UASSERT_OBJ(m_leafp->wordIdx() == -1, nodep, "Unexpected nested WordSel");
m_leafp->wordIdx(constp->toSInt());
iterate(nodep->fromp());
}
virtual void visit(AstVarRef* nodep) override {
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
UASSERT_OBJ(!m_leafp->m_refp, nodep, m_leafp->m_refp << " is already set");
m_leafp->m_refp = nodep;
m_leafp->m_polarity = m_polarity;
m_leafp->m_lsb = m_lsb;
m_leafp->setLeaf(nodep);
m_leafp->polarity(m_polarity);
}
virtual void visit(AstConst* nodep) override {
CONST_BITOP_RETURN_IF(!m_leafp, nodep);
UASSERT_OBJ(!m_leafp->m_constp, nodep, m_leafp->m_constp << " is already set");
m_leafp->m_constp = nodep;
m_leafp->m_lsb = m_lsb;
m_leafp->setLeaf(nodep);
}
virtual void visit(AstRedXor* nodep) override {
Restorer restorer{*this};
CONST_BITOP_RETURN_IF(!VN_IS(m_rootp, Xor), nodep);
AstNode* lhsp = nodep->lhsp();
if (const AstCCast* const castp = VN_CAST(lhsp, CCast)) lhsp = castp->lhsp();
const AstCCast* const castp = VN_CAST(lhsp, CCast);
if (castp) lhsp = castp->lhsp();
if (const AstAnd* const andp = VN_CAST(lhsp, And)) { // '^(mask & leaf)'
CONST_BITOP_RETURN_IF(!andp, lhsp);
const LeafInfo& mask = findLeaf(andp->lhsp(), true);
CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp());
CONST_BITOP_RETURN_IF(!mask.constp() || mask.lsb() != 0, andp->lhsp());
const LeafInfo& ref = findLeaf(andp->rhsp(), false);
CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp());
LeafInfo ref = findLeaf(andp->rhsp(), false);
CONST_BITOP_RETURN_IF(!ref.refp(), andp->rhsp());
if (castp) ref.updateBitRange(castp);
restorer.disableRestore(); // Now all subtree succeeded
const V3Number& maskNum = mask.m_constp->num();
const V3Number& maskNum = mask.constp()->num();
incrOps(nodep, __LINE__);
incrOps(andp, __LINE__);
// Mark all bits checked in this reduction
const int maxBitIdx = std::min(ref.m_lsb + maskNum.width(), ref.width());
for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.m_lsb;
const int maxBitIdx = std::min(ref.lsb() + maskNum.width(), ref.msb() + 1);
for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.lsb();
if (maskNum.bitIs0(maskIdx)) continue;
// Set true, m_polarity takes care of the entire parity
m_bitPolarities.emplace_back(ref, true, bitIdx);
}
} else { // '^leaf'
const LeafInfo& ref = findLeaf(lhsp, false);
CONST_BITOP_RETURN_IF(!ref.m_refp, lhsp);
LeafInfo ref = findLeaf(lhsp, false);
CONST_BITOP_RETURN_IF(!ref.refp(), lhsp);
if (castp) ref.updateBitRange(castp);
restorer.disableRestore(); // Now all checks passed
incrOps(nodep, __LINE__);
// Mark all bits checked by this comparison
for (int bitIdx = ref.m_lsb; bitIdx < ref.width(); ++bitIdx) {
for (int bitIdx = ref.lsb(); bitIdx <= ref.msb(); ++bitIdx) {
m_bitPolarities.emplace_back(ref, true, bitIdx);
}
}
@ -487,12 +528,12 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
for (const bool right : {false, true}) {
Restorer restorer{*this};
LeafInfo leafInfo;
LeafInfo leafInfo{m_lsb};
m_leafp = &leafInfo;
AstNode* opp = right ? nodep->rhsp() : nodep->lhsp();
const bool origFailed = m_failed;
iterate(opp);
if (leafInfo.m_constp || m_failed) {
if (leafInfo.constp() || m_failed) {
// Revert changes in leaf
restorer.restoreNow();
// Reach past a cast then add to frozen nodes to be added to final reduction
@ -502,14 +543,14 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
continue;
}
restorer.disableRestore(); // Now all checks passed
if (leafInfo.m_refp) {
if (leafInfo.refp()) {
// The conditional on the lsb being in range is necessary for some degenerate
// case, e.g.: (IData)((QData)wide[0] >> 32), or <1-bit-var> >> 1, which is
// just zero
if (leafInfo.m_lsb < leafInfo.width()) {
m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.m_polarity,
leafInfo.m_lsb);
} else if (isAndTree() && leafInfo.m_polarity) {
if (leafInfo.lsb() <= leafInfo.msb()) {
m_bitPolarities.emplace_back(leafInfo, isXorTree() || leafInfo.polarity(),
leafInfo.lsb());
} else if (isAndTree() && leafInfo.polarity()) {
// If there is a constant 0 term in an And tree, we must include it. Fudge
// this by adding a bit with both polarities, which will simplify to zero
m_bitPolarities.emplace_back(leafInfo, true, 0);
@ -525,46 +566,59 @@ class ConstBitOpTreeVisitor final : public VNVisitor {
const AstConst* const constp = VN_CAST(lhsp, Const);
CONST_BITOP_RETURN_IF(!constp, nodep->lhsp());
const bool maskFlip = isOrTree();
const V3Number& compNum = constp->num();
auto setPolarities = [this, &compNum](const LeafInfo& ref, const V3Number* maskp) {
const bool maskFlip = isOrTree();
int constantWidth = compNum.width();
if (maskp) constantWidth = std::max(constantWidth, maskp->width());
const int maxBitIdx = std::max(ref.lsb() + constantWidth, ref.msb() + 1);
// Mark all bits checked by this comparison
for (int bitIdx = ref.lsb(); bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.lsb();
const bool mask0 = maskp && maskp->bitIs0(maskIdx);
const bool outOfRange = bitIdx > ref.msb();
if (mask0 || outOfRange) { // RHS is 0
if (compNum.bitIs1(maskIdx)) {
// LHS is 1
// And tree: 1 == 0 => always false, set v && !v
// Or tree : 1 != 0 => always true, set v || !v
m_bitPolarities.emplace_back(ref, true, 0);
m_bitPolarities.emplace_back(ref, false, 0);
break;
} else { // This bitIdx is irrelevant
continue;
}
}
const bool polarity = compNum.bitIs1(maskIdx) != maskFlip;
m_bitPolarities.emplace_back(ref, polarity, bitIdx);
}
};
if (const AstAnd* const andp = VN_CAST(nodep->rhsp(), And)) { // comp == (mask & v)
const LeafInfo& mask = findLeaf(andp->lhsp(), true);
CONST_BITOP_RETURN_IF(!mask.m_constp || mask.m_lsb != 0, andp->lhsp());
CONST_BITOP_RETURN_IF(!mask.constp() || mask.lsb() != 0, andp->lhsp());
const LeafInfo& ref = findLeaf(andp->rhsp(), false);
CONST_BITOP_RETURN_IF(!ref.m_refp, andp->rhsp());
CONST_BITOP_RETURN_IF(!ref.refp(), andp->rhsp());
restorer.disableRestore(); // Now all checks passed
const V3Number& maskNum = mask.m_constp->num();
const V3Number& maskNum = mask.constp()->num();
incrOps(nodep, __LINE__);
incrOps(andp, __LINE__);
// Mark all bits checked by this comparison
const int maxBitIdx = std::min(ref.m_lsb + maskNum.width(), ref.width());
for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.m_lsb;
if (maskNum.bitIs0(maskIdx)) continue;
const bool polarity = compNum.bitIs1(maskIdx) != maskFlip;
m_bitPolarities.emplace_back(ref, polarity, bitIdx);
}
setPolarities(ref, &maskNum);
} else { // comp == v
const LeafInfo& ref = findLeaf(nodep->rhsp(), false);
CONST_BITOP_RETURN_IF(!ref.m_refp, nodep->rhsp());
CONST_BITOP_RETURN_IF(!ref.refp(), nodep->rhsp());
restorer.disableRestore(); // Now all checks passed
incrOps(nodep, __LINE__);
// Mark all bits checked by this comparison
const int maxBitIdx = std::min(ref.m_lsb + compNum.width(), ref.width());
for (int bitIdx = ref.m_lsb; bitIdx < maxBitIdx; ++bitIdx) {
const int maskIdx = bitIdx - ref.m_lsb;
const bool polarity = compNum.bitIs1(maskIdx) != maskFlip;
m_bitPolarities.emplace_back(ref, polarity, bitIdx);
}
setPolarities(ref, nullptr);
}
} else {
CONST_BITOP_SET_FAILED("Mixture of different ops cannot be optimized", nodep);
@ -700,10 +754,11 @@ public:
if (debug() >= 9) { // LCOV_EXCL_START
cout << "Bitop tree considered: " << endl;
for (AstNode* const termp : termps) termp->dumpTree("Reduced term: ");
for (const std::pair<AstNode*, FrozenNodeInfo>& termp : visitor.m_frozenNodes)
for (const std::pair<AstNode*, FrozenNodeInfo>& termp : visitor.m_frozenNodes) {
termp.first->dumpTree("Frozen term with lsb " + std::to_string(termp.second.m_lsb)
+ " polarity " + std::to_string(termp.second.m_polarity)
+ ": ");
}
cout << "Needs flipping: " << needsFlip << endl;
cout << "Needs cleaning: " << needsCleaning << endl;
cout << "Size: " << resultOps << " input size: " << visitor.m_ops << endl;
@ -1034,7 +1089,7 @@ private:
if (orLIsRedundant && orRIsRedundant) {
nodep->replaceWith(
new AstConst(nodep->fileline(), AstConst::DtypedValue(), nodep->dtypep(), 0));
new AstConst(nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()));
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return true;
} else if (orLIsRedundant) {
@ -1710,6 +1765,7 @@ private:
lp->rhsp(lrp);
nodep->lhsp(llp);
nodep->rhsp(rlp);
nodep->dtypep(llp->dtypep()); // dtype of Biop is before shift.
VL_DO_DANGLING(rp->deleteTree(), rp);
VL_DO_DANGLING(rrp->deleteTree(), rrp);
// nodep->dumpTree(cout, " repShiftSame_new: ");
@ -2323,6 +2379,7 @@ private:
iterateChildren(nodep);
}
}
virtual void visit(AstClassOrPackageRef* nodep) override { iterateChildren(nodep); }
virtual void visit(AstPin* nodep) override { iterateChildren(nodep); }
void replaceLogEq(AstLogEq* nodep) {
@ -3528,7 +3585,11 @@ public:
}
virtual ~ConstVisitor() override {
if (m_doCpp) {
V3Stats::addStat("Optimizations, Const bit op reduction", m_statBitOpReduction);
if (m_globalPass) {
V3Stats::addStat("Optimizations, Const bit op reduction", m_statBitOpReduction);
} else {
V3Stats::addStatSum("Optimizations, Const bit op reduction", m_statBitOpReduction);
}
}
}
@ -3621,6 +3682,12 @@ AstNode* V3Const::constifyEdit(AstNode* nodep) {
return nodep;
}
AstNode* V3Const::constifyEditCpp(AstNode* nodep) {
ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ false};
nodep = visitor.mainAcceptEdit(nodep);
return nodep;
}
void V3Const::constifyAllLive(AstNetlist* nodep) {
// Only call from Verilator.cpp, as it uses user#'s
// This only pushes constants up, doesn't make any other edits

View File

@ -39,6 +39,9 @@ public:
static void constifyCpp(AstNetlist* nodep);
// Only the current node and lower
// Return new node that may have replaced nodep
static AstNode* constifyEditCpp(AstNode* nodep);
// Only the current node and lower
// Return new node that may have replaced nodep
static AstNode* constifyEdit(AstNode* nodep);
// Only the current node and lower, with special SenTree optimization
// Return new node that may have replaced nodep

View File

@ -27,9 +27,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Coverage.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <map>
#include <unordered_map>
@ -278,7 +279,7 @@ private:
// We'll do this, and make the if(...) coverinc later.
// Add signal to hold the old value
const string newvarname = string("__Vtogcov__") + nodep->shortName();
const string newvarname = std::string{"__Vtogcov__"} + nodep->shortName();
AstVar* const chgVarp
= new AstVar(nodep->fileline(), VVarType::MODULETEMP, newvarname, nodep);
chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
@ -288,9 +289,9 @@ private:
// This is necessarily an O(n^2) expansion, which is why
// we limit coverage to signals with < 256 bits.
ToggleEnt newvec(string(""),
new AstVarRef(nodep->fileline(), nodep, VAccess::READ),
new AstVarRef(nodep->fileline(), chgVarp, VAccess::WRITE));
ToggleEnt newvec{std::string{""},
new AstVarRef{nodep->fileline(), nodep, VAccess::READ},
new AstVarRef{nodep->fileline(), chgVarp, VAccess::WRITE}};
toggleVarRecurse(nodep->dtypeSkipRefp(), 0, newvec, nodep, chgVarp);
newvec.cleanup();
}
@ -313,11 +314,12 @@ private:
for (int index_docs = bdtypep->lo(); index_docs < bdtypep->hi() + 1;
++index_docs) {
const int index_code = index_docs - bdtypep->lo();
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code, 1),
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code, 1));
ToggleEnt newent{above.m_comment + std::string{"["} + cvtToStr(index_docs)
+ "]",
new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true),
index_code, 1},
new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code, 1}};
toggleVarBottom(newent, varp);
newent.cleanup();
}
@ -327,11 +329,11 @@ private:
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
const int index_code = index_docs - adtypep->lo();
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
new AstArraySel(varp->fileline(),
above.m_varRefp->cloneTree(true), index_code),
new AstArraySel(varp->fileline(),
above.m_chgRefp->cloneTree(true), index_code));
ToggleEnt newent{above.m_comment + std::string{"["} + cvtToStr(index_docs) + "]",
new AstArraySel{varp->fileline(),
above.m_varRefp->cloneTree(true), index_code},
new AstArraySel{varp->fileline(),
above.m_chgRefp->cloneTree(true), index_code}};
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth + 1, newent, varp,
chgVarp);
newent.cleanup();
@ -340,11 +342,11 @@ private:
for (int index_docs = adtypep->lo(); index_docs <= adtypep->hi(); ++index_docs) {
const AstNodeDType* const subtypep = adtypep->subDTypep()->skipRefp();
const int index_code = index_docs - adtypep->lo();
ToggleEnt newent(above.m_comment + string("[") + cvtToStr(index_docs) + "]",
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code * subtypep->width(), subtypep->width()),
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code * subtypep->width(), subtypep->width()));
ToggleEnt newent{above.m_comment + std::string{"["} + cvtToStr(index_docs) + "]",
new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true),
index_code * subtypep->width(), subtypep->width()},
new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code * subtypep->width(), subtypep->width()}};
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth + 1, newent, varp,
chgVarp);
newent.cleanup();
@ -355,11 +357,11 @@ private:
itemp = VN_AS(itemp->nextp(), MemberDType)) {
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp();
const int index_code = itemp->lsb();
ToggleEnt newent(above.m_comment + string(".") + itemp->name(),
new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true),
index_code, subtypep->width()),
new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code, subtypep->width()));
ToggleEnt newent{above.m_comment + std::string{"."} + itemp->name(),
new AstSel{varp->fileline(), above.m_varRefp->cloneTree(true),
index_code, subtypep->width()},
new AstSel{varp->fileline(), above.m_chgRefp->cloneTree(true),
index_code, subtypep->width()}};
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
newent.cleanup();
}
@ -367,9 +369,9 @@ private:
// Arbitrarily handle only the first member of the union
if (const AstMemberDType* const itemp = adtypep->membersp()) {
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefp();
ToggleEnt newent(above.m_comment + string(".") + itemp->name(),
ToggleEnt newent{above.m_comment + std::string{"."} + itemp->name(),
above.m_varRefp->cloneTree(true),
above.m_chgRefp->cloneTree(true));
above.m_chgRefp->cloneTree(true)};
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
newent.cleanup();
}

View File

@ -20,9 +20,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3CoverageJoin.h"
#include "V3DupFinder.h"
#include "V3Global.h"
#include "V3Stats.h"
#include <vector>

View File

@ -36,9 +36,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Dead.h"
#include "V3Ast.h"
#include "V3Global.h"
#include <map>
#include <vector>
@ -85,7 +86,7 @@ private:
}
if (AstNode* const subnodep = nodep->getChildDTypep()) subnodep->user1Inc();
}
void checkVarRef(AstNodeVarRef* nodep) {
void checkVarRef(AstNodeVarRef* nodep) const {
if (nodep->classOrPackagep() && m_elimCells) nodep->classOrPackagep(nullptr);
}
void checkDType(AstNodeDType* nodep) {
@ -316,7 +317,7 @@ private:
}
}
}
bool mightElimVar(AstVar* nodep) {
bool mightElimVar(AstVar* nodep) const {
if (nodep->isSigPublic()) return false; // Can't elim publics!
if (nodep->isIO() || nodep->isClassMember()) return false;
if (nodep->isTemp() && !nodep->isTrace()) return true;

View File

@ -51,9 +51,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Delayed.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3Stats.h"
#include <algorithm>

View File

@ -26,9 +26,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Depth.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3UniqueNames.h"
#include <algorithm>
@ -68,10 +69,7 @@ private:
// Put assignment before the referencing statement
AstAssign* const assp = new AstAssign{
nodep->fileline(), new AstVarRef{nodep->fileline(), varp, VAccess::WRITE}, nodep};
VNRelinker linker2;
m_stmtp->unlinkFrBack(&linker2);
assp->addNext(m_stmtp);
linker2.relink(assp);
m_stmtp->addHereThisAsNext(assp);
}
// VISITORS

View File

@ -23,10 +23,11 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3DepthBlock.h"
#include "V3Ast.h"
#include "V3EmitCBase.h"
#include "V3Global.h"
#include <algorithm>

View File

@ -25,10 +25,11 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Descope.h"
#include "V3Ast.h"
#include "V3EmitCBase.h"
#include "V3Global.h"
#include <map>
@ -248,8 +249,8 @@ private:
VL_RESTORER(m_funcp);
if (!nodep->user1()) {
// Static functions should have been moved under the corresponding AstClassPackage
UASSERT(!(nodep->isStatic() && VN_IS(m_modp, Class)),
"Static function under AstClass");
UASSERT_OBJ(!(nodep->isStatic() && VN_IS(m_modp, Class)), nodep,
"Static function under AstClass");
m_funcp = nodep;
iterateChildren(nodep);
nodep->user1(true);

View File

@ -17,10 +17,11 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3DupFinder.h"
#include "V3Ast.h"
#include "V3File.h"
#include "V3Global.h"
#include <algorithm>
#include <iomanip>

View File

@ -23,8 +23,8 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
#include "V3Error.h"
#include "V3Hasher.h"
#include <map>
@ -44,7 +44,7 @@ class V3DupFinder final : private std::multimap<V3Hash, AstNode*> {
using Super = std::multimap<V3Hash, AstNode*>;
// MEMBERS
const V3Hasher* const m_hasherp; // Pointer to owned hasher
const V3Hasher* const m_hasherp = nullptr; // Pointer to owned hasher
const V3Hasher& m_hasher; // Reference to hasher
public:
@ -52,9 +52,8 @@ public:
V3DupFinder()
: m_hasherp{new V3Hasher}
, m_hasher{*m_hasherp} {}
V3DupFinder(const V3Hasher& hasher)
: m_hasherp{nullptr}
, m_hasher{hasher} {}
explicit V3DupFinder(const V3Hasher& hasher)
: m_hasher{hasher} {}
~V3DupFinder() {
if (m_hasherp) delete m_hasherp;
}

View File

@ -18,6 +18,7 @@
#include "verilatedos.h"
#include "V3EmitCBase.h"
#include "V3Task.h"
//######################################################################

View File

@ -20,12 +20,12 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3File.h"
#include "V3Ast.h"
#include "V3File.h"
#include "V3Global.h"
#include <cstdarg>
#include <cmath>
#include <cstdarg>
//######################################################################
// Set user4p in all CFunc and Var to point to the containing AstNodeModule

View File

@ -21,8 +21,8 @@
#include "V3EmitCConstInit.h"
#include "V3File.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3Stats.h"
#include "V3String.h"
#include <algorithm>
#include <cinttypes>

View File

@ -17,9 +17,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3EmitCFunc.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3EmitCFunc.h"
#include "V3TSP.h"
#include <map>
@ -169,30 +170,8 @@ void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp,
}
}
// We only do one display at once, so can just use static state
static struct EmitDispState {
string m_format; // "%s" and text from user
std::vector<char> m_argsChar; // Format of each argument to be printed
std::vector<AstNode*> m_argsp; // Each argument to be printed
std::vector<string> m_argsFunc; // Function before each argument to be printed
EmitDispState() { clear(); }
void clear() {
m_format = "";
m_argsChar.clear();
m_argsp.clear();
m_argsFunc.clear();
}
void pushFormat(const string& fmt) { m_format += fmt; }
void pushFormat(char fmt) { m_format += fmt; }
void pushArg(char fmtChar, AstNode* nodep, const string& func) {
m_argsChar.push_back(fmtChar);
m_argsp.push_back(nodep);
m_argsFunc.push_back(func);
}
} emitDispState;
void EmitCFunc::displayEmit(AstNode* nodep, bool isScan) {
if (emitDispState.m_format == ""
if (m_emitDispState.m_format == ""
&& VN_IS(nodep, Display)) { // not fscanf etc, as they need to return value
// NOP
} else {
@ -235,12 +214,12 @@ void EmitCFunc::displayEmit(AstNode* nodep, bool isScan) {
} else {
nodep->v3fatalSrc("Unknown displayEmit node type");
}
ofp()->putsQuoted(emitDispState.m_format);
ofp()->putsQuoted(m_emitDispState.m_format);
// Arguments
for (unsigned i = 0; i < emitDispState.m_argsp.size(); i++) {
const char fmt = emitDispState.m_argsChar[i];
AstNode* const argp = emitDispState.m_argsp[i];
const string func = emitDispState.m_argsFunc[i];
for (unsigned i = 0; i < m_emitDispState.m_argsp.size(); i++) {
const char fmt = m_emitDispState.m_argsChar[i];
AstNode* const argp = m_emitDispState.m_argsp[i];
const string func = m_emitDispState.m_argsFunc[i];
if (func != "" || argp) {
puts(",");
ofp()->indentInc();
@ -265,7 +244,7 @@ void EmitCFunc::displayEmit(AstNode* nodep, bool isScan) {
puts(" ");
}
// Prep for next
emitDispState.clear();
m_emitDispState.clear();
}
}
@ -306,16 +285,16 @@ void EmitCFunc::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const
} else {
pfmt = string("%") + vfmt + fmtLetter;
}
emitDispState.pushFormat(pfmt);
m_emitDispState.pushFormat(pfmt);
if (!ignore) {
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");
m_emitDispState.pushArg(' ', nullptr, "-1");
} else {
emitDispState.pushArg(' ', nullptr, cvtToStr(argp->widthMin()));
m_emitDispState.pushArg(' ', nullptr, cvtToStr(argp->widthMin()));
}
emitDispState.pushArg(fmtLetter, argp, "");
m_emitDispState.pushArg(fmtLetter, argp, "");
if (fmtLetter == 't' || fmtLetter == '^') {
const AstSFormatF* fmtp = nullptr;
if (const AstDisplay* const nodep = VN_CAST(dispp, Display)) {
@ -328,10 +307,10 @@ void EmitCFunc::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const
UASSERT_OBJ(fmtp, dispp,
"Use of %t must be under AstDisplay, AstSFormat, or AstSFormatF");
UASSERT_OBJ(!fmtp->timeunit().isNone(), fmtp, "timenunit must be set");
emitDispState.pushArg(' ', nullptr, cvtToStr((int)fmtp->timeunit().powerOfTen()));
m_emitDispState.pushArg(' ', nullptr, cvtToStr((int)fmtp->timeunit().powerOfTen()));
}
} else {
emitDispState.pushArg(fmtLetter, nullptr, "");
m_emitDispState.pushArg(fmtLetter, nullptr, "");
}
}
@ -341,7 +320,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
// Convert Verilog display to C printf formats
// "%0t" becomes "%d"
emitDispState.clear();
m_emitDispState.clear();
string vfmt;
string::const_iterator pos = vformat.begin();
bool inPct = false;
@ -353,7 +332,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
ignore = false;
vfmt = "";
} else if (!inPct) { // Normal text
emitDispState.pushFormat(*pos);
m_emitDispState.pushFormat(*pos);
} else { // Format character
inPct = false;
switch (tolower(pos[0])) {
@ -374,7 +353,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
inPct = true; // Get more digits
break;
case '%':
emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the %
m_emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the %
break;
case '*':
vfmt += pos[0];
@ -410,17 +389,17 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
UASSERT_OBJ(scopenamep, nodep, "Display with %m but no AstScopeName");
const string suffix = scopenamep->scopePrettySymName();
if (suffix == "") {
emitDispState.pushFormat("%S");
m_emitDispState.pushFormat("%S");
} else {
emitDispState.pushFormat("%N"); // Add a . when needed
m_emitDispState.pushFormat("%N"); // Add a . when needed
}
emitDispState.pushArg(' ', nullptr, "vlSymsp->name()");
emitDispState.pushFormat(suffix);
m_emitDispState.pushArg(' ', nullptr, "vlSymsp->name()");
m_emitDispState.pushFormat(suffix);
break;
}
case 'l': {
// Better than not compiling
emitDispState.pushFormat("----");
m_emitDispState.pushFormat("----");
break;
}
default:
@ -476,9 +455,9 @@ void EmitCFunc::emitDereference(const string& pointer) {
void EmitCFunc::emitCvtPackStr(AstNode* nodep) {
if (const AstConst* const constp = VN_CAST(nodep, Const)) {
putbs("std::string(");
putbs("std::string{");
putsQuoted(constp->num().toString());
puts(")");
puts("}");
} else {
putbs("VL_CVT_PACK_STR_N");
emitIQW(nodep);
@ -507,9 +486,9 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string
if (nodep->num().isFourState()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 4-state numbers in this context");
} else if (nodep->num().isString()) {
putbs("std::string(");
putbs("std::string{");
putsQuoted(nodep->num().toString());
puts(")");
puts("}");
} else if (nodep->isWide()) {
int upWidth = nodep->num().widthMin();
int chunks = 0;
@ -622,7 +601,18 @@ void EmitCFunc::emitVarReset(AstVar* varp) {
// If an ARRAYINIT we initialize it using an initial block similar to a signal
// puts("// parameter "+varp->nameProtect()+" = "+varp->valuep()->name()+"\n");
} else if (const AstInitArray* const initarp = VN_CAST(varp->valuep(), InitArray)) {
if (AstAssocArrayDType* const adtypep = VN_CAST(dtypep, AssocArrayDType)) {
if (VN_IS(dtypep, AssocArrayDType)) {
if (initarp->defaultp()) {
emitSetVarConstant(varNameProtected + ".atDefault()",
VN_AS(initarp->defaultp(), Const));
}
const auto& mapr = initarp->map();
for (const auto& itr : mapr) {
AstNode* const valuep = itr.second->valuep();
emitSetVarConstant(varNameProtected + ".at(" + cvtToStr(itr.first) + ")",
VN_AS(valuep, Const));
}
} else if (VN_IS(dtypep, WildcardArrayDType)) {
if (initarp->defaultp()) {
emitSetVarConstant(varNameProtected + ".atDefault()",
VN_AS(initarp->defaultp(), Const));
@ -664,6 +654,11 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
suffix + ".atDefault()" + cvtarray);
} else if (AstWildcardArrayDType* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
// Access std::array as C array
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
suffix + ".atDefault()" + cvtarray);
} else if (VN_IS(dtypep, ClassRefDType)) {
return ""; // Constructor does it
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {

View File

@ -20,8 +20,8 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitCConstInit.h"
#include "V3Global.h"
#include <algorithm>
#include <map>
@ -122,6 +122,28 @@ private:
std::vector<AstChangeDet*> m_blkChangeDetVec; // All encountered changes in block
bool m_emitConstInit = false; // Emitting constant initializer
// State associated with processing $display style string formatting
struct EmitDispState {
string m_format; // "%s" and text from user
std::vector<char> m_argsChar; // Format of each argument to be printed
std::vector<AstNode*> m_argsp; // Each argument to be printed
std::vector<string> m_argsFunc; // Function before each argument to be printed
EmitDispState() { clear(); }
void clear() {
m_format = "";
m_argsChar.clear();
m_argsp.clear();
m_argsFunc.clear();
}
void pushFormat(const string& fmt) { m_format += fmt; }
void pushFormat(char fmt) { m_format += fmt; }
void pushArg(char fmtChar, AstNode* nodep, const string& func) {
m_argsChar.push_back(fmtChar);
m_argsp.push_back(nodep);
m_argsFunc.push_back(func);
}
} m_emitDispState;
protected:
EmitCLazyDecls m_lazyDecls; // Visitor for emitting lazy declarations
bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
@ -357,6 +379,14 @@ public:
}
puts(")");
}
virtual void visit(AstWildcardSel* nodep) override {
iterateAndNextNull(nodep->fromp());
putbs(".at(");
AstWildcardArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), WildcardArrayDType);
UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type");
iterateAndNextNull(nodep->bitp());
puts(")");
}
virtual void visit(AstCCall* nodep) override {
const AstCFunc* const funcp = nodep->funcp();
const AstNodeModule* const funcModp = EmitCParentModule::get(funcp);
@ -581,7 +611,7 @@ public:
}
virtual void visit(AstTestPlusArgs* nodep) override {
puts("VL_TESTPLUSARGS_I(");
putsQuoted(nodep->text());
emitCvtPackStr(nodep->searchp());
puts(")");
}
virtual void visit(AstFError* nodep) override {
@ -1167,6 +1197,24 @@ public:
iterateAndNextNull(nodep->valuep());
puts(")");
}
virtual void visit(AstConsWildcard* nodep) override {
putbs(nodep->dtypep()->cType("", false, false));
puts("()");
if (nodep->defaultp()) {
putbs(".setDefault(");
iterateAndNextNull(nodep->defaultp());
puts(")");
}
}
virtual void visit(AstSetWildcard* nodep) override {
iterateAndNextNull(nodep->lhsp());
putbs(".set(");
iterateAndNextNull(nodep->keyp());
puts(", ");
putbs("");
iterateAndNextNull(nodep->valuep());
puts(")");
}
virtual void visit(AstConsDynArray* nodep) override {
putbs(nodep->dtypep()->cType("", false, false));
if (!nodep->lhsp()) {

View File

@ -17,9 +17,9 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitC.h"
#include "V3EmitCConstInit.h"
#include "V3Global.h"
#include <algorithm>
#include <vector>
@ -236,10 +236,11 @@ class EmitCHeader final : public EmitCConstInit {
void emitAll(const AstNodeModule* modp) {
// Include files required by this AstNodeModule
if (const AstClass* const classp = VN_CAST(modp, Class)) {
if (classp->extendsp())
if (classp->extendsp()) {
puts("#include \""
+ prefixNameProtect(classp->extendsp()->classp()->classOrPackagep())
+ ".h\"\n");
}
}
emitModCUse(modp, VUseType::INT_INCLUDE);
@ -251,15 +252,15 @@ class EmitCHeader final : public EmitCConstInit {
emitTextSection(modp, VNType::atScHdr);
// Open class body {{{
puts("\nclass ");
puts(prefixNameProtect(modp));
if (const AstClass* const classp = VN_CAST(modp, Class)) {
puts("class ");
puts(prefixNameProtect(modp));
if (classp->extendsp()) {
puts(" : public ");
puts(prefixNameProtect(classp->extendsp()->classp()));
}
} else {
puts("VL_MODULE(" + prefixNameProtect(modp) + ")");
puts(" final : public VerilatedModule");
}
puts(" {\n");
ofp()->resetPrivate();

View File

@ -17,10 +17,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitC.h"
#include "V3Ast.h"
#include "V3EmitC.h"
#include "V3EmitCFunc.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3UniqueNames.h"
@ -314,8 +314,8 @@ class EmitCImp final : EmitCFunc {
puts(" \"lineno\",lineno,");
puts(" \"column\",column,\n");
// Need to move hier into scopes and back out if do this
// puts( "\"hier\",std::string(vlSymsp->name())+hierp,");
puts("\"hier\",std::string(name())+hierp,");
// puts( "\"hier\",std::string{vlSymsp->name()} + hierp,");
puts("\"hier\",std::string{name()} + hierp,");
puts(" \"page\",pagep,");
puts(" \"comment\",commentp,");
puts(" (linescovp[0] ? \"linescov\" : \"\"), linescovp);\n");

View File

@ -17,9 +17,9 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitC.h"
#include "V3EmitCBase.h"
#include "V3Global.h"
#include "V3Stats.h"
#include <map>

View File

@ -17,10 +17,11 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitCMain.h"
#include "V3EmitC.h"
#include "V3EmitCBase.h"
#include "V3EmitCMain.h"
#include "V3Global.h"
#include <map>
@ -61,6 +62,7 @@ private:
puts("int main(int argc, char** argv, char**) {\n");
puts("// Setup context, defaults, and parse command line\n");
puts("Verilated::debug(0);\n");
if (v3Global.opt.trace()) puts("Verilated::traceEverOn(true);\n");
puts("const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};\n");
puts("contextp->commandArgs(argc, argv);\n");
puts("\n");

View File

@ -17,11 +17,12 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Os.h"
#include "V3EmitCMake.h"
#include "V3EmitCBase.h"
#include "V3Global.h"
#include "V3HierBlock.h"
#include "V3Os.h"
#include <memory>
@ -36,7 +37,8 @@ class CMakeEmitter final {
// STATIC FUNCTIONS
// Concatenate all strings in 'strs' with ' ' between them.
template <typename List> static string cmake_list(const List& strs) {
template <typename List>
static string cmake_list(const List& strs) {
string s;
if (strs.begin() != strs.end()) {
s.append("\"");
@ -113,12 +115,6 @@ 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 --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().vcd()) ? "1" : "0");
@ -173,7 +169,7 @@ class CMakeEmitter final {
+ ".cpp");
}
}
if (v3Global.opt.mtasks()) {
if (v3Global.opt.threads()) {
global.emplace_back("${VERILATOR_ROOT}/include/verilated_threads.cpp");
}
if (v3Global.opt.usesProfiler()) {
@ -214,19 +210,15 @@ class CMakeEmitter final {
*of << "target_link_libraries(${TOP_TARGET_NAME} PRIVATE " << prefix << ")\n";
if (!children.empty()) {
*of << "target_link_libraries(" << prefix << " INTERFACE";
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
child != children.end(); ++child) {
*of << " " << (*child)->hierPrefix();
}
for (const auto& childr : children) { *of << " " << (childr)->hierPrefix(); }
*of << ")\n";
}
*of << "verilate(" << prefix << " PREFIX " << prefix << " TOP_MODULE "
<< hblockp->modp()->name() << " DIRECTORY "
<< deslash(v3Global.opt.makeDir() + "/" + prefix) << " SOURCES ";
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
child != children.end(); ++child) {
for (const auto& childr : children) {
*of << " "
<< deslash(v3Global.opt.makeDir() + "/" + (*child)->hierWrapper(true));
<< deslash(v3Global.opt.makeDir() + "/" + childr->hierWrapper(true));
}
*of << " ";
const string vFile = hblockp->vFileIfNecessary();

View File

@ -17,9 +17,9 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitC.h"
#include "V3EmitCFunc.h"
#include "V3Global.h"
#include "V3UniqueNames.h"
#include <algorithm>
@ -89,11 +89,12 @@ class EmitCModel final : public EmitCFunc {
puts("\n");
puts("// This class is the main interface to the Verilated model\n");
puts("class " + topClassName() + " VL_NOT_FINAL : ");
if (optSystemC()) {
puts("SC_MODULE(" + topClassName() + ") {\n");
} else {
puts("class " + topClassName() + " VL_NOT_FINAL {\n");
// SC_MODULE, but with multiple-inheritance of VerilatedModel
puts("public ::sc_core::sc_module, ");
}
puts("public VerilatedModel {\n");
ofp()->resetPrivate();
ofp()->putsPrivate(true); // private:
@ -194,9 +195,6 @@ class EmitCModel final : public EmitCFunc {
}
}
puts("/// Return current simulation context for this model.\n");
puts("/// Used to get to e.g. simulation time via contextp()->time()\n");
puts("VerilatedContext* contextp() const;\n");
if (!optSystemC()) {
puts("/// Retrieve name of this model instance (as passed to constructor).\n");
puts("const char* name() const;\n");
@ -221,6 +219,14 @@ class EmitCModel final : public EmitCFunc {
+ topClassName() + "& rhs);\n");
}
puts("\n// Abstract methods from VerilatedModel\n");
puts("const char* hierName() const override final;\n");
puts("const char* modelName() const override final;\n");
puts("unsigned threads() const override final;\n");
if (v3Global.opt.trace()) {
puts("std::unique_ptr<VerilatedTraceConfig> traceConfig() const override final;\n");
}
puts("} VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES);\n");
ofp()->putsEndGuard();
@ -235,10 +241,12 @@ class EmitCModel final : public EmitCFunc {
puts(topClassName() + "::" + topClassName());
if (optSystemC()) {
puts("(sc_module_name /* unused */)\n");
puts(" : vlSymsp{new " + symClassName() + "(nullptr, name(), this)}\n");
puts(" : VerilatedModel{*Verilated::threadContextp()}\n");
puts(" , vlSymsp{new " + symClassName() + "(contextp(), name(), this)}\n");
} else {
puts(+"(VerilatedContext* _vcontextp__, const char* _vcname__)\n");
puts(" : vlSymsp{new " + symClassName() + "(_vcontextp__, _vcname__, this)}\n");
puts(" : VerilatedModel{*_vcontextp__}\n");
puts(" , vlSymsp{new " + symClassName() + "(contextp(), _vcname__, this)}\n");
}
// Set up IO references
@ -263,6 +271,8 @@ class EmitCModel final : public EmitCFunc {
puts(" , rootp{&(vlSymsp->TOP)}\n");
puts("{\n");
puts("// Register model with the context\n");
puts("contextp()->addModel(this);\n");
if (optSystemC()) {
// Create sensitivity list for when to evaluate the model.
@ -301,7 +311,7 @@ class EmitCModel final : public EmitCFunc {
if (!optSystemC()) {
puts("\n");
puts(topClassName() + "::" + topClassName() + "(const char* _vcname__)\n");
puts(" : " + topClassName() + "(nullptr, _vcname__)\n{\n}\n");
puts(" : " + topClassName() + "(Verilated::threadContextp(), _vcname__)\n{\n}\n");
}
}
@ -428,7 +438,7 @@ class EmitCModel final : public EmitCFunc {
}
if (v3Global.opt.profExec()) {
puts("vlSymsp->__Vm_executionProfiler.configure(*(vlSymsp->_vm_contextp__));\n");
puts("vlSymsp->__Vm_executionProfilerp->configure();\n");
puts("VL_EXEC_TRACE_ADD_RECORD(vlSymsp).evalBegin();\n");
}
@ -460,10 +470,6 @@ class EmitCModel final : public EmitCFunc {
}
putSectionDelimiter("Utilities");
// ::contextp
puts("\nVerilatedContext* " + topClassName() + "::contextp() const {\n");
puts(/**/ "return vlSymsp->_vm_contextp__;\n");
puts("}\n");
if (!optSystemC()) {
// ::name
@ -477,6 +483,24 @@ class EmitCModel final : public EmitCFunc {
puts("\nVL_ATTR_COLD void " + topClassName() + "::final() {\n");
puts(/**/ topModNameProtected + "__" + protect("_final") + "(&(vlSymsp->TOP));\n");
puts("}\n");
putSectionDelimiter("Implementations of abstract methods from VerilatedModel\n");
puts("const char* " + topClassName() + "::hierName() const { return vlSymsp->name(); }\n");
puts("const char* " + topClassName() + "::modelName() const { return \"" + topClassName()
+ "\"; }\n");
puts("unsigned " + topClassName() + "::threads() const { return "
+ cvtToStr(std::max(1, v3Global.opt.threads())) + "; }\n");
if (v3Global.opt.trace()) {
puts("std::unique_ptr<VerilatedTraceConfig> " + topClassName()
+ "::traceConfig() const {\n");
puts("return std::unique_ptr<VerilatedTraceConfig>{new VerilatedTraceConfig{");
puts(v3Global.opt.useTraceParallel() ? "true" : "false");
puts(v3Global.opt.useTraceOffload() ? ", true" : ", false");
puts(v3Global.opt.useFstWriterThread() ? ", true" : ", false");
puts("}};\n");
puts("};\n");
}
}
void emitTraceMethods(AstNodeModule* modp) {
@ -529,6 +553,7 @@ class EmitCModel final : public EmitCFunc {
puts(/**/ "}");
}
puts(/**/ "if (false && levels && options) {} // Prevent unused\n");
puts(/**/ "tfp->spTrace()->addModel(this);\n");
puts(/**/ "tfp->spTrace()->addInitCb(&" + protect("trace_init") + ", &(vlSymsp->TOP));\n");
puts(/**/ topModNameProtected + "__" + protect("trace_register")
+ "(&(vlSymsp->TOP), tfp->spTrace());\n");
@ -664,5 +689,5 @@ public:
void V3EmitC::emitcModel() {
UINFO(2, __FUNCTION__ << ": " << endl);
{ EmitCModel emit(v3Global.rootp()); }
{ EmitCModel{v3Global.rootp()}; }
}

View File

@ -17,9 +17,9 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitC.h"
#include "V3EmitCBase.h"
#include "V3Global.h"
#include "V3LanguageWords.h"
#include "V3PartitionGraph.h"
@ -445,17 +445,17 @@ 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.profExec()) {
puts("\n// EXECUTION PROFILING\n");
puts("VlExecutionProfiler* const __Vm_executionProfilerp;\n");
}
puts("\n// MODULE INSTANCE STATE\n");
for (const auto& i : m_scopes) {
const AstScope* const scopep = i.first;
@ -673,12 +673,39 @@ void EmitCSyms::emitSymImp() {
puts("_vm_pgoProfiler.write(\"" + topClassName()
+ "\", _vm_contextp__->profVltFilename());\n");
}
if (v3Global.opt.mtasks()) puts("delete __Vm_threadPoolp;\n");
puts("}\n\n");
puts("}\n");
if (v3Global.needTraceDumper()) {
if (!optSystemC()) {
puts("\nvoid " + symClassName() + "::_traceDump() {\n");
// Caller checked for __Vm_dumperp non-nullptr
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
puts("__Vm_dumperp->dump(VL_TIME_Q());\n");
puts("}\n");
}
puts("\nvoid " + symClassName() + "::_traceDumpOpen() {\n");
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
puts("if (VL_UNLIKELY(!__Vm_dumperp)) {\n");
puts("__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n");
puts("__Vm_modelp->trace(__Vm_dumperp, 0, 0);\n");
puts("std::string dumpfile = _vm_contextp__->dumpfileCheck();\n");
puts("__Vm_dumperp->open(dumpfile.c_str());\n");
puts("__Vm_dumping = true;\n");
puts("}\n");
puts("}\n");
puts("\nvoid " + symClassName() + "::_traceDumpClose() {\n");
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
puts("__Vm_dumping = false;\n");
puts("VL_DO_CLEAR(delete __Vm_dumperp, __Vm_dumperp = nullptr);\n");
puts("}\n");
}
puts("\n");
// Constructor
puts(symClassName() + "::" + symClassName() + "(VerilatedContext* contextp, const char* namep,"
+ topClassName() + "* modelp)\n");
puts(symClassName() + "::" + symClassName()
+ "(VerilatedContext* contextp, const char* namep, " + topClassName() + "* modelp)\n");
puts(" : VerilatedSyms{contextp}\n");
puts(" // Setup internal state of the Syms class\n");
puts(" , __Vm_modelp{modelp}\n");
@ -705,9 +732,13 @@ void EmitCSyms::emitSymImp() {
// Note we create N-1 threads in the thread pool. The thread
// 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) + ", "
+ (v3Global.opt.profExec() ? "&__Vm_executionProfiler" : "nullptr") + "}}\n");
puts(" , __Vm_threadPoolp{static_cast<VlThreadPool*>(contextp->threadPoolp())}\n");
}
if (v3Global.opt.profExec()) {
puts(" , "
"__Vm_executionProfilerp{static_cast<VlExecutionProfiler*>(contextp->"
"enableExecutionProfiler(&VlExecutionProfiler::construct))}\n");
}
puts(" // Setup module instances\n");
@ -771,7 +802,7 @@ void EmitCSyms::emitSymImp() {
puts(protectIf(aboveScopep->nameDotless(), aboveScopep->protect()));
}
puts(".");
puts(protName.substr(protName.rfind(".") + 1));
puts(protName.substr(protName.rfind('.') + 1));
puts(" = &");
puts(protectIf(scopep->nameDotless(), scopep->protect()) + ";\n");
++m_numStmts;
@ -920,35 +951,9 @@ void EmitCSyms::emitSymImp() {
m_ofpBase->puts("}\n");
if (v3Global.needTraceDumper()) {
if (!optSystemC()) {
puts("\nvoid " + symClassName() + "::_traceDump() {\n");
// Caller checked for __Vm_dumperp non-nullptr
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
puts("__Vm_dumperp->dump(VL_TIME_Q());\n");
puts("}\n");
}
puts("\nvoid " + symClassName() + "::_traceDumpOpen() {\n");
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
puts("if (VL_UNLIKELY(!__Vm_dumperp)) {\n");
puts("__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n");
puts("__Vm_modelp->trace(__Vm_dumperp, 0, 0);\n");
puts("std::string dumpfile = _vm_contextp__->dumpfileCheck();\n");
puts("__Vm_dumperp->open(dumpfile.c_str());\n");
puts("__Vm_dumping = true;\n");
puts("}\n");
puts("}\n");
puts("\nvoid " + symClassName() + "::_traceDumpClose() {\n");
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
puts("__Vm_dumping = false;\n");
puts("VL_DO_CLEAR(delete __Vm_dumperp, __Vm_dumperp = nullptr);\n");
puts("}\n");
}
closeSplit();
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
m_ofp = nullptr;
VL_DO_CLEAR(delete m_ofpBase, m_ofpBase = nullptr);
}
//######################################################################

View File

@ -17,11 +17,12 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3EmitMk.h"
#include "V3EmitCBase.h"
#include "V3Global.h"
#include "V3HierBlock.h"
#include "V3Os.h"
#include "V3EmitMk.h"
#include "V3EmitCBase.h"
//######################################################################
// Emit statements and math operators
@ -73,15 +74,6 @@ public:
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 --threads/--trace-thread)\n");
of.puts("VM_TRACE_THREADS = ");
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 = ");
of.puts(v3Global.opt.traceThreads() && v3Global.opt.traceFormat().fst() ? "1" : "0");
of.puts("\n");
of.puts("\n### Object file lists...\n");
for (int support = 0; support < 3; ++support) {
@ -116,7 +108,7 @@ public:
putMakeClassEntry(of, v3Global.opt.traceSourceLang() + ".cpp");
}
}
if (v3Global.opt.mtasks()) putMakeClassEntry(of, "verilated_threads.cpp");
if (v3Global.opt.threads()) putMakeClassEntry(of, "verilated_threads.cpp");
if (v3Global.opt.usesProfiler()) {
putMakeClassEntry(of, "verilated_profiler.cpp");
}
@ -197,7 +189,6 @@ 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");
@ -370,13 +361,13 @@ class EmitMkHierVerilation final {
// Rules to process hierarchical blocks
of.puts("\n# Verilate hierarchical blocks\n");
for (V3HierBlockPlan::const_iterator it = m_planp->begin(); it != m_planp->end(); ++it) {
const string prefix = it->second->hierPrefix();
const string argsFile = it->second->commandArgsFileName(false);
of.puts(it->second->hierGenerated(true));
for (const V3HierBlock* const blockp : m_planp->hierBlocksSorted()) {
const string prefix = blockp->hierPrefix();
const string argsFile = blockp->commandArgsFileName(false);
of.puts(blockp->hierGenerated(true));
of.puts(": $(VM_HIER_INPUT_FILES) $(VM_HIER_VERILOG_LIBS) ");
of.puts(V3Os::filenameNonDir(argsFile) + " ");
const V3HierBlock::HierBlockSet& children = it->second->children();
const V3HierBlock::HierBlockSet& children = blockp->children();
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
child != children.end(); ++child) {
of.puts((*child)->hierWrapper(true) + " ");
@ -385,16 +376,16 @@ class EmitMkHierVerilation final {
emitLaunchVerilator(of, argsFile);
// Rule to build lib*.a
of.puts(it->second->hierLib(true));
of.puts(blockp->hierLib(true));
of.puts(": ");
of.puts(it->second->hierMk(true));
of.puts(blockp->hierMk(true));
of.puts(" ");
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
child != children.end(); ++child) {
of.puts((*child)->hierLib(true));
of.puts(" ");
}
of.puts("\n\t$(MAKE) -f " + it->second->hierMk(false) + " -C " + prefix);
of.puts("\n\t$(MAKE) -f " + blockp->hierMk(false) + " -C " + prefix);
of.puts(" VM_PREFIX=" + prefix);
of.puts("\n\n");
}

View File

@ -17,9 +17,10 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3EmitV.h"
#include "V3EmitCBase.h"
#include "V3Global.h"
#include <algorithm>
#include <map>
@ -207,7 +208,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
putqs(nodep, "end\n");
}
virtual void visit(AstComment* nodep) override {
puts(string("// ") + nodep->name() + "\n");
puts(std::string{"// "} + nodep->name() + "\n");
iterateChildrenConst(nodep);
}
virtual void visit(AstContinue*) override {
@ -493,6 +494,11 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp(), nodep->rhsp(),
nodep->thsp());
}
virtual void visit(AstMemberSel* nodep) override {
iterate(nodep->fromp());
puts(".");
puts(nodep->prettyName());
}
virtual void visit(AstAttrOf* nodep) override {
putfs(nodep, "$_ATTROF(");
iterateAndNextConstNull(nodep->fromp());
@ -700,7 +706,7 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
virtual void visit(AstCell*) override {} // Handled outside the Visit class
// Default
virtual void visit(AstNode* nodep) override {
puts(string("\n???? // ") + nodep->prettyTypeName() + "\n");
puts(std::string{"\n???? // "} + nodep->prettyTypeName() + "\n");
iterateChildrenConst(nodep);
// Not v3fatalSrc so we keep processing
if (!m_suppressUnknown) {
@ -733,8 +739,8 @@ class EmitVFileVisitor final : public EmitVBaseVisitor {
public:
EmitVFileVisitor(AstNode* nodep, V3OutFile* ofp, bool trackText, bool suppressUnknown)
: EmitVBaseVisitor{suppressUnknown, nullptr} {
m_ofp = ofp;
: EmitVBaseVisitor{suppressUnknown, nullptr}
, m_ofp{ofp} {
m_trackText = trackText;
iterate(nodep);
}
@ -785,7 +791,7 @@ class EmitVPrefixedFormatter final : public V3OutFormatter {
m_os << " ";
m_os << m_prefix;
}
m_column++;
++m_column;
m_os << chr;
}
}
@ -871,6 +877,6 @@ void V3EmitV::emitvFiles() {
void V3EmitV::debugEmitV(const string& filename) {
UINFO(2, __FUNCTION__ << ": " << endl);
V3OutVFile of(filename);
V3OutVFile of{filename};
{ EmitVFileVisitor{v3Global.rootp(), &of, true, true}; }
}

View File

@ -17,10 +17,11 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3EmitXml.h"
#include "V3EmitCBase.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3EmitXml.h"
#include "V3EmitCBase.h"
#include <map>
#include <vector>

View File

@ -148,9 +148,9 @@ string V3Error::msgPrefix() {
} else if (code == V3ErrorCode::EC_ERROR) {
return "%Error: ";
} else if (isError(code, supp)) {
return "%Error-" + string(code.ascii()) + ": ";
return "%Error-" + std::string{code.ascii()} + ": ";
} else {
return "%Warning-" + string(code.ascii()) + ": ";
return "%Warning-" + std::string{code.ascii()} + ": ";
}
}
@ -167,7 +167,7 @@ void V3Error::vlAbortOrExit() {
}
void V3Error::vlAbort() {
VL_GCOV_FLUSH();
VL_GCOV_DUMP();
std::abort();
}
@ -176,7 +176,7 @@ void V3Error::vlAbort() {
void V3Error::suppressThisWarning() {
#ifndef V3ERROR_NO_GLOBAL_
V3Stats::addStatSum(string("Warnings, Suppressed ") + s_errorCode.ascii(), 1);
V3Stats::addStatSum(std::string{"Warnings, Suppressed "} + s_errorCode.ascii(), 1);
#endif
s_errorSuppressed = true;
}

View File

@ -28,10 +28,12 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Expand.h"
#include "V3Stats.h"
#include "V3Ast.h"
#include "V3Const.h"
#include "V3Global.h"
#include "V3Stats.h"
#include <algorithm>
@ -82,10 +84,7 @@ private:
static void insertBefore(AstNode* placep, AstNode* newp) {
newp->user1(1); // Already processed, don't need to re-iterate
VNRelinker linker;
placep->unlinkFrBack(&linker);
newp->addNext(placep);
linker.relink(newp);
placep->addHereThisAsNext(newp);
}
static void replaceWithDelete(AstNode* nodep, AstNode* newp) {
newp->user1(1); // Already processed, don't need to re-iterate
@ -163,6 +162,7 @@ private:
new AstShiftL{fl, llowp,
new AstConst{fl, static_cast<uint32_t>(loffset)},
VL_EDATASIZE}}};
newp = V3Const::constifyEditCpp(newp);
} else {
newp = llowp;
}
@ -523,8 +523,9 @@ private:
cleanmask.setMask(VL_BITBIT_E(destp->widthMin()));
newp = new AstAnd{lfl, newp, new AstConst{lfl, cleanmask}};
}
addWordAssign(nodep, w, destp, new AstOr{lfl, oldvalp, newp});
AstNode* const orp
= V3Const::constifyEditCpp(new AstOr{lfl, oldvalp, newp});
addWordAssign(nodep, w, destp, orp);
}
}
VL_DO_DANGLING(rhsp->deleteTree(), rhsp);
@ -544,7 +545,8 @@ private:
AstNode* const shifted = new AstShiftL{
lfl, rhsp, new AstConst{lfl, static_cast<uint32_t>(lsb)}, destp->width()};
AstNode* const cleaned = new AstAnd{lfl, shifted, new AstConst{lfl, cleanmask}};
AstNode* const newp = new AstAssign{nfl, destp, new AstOr{lfl, oldvalp, cleaned}};
AstNode* const orp = V3Const::constifyEditCpp(new AstOr{lfl, oldvalp, cleaned});
AstNode* newp = new AstAssign{nfl, destp, orp};
insertBefore(nodep, newp);
}
return true;

View File

@ -17,11 +17,12 @@
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3File.h"
#include "V3Ast.h"
#include "V3Global.h"
#include "V3Os.h"
#include "V3String.h"
#include "V3Ast.h"
#include <cerrno>
#include <cstdarg>
@ -29,6 +30,7 @@
#include <iomanip>
#include <map>
#include <memory>
#include <sys/stat.h>
#include <sys/types.h>
@ -489,8 +491,8 @@ private:
#ifdef INFILTER_PIPE
int fd_stdin[2];
int fd_stdout[2];
static const int P_RD = 0;
static const int P_WR = 1;
constexpr int P_RD = 0;
constexpr int P_WR = 1;
if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) {
v3fatal("--pipe-filter: Can't pipe: " << strerror(errno));
@ -623,17 +625,9 @@ V3OutFormatter::V3OutFormatter(const string& filename, V3OutFormatter::Language
//----------------------------------------------------------------------
string V3OutFormatter::indentSpaces(int num) {
// Indent the specified number of spaces. Use spaces.
static char str[MAXSPACE + 20];
char* cp = str;
if (num > MAXSPACE) num = MAXSPACE;
while (num > 0) {
*cp++ = ' ';
--num;
}
*cp++ = '\0';
string st{str}; // No const, move optimization
return st;
// Indent the specified number of spaces.
if (num <= 0) return std::string{};
return std::string(std::min<size_t>(num, MAXSPACE), ' ');
}
bool V3OutFormatter::tokenMatch(const char* cp, const char* cmp) {
@ -660,11 +654,15 @@ bool V3OutFormatter::tokenEnd(const char* cp) {
|| tokenMatch(cp, "endtask"));
}
bool V3OutFormatter::tokenNotStart(const char* cp) {
return (tokenMatch(cp, "export") || tokenMatch(cp, "import"));
}
int V3OutFormatter::endLevels(const char* strg) {
int levels = m_indentLevel;
{
const char* cp = strg;
while (isspace(*cp)) cp++;
while (isspace(*cp)) ++cp;
switch (*cp) {
case '\n': // Newlines.. No need for whitespace before it
return 0;
@ -674,12 +672,12 @@ int V3OutFormatter::endLevels(const char* strg) {
{
// label/public/private: Deindent by 2 spaces
const char* mp = cp;
for (; isalnum(*mp); mp++) {}
for (; isalnum(*mp); ++mp) {}
if (mp[0] == ':' && mp[1] != ':') return (levels - m_blockIndent / 2);
}
}
// We want "} else {" to be one level to the left of normal
for (const char* cp = strg; *cp; cp++) {
for (const char* cp = strg; *cp; ++cp) {
switch (*cp) {
case '}':
case ')': levels -= m_blockIndent; break;
@ -708,17 +706,19 @@ void V3OutFormatter::puts(const char* strg) {
putsNoTracking(indentSpaces(endLevels(strg)));
m_prependIndent = false;
}
bool notstart = false;
bool wordstart = true;
bool equalsForBracket = false; // Looking for "= {"
for (const char* cp = strg; *cp; cp++) {
for (const char* cp = strg; *cp; ++cp) {
putcNoTracking(*cp);
if (isalpha(*cp)) {
if (wordstart && m_lang == LA_VERILOG && tokenStart(cp)) indentInc();
if (wordstart && m_lang == LA_VERILOG && tokenNotStart(cp)) notstart = true;
if (wordstart && m_lang == LA_VERILOG && !notstart && tokenStart(cp)) indentInc();
if (wordstart && m_lang == LA_VERILOG && tokenEnd(cp)) indentDec();
}
switch (*cp) {
case '\n':
m_lineno++;
++m_lineno;
wordstart = true;
if (cp[1] == '\0') {
// Add the indent later, may be a indentInc/indentDec
@ -739,7 +739,7 @@ void V3OutFormatter::puts(const char* strg) {
if (m_lang == LA_C || m_lang == LA_VERILOG) {
if (cp > strg && cp[-1] == '/' && !m_inStringLiteral) {
// Output ignoring contents to EOL
cp++;
++cp;
while (*cp && cp[1] && cp[1] != '\n') putcNoTracking(*cp++);
if (*cp) putcNoTracking(*cp);
}
@ -839,7 +839,7 @@ void V3OutFormatter::putcNoTracking(char chr) {
}
switch (chr) {
case '\n':
m_lineno++;
++m_lineno;
m_column = 0;
m_nobreak = true;
break;
@ -847,9 +847,9 @@ void V3OutFormatter::putcNoTracking(char chr) {
case ' ':
case '(':
case '|':
case '&': m_column++; break;
case '&': ++m_column; break;
default:
m_column++;
++m_column;
m_nobreak = false;
break;
}
@ -864,26 +864,26 @@ string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::L
// Encode chars into XML string
for (const char c : namein) {
if (c == '"') {
out += string("&quot;");
out += std::string{"&quot;"};
} else if (c == '\'') {
out += string("&apos;");
out += std::string{"&apos;"};
} else if (c == '<') {
out += string("&lt;");
out += std::string{"&lt;"};
} else if (c == '>') {
out += string("&gt;");
out += std::string{"&gt;"};
} else if (c == '&') {
out += string("&amp;");
out += std::string{"&amp;"};
} else if (isprint(c)) {
out += c;
} else {
out += string("&#") + cvtToStr((unsigned int)(c & 0xff)) + ";";
out += std::string{"&#"} + cvtToStr((unsigned int)(c & 0xff)) + ";";
}
}
} else {
// Encode control chars into C style escapes
for (const char c : namein) {
if (c == '\\' || c == '"') {
out += string("\\") + c;
out += std::string{"\\"} + c;
} else if (c == '\n') {
out += "\\n";
} else if (c == '\r') {
@ -894,8 +894,8 @@ string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::L
out += c;
} else {
// This will also cover \a etc
const string octal = string("\\") + cvtToStr((c >> 6) & 3) + cvtToStr((c >> 3) & 7)
+ cvtToStr(c & 7);
const string octal = std::string{"\\"} + cvtToStr((c >> 6) & 3)
+ cvtToStr((c >> 3) & 7) + cvtToStr(c & 7);
out += octal;
}
}
@ -920,13 +920,16 @@ void V3OutFormatter::printf(const char* fmt...) {
// V3OutFormatter: A class for printing to a file, with automatic indentation of C++ code.
V3OutFile::V3OutFile(const string& filename, V3OutFormatter::Language lang)
: V3OutFormatter{filename, lang} {
: V3OutFormatter{filename, lang}
, m_bufferp{new std::array<char, WRITE_BUFFER_SIZE_BYTES>{}} {
if ((m_fp = V3File::new_fopen_w(filename)) == nullptr) {
v3fatal("Cannot write " << filename);
}
}
V3OutFile::~V3OutFile() {
writeBlock();
if (m_fp) fclose(m_fp);
m_fp = nullptr;
}
@ -939,7 +942,8 @@ void V3OutFile::putsForceIncs() {
void V3OutCFile::putsGuard() {
UASSERT(!m_guard, "Already called putsGuard in emit file");
m_guard = true;
string var = VString::upcase(string("VERILATED_") + V3Os::filenameNonDir(filename()) + "_");
string var
= VString::upcase(std::string{"VERILATED_"} + V3Os::filenameNonDir(filename()) + "_");
for (char& c : var) {
if (!isalnum(c)) c = '_';
}

View File

@ -22,11 +22,13 @@
#include "V3Error.h"
#include <stack>
#include <set>
#include <list>
#include <vector>
#include <array>
#include <fstream>
#include <list>
#include <memory>
#include <set>
#include <stack>
#include <vector>
//============================================================================
// V3File: Create streams, recording dependency information
@ -154,9 +156,6 @@ public:
puts(strg);
}
bool exceededWidth() const { return m_column > m_commaWidth; }
bool tokenMatch(const char* cp, const char* cmp);
bool tokenStart(const char* cp);
bool tokenEnd(const char* cp);
void indentInc() { m_indentLevel += m_blockIndent; }
void indentDec() {
m_indentLevel -= m_blockIndent;
@ -173,6 +172,10 @@ public:
static string indentSpaces(int num);
// Add escaped characters to strings
static string quoteNameControls(const string& namein, Language lang = LA_C);
static bool tokenMatch(const char* cp, const char* cmp);
static bool tokenNotStart(const char* cp); // Import/export meaning no endfunction
static bool tokenStart(const char* cp);
static bool tokenEnd(const char* cp);
// CALLBACKS - MUST OVERRIDE
virtual void putcOutput(char chr) = 0;
@ -183,18 +186,56 @@ public:
// V3OutFile: A class for printing to a file, with automatic indentation of C++ code.
class V3OutFile VL_NOT_FINAL : public V3OutFormatter {
// Size of m_bufferp.
// 128kB has been experimentally determined to be in the zone of buffer sizes that work best.
// It is also considered to be the smallest I/O buffer size in GNU coreutils (io_blksize) that
// allows to best minimize syscall overhead.
// The hard boundaries are CPU L2/L3 cache size on the top and filesystem block size
// on the bottom.
static constexpr std::size_t WRITE_BUFFER_SIZE_BYTES = 128 * 1024;
// MEMBERS
std::unique_ptr<std::array<char, WRITE_BUFFER_SIZE_BYTES>> m_bufferp; // Write buffer
std::size_t m_usedBytes = 0; // Number of bytes stored in m_bufferp
FILE* m_fp = nullptr;
public:
V3OutFile(const string& filename, V3OutFormatter::Language lang);
V3OutFile(const V3OutFile&) = delete;
V3OutFile& operator=(const V3OutFile&) = delete;
V3OutFile(V3OutFile&&) = delete;
V3OutFile& operator=(V3OutFile&&) = delete;
virtual ~V3OutFile() override;
void putsForceIncs();
private:
void writeBlock() {
if (VL_LIKELY(m_usedBytes > 0)) fwrite(m_bufferp->data(), m_usedBytes, 1, m_fp);
m_usedBytes = 0;
}
// CALLBACKS
virtual void putcOutput(char chr) override { fputc(chr, m_fp); }
virtual void putsOutput(const char* str) override { fputs(str, m_fp); }
virtual void putcOutput(char chr) override {
m_bufferp->at(m_usedBytes++) = chr;
if (VL_UNLIKELY(m_usedBytes >= WRITE_BUFFER_SIZE_BYTES)) writeBlock();
}
virtual void putsOutput(const char* str) override {
std::size_t len = strlen(str);
std::size_t availableBytes = WRITE_BUFFER_SIZE_BYTES - m_usedBytes;
while (VL_UNLIKELY(len >= availableBytes)) {
memcpy(m_bufferp->data() + m_usedBytes, str, availableBytes);
m_usedBytes = WRITE_BUFFER_SIZE_BYTES;
writeBlock();
str += availableBytes;
len -= availableBytes;
availableBytes = WRITE_BUFFER_SIZE_BYTES;
}
if (len > 0) {
memcpy(m_bufferp->data() + m_usedBytes, str, len);
m_usedBytes += len;
}
}
};
class V3OutCFile VL_NOT_FINAL : public V3OutFile {

View File

@ -72,10 +72,10 @@ int FileLineSingleton::nameToNumber(const string& filename) {
//! Experimental. Updated to also put out the language.
void FileLineSingleton::fileNameNumMapDumpXml(std::ostream& os) {
os << "<files>\n";
for (auto it = m_namemap.cbegin(); it != m_namemap.cend(); ++it) {
os << "<file id=\"" << filenameLetters(it->second) << "\" filename=\""
<< V3OutFormatter::quoteNameControls(it->first, V3OutFormatter::LA_XML)
<< "\" language=\"" << numberToLang(it->second).ascii() << "\"/>\n";
for (const auto& itr : m_namemap) {
os << "<file id=\"" << filenameLetters(itr.second) << "\" filename=\""
<< V3OutFormatter::quoteNameControls(itr.first, V3OutFormatter::LA_XML)
<< "\" language=\"" << numberToLang(itr.second).ascii() << "\"/>\n";
}
os << "</files>\n";
}
@ -126,7 +126,7 @@ string VFileContent::getLine(int lineno) const {
return "";
}
}
const string text = m_lines[lineno];
string text = m_lines[lineno];
UINFO(9, "Get Stream[ct" << m_id << "+" << lineno << "]: " << text);
return text;
}
@ -166,7 +166,7 @@ string FileLine::xmlDetailedLocation() const {
}
string FileLine::lineDirectiveStrg(int enterExit) const {
return std::string("`line ") + cvtToStr(lastLineno()) + " \"" + filename() + "\" "
return std::string{"`line "} + cvtToStr(lastLineno()) + " \"" + filename() + "\" "
+ cvtToStr(enterExit) + "\n";
}

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