Merge from master for release.
This commit is contained in:
commit
b154af87a1
|
|
@ -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
43
Changes
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
12
configure.ac
12
configure.ac
|
|
@ -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)
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -134,7 +134,6 @@ public:
|
|||
initUnpacked(ulims);
|
||||
}
|
||||
|
||||
public:
|
||||
~VerilatedVarProps() = default;
|
||||
// METHODS
|
||||
bool magicOk() const { return m_magic == MAGIC; }
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#define VERILATOR_VERILATED_SYMS_H_
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "verilated.h"
|
||||
#include "verilated_sym_props.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
//=========================================================================
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
485
src/V3Ast.h
485
src/V3Ast.h
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
321
src/V3AstNodes.h
321
src/V3AstNodes.h
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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}; }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@
|
|||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
|
|
|||
207
src/V3Const.cpp
207
src/V3Const.cpp
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "verilatedos.h"
|
||||
|
||||
#include "V3EmitCBase.h"
|
||||
|
||||
#include "V3Task.h"
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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()}; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(""");
|
||||
out += std::string{"""};
|
||||
} else if (c == '\'') {
|
||||
out += string("'");
|
||||
out += std::string{"'"};
|
||||
} else if (c == '<') {
|
||||
out += string("<");
|
||||
out += std::string{"<"};
|
||||
} else if (c == '>') {
|
||||
out += string(">");
|
||||
out += std::string{">"};
|
||||
} else if (c == '&') {
|
||||
out += string("&");
|
||||
out += std::string{"&"};
|
||||
} 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 = '_';
|
||||
}
|
||||
|
|
|
|||
59
src/V3File.h
59
src/V3File.h
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue