'Merge from master for release.'
This commit is contained in:
commit
f83817510d
|
|
@ -13,7 +13,7 @@ AllowShortBlocksOnASingleLine: true
|
|||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
|
|
|
|||
|
|
@ -11,4 +11,6 @@ Thanks for taking the time to report this.
|
|||
|
||||
Can you attach an example that shows the issue? (Must be openly licensed, ideally in test_regress format.)
|
||||
|
||||
May we assist you in trying to fix this yourself?
|
||||
What 'verilator --version' are you using? Did you try it with the git master version?
|
||||
|
||||
Would you be willing to try to fix Verilator yourself with assistance?
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ env:
|
|||
- VERILATOR_NUM_JOBS=$(echo `nproc` + 1 | bc)
|
||||
- VERILATOR_CONFIG_FLAGS="--enable-maintainer-mode --enable-longtests"
|
||||
- VERILATOR_AUTHOR_SITE=1
|
||||
- OBJCACHE=ccache
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
|
@ -28,6 +27,7 @@ cache:
|
|||
before_install:
|
||||
# Perl modules needed for testing
|
||||
# Not listing Bit::Vector as slow to install, and only skips one test
|
||||
- touch temp.cpp ; g++ -E -dM -c temp.cpp | sort ; rm -rf temp.cpp
|
||||
- yes yes | sudo cpan -fi Unix::Processors Parallel::Forker
|
||||
- sudo apt-get install gdb gtkwave
|
||||
- sudo apt-get install libgoogle-perftools-dev
|
||||
|
|
|
|||
46
Changes
46
Changes
|
|
@ -3,6 +3,52 @@ Revision history for Verilator
|
|||
The contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
|
||||
* Verilator 4.034 2020-05-03
|
||||
|
||||
** Add simplistic class support with many restrictions, see manual, #377.
|
||||
|
||||
** Support IEEE time units and time precisions, #234.
|
||||
Includes `timescale, $printtimescale, $timeformat.
|
||||
VL_TIME_MULTIPLIER, VL_TIME_PRECISION, VL_TIME_UNIT have been removed
|
||||
and the time precision must now match the SystemC time precision. To
|
||||
get closer behavior to older versions, use e.g. --timescale-override
|
||||
"1ps/1ps".
|
||||
|
||||
** Add --build to call make automatically, #2249. [Yutetsu TAKATSUKASA]
|
||||
|
||||
** Configuring with ccache present now defaults to using it; see OBJCACHE.
|
||||
|
||||
** Fix DPI import/export to be standard compliant, #2236. [Geza Lore]
|
||||
|
||||
** Add --trace-threads for general multithreaded tracing, #2269. [Geza Lore]
|
||||
|
||||
*** Add --flatten for use with --xml-only, #2270. [James Hanlon]
|
||||
|
||||
**** Greatly improve FST/VCD dump performance, #2244, #2246, #2250, #2257. [Geza Lore]
|
||||
|
||||
**** Support $ferror, and $fflush without arguments, #1638.
|
||||
|
||||
**** Support event data type (with some restrictions).
|
||||
|
||||
**** Support $root, #2150. [Keyi Zhang]
|
||||
|
||||
**** Add error if use SystemC 2.2 and earlier (pre-2011) as is deprecated.
|
||||
|
||||
**** Fix build of fast path tracing code to use OPT_FAST, #2245. [Geza Lore]
|
||||
|
||||
**** Fix arrayed instances connecting to slices, #2263. [Don/engr248]
|
||||
|
||||
**** Fix error on unpacked connecting to packed, #2288. [Joseph Shaker]
|
||||
|
||||
**** Fix logical not optimization with empty begin, #2291. [Baltazar Ortiz]
|
||||
|
||||
**** Fix reduction OR on wide data, broke in v4.026, #2300. [Jack Koenig]
|
||||
|
||||
**** Fix clock enables with bit-extends, #2299. [Marco Widmer]
|
||||
|
||||
**** Fix MacOs Homebrew by removing default LIBS, #2298. [Ryan Clarke]
|
||||
|
||||
|
||||
* Verilator 4.032 2020-04-04
|
||||
|
||||
*** Add column numbers to errors and warnings.
|
||||
|
|
|
|||
11
Makefile.in
11
Makefile.in
|
|
@ -336,6 +336,7 @@ installdata:
|
|||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/cmake_tracing_c
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/cmake_tracing_sc
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/cmake_protect_lib
|
||||
$(MKINSTALLDIRS) $(DESTDIR)$(pkgdatadir)/examples/xml_py
|
||||
cd $(srcdir) \
|
||||
; for p in $(VL_INST_DATA_SRCDIR_FILES) ; do \
|
||||
$(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \
|
||||
|
|
@ -370,8 +371,8 @@ uninstall:
|
|||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/cmake_tracing_c
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/cmake_tracing_sc
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/cmake_protect_lib
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples/xml_py
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/examples
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)/cmake
|
||||
-rmdir $(DESTDIR)$(pkgdatadir)
|
||||
-rmdir $(DESTDIR)$(pkgconfigdir)
|
||||
|
||||
|
|
@ -455,6 +456,14 @@ analyzer-include:
|
|||
-rm -rf examples/*/obj*
|
||||
scan-build $(MAKE) -k examples
|
||||
|
||||
CLANGFORMAT = clang-format
|
||||
CLANGFORMAT_FLAGS = -i
|
||||
|
||||
clang-format:
|
||||
@$(CLANGFORMAT) --version | egrep 10.0 > /dev/null \
|
||||
|| echo "*** You are not using clang-format 10.0, indents may differ from master's ***"
|
||||
$(CLANGFORMAT) $(CLANGFORMAT_FLAGS) $(CPPCHECK_CPP) $(CPPCHECK_H)
|
||||
|
||||
ftp: info
|
||||
|
||||
install-msg:
|
||||
|
|
|
|||
36
README.adoc
36
README.adoc
|
|
@ -4,7 +4,7 @@
|
|||
ifdef::env-github[]
|
||||
image:https://img.shields.io/badge/License-LGPL%20v3-blue.svg[license LGPLv3,link=https://www.gnu.org/licenses/lgpl-3.0]
|
||||
image:https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg[license Artistic-2.0,link=https://opensource.org/licenses/Artistic-2.0]
|
||||
image:https://api.codacy.com/project/badge/Grade/48478c986f13400682ffe4a5e0939b3a[Code Quality,link=https://www.codacy.com/gh/verilator/verilator]
|
||||
image:https://api.codacy.com/project/badge/Grade/fa78caa433c84a4ab9049c43e9debc6f[Code Quality,link=https://www.codacy.com/gh/verilator/verilator]
|
||||
image:https://travis-ci.com/verilator/verilator.svg?branch=master[Build Status (Travis CI),link=https://travis-ci.com/verilator/verilator]
|
||||
endif::[]
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ endif::[]
|
|||
|
||||
[cols="a,a",indent=0,frame="none"]
|
||||
|===
|
||||
^.^| *Welcome to Verilator, the fastest free Verilog HDL simulator.*
|
||||
^.^| *Welcome to Verilator, the fastest Verilog HDL simulator.*
|
||||
+++ <br/> +++ • Accepts synthesizable Verilog or SystemVerilog
|
||||
+++ <br/> +++ • Performs lint code-quality checks
|
||||
+++ <br/> +++ • Compiles into multithreaded {cpp}, or SystemC
|
||||
|
|
@ -56,7 +56,7 @@ endif::[]
|
|||
== What Verilator Does
|
||||
|
||||
Verilator is invoked with parameters similar to GCC or Synopsys's VCS. It
|
||||
"Verilates" the specified synthesizable Verilog or SystemVerilog code by
|
||||
"Verilates" the specified Verilog or SystemVerilog code by
|
||||
reading it, performing lint checks, and optionally inserting assertion
|
||||
checks and coverage-analysis points. It outputs single- or multi-threaded
|
||||
.cpp and .h files, the "Verilated" code.
|
||||
|
|
@ -72,30 +72,30 @@ Verilator may not be the best choice if you are expecting a full featured
|
|||
replacement for NC-Verilog, VCS or another commercial Verilog simulator, or
|
||||
if you are looking for a behavioral Verilog simulator e.g. for a quick
|
||||
class project (we recommend http://iverilog.icarus.com[Icarus Verilog] for
|
||||
this.) However, if you are looking for a path to migrate synthesizable
|
||||
Verilog to {cpp} or SystemC, and your team is comfortable writing just a
|
||||
this.) However, if you are looking for a path to migrate SystemVerilog to
|
||||
{cpp} or SystemC, and your team is comfortable writing just a
|
||||
touch of {cpp} code, Verilator is the tool for you.
|
||||
|
||||
== Performance
|
||||
|
||||
Verilator does not simply convert Verilog HDL to {cpp} or SystemC. Rather
|
||||
than only translate, Verilator compiles your code into a much faster
|
||||
optimized and optionally thread-partitioned model, which is in turn wrapped
|
||||
inside a {cpp}/SystemC/{cpp}-under-Python module. The results are a compiled Verilog
|
||||
model that executes even on a single-thread over 10x faster than standalone
|
||||
SystemC, and on a single thread is about 100 times faster than interpreted
|
||||
Verilog simulators such as http://iverilog.icarus.com[Icarus
|
||||
Verilator does not simply convert Verilog HDL to {cpp} or SystemC. Rather,
|
||||
Verilator compiles your code into a much faster optimized and optionally
|
||||
thread-partitioned model, which is in turn wrapped inside a
|
||||
{cpp}/SystemC/{cpp}-under-Python module. The results are a compiled
|
||||
Verilog model that executes even on a single-thread over 10x faster than
|
||||
standalone SystemC, and on a single thread is about 100 times faster than
|
||||
interpreted Verilog simulators such as http://iverilog.icarus.com[Icarus
|
||||
Verilog]. Another 2-10x speedup might be gained from multithreading
|
||||
(yielding 200-1000x total over interpreted simulators).
|
||||
|
||||
Verilator has typically similar or better performance versus the commercial
|
||||
Verilog simulators (Carbon Design Systems Carbonator, Modelsim, Cadence
|
||||
Incisive/NC-Verilog, Synopsys VCS, VTOC, and Pragmatic CVer/CVC). But,
|
||||
Verilator is free, so you can spend on computes rather than licenses. Thus
|
||||
Verilator gives you more cycles/dollar than anything else available.
|
||||
Verilator has typically similar or better performance versus the
|
||||
closed-source Verilog simulators (Carbon Design Systems Carbonator,
|
||||
Modelsim, Cadence Incisive/NC-Verilog, Synopsys VCS, VTOC, and Pragmatic
|
||||
CVer/CVC). But, Verilator is open-sourced, so you can spend on computes
|
||||
rather than licenses. Thus Verilator gives you the best cycles/dollar.
|
||||
|
||||
For more information on how Verilator stacks up to some of the other
|
||||
commercial and free Verilog simulators, see the
|
||||
closed-sourced and open-sourced Verilog simulators, see the
|
||||
https://www.veripool.org/verilog_sim_benchmarks.html[Verilog Simulator
|
||||
Benchmarks]. (If you benchmark Verilator, please see the notes in the
|
||||
https://verilator.org/verilator_doc.pdf[Verilator manual (PDF)], and also
|
||||
|
|
|
|||
273
bin/verilator
273
bin/verilator
|
|
@ -184,11 +184,14 @@ sub run {
|
|||
if (($status & 127) == 4 # SIGILL
|
||||
|| ($status & 127) == 8 # SIGFPA
|
||||
|| ($status & 127) == 11) { # SIGSEGV
|
||||
warn "%Error: Verilator internal fault, sorry. Consider trying --debug --gdbbt\n" if !$Debug;
|
||||
warn "%Error: Verilator internal fault, sorry. "
|
||||
."Suggest trying --debug --gdbbt\n" if !$Debug;
|
||||
} elsif (($status & 127) == 6) { # SIGABRT
|
||||
warn "%Error: Verilator aborted. Consider trying --debug --gdbbt\n" if !$Debug;
|
||||
warn "%Error: Verilator aborted. "
|
||||
."Suggest trying --debug --gdbbt\n" if !$Debug;
|
||||
} else {
|
||||
warn "%Error: Verilator threw signal $status. Consider trying --debug --gdbbt\n" if !$Debug;
|
||||
warn "%Error: Verilator threw signal $status. "
|
||||
."Suggest trying --debug --gdbbt\n" if !$Debug;
|
||||
}
|
||||
}
|
||||
if (!$opt_quiet_exit && ($status != 256 || $Debug)) { # i.e. not normal exit(1)
|
||||
|
|
@ -271,11 +274,12 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
|||
--bbox-sys Blackbox unknown $system calls
|
||||
--bbox-unsup Blackbox unsupported language features
|
||||
--bin <filename> Override Verilator binary
|
||||
--build Build model executable/library after Verilation
|
||||
-CFLAGS <flags> C++ Compiler flags for makefile
|
||||
--cc Create C++ output
|
||||
--cdc Clock domain crossing analysis
|
||||
--clk <signal-name> Mark specified signal as clock
|
||||
--make <make-system> Generate scripts for specified make system
|
||||
--make <build-tool> Generate scripts for specified build tool
|
||||
--compiler <compiler-name> Tune for specified C++ compiler
|
||||
--converge-limit <loops> Tune convergence settle time
|
||||
--coverage Enable all coverage
|
||||
|
|
@ -302,6 +306,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
|||
-F <file> Parse options from a file, relatively
|
||||
-f <file> Parse options from a file
|
||||
-FI <file> Force include of a file
|
||||
--flatten Force inlining of all modules, tasks and functions
|
||||
-G<name>=<value> Overwrite toplevel parameter
|
||||
--gdb Run Verilator under GDB interactively
|
||||
--gdbbt Run Verilator under GDB for backtrace
|
||||
|
|
@ -309,6 +314,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
|||
--getenv <var> Get environment variable with defaults
|
||||
--help Display this help
|
||||
-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
|
||||
|
|
@ -319,6 +325,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
|||
--language <lang> Default language standard to parse
|
||||
+libext+<ext>+[ext]... Extensions for finding modules
|
||||
--lint-only Lint, but do not make output
|
||||
-MAKEFLAGS <flags> Options to make during --build
|
||||
--max-num-width <value> Maximum number width (default: 64K)
|
||||
--MMD Create .d dependency files
|
||||
--MP Create phony dependency targets
|
||||
|
|
@ -334,6 +341,7 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
|||
-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 .cpp functions
|
||||
--output-split-ctrace <statements> Split tracing functions
|
||||
|
|
@ -368,16 +376,18 @@ detailed descriptions in L</"VERILATION ARGUMENTS"> for more information.
|
|||
--threads <threads> Enable multithreading
|
||||
--threads-dpi <mode> Enable multithreaded DPI
|
||||
--threads-max-mtasks <mtasks> Tune maximum mtask partitioning
|
||||
--timescale <timescale> Sets default timescale
|
||||
--timescale-override <timescale> Overrides all timescales
|
||||
--top-module <topname> Name of top level input module
|
||||
--trace Enable waveform creation
|
||||
--trace-depth <levels> Depth of tracing
|
||||
--trace-coverage Enable tracing of coverage
|
||||
--trace-depth <levels> Depth of tracing
|
||||
--trace-fst Enable FST waveform creation
|
||||
--trace-fst-thread Enable FST threaded waveform creation
|
||||
--trace-max-array <depth> Maximum bit width for tracing
|
||||
--trace-max-width <width> Maximum array depth for tracing
|
||||
--trace-params Enable tracing of parameters
|
||||
--trace-structs Enable tracing structure names
|
||||
--trace-threads <threads> Enable waveform creation on separate threads
|
||||
--trace-underscore Enable tracing of _signals
|
||||
-U<var> Undefine preprocessor define
|
||||
--unroll-count <loops> Tune maximum loop iterations
|
||||
|
|
@ -515,6 +525,8 @@ unsized zero. Arguments to such functions will be parsed, but not
|
|||
otherwise checked. This prevents errors when linting in the presence of
|
||||
company specific PLI calls.
|
||||
|
||||
Using this argument will likely cause incorrect simulation.
|
||||
|
||||
=item --bbox-unsup
|
||||
|
||||
Black box some unsupported language features, currently UDP tables, the
|
||||
|
|
@ -522,6 +534,8 @@ cmos and tran gate primitives, deassign statements, and mixed edge errors.
|
|||
This may enable linting the rest of the design even when unsupported
|
||||
constructs are present.
|
||||
|
||||
Using this argument will likely cause incorrect simulation.
|
||||
|
||||
=item --bin I<filename>
|
||||
|
||||
Rarely needed. Override the default filename for Verilator itself. When a
|
||||
|
|
@ -529,6 +543,13 @@ dependency (.d) file is created, this filename will become a source
|
|||
dependency, such that a change in this binary will have make rebuild the
|
||||
output files.
|
||||
|
||||
=item --build
|
||||
|
||||
After generating the SystemC/C++ code, Verilator will invoke the toolchain to
|
||||
build the model library (and executable when C<--exe> is also used). Verilator
|
||||
manages the build itself, and for this --build requires GNU Make to be
|
||||
available on the platform.
|
||||
|
||||
=item -CFLAGS I<flags>
|
||||
|
||||
Add specified C compiler flag to the generated makefiles. For multiple
|
||||
|
|
@ -574,12 +595,17 @@ If clock signals are assigned to vectors and then later used individually,
|
|||
Verilator will attempt to decompose the vector and connect the single-bit
|
||||
clock signals directly. This should be transparent to the user.
|
||||
|
||||
=item --make I<make-system>
|
||||
=item --make I<build-tool>
|
||||
|
||||
Generates a script for the specified make system.
|
||||
Generates a script for the specified build tool.
|
||||
|
||||
Supported make systems are gmake and cmake. Both can be specified.
|
||||
If no make system is specified, gmake is assumed.
|
||||
Supported values are C<gmake> for GNU Make and C<cmake> for CMake. Both can be
|
||||
specified together. If no build tool is specified, gmake is assumed. The
|
||||
executable of gmake can be configured via environment variable "MAKE".
|
||||
|
||||
When using --build Verilator takes over the responsibility of building the
|
||||
model library/executable. For this reason --make cannot be specified when
|
||||
using --build.
|
||||
|
||||
=item --compiler I<compiler-name>
|
||||
|
||||
|
|
@ -829,6 +855,12 @@ specified file might be used to contain define prototypes of custom
|
|||
VL_VPRINTF functions, and may need to include verilatedos.h as this file is
|
||||
included before any other standard includes.
|
||||
|
||||
=item --flatten
|
||||
|
||||
Force flattening of the design's hierarchy, with all modules, tasks and
|
||||
functions inlined. Typically used with C<--xml-only>. Note flattening
|
||||
large designs may require significant CPU time, memory and storage.
|
||||
|
||||
=item -GI<name>=I<value>
|
||||
|
||||
Overwrites the given parameter of the toplevel module. The value is limited
|
||||
|
|
@ -921,6 +953,14 @@ values, or a value < 1 will inline everything, will lead to longer compile
|
|||
times, but potentially faster simulation runtimes. This setting is ignored
|
||||
for very small modules; they will always be inlined, if allowed.
|
||||
|
||||
=item -j <value>
|
||||
|
||||
Specify the level of parallelism for --build. <value> must be a positive
|
||||
integer specifying the maximum number of parallel build jobs, or can be
|
||||
omitted. When <value> is omitted, the build will not try to limit the number of
|
||||
parallel build jobs but attempt to execute all independent build steps in
|
||||
parallel.
|
||||
|
||||
=item -LDFLAGS I<flags>
|
||||
|
||||
Add specified C linker flags to the generated makefiles. For multiple
|
||||
|
|
@ -966,6 +1006,14 @@ stylistic and not enabled by default.
|
|||
If the design is not to be completely Verilated see also the --bbox-sys and
|
||||
--bbox-unsup options.
|
||||
|
||||
=item -MAKEFLAGS <string>
|
||||
|
||||
When using --build, add the specified flag to the invoked make command line.
|
||||
For multiple flags either pass them as a single argument with space separators
|
||||
quoted in the shell (e.g. C<-MAKEFLAGS "-a -b">), or use multiple -MAKEFLAGS
|
||||
arguments (e.g. C<-MAKEFLAGS -l -MAKEFLAGS -k>). Use of this option should not
|
||||
be required for simple builds using the host toolchain.
|
||||
|
||||
=item --max-num-width I<value>
|
||||
|
||||
Set the maximum number literal width (e.g. in 1024'd22 this it the 1024).
|
||||
|
|
@ -1080,7 +1128,8 @@ design --output-split 20000 resulted in splitting into approximately
|
|||
one-minute-compile chunks.
|
||||
|
||||
Typically when using this, make with VM_PARALLEL_BUILDS=1 (set for you if
|
||||
using the default makefiles), and use I<ccache>.
|
||||
using the default makefiles), and use I<ccache> (set for you if present at
|
||||
configure time).
|
||||
|
||||
=item --output-split-cfuncs I<statements>
|
||||
|
||||
|
|
@ -1236,6 +1285,10 @@ This allows for the secure delivery of sensitive IP without the need for
|
|||
encrypted RTL (i.e. IEEE P1735). See examples/make_protect_lib in the
|
||||
distribution for a demonstration of how to build and use the DPI library.
|
||||
|
||||
When using --protect-lib it is advised to also use C<--timescale-override
|
||||
/1fs> to ensure the model has a time resolution that is always compatible
|
||||
with the time precision of the upper instantiating module.
|
||||
|
||||
=item --private
|
||||
|
||||
Opposite of --public. Is the default; this option exists for backwards
|
||||
|
|
@ -1394,6 +1447,24 @@ Rarely needed. When using --threads, specify the number of mtasks the
|
|||
model is to be partitioned into. If unspecified, Verilator approximates a
|
||||
good value.
|
||||
|
||||
=item --timescale I<timeunit>/I<timeprecision>
|
||||
|
||||
Sets default timescale, timeunit and timeprecision for when `timescale does
|
||||
not occur in sources. Default is "1ps/1ps" (to match SystemC). This is
|
||||
overriden by C<--timescale-override>.
|
||||
|
||||
=item --timescale-override I<timeunit>/I<timeprecision>
|
||||
|
||||
=item --timescale-override /I<timeprecision>
|
||||
|
||||
Overrides all `timescales in sources. The timeunit may be left empty to
|
||||
specify only to override the timeprecision, e.g. "/1fs".
|
||||
|
||||
The time precision must be consistent with SystemC's
|
||||
sc_set_time_resolution, or the C++ code instantiating the Verilated module.
|
||||
As 1fs is the finest time precision it may be desirable to always use a
|
||||
precision of 1fs.
|
||||
|
||||
=item --top-module I<topname>
|
||||
|
||||
When the input Verilog contains more than one top level module, specifies
|
||||
|
|
@ -1416,6 +1487,8 @@ need to add these to your Makefile manually.
|
|||
Having tracing compiled in may result in some small performance losses,
|
||||
even when waveforms are not turned on during model execution.
|
||||
|
||||
See also C<--trace-threads>.
|
||||
|
||||
=item --trace-coverage
|
||||
|
||||
With --trace and --coverage-*, enable tracing to include a traced signal
|
||||
|
|
@ -1437,14 +1510,8 @@ improve simulation runtime and trace file size.
|
|||
|
||||
=item --trace-fst
|
||||
|
||||
Enable FST waveform tracing in the model. This overrides C<--trace> and
|
||||
C<--trace-fst-thread>. See also C<--trace-fst-thread>.
|
||||
|
||||
=item --trace-fst-thread
|
||||
|
||||
Enable FST waveform tracing in the model, using a separate thread. This is
|
||||
typically faster in simulation runtime but slower in total computes than
|
||||
C<--trace-fst>. This overrides C<--trace> and C<--trace-fst>.
|
||||
Enable FST waveform tracing in the model. This overrides C<--trace>.
|
||||
See also C<--trace-threads>.
|
||||
|
||||
=item --trace-max-array I<depth>
|
||||
|
||||
|
|
@ -1469,6 +1536,15 @@ array fields, rather than a single combined packed bus. Due to VCD file
|
|||
format constraints this may result in significantly slower trace times and
|
||||
larger trace files.
|
||||
|
||||
=item --trace-threads I<threads>
|
||||
|
||||
Enable waveform tracing using separate threads. This is typically faster in
|
||||
simulation runtime but uses more total compute. This option is independend of,
|
||||
and works with, both C<--trace> and C<--trace-fst>. Different trace formats can
|
||||
take advantage of more trace threads to varying degrees. Currently VCD tracing
|
||||
can utilize at most --trace-threads 1, and FST tracing can utilize at most
|
||||
--trace-threads 2. This overrides C<--no-threads>.
|
||||
|
||||
=item --trace-underscore
|
||||
|
||||
Enable tracing of signals that start with an underscore. Normally, these
|
||||
|
|
@ -1505,6 +1581,12 @@ Read the filename as a Verilog library. Any modules in the file may be
|
|||
used to resolve cell instantiations in the top level module, else ignored.
|
||||
Note -v is fairly standard across Verilog tools.
|
||||
|
||||
=item --no-verilate
|
||||
|
||||
When using --build, disable generation of C++/SC code, and execute only the
|
||||
build. This can be useful for rebuilding verilated code produced by a previous
|
||||
invocation of Verilator.
|
||||
|
||||
=item +verilog1995ext+I<ext>
|
||||
|
||||
=item +verilog2001ext+I<ext>
|
||||
|
|
@ -1752,10 +1834,10 @@ Defaults to "profile_threads.dat".
|
|||
=item +verilator+prof+threads+start+I<value>
|
||||
|
||||
When using --prof-threads at simulation runtime, Verilator will wait until
|
||||
$time is at this value, then start the profiling warmup, then
|
||||
capturing. Generally this should be set to some time that is well within
|
||||
the normal operation of the simulation, i.e. outside of reset. If 0, the
|
||||
dump is disabled. Defaults to 1.
|
||||
$time is at this value (expressed in units of the time precision), then
|
||||
start the profiling warmup, then capturing. Generally this should be set to
|
||||
some time that is well within the normal operation of the simulation,
|
||||
i.e. outside of reset. If 0, the dump is disabled. Defaults to 1.
|
||||
|
||||
=item +verilator+prof+threads+window+I<value>
|
||||
|
||||
|
|
@ -1833,20 +1915,17 @@ compiled Verilator, you need to point to the kit:
|
|||
|
||||
Now we run Verilator on our little example.
|
||||
|
||||
verilator -Wall --cc our.v --exe sim_main.cpp
|
||||
verilator -Wall --cc our.v --exe --build sim_main.cpp
|
||||
|
||||
We can see the source code under the "obj_dir" directory. See the FILES
|
||||
section below for descriptions of some of the files that were created.
|
||||
|
||||
ls -l obj_dir
|
||||
|
||||
We then can compile it
|
||||
|
||||
make -j -C obj_dir -f Vour.mk Vour
|
||||
|
||||
(Verilator included a default compile rule and link rule, since we used
|
||||
--exe and passed a .cpp file on the Verilator command line. You can also
|
||||
write your own compile rules, as we'll show in the SYSTEMC section.)
|
||||
--exe and passed a .cpp file on the Verilator command line. Verilator also
|
||||
then used C<make> to build a final executable. You can also write your own
|
||||
compile rules, and run make yourself as we'll show in the SYSTEMC section.)
|
||||
|
||||
And now we run it
|
||||
|
||||
|
|
@ -2001,18 +2080,18 @@ OPT, OPT_FAST, or OPT_SLOW lib/verilated.mk. Or, use the -CFLAGS and/or
|
|||
the compiler or linker. Or, just for one run, pass them on the command
|
||||
line to make:
|
||||
|
||||
make OPT_FAST="-Os -fno-stack-protector" -f Vour.mk Vour__ALL.a
|
||||
make OPT_FAST="-Os -march=native -fno-stack-protector" -f Vour.mk Vour__ALL.a
|
||||
|
||||
OPT_FAST specifies optimizations for those programs that are part of the
|
||||
fast path, mostly code that is executed every cycle. OPT_SLOW specifies
|
||||
optimizations for slow-path files (plus tracing), which execute only
|
||||
rarely, yet take a long time to compile with optimization on. OPT
|
||||
specifies overall optimization and affects all compiles, including those
|
||||
OPT_FAST and OPT_SLOW control. For best results, use OPT="-Os", and link
|
||||
with "-static". Nearly the same results can be had with much better
|
||||
compile times with OPT_FAST="-O1 -fstrict-aliasing". Higher optimization
|
||||
such as "-O2" or "-O3" may help, but gcc compile times may be excessive
|
||||
under O3 on even medium sized designs.
|
||||
OPT_FAST and OPT_SLOW control. For best results, use OPT="-Os
|
||||
-march=native", and link with "-static". Nearly the same results can be
|
||||
had with much better compile times with OPT_FAST="-O1 -fstrict-aliasing".
|
||||
Higher optimization such as "-O2" or "-O3" may help, but gcc compile times
|
||||
may be excessive under O3 on even medium sized designs.
|
||||
|
||||
Unfortunately, using the optimizer with SystemC files can result in
|
||||
compiles taking several minutes. (The SystemC libraries have many little
|
||||
|
|
@ -2105,9 +2184,8 @@ After running Make, the C++ compiler may produce the following:
|
|||
{mod_prefix}{misc}.o // Intermediate objects
|
||||
{prefix} // Final executable (w/--exe argument)
|
||||
{prefix}__ALL.a // Library of all Verilated objects
|
||||
{prefix}__ALLboth.cpp // Include of classes for single compile
|
||||
{prefix}__ALLcls.cpp // Include of user classes for single compile
|
||||
{prefix}__ALLsup.cpp // Include of support files for single compile
|
||||
{prefix}__ALLfast.cpp // Include of hot code for single compile
|
||||
{prefix}__ALLslow.cpp // Include of slow code for single compile
|
||||
{prefix}{misc}.d // Intermediate dependencies
|
||||
{prefix}{misc}.o // Intermediate objects
|
||||
|
||||
|
|
@ -2122,12 +2200,19 @@ A generic Linux/OS variable specifying what directories have shared object
|
|||
(.so) files. This path should include SystemC and any other shared objects
|
||||
needed at simulation runtime.
|
||||
|
||||
=item MAKE
|
||||
|
||||
Names the executable of the make command invoked when using the --build option.
|
||||
Some operating systems may require "gmake" to this variable to launch GNU make.
|
||||
If this variable is not specified, "make" is used.
|
||||
|
||||
=item OBJCACHE
|
||||
|
||||
Optionally specifies a caching or distribution program to place in front of
|
||||
all runs of the C++ Compiler. For example, "objcache --read --write", or
|
||||
"ccache". If using distcc or icecc/icecream, they would generally be run
|
||||
under either objcache or ccache; see the documentation for those programs.
|
||||
all runs of the C++ compiler. For example, "ccache". If using distcc or
|
||||
icecc/icecream, they would generally be run under cache; see the
|
||||
documentation for those programs. If OBJCACHE is not set, and at configure
|
||||
time ccache was present, ccache will be used as a default.
|
||||
|
||||
=item SYSTEMC
|
||||
|
||||
|
|
@ -2212,7 +2297,8 @@ example:
|
|||
|
||||
vluint64_t main_time = 0; // Current simulation time
|
||||
// This is a 64-bit integer to reduce wrap over issues and
|
||||
// allow modulus. You can also use a double, if you wish.
|
||||
// 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
|
||||
|
|
@ -2683,8 +2769,8 @@ underneath NC:
|
|||
cd obj_dir
|
||||
ncsc_run \
|
||||
sc_main.cpp \
|
||||
Vour__ALLcls.cpp \
|
||||
Vour__ALLsup.cpp \
|
||||
Vour__ALLfast.cpp \
|
||||
Vour__ALLslow.cpp \
|
||||
verilated.cpp
|
||||
|
||||
For larger designs you'll want to automate this using makefiles, which pull
|
||||
|
|
@ -2704,7 +2790,7 @@ performance.
|
|||
With --threads 1, the generated model is single threaded, however the
|
||||
support libraries are multithread safe. This allows different
|
||||
instantiations of model(s) to potentially each be run under a different
|
||||
thread. All threading is the responsibility of the user's C++ testbench.
|
||||
thread. All threading is the responsibility of the user's C++ testbench.
|
||||
|
||||
With --threads N, where N is at least 2, the generated model will be
|
||||
designed to run in parallel on N threads. The thread calling eval()
|
||||
|
|
@ -2715,9 +2801,6 @@ Verilated model should not livelock nor deadlock, however, you can expect
|
|||
performance to be far worse than it would be with proper ratio of
|
||||
threads and CPU cores.
|
||||
|
||||
With --trace-fst-thread, tracing occurs in a separate thread from the main
|
||||
simulation thread(s). This option is orthogonal to --threads.
|
||||
|
||||
The remainder of this section describe behavior with --threads 1 or
|
||||
--threads N (not --no-threads).
|
||||
|
||||
|
|
@ -2731,12 +2814,28 @@ be done by a "main thread". In most cases the eval thread and main thread
|
|||
are the same thread (i.e. the user's top C++ testbench runs on a single
|
||||
thread), but this is not required.
|
||||
|
||||
The --trace-threads options can be used to produce trace dumps using multiple
|
||||
threads. If --trace-threads is set without --threads, then --trace-threads will
|
||||
imply --threads 1, i.e.: the support libraries will be thread safe.
|
||||
|
||||
With --trace-threads 0, trace dumps are produced on the main thread. This again
|
||||
gives the highest single thread performance.
|
||||
|
||||
With --trace-threads N, where N is at least 1, N additional threads will be
|
||||
created and managed by the trace files (e.g.: VerilatedVcdC or VerilatedFstC),
|
||||
to generate the trace dump. The main thread will be released to proceed with
|
||||
execution as soon as possible, though some blocking of the main thread is still
|
||||
necessary while capturing the trace. Different trace formats can utilize a
|
||||
various number of threads. See the --trace-threads option.
|
||||
|
||||
When running a multithreaded model, the default Linux task scheduler often
|
||||
works against the model, by assuming threads are short lived, and thus
|
||||
often schedules threads using multiple hyperthreads within the same
|
||||
physical core. For best performance use the C<numactl> program to (when the
|
||||
threading count fits) select unique physical cores on the same socket. For
|
||||
example, if a model was Verilated with "--threads 4", we consult
|
||||
threading count fits) select unique physical cores on the same socket. The
|
||||
same applies for --trace-threads as well.
|
||||
|
||||
As an example, if a model was Verilated with "--threads 4", we consult
|
||||
|
||||
egrep 'processor|physical id|core id' /proc/cpuinfo
|
||||
|
||||
|
|
@ -3558,6 +3657,12 @@ ___05F (5F is the hex code of an underscore.)
|
|||
Verilator only supports "bind" to a target module name, not an instance
|
||||
path.
|
||||
|
||||
=head2 Class
|
||||
|
||||
Verilator class support is very limited and in active development.
|
||||
Verilator supports members, and methods. Verilator doe not support class
|
||||
static members, class extend, or class parameters.
|
||||
|
||||
=head2 Dotted cross-hierarchy references
|
||||
|
||||
Verilator supports dotted references to variables, functions and tasks in
|
||||
|
|
@ -3744,7 +3849,7 @@ This section describes specific limitations for each language keyword.
|
|||
`begin_keywords, `begin_keywords, `define, `else, `elsif, `end_keywords,
|
||||
`endif, `error, `ifdef, `ifndef, `include, `line, `systemc_ctor,
|
||||
`systemc_dtor, `systemc_header, `systemc_imp_header,
|
||||
`systemc_implementation, `systemc_interface, `timescale, `undef, `verilog
|
||||
`systemc_implementation, `systemc_interface, `undef, `verilog
|
||||
|
||||
Fully supported.
|
||||
|
||||
|
|
@ -3860,7 +3965,8 @@ $dumplimit/$dumpportlimit are currently ignored.
|
|||
|
||||
The rarely used optional parameter to $finish and $stop is ignored.
|
||||
|
||||
=item $fopen, $fclose, $fdisplay, $feof, $fflush, $fgetc, $fgets, $fscanf, $fwrite
|
||||
=item $fopen, $fclose, $fdisplay, $ferror, $feof, $fflush, $fgetc, $fgets,
|
||||
$fscanf, $fwrite
|
||||
|
||||
File descriptors passed to the file PLI calls must be file descriptors, not
|
||||
MCDs, which includes the mode parameter to $fopen being mandatory.
|
||||
|
|
@ -3895,13 +4001,6 @@ Supported, but the instantiating C++/SystemC testbench must call
|
|||
to register the command line before calling $test$plusargs or
|
||||
$value$plusargs.
|
||||
|
||||
=item $timeformat
|
||||
|
||||
Not supported as Verilator needs to determine all formatting at compile
|
||||
time. Generally you can just ifdef them out for no ill effect. Note also
|
||||
VL_TIME_MULTIPLIER can be defined at compile time to move the decimal point
|
||||
when displaying all times, model wide.
|
||||
|
||||
=back
|
||||
|
||||
|
||||
|
|
@ -4574,6 +4673,18 @@ increases.
|
|||
Ignoring this warning will only slow simulations, it will simulate
|
||||
correctly.
|
||||
|
||||
=item TIMESCALEMOD
|
||||
|
||||
Error that `timescale is used in some but not all modules.
|
||||
Recommend using --timescale argument, or in front of all modules use:
|
||||
|
||||
`include "timescale.vh"
|
||||
|
||||
Then in that file set the timescale.
|
||||
|
||||
This is an error due to IEEE specifications, but it may be disabled similar
|
||||
to warnings. Ignoring this error may result in a module having an
|
||||
unexpected timescale.
|
||||
|
||||
=item UNDRIVEN
|
||||
|
||||
|
|
@ -4809,10 +4920,12 @@ configured with --enable-prec11. This flag will be removed and C++11
|
|||
compilers will be required for both compiling Verilator and compiling
|
||||
Verilated models no sooner than September 2020.
|
||||
|
||||
=item SystemC 2.1 and earlier support
|
||||
=item SystemC 2.2 and earlier support
|
||||
|
||||
Support for SystemC versions 2.1 and earlier and the related sc_clock
|
||||
variable attribute will be removed no sooner than July 2020.
|
||||
Support for SystemC versions 2.2 and earlier including the related sc_clock
|
||||
variable attribute will be removed no sooner than August 2020. The
|
||||
supported versions will be SystemC 2.3.0 (SYSTEMC_VERSION 20111121) and
|
||||
later (presently 2.3.0, 2.3.1, 2.3.2, 2.3.3).
|
||||
|
||||
=item Configuration File -msg
|
||||
|
||||
|
|
@ -4961,8 +5074,8 @@ to VerilatedVcdSc in the examples/make_tracing_sc/sc_main.cpp file of the
|
|||
distribution, and below.
|
||||
|
||||
Alternatively you may use the C++ trace mechanism described in the previous
|
||||
question, however the timescale and timeprecision will not inherited from
|
||||
your SystemC settings.
|
||||
question, note the timescale and timeprecision will be inherited from your
|
||||
SystemC settings.
|
||||
|
||||
You also need to compile verilated_vcd_sc.cpp and verilated_vcd_c.cpp and
|
||||
add them to your link, preferably by adding the dependencies in
|
||||
|
|
@ -4972,6 +5085,12 @@ using the Verilator --exe flag.
|
|||
Note you can also call ->trace on multiple Verilated objects with the same
|
||||
trace file if you want all data to land in the same output file.
|
||||
|
||||
When using SystemC 2.3, the SystemC library must have been built with the
|
||||
experiemntal simulation phase callback based tracing disabled. This is
|
||||
disabled by default when building SystemC with its configure based build
|
||||
system, but when building SystemC with CMake, you must pass
|
||||
-DENABLE_PHASE_CALLBACKS_TRACING=OFF to disable this feature.
|
||||
|
||||
#include "verilated_vcd_sc.h"
|
||||
...
|
||||
int main(int argc, char** argv, char** env) {
|
||||
|
|
@ -5128,9 +5247,9 @@ now relatively old GCC 3.0 to 3.3 being horrible.
|
|||
|
||||
Compile in parallel on many machines and use caching; see the web for the
|
||||
ccache, distcc and icecream packages. ccache will skip GCC runs between
|
||||
identical source builds, even across different users. You can use the
|
||||
OBJCACHE environment variable to use these CC wrappers. Also see the
|
||||
--output-split option.
|
||||
identical source builds, even across different users. If ccache was
|
||||
installed when Verilator was built it is used, or see OBJCACHE environment
|
||||
variable to override this. Also see the --output-split option.
|
||||
|
||||
To reduce the compile time of classes that use a Verilated module (e.g. a
|
||||
top CPP file) you may wish to add /*verilator no_inline_module*/ to your
|
||||
|
|
@ -5164,17 +5283,21 @@ be accessing with a /*verilator public*/ comment before the closing
|
|||
semicolon. Then scope into the C++ class to read the value of the signal,
|
||||
as you would any other member variable.
|
||||
|
||||
Signals are the smallest of 8-bit chars, 16-bit shorts, 32-bit longs, or
|
||||
64-bit long longs that fits the width of the signal. Generally, you can
|
||||
use just uint32_t's for 1 to 32 bits, or vluint64_t for 1 to 64 bits, and
|
||||
the compiler will properly up-convert smaller entities.
|
||||
Signals are the smallest of 8-bit unsigned chars (equivalent to uint8_t),
|
||||
16-bit unsigned shorts (uint16_t), 32-bit unsigned longs (uint32_t), or
|
||||
64-bit unsigned long longs (uint64_t) that fits the width of the signal.
|
||||
Generally, you can use just uint32_t's for 1 to 32 bits, or vluint64_t for
|
||||
1 to 64 bits, and the compiler will properly up-convert smaller entities.
|
||||
Note even signed ports are declared as unsigned; you must sign extend
|
||||
yourself to the appropriate signal width.
|
||||
|
||||
Signals wider than 64 bits are stored as an array of 32-bit uint32_t's.
|
||||
Thus to read bits 31:0, access signal[0], and for bits 63:32, access
|
||||
signal[1]. Unused bits (for example bit numbers 65-96 of a 65-bit vector)
|
||||
will always be zero. if you change the value you must make sure to pack
|
||||
zeros in the unused bits or core-dumps may result. (Because Verilator
|
||||
strips array bound checks where it believes them to be unnecessary.)
|
||||
will always be zero. If you change the value you must make sure to pack
|
||||
zeros in the unused bits or core-dumps may result, because Verilator strips
|
||||
array bound checks where it believes them to be unnecessary to improve
|
||||
performance.
|
||||
|
||||
In the SYSTEMC example above, if you had in our.v:
|
||||
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@ RUN apt-get update \
|
|||
bison=2:3.0.4.dfsg-1build1 \
|
||||
build-essential=12.4ubuntu1 \
|
||||
ca-certificates=20180409 \
|
||||
ccache \
|
||||
flex=2.6.4-6 \
|
||||
git=1:2.17.1-1ubuntu0.5 \
|
||||
git \
|
||||
libfl-dev=2.6.4-6 \
|
||||
perl=5.26.1-6ubuntu0.3 \
|
||||
python3=3.6.7-1~18.04 \
|
||||
libgoogle-perftools-dev \
|
||||
perl \
|
||||
python3 \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[4.032 2020-04-04],
|
||||
AC_INIT([Verilator],[4.034 2020-05-03],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
# When releasing, also update header of Changes file
|
||||
|
|
@ -159,6 +159,12 @@ if test "x$YACC" = "x" ; then
|
|||
AC_MSG_ERROR([Cannot find "bison" in your PATH, please install it])
|
||||
fi
|
||||
|
||||
AC_CHECK_PROG(OBJCACHE,ccache,ccache)
|
||||
if test "x$OBJCACHE" != "x" ; then
|
||||
objcache_version=$($OBJCACHE --version | head -1)
|
||||
AC_MSG_RESULT([objcache is $OBJCACHE --version = $objcache_version])
|
||||
fi
|
||||
|
||||
# Checks for libraries.
|
||||
|
||||
# Checks for typedefs, structures
|
||||
|
|
@ -328,6 +334,7 @@ m4_foreach([cflag],[
|
|||
[-mno-cet],
|
||||
[-Qunused-arguments],
|
||||
[-Wno-bool-operation],
|
||||
[-Wno-tautological-bitwise-compare],
|
||||
[-Wno-parentheses-equality],
|
||||
[-Wno-sign-compare],
|
||||
[-Wno-uninitialized],
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
The contributors listed below have certified their Verilator contributions
|
||||
under the Developer Certificate of Origin
|
||||
(https://developercertificate.org/).
|
||||
under the Developer Certificate of Origin (https://developercertificate.org/).
|
||||
|
||||
Please see the Verilator manual for 200+ additional contributors. Thanks to
|
||||
all.
|
||||
Please see the Verilator manual for 200+ additional contributors. Thanks to all.
|
||||
|
||||
Ahmed El-Mahmoudy
|
||||
Alex Chadwick
|
||||
|
|
@ -16,8 +14,10 @@ Geza Lore
|
|||
Gianfranco Costamagna
|
||||
Howard Su
|
||||
Iztok Jeras
|
||||
James Hanlon
|
||||
Jeremy Bennett
|
||||
John Coiner
|
||||
John Demme
|
||||
Julien Margetts
|
||||
Kanad Kanhere
|
||||
Kevin Kiningham
|
||||
|
|
@ -28,10 +28,14 @@ Maciej Sobkowski
|
|||
Marco Widmer
|
||||
Matthew Ballance
|
||||
Mike Popoloski
|
||||
Nathan Kohagen
|
||||
Nathan Myers
|
||||
Patrick Stewart
|
||||
Peter Horvath
|
||||
Peter Monsson
|
||||
Philipp Wagner
|
||||
Pieter Kapsenberg
|
||||
Qingyao Sun
|
||||
Richard Myers
|
||||
Sean Cross
|
||||
Sebastien Van Cauwenberghe
|
||||
|
|
@ -39,6 +43,7 @@ Stefan Wallentowitz
|
|||
Tobias Rosenkranz
|
||||
Tobias Wölfel
|
||||
Todd Strader
|
||||
Veripool API Bot
|
||||
Wilson Snyder
|
||||
Yutetsu TAKATSUKASA
|
||||
Yves Mathieu
|
||||
|
|
|
|||
|
|
@ -1,159 +0,0 @@
|
|||
clang-format is used to standardize the indentation of the internal C++
|
||||
code.
|
||||
|
||||
For the most part clang-format changes provide good consistency, the two
|
||||
main exceptions being the indentation of preprocessor directives, and
|
||||
tables of statements.
|
||||
|
||||
Reformatting is generally performed only before other large changes are to
|
||||
be made to a file. The following files are not yet clang-format clean:
|
||||
|
||||
clang-format -i include/verilated.h
|
||||
clang-format -i include/verilated_dpi.h
|
||||
clang-format -i include/verilated_fst_c.h
|
||||
clang-format -i include/verilated_heavy.h
|
||||
clang-format -i include/verilated_imp.h
|
||||
clang-format -i include/verilated_save.h
|
||||
clang-format -i include/verilated_sym_props.h
|
||||
clang-format -i include/verilated_unordered_set_map.h
|
||||
clang-format -i include/verilated_vcd_c.h
|
||||
clang-format -i include/verilatedos.h
|
||||
|
||||
clang-format -i include/verilated.cpp
|
||||
clang-format -i include/verilated_cov.cpp
|
||||
clang-format -i include/verilated_dpi.cpp
|
||||
clang-format -i include/verilated_fst_c.cpp
|
||||
clang-format -i include/verilated_save.cpp
|
||||
clang-format -i include/verilated_threads.cpp
|
||||
clang-format -i include/verilated_vcd_c.cpp
|
||||
clang-format -i include/verilated_vpi.cpp
|
||||
|
||||
clang-format -i src/V3Ast.h
|
||||
clang-format -i src/V3AstNodes.h
|
||||
clang-format -i src/V3EmitCBase.h
|
||||
clang-format -i src/V3Error.h
|
||||
clang-format -i src/V3File.h
|
||||
clang-format -i src/V3FileLine.h
|
||||
clang-format -i src/V3Global.h
|
||||
clang-format -i src/V3Graph.h
|
||||
clang-format -i src/V3GraphDfa.h
|
||||
clang-format -i src/V3GraphStream.h
|
||||
clang-format -i src/V3Hashed.h
|
||||
clang-format -i src/V3LanguageWords.h
|
||||
clang-format -i src/V3LinkDot.h
|
||||
clang-format -i src/V3List.h
|
||||
clang-format -i src/V3Number.h
|
||||
clang-format -i src/V3Options.h
|
||||
clang-format -i src/V3OrderGraph.h
|
||||
clang-format -i src/V3Os.h
|
||||
clang-format -i src/V3ParseImp.h
|
||||
clang-format -i src/V3ParseSym.h
|
||||
clang-format -i src/V3Partition.h
|
||||
clang-format -i src/V3PartitionGraph.h
|
||||
clang-format -i src/V3PreLex.h
|
||||
clang-format -i src/V3PreProc.h
|
||||
clang-format -i src/V3Scoreboard.h
|
||||
clang-format -i src/V3SenTree.h
|
||||
clang-format -i src/V3Simulate.h
|
||||
clang-format -i src/V3Stats.h
|
||||
clang-format -i src/V3String.h
|
||||
clang-format -i src/V3SymTable.h
|
||||
clang-format -i src/V3TSP.h
|
||||
clang-format -i src/V3Task.h
|
||||
clang-format -i src/V3WidthCommit.h
|
||||
|
||||
clang-format -i src/V3Active.cpp
|
||||
clang-format -i src/V3ActiveTop.cpp
|
||||
clang-format -i src/V3Assert.cpp
|
||||
clang-format -i src/V3AssertPre.cpp
|
||||
clang-format -i src/V3Ast.cpp
|
||||
clang-format -i src/V3AstNodes.cpp
|
||||
clang-format -i src/V3Begin.cpp
|
||||
clang-format -i src/V3Branch.cpp
|
||||
clang-format -i src/V3Broken.cpp
|
||||
clang-format -i src/V3CCtors.cpp
|
||||
clang-format -i src/V3Case.cpp
|
||||
clang-format -i src/V3Cast.cpp
|
||||
clang-format -i src/V3Cdc.cpp
|
||||
clang-format -i src/V3Changed.cpp
|
||||
clang-format -i src/V3Clean.cpp
|
||||
clang-format -i src/V3Clock.cpp
|
||||
clang-format -i src/V3Combine.cpp
|
||||
clang-format -i src/V3Const.cpp
|
||||
clang-format -i src/V3Coverage.cpp
|
||||
clang-format -i src/V3CoverageJoin.cpp
|
||||
clang-format -i src/V3Dead.cpp
|
||||
clang-format -i src/V3Delayed.cpp
|
||||
clang-format -i src/V3Depth.cpp
|
||||
clang-format -i src/V3DepthBlock.cpp
|
||||
clang-format -i src/V3EmitC.cpp
|
||||
clang-format -i src/V3EmitCInlines.cpp
|
||||
clang-format -i src/V3EmitCMake.cpp
|
||||
clang-format -i src/V3EmitCSyms.cpp
|
||||
clang-format -i src/V3EmitMk.cpp
|
||||
clang-format -i src/V3EmitV.cpp
|
||||
clang-format -i src/V3EmitXml.cpp
|
||||
clang-format -i src/V3Error.cpp
|
||||
clang-format -i src/V3Expand.cpp
|
||||
clang-format -i src/V3File.cpp
|
||||
clang-format -i src/V3FileLine.cpp
|
||||
clang-format -i src/V3Gate.cpp
|
||||
clang-format -i src/V3GenClk.cpp
|
||||
clang-format -i src/V3Graph.cpp
|
||||
clang-format -i src/V3GraphAcyc.cpp
|
||||
clang-format -i src/V3GraphAlg.cpp
|
||||
clang-format -i src/V3GraphDfa.cpp
|
||||
clang-format -i src/V3GraphPathChecker.cpp
|
||||
clang-format -i src/V3GraphTest.cpp
|
||||
clang-format -i src/V3Hashed.cpp
|
||||
clang-format -i src/V3Inline.cpp
|
||||
clang-format -i src/V3Inst.cpp
|
||||
clang-format -i src/V3InstrCount.cpp
|
||||
clang-format -i src/V3Life.cpp
|
||||
clang-format -i src/V3LifePost.cpp
|
||||
clang-format -i src/V3LinkCells.cpp
|
||||
clang-format -i src/V3LinkDot.cpp
|
||||
clang-format -i src/V3LinkJump.cpp
|
||||
clang-format -i src/V3LinkLValue.cpp
|
||||
clang-format -i src/V3LinkLevel.cpp
|
||||
clang-format -i src/V3LinkParse.cpp
|
||||
clang-format -i src/V3LinkResolve.cpp
|
||||
clang-format -i src/V3Localize.cpp
|
||||
clang-format -i src/V3Name.cpp
|
||||
clang-format -i src/V3Number.cpp
|
||||
clang-format -i src/V3Number_test.cpp
|
||||
clang-format -i src/V3Options.cpp
|
||||
clang-format -i src/V3Order.cpp
|
||||
clang-format -i src/V3Os.cpp
|
||||
clang-format -i src/V3Param.cpp
|
||||
clang-format -i src/V3ParseGrammar.cpp
|
||||
clang-format -i src/V3ParseImp.cpp
|
||||
clang-format -i src/V3ParseLex.cpp
|
||||
clang-format -i src/V3Partition.cpp
|
||||
clang-format -i src/V3PreProc.cpp
|
||||
clang-format -i src/V3PreShell.cpp
|
||||
clang-format -i src/V3Premit.cpp
|
||||
clang-format -i src/V3ProtectLib.cpp
|
||||
clang-format -i src/V3Reloop.cpp
|
||||
clang-format -i src/V3Scope.cpp
|
||||
clang-format -i src/V3Scoreboard.cpp
|
||||
clang-format -i src/V3Slice.cpp
|
||||
clang-format -i src/V3Split.cpp
|
||||
clang-format -i src/V3SplitAs.cpp
|
||||
clang-format -i src/V3SplitVar.cpp
|
||||
clang-format -i src/V3Stats.cpp
|
||||
clang-format -i src/V3StatsReport.cpp
|
||||
clang-format -i src/V3String.cpp
|
||||
clang-format -i src/V3Subst.cpp
|
||||
clang-format -i src/V3TSP.cpp
|
||||
clang-format -i src/V3Table.cpp
|
||||
clang-format -i src/V3Task.cpp
|
||||
clang-format -i src/V3Trace.cpp
|
||||
clang-format -i src/V3TraceDecl.cpp
|
||||
clang-format -i src/V3Tristate.cpp
|
||||
clang-format -i src/V3Undriven.cpp
|
||||
clang-format -i src/V3Unknown.cpp
|
||||
clang-format -i src/V3Unroll.cpp
|
||||
clang-format -i src/V3Width.cpp
|
||||
clang-format -i src/V3WidthSel.cpp
|
||||
clang-format -i src/Verilator.cpp
|
||||
|
|
@ -78,17 +78,7 @@ MSVC++.
|
|||
|
||||
=== Install Prerequisites
|
||||
|
||||
To build Verilator you will need to install some standard packages:
|
||||
|
||||
sudo apt-get install git
|
||||
sudo apt-get install autoconf
|
||||
sudo apt-get install flex bison
|
||||
|
||||
The following are optional, but improve compilation speed:
|
||||
|
||||
sudo apt-get install libgoogle-perftools-dev
|
||||
|
||||
Additionally, to build or run Verilator you need these standard packages:
|
||||
To build or run Verilator you need these standard packages:
|
||||
|
||||
sudo apt-get install perl python3
|
||||
sudo apt-get install make
|
||||
|
|
@ -96,9 +86,21 @@ Additionally, to build or run Verilator you need these standard packages:
|
|||
sudo apt-get install libgz # Non-Ubuntu (ignore if gives error)
|
||||
sudo apt-get install libfl2 libfl-dev zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error)
|
||||
|
||||
Those developing Verilator may also want these (see internals.adoc):
|
||||
To build or run the following are optional but should be installed for
|
||||
good performance:
|
||||
|
||||
sudo apt-get install gdb asciidoctor graphviz cmake
|
||||
sudo apt-get install ccache # If present at build, needed for run
|
||||
sudo apt-get install libgoogle-perftools-dev numactl
|
||||
|
||||
To build Verilator you will need to install these packages; these do not
|
||||
need to be present to run Verilator:
|
||||
|
||||
sudo apt-get install git
|
||||
sudo apt-get install autoconf flex bison
|
||||
|
||||
Those developing Verilator itself may also want these (see internals.adoc):
|
||||
|
||||
sudo apt-get install gdb asciidoctor graphviz cmake clang-format
|
||||
cpan install Pod::Perldoc
|
||||
cpan install Unix::Processors
|
||||
cpan install Parallel::Forker
|
||||
|
|
|
|||
|
|
@ -405,6 +405,15 @@ This sets indentation to the `cc-mode` defaults. (Verilator predates a
|
|||
CC-mode change of several years ago which overrides the defaults with GNU
|
||||
style indentation; the `c-set-style` undoes that.)
|
||||
|
||||
* Use "mixedCapsSymbols" instead of "underlined_symbols".
|
||||
|
||||
* Uas a "p" suffix on variables that are pointers, e.g. "nodep".
|
||||
|
||||
* Comment every member variable.
|
||||
|
||||
Indentation is automatically maintained with "make clang-format" (using
|
||||
clang-format version 10.0.0). For those manually formatting:
|
||||
|
||||
* Use 4 spaces per level, and no tabs.
|
||||
|
||||
* Use 2 spaces between the end of source and the beginning of a comment.
|
||||
|
|
@ -414,12 +423,6 @@ style indentation; the `c-set-style` undoes that.)
|
|||
* No spaces before semicolons, nor between a function's name and open
|
||||
parenthesis (only applies to functions; if/else has a following space).
|
||||
|
||||
* Use "mixedCapsSymbols" instead of "underlined_symbols".
|
||||
|
||||
* Uas a "p" suffix on variables that are pointers, e.g. "nodep".
|
||||
|
||||
* Comment every member variable.
|
||||
|
||||
=== The `astgen` Script
|
||||
|
||||
Some of the code implementing passes is extremely repetitive, and must be
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ parser.
|
|||
|
||||
== Structure
|
||||
|
||||
The XML document is consists of 4 sections within the top level `verilator_xml`
|
||||
The XML document consists of 4 sections within the top level `verilator_xml`
|
||||
element:
|
||||
|
||||
`<files>`...`</files>`::
|
||||
|
|
|
|||
|
|
@ -51,11 +51,11 @@ run:
|
|||
@echo "-- Verilator CMake hello world example"
|
||||
|
||||
@echo
|
||||
@echo "-- CMake ----------------"
|
||||
@echo "-- VERILATE ----------------"
|
||||
mkdir -p build && cd build && cmake ..
|
||||
|
||||
@echo
|
||||
@echo "-- COMPILE -----------------"
|
||||
@echo "-- BUILD -------------------"
|
||||
cmake --build build
|
||||
|
||||
@echo
|
||||
|
|
|
|||
|
|
@ -87,11 +87,11 @@ run:
|
|||
@echo "-- Verilator CMake SystemC hello-world simple example"
|
||||
|
||||
@echo
|
||||
@echo "-- CMake ----------------"
|
||||
@echo "-- VERILATE ----------------"
|
||||
mkdir -p build && cd build && cmake ..
|
||||
|
||||
@echo
|
||||
@echo "-- COMPILE -----------------"
|
||||
@echo "-- BUILD -------------------"
|
||||
cmake --build build
|
||||
|
||||
@echo
|
||||
|
|
|
|||
|
|
@ -51,11 +51,11 @@ run:
|
|||
@echo "-- Verilator CMake protect_lib example"
|
||||
|
||||
@echo
|
||||
@echo "-- CMake ----------------"
|
||||
@echo "-- VERILATE ----------------"
|
||||
mkdir -p build && cd build && cmake ..
|
||||
|
||||
@echo
|
||||
@echo "-- COMPILE -----------------"
|
||||
@echo "-- BUILD -------------------"
|
||||
cmake --build build
|
||||
|
||||
@echo
|
||||
|
|
|
|||
|
|
@ -51,11 +51,11 @@ run:
|
|||
@echo "-- Verilator CMake tracing example"
|
||||
|
||||
@echo
|
||||
@echo "-- CMake ----------------"
|
||||
@echo "-- VERILATE ----------------"
|
||||
mkdir -p build && cd build && cmake ..
|
||||
|
||||
@echo
|
||||
@echo "-- COMPILE -----------------"
|
||||
@echo "-- BUILD -------------------"
|
||||
cmake --build build
|
||||
|
||||
@echo
|
||||
|
|
|
|||
|
|
@ -87,11 +87,11 @@ run:
|
|||
@echo "-- Verilator CMake SystemC tracing example"
|
||||
|
||||
@echo
|
||||
@echo "-- CMake ----------------"
|
||||
@echo "-- VERILATE ----------------"
|
||||
mkdir -p build && cd build && cmake ..
|
||||
|
||||
@echo
|
||||
@echo "-- COMPILE -----------------"
|
||||
@echo "-- BUILD -------------------"
|
||||
cmake --build build
|
||||
|
||||
@echo
|
||||
|
|
|
|||
|
|
@ -36,10 +36,8 @@ endif
|
|||
|
||||
default:
|
||||
@echo "-- Verilator hello-world simple example"
|
||||
@echo "-- VERILATE ----------------"
|
||||
$(VERILATOR) -cc --exe top.v sim_main.cpp
|
||||
@echo "-- COMPILE -----------------"
|
||||
$(MAKE) -j 4 -C obj_dir -f Vtop.mk
|
||||
@echo "-- VERILATE & BUILD --------"
|
||||
$(VERILATOR) -cc --exe --build -j top.v sim_main.cpp
|
||||
@echo "-- RUN ---------------------"
|
||||
obj_dir/Vtop
|
||||
@echo "-- DONE --------------------"
|
||||
|
|
|
|||
|
|
@ -45,10 +45,8 @@ endif
|
|||
|
||||
run:
|
||||
@echo "-- Verilator hello-world simple example"
|
||||
@echo "-- VERILATE ----------------"
|
||||
$(VERILATOR) -sc --exe top.v sc_main.cpp
|
||||
@echo "-- COMPILE -----------------"
|
||||
$(MAKE) -j 4 -C obj_dir -f Vtop.mk
|
||||
@echo "-- VERILATE & COMPILE ------"
|
||||
$(VERILATOR) -sc --exe --build -j top.v sc_main.cpp
|
||||
@echo "-- RUN ---------------------"
|
||||
obj_dir/Vtop
|
||||
@echo "-- DONE --------------------"
|
||||
|
|
|
|||
|
|
@ -29,16 +29,16 @@ int sc_main(int argc, char* argv[]) {
|
|||
Vtop* top = new Vtop("top");
|
||||
|
||||
// Initialize SC model
|
||||
#if (SYSTEMC_VERSION>=20070314)
|
||||
sc_start(1,SC_NS);
|
||||
#if (SYSTEMC_VERSION >= 20070314)
|
||||
sc_start(1, SC_NS);
|
||||
#else
|
||||
sc_start(1);
|
||||
#endif
|
||||
|
||||
// Simulate until $finish
|
||||
while (!Verilated::gotFinish()) {
|
||||
#if (SYSTEMC_VERSION>=20070314)
|
||||
sc_start(1,SC_NS);
|
||||
#if (SYSTEMC_VERSION >= 20070314)
|
||||
sc_start(1, SC_NS);
|
||||
#else
|
||||
sc_start(1);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -6,14 +6,16 @@
|
|||
|
||||
// This module will be used as libsecret.a or libsecret.so without
|
||||
// exposing the source.
|
||||
module secret_impl(
|
||||
input [31:0] a,
|
||||
input [31:0] b,
|
||||
output logic [31:0] x,
|
||||
input clk);
|
||||
|
||||
logic [31:0] accum_q = 0;
|
||||
logic [31:0] secret_value = 9;
|
||||
module secret_impl
|
||||
(
|
||||
input [31:0] a,
|
||||
input [31:0] b,
|
||||
output logic [31:0] x,
|
||||
input clk);
|
||||
|
||||
logic [31:0] accum_q = 0;
|
||||
logic [31:0] secret_value = 9;
|
||||
|
||||
initial $display("%m: initialized");
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,7 @@
|
|||
#endif
|
||||
|
||||
vluint64_t main_time = 0;
|
||||
double sc_time_stamp() {
|
||||
return main_time;
|
||||
}
|
||||
double sc_time_stamp() { return main_time; }
|
||||
|
||||
int main(int argc, char** argv, char** env) {
|
||||
if (0 && argc && argv && env) {}
|
||||
|
|
@ -35,7 +33,7 @@ int main(int argc, char** argv, char** env) {
|
|||
// When tracing, the contents of the secret module will not be seen
|
||||
VerilatedVcdC* tfp = NULL;
|
||||
const char* flag = Verilated::commandArgsPlusMatch("trace");
|
||||
if (flag && 0==strcmp(flag, "+trace")) {
|
||||
if (flag && 0 == strcmp(flag, "+trace")) {
|
||||
Verilated::traceEverOn(true);
|
||||
VL_PRINTF("Enabling waves into logs/vlt_dump.vcd...\n");
|
||||
tfp = new VerilatedVcdC;
|
||||
|
|
@ -62,11 +60,15 @@ int main(int argc, char** argv, char** env) {
|
|||
|
||||
// Close trace if opened
|
||||
#if VM_TRACE
|
||||
if (tfp) { tfp->close(); tfp = NULL; }
|
||||
if (tfp) {
|
||||
tfp->close();
|
||||
tfp = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Destroy model
|
||||
delete top; top = NULL;
|
||||
delete top;
|
||||
top = NULL;
|
||||
|
||||
// Fin
|
||||
exit(0);
|
||||
|
|
|
|||
|
|
@ -68,11 +68,13 @@ run:
|
|||
$(VERILATOR) $(VERILATOR_FLAGS) $(VERILATOR_INPUT)
|
||||
|
||||
@echo
|
||||
@echo "-- COMPILE -----------------"
|
||||
# To compile, we can either just do what Verilator asks,
|
||||
# or call a submakefile where we can override the rules ourselves
|
||||
# $(MAKE) -j 4 -C obj_dir -f Vtop.mk
|
||||
$(MAKE) -j 4 -C obj_dir -f ../Makefile_obj
|
||||
@echo "-- BUILD -------------------"
|
||||
# To compile, we can either
|
||||
# 1. Pass --build to Verilator by editing VERILATOR_FLAGS above.
|
||||
# 2. Or, run the make rules Verilator does:
|
||||
# $(MAKE) -j -C obj_dir -f Vtop.mk
|
||||
# 3. Or, call a submakefile where we can override the rules ourselves:
|
||||
$(MAKE) -j -C obj_dir -f ../Makefile_obj
|
||||
|
||||
@echo
|
||||
@echo "-- RUN ---------------------"
|
||||
|
|
|
|||
|
|
@ -83,9 +83,9 @@ int main(int argc, char** argv, char** env) {
|
|||
|
||||
// Read outputs
|
||||
VL_PRINTF("[%" VL_PRI64 "d] clk=%x rstl=%x iquad=%" VL_PRI64 "x"
|
||||
" -> oquad=%" VL_PRI64"x owide=%x_%08x_%08x\n",
|
||||
main_time, top->clk, top->reset_l, top->in_quad,
|
||||
top->out_quad, top->out_wide[2], top->out_wide[1], top->out_wide[0]);
|
||||
" -> oquad=%" VL_PRI64 "x owide=%x_%08x_%08x\n",
|
||||
main_time, top->clk, top->reset_l, top->in_quad, top->out_quad, top->out_wide[2],
|
||||
top->out_wide[1], top->out_wide[0]);
|
||||
}
|
||||
|
||||
// Final model cleanup
|
||||
|
|
@ -98,7 +98,8 @@ int main(int argc, char** argv, char** env) {
|
|||
#endif
|
||||
|
||||
// Destroy model
|
||||
delete top; top = NULL;
|
||||
delete top;
|
||||
top = NULL;
|
||||
|
||||
// Fin
|
||||
exit(0);
|
||||
|
|
|
|||
|
|
@ -11,15 +11,15 @@
|
|||
module top
|
||||
(
|
||||
// Declare some signals so we can see how I/O works
|
||||
input clk,
|
||||
input reset_l,
|
||||
input clk,
|
||||
input reset_l,
|
||||
|
||||
output wire [1:0] out_small,
|
||||
output wire [39:0] out_quad,
|
||||
output wire [69:0] out_wide,
|
||||
input [1:0] in_small,
|
||||
input [39:0] in_quad,
|
||||
input [69:0] in_wide
|
||||
input [1:0] in_small,
|
||||
input [39:0] in_quad,
|
||||
input [69:0] in_wide
|
||||
);
|
||||
|
||||
// Connect up the outputs, using some trivial logic
|
||||
|
|
|
|||
|
|
@ -77,10 +77,12 @@ run:
|
|||
|
||||
@echo
|
||||
@echo "-- COMPILE -----------------"
|
||||
# To compile, we can either just do what Verilator asks,
|
||||
# or call a submakefile where we can override the rules ourselves
|
||||
# $(MAKE) -j 4 -C obj_dir -f Vtop.mk
|
||||
$(MAKE) -j 4 -C obj_dir -f ../Makefile_obj
|
||||
# To compile, we can either
|
||||
# 1. Pass --build to Verilator by editing VERILATOR_FLAGS above.
|
||||
# 2. Or, run the make rules Verilator does:
|
||||
# $(MAKE) -j -C obj_dir -f Vtop.mk
|
||||
# 3. Or, call a submakefile where we can override the rules ourselves:
|
||||
$(MAKE) -j -C obj_dir -f ../Makefile_obj
|
||||
|
||||
@echo
|
||||
@echo "-- RUN ---------------------"
|
||||
|
|
|
|||
|
|
@ -45,19 +45,19 @@ int sc_main(int argc, char* argv[]) {
|
|||
ios::sync_with_stdio();
|
||||
|
||||
// Defaults time
|
||||
#if (SYSTEMC_VERSION>20011000)
|
||||
#if (SYSTEMC_VERSION > 20011000)
|
||||
#else
|
||||
sc_time dut(1.0, sc_ns);
|
||||
sc_set_default_time_unit(dut);
|
||||
#endif
|
||||
|
||||
// Define clocks
|
||||
#if (SYSTEMC_VERSION>=20070314)
|
||||
sc_clock clk ("clk", 10,SC_NS, 0.5, 3,SC_NS, true);
|
||||
sc_clock fastclk ("fastclk", 2,SC_NS, 0.5, 2,SC_NS, true);
|
||||
#if (SYSTEMC_VERSION >= 20070314)
|
||||
sc_clock clk("clk", 10, SC_NS, 0.5, 3, SC_NS, true);
|
||||
sc_clock fastclk("fastclk", 2, SC_NS, 0.5, 2, SC_NS, true);
|
||||
#else
|
||||
sc_clock clk ("clk", 10, 0.5, 3, true);
|
||||
sc_clock fastclk ("fastclk", 2, 0.5, 2, true);
|
||||
sc_clock clk("clk", 10, 0.5, 3, true);
|
||||
sc_clock fastclk("fastclk", 2, 0.5, 2, true);
|
||||
#endif
|
||||
|
||||
// Define interconnect
|
||||
|
|
@ -72,15 +72,15 @@ int sc_main(int argc, char* argv[]) {
|
|||
// Construct the Verilated model, from inside Vtop.h
|
||||
Vtop* top = new Vtop("top");
|
||||
// Attach signals to the model
|
||||
top->clk (clk);
|
||||
top->fastclk (fastclk);
|
||||
top->reset_l (reset_l);
|
||||
top->in_small (in_small);
|
||||
top->in_quad (in_quad);
|
||||
top->in_wide (in_wide);
|
||||
top->out_small (out_small);
|
||||
top->out_quad (out_quad);
|
||||
top->out_wide (out_wide);
|
||||
top->clk(clk);
|
||||
top->fastclk(fastclk);
|
||||
top->reset_l(reset_l);
|
||||
top->in_small(in_small);
|
||||
top->in_quad(in_quad);
|
||||
top->in_wide(in_wide);
|
||||
top->out_small(out_small);
|
||||
top->out_quad(out_quad);
|
||||
top->out_wide(out_wide);
|
||||
|
||||
#if VM_TRACE
|
||||
// Before any evaluation, need to know to calculate those signals only used for tracing
|
||||
|
|
@ -89,8 +89,8 @@ int sc_main(int argc, char* argv[]) {
|
|||
|
||||
// You must do one evaluation before enabling waves, in order to allow
|
||||
// SystemC to interconnect everything for testing.
|
||||
#if (SYSTEMC_VERSION>=20070314)
|
||||
sc_start(1,SC_NS);
|
||||
#if (SYSTEMC_VERSION >= 20070314)
|
||||
sc_start(1, SC_NS);
|
||||
#else
|
||||
sc_start(1);
|
||||
#endif
|
||||
|
|
@ -100,7 +100,7 @@ int sc_main(int argc, char* argv[]) {
|
|||
// and if at run time passed the +trace argument, turn on tracing
|
||||
VerilatedVcdSc* tfp = NULL;
|
||||
const char* flag = Verilated::commandArgsPlusMatch("trace");
|
||||
if (flag && 0==strcmp(flag, "+trace")) {
|
||||
if (flag && 0 == strcmp(flag, "+trace")) {
|
||||
cout << "Enabling waves into logs/vlt_dump.vcd...\n";
|
||||
tfp = new VerilatedVcdSc;
|
||||
top->trace(tfp, 99); // Trace 99 levels of hierarchy
|
||||
|
|
@ -118,15 +118,15 @@ int sc_main(int argc, char* argv[]) {
|
|||
#endif
|
||||
|
||||
// Apply inputs
|
||||
if (VL_TIME_Q() > 1 && VL_TIME_Q() < 10) {
|
||||
if (sc_time_stamp() > sc_time(1, SC_NS) && sc_time_stamp() < sc_time(10, SC_NS)) {
|
||||
reset_l = !1; // Assert reset
|
||||
} else if (VL_TIME_Q() > 1) {
|
||||
} else {
|
||||
reset_l = !0; // Deassert reset
|
||||
}
|
||||
|
||||
// Simulate 1ns
|
||||
#if (SYSTEMC_VERSION>=20070314)
|
||||
sc_start(1,SC_NS);
|
||||
#if (SYSTEMC_VERSION >= 20070314)
|
||||
sc_start(1, SC_NS);
|
||||
#else
|
||||
sc_start(1);
|
||||
#endif
|
||||
|
|
@ -137,7 +137,10 @@ int sc_main(int argc, char* argv[]) {
|
|||
|
||||
// Close trace if opened
|
||||
#if VM_TRACE
|
||||
if (tfp) { tfp->close(); tfp = NULL; }
|
||||
if (tfp) {
|
||||
tfp->close();
|
||||
tfp = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Coverage analysis (since test passed)
|
||||
|
|
@ -147,7 +150,8 @@ int sc_main(int argc, char* argv[]) {
|
|||
#endif
|
||||
|
||||
// Destroy model
|
||||
delete top; top = NULL;
|
||||
delete top;
|
||||
top = NULL;
|
||||
|
||||
// Fin
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -11,16 +11,16 @@
|
|||
module top
|
||||
(
|
||||
// Declare some signals so we can see how I/O works
|
||||
input clk,
|
||||
input fastclk,
|
||||
input reset_l,
|
||||
input clk,
|
||||
input fastclk,
|
||||
input reset_l,
|
||||
|
||||
output wire [1:0] out_small,
|
||||
output wire [39:0] out_quad,
|
||||
output wire [69:0] out_wide,
|
||||
input [1:0] in_small,
|
||||
input [39:0] in_quad,
|
||||
input [69:0] in_wide
|
||||
input [1:0] in_small,
|
||||
input [39:0] in_quad,
|
||||
input [69:0] in_wide
|
||||
);
|
||||
|
||||
// Connect up the outputs, using some trivial logic
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
module sub
|
||||
#(parameter type TYPE_t = logic)
|
||||
(
|
||||
input TYPE_t in,
|
||||
input TYPE_t in,
|
||||
output TYPE_t out
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,16 +7,16 @@
|
|||
|
||||
module top
|
||||
(
|
||||
input clk,
|
||||
input fastclk,
|
||||
input reset_l,
|
||||
input clk,
|
||||
input fastclk,
|
||||
input reset_l,
|
||||
|
||||
output wire [1:0] out_small,
|
||||
output wire [39:0] out_quad,
|
||||
output wire [69:0] out_wide,
|
||||
input [1:0] in_small,
|
||||
input [39:0] in_quad,
|
||||
input [69:0] in_wide
|
||||
input [1:0] in_small,
|
||||
input [39:0] in_quad,
|
||||
input [69:0] in_wide
|
||||
);
|
||||
|
||||
sub #(.TYPE_t(logic [1:0])) sub_small
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
1324
include/verilated.h
1324
include/verilated.h
File diff suppressed because it is too large
Load Diff
|
|
@ -12,8 +12,9 @@
|
|||
PERL = @PERL@
|
||||
CXX = @CXX@
|
||||
LINK = @CXX@
|
||||
AR = ar
|
||||
AR = ar
|
||||
RANLIB = ranlib
|
||||
OBJCACHE ?= @OBJCACHE@
|
||||
|
||||
CFG_WITH_CCWARN = @CFG_WITH_CCWARN@
|
||||
CFG_WITH_LONGTESTS = @CFG_WITH_LONGTESTS@
|
||||
|
|
@ -114,24 +115,49 @@ endif
|
|||
#######################################################################
|
||||
##### Threaded builds
|
||||
|
||||
ifneq ($(VM_C11),0)
|
||||
ifneq ($(VM_C11),)
|
||||
VK_C11=1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(VM_THREADS),0)
|
||||
ifneq ($(VM_THREADS),)
|
||||
CPPFLAGS += -DVL_THREADED
|
||||
VK_C11=1
|
||||
VK_LIBS_THREADED=1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(VM_TRACE_THREADED),0)
|
||||
ifneq ($(VM_TRACE_THREADED),)
|
||||
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
|
||||
CPPFLAGS += $(CFG_CXXFLAGS_STD_NEWEST)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(VK_LIBS_THREADED),0)
|
||||
ifneq ($(VK_LIBS_THREADED),)
|
||||
# Need C++11 at least, so always default to newest
|
||||
CPPFLAGS += $(CFG_CXXFLAGS_STD_NEWEST)
|
||||
LDLIBS += $(CFG_LDLIBS_THREADS)
|
||||
endif
|
||||
endif
|
||||
|
|
@ -141,18 +167,14 @@ endif
|
|||
|
||||
preproc:
|
||||
|
||||
#######################################################################
|
||||
##### C/H builds
|
||||
|
||||
LIBS += -lm -lstdc++
|
||||
|
||||
#######################################################################
|
||||
# Overall Objects Linking
|
||||
|
||||
VK_CLASSES_H = $(addsuffix .h, $(VM_CLASSES))
|
||||
VK_CLASSES_CPP = $(addsuffix .cpp, $(VM_CLASSES))
|
||||
VK_CLASSES_FAST_CPP = $(addsuffix .cpp, $(VM_CLASSES_FAST))
|
||||
VK_CLASSES_SLOW_CPP = $(addsuffix .cpp, $(VM_CLASSES_SLOW))
|
||||
|
||||
VK_SUPPORT_CPP = $(addsuffix .cpp, $(VM_SUPPORT))
|
||||
VK_SUPPORT_FAST_CPP = $(addsuffix .cpp, $(VM_SUPPORT_FAST))
|
||||
VK_SUPPORT_SLOW_CPP = $(addsuffix .cpp, $(VM_SUPPORT_SLOW))
|
||||
|
||||
VK_USER_OBJS = $(addsuffix .o, $(VM_USER_CLASSES))
|
||||
|
||||
|
|
@ -161,11 +183,11 @@ VK_GLOBAL_OBJS = $(addsuffix .o, $(VM_GLOBAL_FAST) $(VM_GLOBAL_SLOW))
|
|||
ifneq ($(VM_PARALLEL_BUILDS),1)
|
||||
# Fast building, all .cpp's in one fell swoop
|
||||
# This saves about 5 sec per module, but can be slower if only a little changes
|
||||
VK_OBJS += $(VM_PREFIX)__ALLcls.o $(VM_PREFIX)__ALLsup.o
|
||||
all_cpp: $(VM_PREFIX)__ALLcls.cpp $(VM_PREFIX)__ALLsup.cpp
|
||||
$(VM_PREFIX)__ALLcls.cpp: $(VK_CLASSES_CPP)
|
||||
VK_OBJS += $(VM_PREFIX)__ALLfast.o $(VM_PREFIX)__ALLslow.o
|
||||
all_cpp: $(VM_PREFIX)__ALLfast.cpp $(VM_PREFIX)__ALLslow.cpp
|
||||
$(VM_PREFIX)__ALLfast.cpp: $(VK_CLASSES_FAST_CPP) $(VK_SUPPORT_FAST_CPP)
|
||||
$(VERILATOR_INCLUDER) -DVL_INCLUDE_OPT=include $^ > $@
|
||||
$(VM_PREFIX)__ALLsup.cpp: $(VK_SUPPORT_CPP)
|
||||
$(VM_PREFIX)__ALLslow.cpp: $(VK_CLASSES_SLOW_CPP) $(VK_SUPPORT_SLOW_CPP)
|
||||
$(VERILATOR_INCLUDER) -DVL_INCLUDE_OPT=include $^ > $@
|
||||
else
|
||||
#Slow way of building... Each .cpp file by itself
|
||||
|
|
@ -180,10 +202,10 @@ $(VM_PREFIX)__ALL.a: $(VK_OBJS)
|
|||
### Compile rules
|
||||
|
||||
ifneq ($(VM_DEFAULT_RULES),0)
|
||||
$(VM_PREFIX)__ALLcls.o: $(VM_PREFIX)__ALLcls.cpp
|
||||
$(VM_PREFIX)__ALLfast.o: $(VM_PREFIX)__ALLfast.cpp
|
||||
$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ $<
|
||||
|
||||
$(VM_PREFIX)__ALLsup.o: $(VM_PREFIX)__ALLsup.cpp
|
||||
$(VM_PREFIX)__ALLslow.o: $(VM_PREFIX)__ALLslow.cpp
|
||||
$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_SLOW) -c -o $@ $<
|
||||
|
||||
# VM_GLOBAL_FAST files including verilated.o use this rule
|
||||
|
|
|
|||
|
|
@ -16,9 +16,8 @@
|
|||
///
|
||||
//*************************************************************************
|
||||
|
||||
|
||||
///**** Product and Version name
|
||||
|
||||
// Autoconf substitutes this with the strings from AC_INIT.
|
||||
#define VERILATOR_PRODUCT "@PACKAGE_NAME@"
|
||||
#define VERILATOR_VERSION "@PACKAGE_VERSION@"
|
||||
#define VERILATOR_PRODUCT "@PACKAGE_NAME@"
|
||||
#define VERILATOR_VERSION "@PACKAGE_VERSION@"
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public: // But only local to this file
|
|||
// CONSTRUCTORS
|
||||
// Derived classes should call zero() in their constructor
|
||||
VerilatedCovImpItem() {
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
for (int i = 0; i < MAX_KEYS; ++i) {
|
||||
m_keys[i] = KEY_UNDEF;
|
||||
m_vals[i] = 0;
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ public: // But only local to this file
|
|||
template <class T> class VerilatedCoverItemSpec : public VerilatedCovImpItem {
|
||||
private:
|
||||
// MEMBERS
|
||||
T* m_countp; ///< Count value
|
||||
T* m_countp; ///< Count value
|
||||
public:
|
||||
// METHODS
|
||||
// cppcheck-suppress truncLongCastReturn
|
||||
|
|
@ -74,7 +74,10 @@ public:
|
|||
virtual void zero() const VL_OVERRIDE { *m_countp = 0; }
|
||||
// CONSTRUCTORS
|
||||
// cppcheck-suppress noExplicitConstructor
|
||||
explicit VerilatedCoverItemSpec(T* countp) : m_countp(countp) { *m_countp = 0; }
|
||||
explicit VerilatedCoverItemSpec(T* countp)
|
||||
: m_countp(countp) {
|
||||
*m_countp = 0;
|
||||
}
|
||||
virtual ~VerilatedCoverItemSpec() VL_OVERRIDE {}
|
||||
};
|
||||
|
||||
|
|
@ -87,20 +90,20 @@ public:
|
|||
class VerilatedCovImp : VerilatedCovImpBase {
|
||||
private:
|
||||
// TYPES
|
||||
typedef std::map<std::string,int> ValueIndexMap;
|
||||
typedef std::map<int,std::string> IndexValueMap;
|
||||
typedef std::map<std::string, int> ValueIndexMap;
|
||||
typedef std::map<int, std::string> IndexValueMap;
|
||||
typedef std::deque<VerilatedCovImpItem*> ItemList;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
VerilatedMutex m_mutex; ///< Protects all members, when VL_THREADED. Wrapper deals with setting it.
|
||||
ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex); ///< For each key/value a unique arbitrary index value
|
||||
IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex); ///< For each key/value a unique arbitrary index value
|
||||
ItemList m_items VL_GUARDED_BY(m_mutex); ///< List of all items
|
||||
VerilatedMutex m_mutex; ///< Protects all members
|
||||
ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex); ///< Unique arbitrary value for values
|
||||
IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex); ///< Unique arbitrary value for keys
|
||||
ItemList m_items VL_GUARDED_BY(m_mutex); ///< List of all items
|
||||
|
||||
VerilatedCovImpItem* m_insertp VL_GUARDED_BY(m_mutex); ///< Item about to insert
|
||||
const char* m_insertFilenamep VL_GUARDED_BY(m_mutex); ///< Filename about to insert
|
||||
int m_insertLineno VL_GUARDED_BY(m_mutex); ///< Line number about to insert
|
||||
const char* m_insertFilenamep VL_GUARDED_BY(m_mutex); ///< Filename about to insert
|
||||
int m_insertLineno VL_GUARDED_BY(m_mutex); ///< Line number about to insert
|
||||
|
||||
// CONSTRUCTORS
|
||||
VerilatedCovImp() {
|
||||
|
|
@ -109,6 +112,7 @@ private:
|
|||
m_insertLineno = 0;
|
||||
}
|
||||
VL_UNCOPYABLE(VerilatedCovImp);
|
||||
|
||||
public:
|
||||
~VerilatedCovImp() { clearGuts(); }
|
||||
static VerilatedCovImp& imp() VL_MT_SAFE {
|
||||
|
|
@ -119,10 +123,11 @@ public:
|
|||
private:
|
||||
// PRIVATE METHODS
|
||||
int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) {
|
||||
static int nextIndex = KEY_UNDEF+1;
|
||||
static int nextIndex = KEY_UNDEF + 1;
|
||||
ValueIndexMap::iterator iter = m_valueIndexes.find(value);
|
||||
if (iter != m_valueIndexes.end()) return iter->second;
|
||||
nextIndex++; assert(nextIndex>0);
|
||||
nextIndex++;
|
||||
assert(nextIndex > 0); // Didn't rollover
|
||||
m_valueIndexes.insert(std::make_pair(value, nextIndex));
|
||||
m_indexValues.insert(std::make_pair(nextIndex, value));
|
||||
return nextIndex;
|
||||
|
|
@ -131,8 +136,9 @@ private:
|
|||
// Quote any special characters
|
||||
std::string rtn;
|
||||
for (const char* pos = text.c_str(); *pos; ++pos) {
|
||||
if (!isprint(*pos) || *pos=='%' || *pos=='"') {
|
||||
char hex[10]; sprintf(hex, "%%%02X", pos[0]);
|
||||
if (!isprint(*pos) || *pos == '%' || *pos == '"') {
|
||||
char hex[10];
|
||||
sprintf(hex, "%%%02X", pos[0]);
|
||||
rtn += hex;
|
||||
} else {
|
||||
rtn += *pos;
|
||||
|
|
@ -145,18 +151,19 @@ private:
|
|||
// don't want applications to either get confused if they use
|
||||
// a letter differently, nor want them to rely on our compression...
|
||||
// (Considered using numeric keys, but will remain back compatible.)
|
||||
if (key.length()<2) return false;
|
||||
if (key.length()==2 && isdigit(key[1])) return false;
|
||||
if (key.length() < 2) return false;
|
||||
if (key.length() == 2 && isdigit(key[1])) return false;
|
||||
return true;
|
||||
}
|
||||
static std::string keyValueFormatter(const std::string& key, const std::string& value) VL_PURE {
|
||||
static std::string keyValueFormatter(const std::string& key,
|
||||
const std::string& value) VL_PURE {
|
||||
std::string name;
|
||||
if (key.length()==1 && isalpha(key[0])) {
|
||||
name += std::string("\001")+key;
|
||||
if (key.length() == 1 && isalpha(key[0])) {
|
||||
name += std::string("\001") + key;
|
||||
} else {
|
||||
name += std::string("\001")+dequote(key);
|
||||
name += std::string("\001") + dequote(key);
|
||||
}
|
||||
name += std::string("\002")+dequote(value);
|
||||
name += std::string("\002") + dequote(value);
|
||||
return name;
|
||||
}
|
||||
static std::string combineHier(const std::string& old, const std::string& add) VL_PURE {
|
||||
|
|
@ -173,28 +180,35 @@ private:
|
|||
// Scan forward to first mismatch
|
||||
const char* apre = a;
|
||||
const char* bpre = b;
|
||||
while (*apre == *bpre) { apre++; bpre++; }
|
||||
while (*apre == *bpre) {
|
||||
apre++;
|
||||
bpre++;
|
||||
}
|
||||
|
||||
// We used to backup and split on only .'s but it seems better to be verbose
|
||||
// and not assume . is the separator
|
||||
std::string prefix = std::string(a, apre-a);
|
||||
std::string prefix = std::string(a, apre - a);
|
||||
|
||||
// Scan backward to last mismatch
|
||||
const char* apost = a+strlen(a)-1;
|
||||
const char* bpost = b+strlen(b)-1;
|
||||
while (*apost == *bpost
|
||||
&& apost>apre && bpost>bpre) { apost--; bpost--; }
|
||||
const char* apost = a + strlen(a) - 1;
|
||||
const char* bpost = b + strlen(b) - 1;
|
||||
while (*apost == *bpost && apost > apre && bpost > bpre) {
|
||||
apost--;
|
||||
bpost--;
|
||||
}
|
||||
|
||||
// Forward to . so we have a whole word
|
||||
std::string suffix = *bpost ? std::string(bpost+1) : "";
|
||||
std::string suffix = *bpost ? std::string(bpost + 1) : "";
|
||||
|
||||
std::string out = prefix+"*"+suffix;
|
||||
std::string out = prefix + "*" + suffix;
|
||||
|
||||
//cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add<<"\nch o="<<out<<endl;
|
||||
// cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add
|
||||
// <<"\ncho="<<out<<endl;
|
||||
return out;
|
||||
}
|
||||
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match) VL_REQUIRES(m_mutex) {
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match)
|
||||
VL_REQUIRES(m_mutex) {
|
||||
for (int i = 0; i < MAX_KEYS; ++i) {
|
||||
if (itemp->m_keys[i] != KEY_UNDEF) {
|
||||
// We don't compare keys, only values
|
||||
std::string val = m_indexValues[itemp->m_vals[i]];
|
||||
|
|
@ -207,17 +221,23 @@ private:
|
|||
}
|
||||
static void selftest() VL_MT_SAFE {
|
||||
// Little selftest
|
||||
if (combineHier("a.b.c","a.b.c") !="a.b.c") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("a.b.c","a.b") !="a.b*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("a.x.c","a.y.c") !="a.*.c") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("a.z.z.z.c","a.b.c") !="a.*.c") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("z","a") !="*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("q.a","q.b") !="q.*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("q.za","q.zb") !="q.z*") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier("1.2.3.a","9.8.7.a") !="*.a") VL_FATAL_MT(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
#define VL_CST_CHECK(got, exp) \
|
||||
do { \
|
||||
if ((got) != (exp)) VL_FATAL_MT(__FILE__, __LINE__, "", "%Error: selftest\n"); \
|
||||
} while (0)
|
||||
|
||||
VL_CST_CHECK(combineHier("a.b.c", "a.b.c"), "a.b.c");
|
||||
VL_CST_CHECK(combineHier("a.b.c", "a.b"), "a.b*");
|
||||
VL_CST_CHECK(combineHier("a.x.c", "a.y.c"), "a.*.c");
|
||||
VL_CST_CHECK(combineHier("a.z.z.z.c", "a.b.c"), "a.*.c");
|
||||
VL_CST_CHECK(combineHier("z", "a"), "*");
|
||||
VL_CST_CHECK(combineHier("q.a", "q.b"), "q.*");
|
||||
VL_CST_CHECK(combineHier("q.za", "q.zb"), "q.z*");
|
||||
VL_CST_CHECK(combineHier("1.2.3.a", "9.8.7.a"), "*.a");
|
||||
#undef VL_CST_CHECK
|
||||
}
|
||||
void clearGuts() VL_REQUIRES(m_mutex) {
|
||||
for (ItemList::const_iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
for (ItemList::const_iterator it = m_items.begin(); it != m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
VL_DO_DANGLING(delete itemp, itemp);
|
||||
}
|
||||
|
|
@ -238,7 +258,7 @@ public:
|
|||
VerilatedLockGuard lock(m_mutex);
|
||||
if (matchp && matchp[0]) {
|
||||
ItemList newlist;
|
||||
for (ItemList::iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
for (ItemList::iterator it = m_items.begin(); it != m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
if (!itemMatchesString(itemp, matchp)) {
|
||||
VL_DO_DANGLING(delete itemp, itemp);
|
||||
|
|
@ -252,7 +272,7 @@ public:
|
|||
void zero() VL_EXCLUDES(m_mutex) {
|
||||
Verilated::quiesce();
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
for (ItemList::const_iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
for (ItemList::const_iterator it = m_items.begin(); it != m_items.end(); ++it) {
|
||||
(*it)->zero();
|
||||
}
|
||||
}
|
||||
|
|
@ -268,33 +288,33 @@ public:
|
|||
m_insertFilenamep = filenamep;
|
||||
m_insertLineno = lineno;
|
||||
}
|
||||
void insertp(const char* ckeyps[MAX_KEYS],
|
||||
const char* valps[MAX_KEYS]) VL_EXCLUDES(m_mutex) {
|
||||
void insertp(const char* ckeyps[MAX_KEYS], const char* valps[MAX_KEYS]) VL_EXCLUDES(m_mutex) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
assert(m_insertp);
|
||||
// First two key/vals are filename
|
||||
ckeyps[0]="filename"; valps[0]=m_insertFilenamep;
|
||||
ckeyps[0] = "filename";
|
||||
valps[0] = m_insertFilenamep;
|
||||
std::string linestr = vlCovCvtToStr(m_insertLineno);
|
||||
ckeyps[1]="lineno"; valps[1]=linestr.c_str();
|
||||
ckeyps[1] = "lineno";
|
||||
valps[1] = linestr.c_str();
|
||||
// Default page if not specified
|
||||
const char* fnstartp = m_insertFilenamep;
|
||||
while (const char* foundp = strchr(fnstartp,'/')) fnstartp = foundp+1;
|
||||
while (const char* foundp = strchr(fnstartp, '/')) fnstartp = foundp + 1;
|
||||
const char* fnendp = fnstartp;
|
||||
while (*fnendp && *fnendp!='.') fnendp++;
|
||||
std::string page_default = "sp_user/"+std::string(fnstartp, fnendp-fnstartp);
|
||||
ckeyps[2]="page"; valps[2]=page_default.c_str();
|
||||
for (; *fnendp && *fnendp != '.'; fnendp++) {}
|
||||
std::string page_default = "sp_user/" + std::string(fnstartp, fnendp - fnstartp);
|
||||
ckeyps[2] = "page";
|
||||
valps[2] = page_default.c_str();
|
||||
|
||||
// Keys -> strings
|
||||
std::string keys[MAX_KEYS];
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
if (ckeyps[i] && ckeyps[i][0]) {
|
||||
keys[i] = ckeyps[i];
|
||||
}
|
||||
for (int i = 0; i < MAX_KEYS; ++i) {
|
||||
if (ckeyps[i] && ckeyps[i][0]) { keys[i] = ckeyps[i]; }
|
||||
}
|
||||
// Ignore empty keys
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
for (int i = 0; i < MAX_KEYS; ++i) {
|
||||
if (!keys[i].empty()) {
|
||||
for (int j=i+1; j<MAX_KEYS; ++j) {
|
||||
for (int j = i + 1; j < MAX_KEYS; ++j) {
|
||||
if (keys[i] == keys[j]) { // Duplicate key. Keep the last one
|
||||
keys[i] = "";
|
||||
break;
|
||||
|
|
@ -304,18 +324,18 @@ public:
|
|||
}
|
||||
// Insert the values
|
||||
int addKeynum = 0;
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
for (int i = 0; i < MAX_KEYS; ++i) {
|
||||
const std::string key = keys[i];
|
||||
if (!keys[i].empty()) {
|
||||
const std::string val = valps[i];
|
||||
//cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
|
||||
// cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
|
||||
m_insertp->m_keys[addKeynum] = valueIndex(key);
|
||||
m_insertp->m_vals[addKeynum] = valueIndex(val);
|
||||
addKeynum++;
|
||||
if (!legalKey(key)) {
|
||||
std::string msg
|
||||
= ("%Error: Coverage keys of one character, or letter+digit are illegal: "
|
||||
+key);
|
||||
+ key);
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
}
|
||||
}
|
||||
|
|
@ -335,22 +355,22 @@ public:
|
|||
|
||||
std::ofstream os(filename);
|
||||
if (os.fail()) {
|
||||
std::string msg = std::string("%Error: Can't write '")+filename+"'";
|
||||
std::string msg = std::string("%Error: Can't write '") + filename + "'";
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
return;
|
||||
}
|
||||
os << "# SystemC::Coverage-3\n";
|
||||
|
||||
// Build list of events; totalize if collapsing hierarchy
|
||||
typedef std::map<std::string,std::pair<std::string,vluint64_t> > EventMap;
|
||||
typedef std::map<std::string, std::pair<std::string, vluint64_t> > EventMap;
|
||||
EventMap eventCounts;
|
||||
for (ItemList::iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
for (ItemList::iterator it = m_items.begin(); it != m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
std::string name;
|
||||
std::string hier;
|
||||
bool per_instance = false;
|
||||
|
||||
for (int i=0; i<MAX_KEYS; ++i) {
|
||||
for (int i = 0; i < MAX_KEYS; ++i) {
|
||||
if (itemp->m_keys[i] != KEY_UNDEF) {
|
||||
std::string key = VerilatedCovKey::shortKey(m_indexValues[itemp->m_keys[i]]);
|
||||
std::string val = m_indexValues[itemp->m_vals[i]];
|
||||
|
|
@ -380,19 +400,19 @@ public:
|
|||
if (cit != eventCounts.end()) {
|
||||
const std::string& oldhier = cit->second.first;
|
||||
cit->second.second += itemp->count();
|
||||
cit->second.first = combineHier(oldhier, hier);
|
||||
cit->second.first = combineHier(oldhier, hier);
|
||||
} else {
|
||||
eventCounts.insert(std::make_pair(name, make_pair(hier, itemp->count())));
|
||||
}
|
||||
}
|
||||
|
||||
// Output body
|
||||
for (EventMap::const_iterator it=eventCounts.begin(); it!=eventCounts.end(); ++it) {
|
||||
os<<"C '"<<std::dec;
|
||||
os<<it->first;
|
||||
if (!it->second.first.empty()) os<<keyValueFormatter(VL_CIK_HIER, it->second.first);
|
||||
os<<"' "<<it->second.second;
|
||||
os<<std::endl;
|
||||
for (EventMap::const_iterator it = eventCounts.begin(); it != eventCounts.end(); ++it) {
|
||||
os << "C '" << std::dec;
|
||||
os << it->first;
|
||||
if (!it->second.first.empty()) os << keyValueFormatter(VL_CIK_HIER, it->second.first);
|
||||
os << "' " << it->second.second;
|
||||
os << std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -400,15 +420,11 @@ public:
|
|||
//=============================================================================
|
||||
// VerilatedCov
|
||||
|
||||
void VerilatedCov::clear() VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().clear();
|
||||
}
|
||||
void VerilatedCov::clear() VL_MT_SAFE { VerilatedCovImp::imp().clear(); }
|
||||
void VerilatedCov::clearNonMatch(const char* matchp) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().clearNonMatch(matchp);
|
||||
}
|
||||
void VerilatedCov::zero() VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().zero();
|
||||
}
|
||||
void VerilatedCov::zero() VL_MT_SAFE { VerilatedCovImp::imp().zero(); }
|
||||
void VerilatedCov::write(const char* filenamep) VL_MT_SAFE {
|
||||
VerilatedCovImp::imp().write(filenamep);
|
||||
}
|
||||
|
|
@ -422,48 +438,49 @@ void VerilatedCov::_insertf(const char* filename, int lineno) VL_MT_SAFE {
|
|||
VerilatedCovImp::imp().insertf(filename, lineno);
|
||||
}
|
||||
|
||||
#define K(n) const char* key ## n
|
||||
#define A(n) const char* key ## n, const char* valp ## n // Argument list
|
||||
#define C(n) key ## n, valp ## n // Calling argument list
|
||||
#define N(n) "","" // Null argument list
|
||||
void VerilatedCov::_insertp(A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19),
|
||||
A(20),A(21),A(22),A(23),A(24),A(25),A(26),A(27),A(28),A(29)) VL_MT_SAFE {
|
||||
#define K(n) const char* key##n
|
||||
#define A(n) const char *key##n, const char *valp##n // Argument list
|
||||
#define C(n) key##n, valp##n // Calling argument list
|
||||
#define N(n) "", "" // Null argument list
|
||||
void VerilatedCov::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10),
|
||||
A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20),
|
||||
A(21), A(22), A(23), A(24), A(25), A(26), A(27), A(28),
|
||||
A(29)) VL_MT_SAFE {
|
||||
const char* keyps[VerilatedCovImpBase::MAX_KEYS]
|
||||
= {NULL,NULL,NULL, // filename,lineno,page
|
||||
key0,key1,key2,key3,key4,key5,key6,key7,key8,key9,
|
||||
key10,key11,key12,key13,key14,key15,key16,key17,key18,key19,
|
||||
key20,key21,key22,key23,key24,key25,key26,key27,key28,key29};
|
||||
= {NULL, NULL, NULL, // filename,lineno,page
|
||||
key0, key1, key2, key3, key4, key5, key6, key7, key8, key9,
|
||||
key10, key11, key12, key13, key14, key15, key16, key17, key18, key19,
|
||||
key20, key21, key22, key23, key24, key25, key26, key27, key28, key29};
|
||||
const char* valps[VerilatedCovImpBase::MAX_KEYS]
|
||||
= {NULL,NULL,NULL, // filename,lineno,page
|
||||
valp0,valp1,valp2,valp3,valp4,valp5,valp6,valp7,valp8,valp9,
|
||||
valp10,valp11,valp12,valp13,valp14,valp15,valp16,valp17,valp18,valp19,
|
||||
valp20,valp21,valp22,valp23,valp24,valp25,valp26,valp27,valp28,valp29};
|
||||
= {NULL, NULL, NULL, // filename,lineno,page
|
||||
valp0, valp1, valp2, valp3, valp4, valp5, valp6, valp7, valp8, valp9,
|
||||
valp10, valp11, valp12, valp13, valp14, valp15, valp16, valp17, valp18, valp19,
|
||||
valp20, valp21, valp22, valp23, valp24, valp25, valp26, valp27, valp28, valp29};
|
||||
VerilatedCovImp::imp().insertp(keyps, valps);
|
||||
}
|
||||
|
||||
// And versions with fewer arguments (oh for a language with named parameters!)
|
||||
void VerilatedCov::_insertp(A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)) VL_MT_SAFE {
|
||||
_insertp(C(0),C(1),C(2),C(3),C(4),C(5),C(6),C(7),C(8),C(9),
|
||||
N(10),N(11),N(12),N(13),N(14),N(15),N(16),N(17),N(18),N(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
void VerilatedCov::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8),
|
||||
A(9)) VL_MT_SAFE {
|
||||
_insertp(C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7), C(8), C(9), N(10), N(11), N(12),
|
||||
N(13), N(14), N(15), N(16), N(17), N(18), N(19), N(20), N(21), N(22), N(23), N(24),
|
||||
N(25), N(26), N(27), N(28), N(29));
|
||||
}
|
||||
void VerilatedCov::_insertp(A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19)) VL_MT_SAFE {
|
||||
_insertp(C(0),C(1),C(2),C(3),C(4),C(5),C(6),C(7),C(8),C(9),
|
||||
C(10),C(11),C(12),C(13),C(14),C(15),C(16),C(17),C(18),C(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
void VerilatedCov::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10),
|
||||
A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18),
|
||||
A(19)) VL_MT_SAFE {
|
||||
_insertp(C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7), C(8), C(9), C(10), C(11), C(12),
|
||||
C(13), C(14), C(15), C(16), C(17), C(18), C(19), N(20), N(21), N(22), N(23), N(24),
|
||||
N(25), N(26), N(27), N(28), N(29));
|
||||
}
|
||||
// Backward compatibility for Verilator
|
||||
void VerilatedCov::_insertp(A(0), A(1), K(2),int val2, K(3),int val3,
|
||||
K(4),const std::string& val4, A(5),A(6)) VL_MT_SAFE {
|
||||
void VerilatedCov::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4),
|
||||
const std::string& val4, A(5), A(6)) VL_MT_SAFE {
|
||||
std::string val2str = vlCovCvtToStr(val2);
|
||||
std::string val3str = vlCovCvtToStr(val3);
|
||||
_insertp(C(0),C(1),
|
||||
key2,val2str.c_str(), key3,val3str.c_str(), key4, val4.c_str(),
|
||||
C(5),C(6),N(7),N(8),N(9),
|
||||
N(10),N(11),N(12),N(13),N(14),N(15),N(16),N(17),N(18),N(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
_insertp(C(0), C(1), key2, val2str.c_str(), key3, val3str.c_str(), key4, val4.c_str(), C(5),
|
||||
C(6), N(7), N(8), N(9), N(10), N(11), N(12), N(13), N(14), N(15), N(16), N(17), N(18),
|
||||
N(19), N(20), N(21), N(22), N(23), N(24), N(25), N(26), N(27), N(28), N(29));
|
||||
}
|
||||
#undef A
|
||||
#undef C
|
||||
|
|
|
|||
|
|
@ -28,17 +28,17 @@
|
|||
//=============================================================================
|
||||
/// Conditionally compile coverage code
|
||||
|
||||
// clang-format off
|
||||
#ifdef VM_COVERAGE
|
||||
# define VL_IF_COVER(stmts) \
|
||||
do { \
|
||||
stmts; \
|
||||
} while (false)
|
||||
do { stmts; } while (false)
|
||||
#else
|
||||
# define VL_IF_COVER(stmts) \
|
||||
do { \
|
||||
if (false) { stmts; } \
|
||||
} while (false)
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
//=============================================================================
|
||||
/// Insert a item for coverage analysis.
|
||||
|
|
@ -68,16 +68,17 @@
|
|||
/// VL_COVER_INSERT(&m_cases[i], "comment", "Coverage Case", "i", cvtToNumStr(i));
|
||||
/// }
|
||||
|
||||
#define VL_COVER_INSERT(countp,...) \
|
||||
VL_IF_COVER(VerilatedCov::_inserti(countp); \
|
||||
VerilatedCov::_insertf(__FILE__, __LINE__); \
|
||||
#define VL_COVER_INSERT(countp, ...) \
|
||||
VL_IF_COVER(VerilatedCov::_inserti(countp); VerilatedCov::_insertf(__FILE__, __LINE__); \
|
||||
VerilatedCov::_insertp("hier", name(), __VA_ARGS__))
|
||||
|
||||
//=============================================================================
|
||||
/// Convert VL_COVER_INSERT value arguments to strings
|
||||
|
||||
template< class T> std::string vlCovCvtToStr(const T& t) VL_PURE {
|
||||
std::ostringstream os; os<<t; return os.str();
|
||||
template <class T> std::string vlCovCvtToStr(const T& t) VL_PURE {
|
||||
std::ostringstream os;
|
||||
os << t;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
|
@ -89,6 +90,7 @@ template< class T> std::string vlCovCvtToStr(const T& t) VL_PURE {
|
|||
|
||||
class VerilatedCov {
|
||||
VL_UNCOPYABLE(VerilatedCov);
|
||||
|
||||
public:
|
||||
// GLOBAL METHODS
|
||||
/// Return default filename
|
||||
|
|
@ -108,18 +110,18 @@ public:
|
|||
// We could have just the maximum argument version, but this compiles
|
||||
// much slower (nearly 2x) than having smaller versions also. However
|
||||
// there's not much more gain in having a version for each number of args.
|
||||
#define K(n) const char* key ## n
|
||||
#define A(n) const char* key ## n, const char* valp ## n // Argument list
|
||||
#define D(n) const char* key ## n = NULL, const char* valp ## n = NULL // Argument list
|
||||
static void _insertp(D(0),D(1),D(2),D(3),D(4),D(5),D(6),D(7),D(8),D(9));
|
||||
static void _insertp(A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)
|
||||
,A(10),D(11),D(12),D(13),D(14),D(15),D(16),D(17),D(18),D(19));
|
||||
static void _insertp(A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)
|
||||
,A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19)
|
||||
,A(20),D(21),D(22),D(23),D(24),D(25),D(26),D(27),D(28),D(29));
|
||||
#define K(n) const char* key##n
|
||||
#define A(n) const char *key##n, const char *valp##n // Argument list
|
||||
#define D(n) const char *key##n = NULL, const char *valp##n = NULL // Argument list
|
||||
static void _insertp(D(0), D(1), D(2), D(3), D(4), D(5), D(6), D(7), D(8), D(9));
|
||||
static void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), D(11),
|
||||
D(12), D(13), D(14), D(15), D(16), D(17), D(18), D(19));
|
||||
static void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), A(11),
|
||||
A(12), A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20), D(21),
|
||||
D(22), D(23), D(24), D(25), D(26), D(27), D(28), D(29));
|
||||
// Backward compatibility for Verilator
|
||||
static void _insertp(A(0), A(1), K(2),int val2, K(3),int val3,
|
||||
K(4),const std::string& val4, A(5),A(6));
|
||||
static void _insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), const std::string& val4,
|
||||
A(5), A(6));
|
||||
|
||||
#undef K
|
||||
#undef A
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#define VLCOVGEN_ITEM(string_parsed_by_vlcovgen)
|
||||
|
||||
// clang-format off
|
||||
VLCOVGEN_ITEM("name=>'col0_name', short=>'C0', group=>1, default=>undef, descr=>'The column title for the header line of this column'")
|
||||
VLCOVGEN_ITEM("name=>'col1_name', short=>'C1', group=>1, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'col2_name', short=>'C2', group=>1, default=>undef, ")
|
||||
|
|
@ -59,6 +60,7 @@ VLCOVGEN_ITEM("name=>'row1', short=>'r1', group=>0, default=>undef, ")
|
|||
VLCOVGEN_ITEM("name=>'row2', short=>'r2', group=>0, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'row3', short=>'r3', group=>0, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'weight', short=>'w', group=>0, default=>undef, descr=>'For totaling items, weight of this item'")
|
||||
// clang-format on
|
||||
|
||||
// VLCOVGEN_CIK_AUTO_EDIT_BEGIN
|
||||
#define VL_CIK_COL0 "c0"
|
||||
|
|
|
|||
|
|
@ -40,88 +40,80 @@
|
|||
// Not supported yet
|
||||
#define _VL_SVDPI_UNIMP() \
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", \
|
||||
(std::string("%%Error: Unsupported DPI function: ")+VL_FUNC).c_str())
|
||||
(std::string("%%Error: Unsupported DPI function: ") + VL_FUNC).c_str())
|
||||
|
||||
#define _VL_SVDPI_WARN(...) \
|
||||
VL_PRINTF_MT(__VA_ARGS__)
|
||||
#define _VL_SVDPI_WARN(...) VL_PRINTF_MT(__VA_ARGS__)
|
||||
|
||||
// Function requires a "context" in the import declaration
|
||||
#define _VL_SVDPI_CONTEXT_WARN() \
|
||||
_VL_SVDPI_WARN("%%Warning: DPI C Function called by Verilog DPI import with missing 'context' keyword.\n");
|
||||
_VL_SVDPI_WARN("%%Warning: DPI C Function called by Verilog DPI import with missing " \
|
||||
"'context' keyword.\n");
|
||||
|
||||
//======================================================================
|
||||
//======================================================================
|
||||
//======================================================================
|
||||
// DPI ROUTINES
|
||||
|
||||
const char* svDpiVersion() {
|
||||
return "1800-2005";
|
||||
}
|
||||
const char* svDpiVersion() { return "1800-2005"; }
|
||||
|
||||
//======================================================================
|
||||
// Bit-select utility functions.
|
||||
|
||||
svBit svGetBitselBit(const svBitVecVal* sp, int bit) {
|
||||
return VL_BITRSHIFT_W(sp,bit) & 1;
|
||||
}
|
||||
svBit svGetBitselBit(const svBitVecVal* sp, int bit) { return VL_BITRSHIFT_W(sp, bit) & 1; }
|
||||
svLogic svGetBitselLogic(const svLogicVecVal* sp, int bit) {
|
||||
// Not VL_BITRSHIFT_W as sp is a different structure type
|
||||
// Verilator doesn't support X/Z so only aval
|
||||
return (((sp[VL_BITWORD_I(bit)].aval >> VL_BITBIT_I(bit)) & 1)
|
||||
| (((sp[VL_BITWORD_I(bit)].bval >> VL_BITBIT_I(bit)) & 1)<<1));
|
||||
| (((sp[VL_BITWORD_I(bit)].bval >> VL_BITBIT_I(bit)) & 1) << 1));
|
||||
}
|
||||
|
||||
void svPutBitselBit(svBitVecVal* dp, int bit, svBit s) {
|
||||
VL_ASSIGNBIT_WI(32, bit, dp, s);
|
||||
}
|
||||
void svPutBitselBit(svBitVecVal* dp, int bit, svBit s) { VL_ASSIGNBIT_WI(32, bit, dp, s); }
|
||||
void svPutBitselLogic(svLogicVecVal* dp, int bit, svLogic s) {
|
||||
// Verilator doesn't support X/Z so only aval
|
||||
dp[VL_BITWORD_I(bit)].aval
|
||||
= ((dp[VL_BITWORD_I(bit)].aval & ~(VL_UL(1)<<VL_BITBIT_I(bit)))
|
||||
| ((s&1)<<VL_BITBIT_I(bit)));
|
||||
dp[VL_BITWORD_I(bit)].bval
|
||||
= ((dp[VL_BITWORD_I(bit)].bval & ~(VL_UL(1)<<VL_BITBIT_I(bit)))
|
||||
| ((s&2)>>1<<VL_BITBIT_I(bit)));
|
||||
dp[VL_BITWORD_I(bit)].aval = ((dp[VL_BITWORD_I(bit)].aval & ~(VL_UL(1) << VL_BITBIT_I(bit)))
|
||||
| ((s & 1) << VL_BITBIT_I(bit)));
|
||||
dp[VL_BITWORD_I(bit)].bval = ((dp[VL_BITWORD_I(bit)].bval & ~(VL_UL(1) << VL_BITBIT_I(bit)))
|
||||
| ((s & 2) >> 1 << VL_BITBIT_I(bit)));
|
||||
}
|
||||
|
||||
void svGetPartselBit(svBitVecVal* dp, const svBitVecVal* sp, int lsb, int width) {
|
||||
// See also VL_SEL_WWI
|
||||
int msb = lsb+width-1;
|
||||
int msb = lsb + width - 1;
|
||||
int word_shift = VL_BITWORD_I(lsb);
|
||||
if (VL_BITBIT_I(lsb)==0) {
|
||||
if (VL_BITBIT_I(lsb) == 0) {
|
||||
// Just a word extract
|
||||
for (int i=0; i<VL_WORDS_I(width); ++i) dp[i] = sp[i+word_shift];
|
||||
for (int i = 0; i < VL_WORDS_I(width); ++i) dp[i] = sp[i + word_shift];
|
||||
} else {
|
||||
int loffset = lsb & VL_SIZEBITS_I;
|
||||
int nbitsfromlow = 32-loffset; // bits that end up in lword (know loffset!=0)
|
||||
int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
|
||||
// Middle words
|
||||
int words = VL_WORDS_I(msb-lsb+1);
|
||||
for (int i=0; i<words; ++i) {
|
||||
dp[i] = sp[i+word_shift]>>loffset;
|
||||
int upperword = i+word_shift+1;
|
||||
int words = VL_WORDS_I(msb - lsb + 1);
|
||||
for (int i = 0; i < words; ++i) {
|
||||
dp[i] = sp[i + word_shift] >> loffset;
|
||||
int upperword = i + word_shift + 1;
|
||||
if (upperword <= static_cast<int>(VL_BITWORD_I(msb))) {
|
||||
dp[i] |= sp[upperword] << nbitsfromlow;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clean result
|
||||
dp[VL_WORDS_I(width)-1] &= VL_MASK_I(width);
|
||||
dp[VL_WORDS_I(width) - 1] &= VL_MASK_I(width);
|
||||
}
|
||||
void svGetPartselLogic(svLogicVecVal* dp, const svLogicVecVal* sp, int lsb, int width) {
|
||||
int msb = lsb+width-1;
|
||||
int msb = lsb + width - 1;
|
||||
int word_shift = VL_BITWORD_I(lsb);
|
||||
if (VL_BITBIT_I(lsb)==0) {
|
||||
if (VL_BITBIT_I(lsb) == 0) {
|
||||
// Just a word extract
|
||||
for (int i=0; i<VL_WORDS_I(width); ++i) dp[i] = sp[i+word_shift];
|
||||
for (int i = 0; i < VL_WORDS_I(width); ++i) dp[i] = sp[i + word_shift];
|
||||
} else {
|
||||
int loffset = lsb & VL_SIZEBITS_I;
|
||||
int nbitsfromlow = 32-loffset; // bits that end up in lword (know loffset!=0)
|
||||
int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0)
|
||||
// Middle words
|
||||
int words = VL_WORDS_I(msb-lsb+1);
|
||||
for (int i=0; i<words; ++i) {
|
||||
dp[i].aval = sp[i+word_shift].aval >> loffset;
|
||||
dp[i].bval = sp[i+word_shift].bval >> loffset;
|
||||
int upperword = i+word_shift+1;
|
||||
int words = VL_WORDS_I(msb - lsb + 1);
|
||||
for (int i = 0; i < words; ++i) {
|
||||
dp[i].aval = sp[i + word_shift].aval >> loffset;
|
||||
dp[i].bval = sp[i + word_shift].bval >> loffset;
|
||||
int upperword = i + word_shift + 1;
|
||||
if (upperword <= static_cast<int>(VL_BITWORD_I(msb))) {
|
||||
dp[i].aval |= sp[upperword].aval << nbitsfromlow;
|
||||
dp[i].bval |= sp[upperword].bval << nbitsfromlow;
|
||||
|
|
@ -129,58 +121,56 @@ void svGetPartselLogic(svLogicVecVal* dp, const svLogicVecVal* sp, int lsb, int
|
|||
}
|
||||
}
|
||||
// Clean result
|
||||
dp[VL_WORDS_I(width)-1].aval &= VL_MASK_I(width);
|
||||
dp[VL_WORDS_I(width)-1].bval &= VL_MASK_I(width);
|
||||
dp[VL_WORDS_I(width) - 1].aval &= VL_MASK_I(width);
|
||||
dp[VL_WORDS_I(width) - 1].bval &= VL_MASK_I(width);
|
||||
}
|
||||
void svPutPartselBit(svBitVecVal* dp, const svBitVecVal s, int lbit, int width) {
|
||||
// See also _VL_INSERT_WI
|
||||
int hbit = lbit+width-1;
|
||||
int hbit = lbit + width - 1;
|
||||
int hoffset = VL_BITBIT_I(hbit);
|
||||
int loffset = VL_BITBIT_I(lbit);
|
||||
if (hoffset==VL_SIZEBITS_I && loffset==0) {
|
||||
if (hoffset == VL_SIZEBITS_I && loffset == 0) {
|
||||
// Fast and common case, word based insertion
|
||||
dp[VL_BITWORD_I(lbit)] = s;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
int hword = VL_BITWORD_I(hbit);
|
||||
int lword = VL_BITWORD_I(lbit);
|
||||
if (hword==lword) { // know < 32 bits because above checks it
|
||||
IData insmask = (VL_MASK_I(hoffset-loffset+1))<<loffset;
|
||||
dp[lword] = (dp[lword] & ~insmask) | ((s<<loffset) & insmask);
|
||||
if (hword == lword) { // know < 32 bits because above checks it
|
||||
IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
|
||||
dp[lword] = (dp[lword] & ~insmask) | ((s << loffset) & insmask);
|
||||
} else {
|
||||
IData hinsmask = (VL_MASK_I(hoffset-0+1))<<0;
|
||||
IData linsmask = (VL_MASK_I(31-loffset+1))<<loffset;
|
||||
int nbitsonright = 32-loffset; // bits that end up in lword
|
||||
dp[lword] = (dp[lword] & ~linsmask) | ((s<<loffset) & linsmask);
|
||||
dp[hword] = (dp[hword] & ~hinsmask) | ((s>>nbitsonright) & hinsmask);
|
||||
IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
|
||||
IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
|
||||
int nbitsonright = 32 - loffset; // bits that end up in lword
|
||||
dp[lword] = (dp[lword] & ~linsmask) | ((s << loffset) & linsmask);
|
||||
dp[hword] = (dp[hword] & ~hinsmask) | ((s >> nbitsonright) & hinsmask);
|
||||
}
|
||||
}
|
||||
}
|
||||
// cppcheck-suppress passedByValue
|
||||
void svPutPartselLogic(svLogicVecVal* dp, const svLogicVecVal s, int lbit, int width) {
|
||||
int hbit = lbit+width-1;
|
||||
int hbit = lbit + width - 1;
|
||||
int hoffset = VL_BITBIT_I(hbit);
|
||||
int loffset = VL_BITBIT_I(lbit);
|
||||
if (hoffset==VL_SIZEBITS_I && loffset==0) {
|
||||
if (hoffset == VL_SIZEBITS_I && loffset == 0) {
|
||||
// Fast and common case, word based insertion
|
||||
dp[VL_BITWORD_I(lbit)].aval = s.aval;
|
||||
dp[VL_BITWORD_I(lbit)].bval = s.bval;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
int hword = VL_BITWORD_I(hbit);
|
||||
int lword = VL_BITWORD_I(lbit);
|
||||
if (hword==lword) { // know < 32 bits because above checks it
|
||||
IData insmask = (VL_MASK_I(hoffset-loffset+1))<<loffset;
|
||||
dp[lword].aval = (dp[lword].aval & ~insmask) | ((s.aval<<loffset) & insmask);
|
||||
dp[lword].bval = (dp[lword].bval & ~insmask) | ((s.bval<<loffset) & insmask);
|
||||
if (hword == lword) { // know < 32 bits because above checks it
|
||||
IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
|
||||
dp[lword].aval = (dp[lword].aval & ~insmask) | ((s.aval << loffset) & insmask);
|
||||
dp[lword].bval = (dp[lword].bval & ~insmask) | ((s.bval << loffset) & insmask);
|
||||
} else {
|
||||
IData hinsmask = (VL_MASK_I(hoffset-0+1))<<0;
|
||||
IData linsmask = (VL_MASK_I(31-loffset+1))<<loffset;
|
||||
int nbitsonright = 32-loffset; // bits that end up in lword
|
||||
dp[lword].aval = (dp[lword].aval & ~linsmask) | ((s.aval<<loffset) & linsmask);
|
||||
dp[lword].bval = (dp[lword].bval & ~linsmask) | ((s.bval<<loffset) & linsmask);
|
||||
dp[hword].aval = (dp[hword].aval & ~hinsmask) | ((s.aval>>nbitsonright) & hinsmask);
|
||||
dp[hword].bval = (dp[hword].bval & ~hinsmask) | ((s.bval>>nbitsonright) & hinsmask);
|
||||
IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
|
||||
IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
|
||||
int nbitsonright = 32 - loffset; // bits that end up in lword
|
||||
dp[lword].aval = (dp[lword].aval & ~linsmask) | ((s.aval << loffset) & linsmask);
|
||||
dp[lword].bval = (dp[lword].bval & ~linsmask) | ((s.bval << loffset) & linsmask);
|
||||
dp[hword].aval = (dp[hword].aval & ~hinsmask) | ((s.aval >> nbitsonright) & hinsmask);
|
||||
dp[hword].bval = (dp[hword].bval & ~hinsmask) | ((s.bval >> nbitsonright) & hinsmask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -204,27 +194,14 @@ static inline const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHa
|
|||
//======================================================================
|
||||
// Open array querying functions
|
||||
|
||||
int svLeft(const svOpenArrayHandle h, int d) {
|
||||
return _vl_openhandle_varp(h)->left(d);
|
||||
}
|
||||
int svRight(const svOpenArrayHandle h, int d) {
|
||||
return _vl_openhandle_varp(h)->right(d);
|
||||
}
|
||||
int svLow(const svOpenArrayHandle h, int d) {
|
||||
return _vl_openhandle_varp(h)->low(d);
|
||||
}
|
||||
int svHigh(const svOpenArrayHandle h, int d) {
|
||||
return _vl_openhandle_varp(h)->high(d);
|
||||
}
|
||||
int svIncrement(const svOpenArrayHandle h, int d) {
|
||||
return _vl_openhandle_varp(h)->increment(d);
|
||||
}
|
||||
int svSize(const svOpenArrayHandle h, int d) {
|
||||
return _vl_openhandle_varp(h)->elements(d);
|
||||
}
|
||||
int svDimensions(const svOpenArrayHandle h) {
|
||||
return _vl_openhandle_varp(h)->udims();
|
||||
}
|
||||
int svLeft(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->left(d); }
|
||||
int svRight(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->right(d); }
|
||||
int svLow(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->low(d); }
|
||||
int svHigh(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->high(d); }
|
||||
int svIncrement(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->increment(d); }
|
||||
int svSize(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->elements(d); }
|
||||
int svDimensions(const svOpenArrayHandle h) { return _vl_openhandle_varp(h)->udims(); }
|
||||
|
||||
/// Return pointer to open array data, or NULL if not in IEEE standard C layout
|
||||
void* svGetArrayPtr(const svOpenArrayHandle h) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
|
||||
|
|
@ -242,8 +219,8 @@ int svSizeOfArray(const svOpenArrayHandle h) {
|
|||
//======================================================================
|
||||
// Open array access internals
|
||||
|
||||
static void* _vl_sv_adjusted_datap(const VerilatedDpiOpenVar* varp,
|
||||
int nargs, int indx1, int indx2, int indx3) {
|
||||
static void* _vl_sv_adjusted_datap(const VerilatedDpiOpenVar* varp, int nargs, int indx1,
|
||||
int indx2, int indx3) {
|
||||
void* datap = varp->datap();
|
||||
if (VL_UNLIKELY(nargs != varp->udims())) {
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function called on"
|
||||
|
|
@ -251,26 +228,29 @@ static void* _vl_sv_adjusted_datap(const VerilatedDpiOpenVar* varp,
|
|||
varp->udims(), nargs);
|
||||
return NULL;
|
||||
}
|
||||
if (nargs>=1) {
|
||||
if (nargs >= 1) {
|
||||
datap = varp->datapAdjustIndex(datap, 1, indx1);
|
||||
if (VL_UNLIKELY(!datap)) {
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function index 1 out of bounds; %d outside [%d:%d].\n",
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function index 1 "
|
||||
"out of bounds; %d outside [%d:%d].\n",
|
||||
indx1, varp->left(1), varp->right(1));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (nargs>=2) {
|
||||
if (nargs >= 2) {
|
||||
datap = varp->datapAdjustIndex(datap, 2, indx2);
|
||||
if (VL_UNLIKELY(!datap)) {
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function index 2 out of bounds; %d outside [%d:%d].\n",
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function index 2 "
|
||||
"out of bounds; %d outside [%d:%d].\n",
|
||||
indx2, varp->left(2), varp->right(2));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (nargs>=3) {
|
||||
if (nargs >= 3) {
|
||||
datap = varp->datapAdjustIndex(datap, 3, indx3);
|
||||
if (VL_UNLIKELY(!datap)) {
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function index 3 out of bounds; %d outside [%d:%d].\n",
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function index 3 "
|
||||
"out of bounds; %d outside [%d:%d].\n",
|
||||
indx1, varp->left(3), varp->right(3));
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -280,7 +260,8 @@ static void* _vl_sv_adjusted_datap(const VerilatedDpiOpenVar* varp,
|
|||
|
||||
static int _vl_sv_adjusted_bit(const VerilatedDpiOpenVar* varp, int indx) {
|
||||
if (VL_UNLIKELY(indx < varp->low(0) || indx > varp->high(0))) {
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function packed index out of bounds; %d outside [%d:%d].\n",
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function packed index out of bounds; %d "
|
||||
"outside [%d:%d].\n",
|
||||
indx, varp->left(0), varp->right(0));
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -288,8 +269,8 @@ static int _vl_sv_adjusted_bit(const VerilatedDpiOpenVar* varp, int indx) {
|
|||
}
|
||||
|
||||
/// Return pointer to simulator open array element, or NULL if outside range
|
||||
static void* _vl_svGetArrElemPtr(const svOpenArrayHandle h,
|
||||
int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
static void* _vl_svGetArrElemPtr(const svOpenArrayHandle h, int nargs, int indx1, int indx2,
|
||||
int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
|
||||
if (VL_UNLIKELY(!varp->isDpiStdLayout())) return NULL;
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
|
|
@ -297,23 +278,25 @@ static void* _vl_svGetArrElemPtr(const svOpenArrayHandle h,
|
|||
}
|
||||
|
||||
/// Copy to user bit array from simulator open array
|
||||
static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
|
||||
int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int nargs,
|
||||
int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) {
|
||||
case VLVT_UINT8: d[0] = *(reinterpret_cast<CData*>(datap)); return;
|
||||
case VLVT_UINT8: d[0] = *(reinterpret_cast<CData*>(datap)); return;
|
||||
case VLVT_UINT16: d[0] = *(reinterpret_cast<SData*>(datap)); return;
|
||||
case VLVT_UINT32: d[0] = *(reinterpret_cast<IData*>(datap)); return;
|
||||
case VLVT_UINT64: {
|
||||
WData lwp[2]; VL_SET_WQ(lwp, *(reinterpret_cast<QData*>(datap)));
|
||||
d[0] = lwp[0]; d[1] = lwp[1];
|
||||
WData lwp[2];
|
||||
VL_SET_WQ(lwp, *(reinterpret_cast<QData*>(datap)));
|
||||
d[0] = lwp[0];
|
||||
d[1] = lwp[1];
|
||||
break;
|
||||
}
|
||||
case VLVT_WDATA: {
|
||||
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(datap));
|
||||
for (int i=0; i<VL_WORDS_I(varp->packed().elements()); ++i) d[i] = wdatap[i];
|
||||
for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) d[i] = wdatap[i];
|
||||
return;
|
||||
}
|
||||
default:
|
||||
|
|
@ -323,25 +306,38 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
|
|||
}
|
||||
}
|
||||
/// Copy to user logic array from simulator open array
|
||||
static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s,
|
||||
int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int nargs,
|
||||
int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) {
|
||||
case VLVT_UINT8: d[0].aval = *(reinterpret_cast<CData*>(datap)); d[0].bval=0; return;
|
||||
case VLVT_UINT16: d[0].aval = *(reinterpret_cast<SData*>(datap)); d[0].bval=0; return;
|
||||
case VLVT_UINT32: d[0].aval = *(reinterpret_cast<IData*>(datap)); d[0].bval=0; return;
|
||||
case VLVT_UINT8:
|
||||
d[0].aval = *(reinterpret_cast<CData*>(datap));
|
||||
d[0].bval = 0;
|
||||
return;
|
||||
case VLVT_UINT16:
|
||||
d[0].aval = *(reinterpret_cast<SData*>(datap));
|
||||
d[0].bval = 0;
|
||||
return;
|
||||
case VLVT_UINT32:
|
||||
d[0].aval = *(reinterpret_cast<IData*>(datap));
|
||||
d[0].bval = 0;
|
||||
return;
|
||||
case VLVT_UINT64: {
|
||||
WData lwp[2]; VL_SET_WQ(lwp, *(reinterpret_cast<QData*>(datap)));
|
||||
d[0].aval = lwp[0]; d[0].bval=0;
|
||||
d[1].aval = lwp[1]; d[0].bval=0;
|
||||
WData lwp[2];
|
||||
VL_SET_WQ(lwp, *(reinterpret_cast<QData*>(datap)));
|
||||
d[0].aval = lwp[0];
|
||||
d[0].bval = 0;
|
||||
d[1].aval = lwp[1];
|
||||
d[0].bval = 0;
|
||||
break;
|
||||
}
|
||||
case VLVT_WDATA: {
|
||||
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(datap));
|
||||
for (int i=0; i<VL_WORDS_I(varp->packed().elements()); ++i) {
|
||||
d[i].aval = wdatap[i]; d[i].bval = 0;
|
||||
for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) {
|
||||
d[i].aval = wdatap[i];
|
||||
d[i].bval = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -353,19 +349,19 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl
|
|||
}
|
||||
|
||||
/// Copy to simulator open array from from user bit array
|
||||
static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s,
|
||||
int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int nargs,
|
||||
int indx1, int indx2, int indx3) VL_MT_SAFE {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) {
|
||||
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0]; return;
|
||||
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0]; return;
|
||||
case VLVT_UINT16: *(reinterpret_cast<SData*>(datap)) = s[0]; return;
|
||||
case VLVT_UINT32: *(reinterpret_cast<IData*>(datap)) = s[0]; return;
|
||||
case VLVT_UINT64: *(reinterpret_cast<QData*>(datap)) = _VL_SET_QII(s[1], s[0]); break;
|
||||
case VLVT_WDATA: {
|
||||
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(datap));
|
||||
for (int i=0; i<VL_WORDS_I(varp->packed().elements()); ++i) wdatap[i] = s[i];
|
||||
for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) wdatap[i] = s[i];
|
||||
return;
|
||||
}
|
||||
default:
|
||||
|
|
@ -381,80 +377,82 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic
|
|||
void* datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) {
|
||||
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0].aval; return;
|
||||
case VLVT_UINT8: *(reinterpret_cast<CData*>(datap)) = s[0].aval; return;
|
||||
case VLVT_UINT16: *(reinterpret_cast<SData*>(datap)) = s[0].aval; return;
|
||||
case VLVT_UINT32: *(reinterpret_cast<IData*>(datap)) = s[0].aval; return;
|
||||
case VLVT_UINT64: *(reinterpret_cast<QData*>(datap)) = _VL_SET_QII(s[1].aval, s[0].aval); break;
|
||||
case VLVT_UINT64:
|
||||
*(reinterpret_cast<QData*>(datap)) = _VL_SET_QII(s[1].aval, s[0].aval);
|
||||
break;
|
||||
case VLVT_WDATA: {
|
||||
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(datap));
|
||||
for (int i=0; i<VL_WORDS_I(varp->packed().elements()); ++i) wdatap[i] = s[i].aval;
|
||||
for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) wdatap[i] = s[i].aval;
|
||||
return;
|
||||
}
|
||||
default:
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype());
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
|
||||
varp->vltype());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Return bit from simulator open array
|
||||
static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s,
|
||||
int nargs, int indx1, int indx2, int indx3, int indx4) VL_MT_SAFE {
|
||||
static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1, int indx2,
|
||||
int indx3, int indx4) VL_MT_SAFE {
|
||||
// One extra index supported, as need bit number
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
void* datap;
|
||||
int lsb;
|
||||
if (varp->packed().elements()) {
|
||||
datap = _vl_sv_adjusted_datap(varp, nargs-1, indx1, indx2, indx3);
|
||||
lsb = _vl_sv_adjusted_bit(varp, ((nargs==1) ? indx1
|
||||
: (nargs==2) ? indx2
|
||||
: (nargs==3) ? indx3
|
||||
: indx4));
|
||||
datap = _vl_sv_adjusted_datap(varp, nargs - 1, indx1, indx2, indx3);
|
||||
lsb = _vl_sv_adjusted_bit(
|
||||
varp, ((nargs == 1) ? indx1 : (nargs == 2) ? indx2 : (nargs == 3) ? indx3 : indx4));
|
||||
} else {
|
||||
datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
lsb = 0;
|
||||
}
|
||||
if (VL_UNLIKELY(!datap)) return 0;
|
||||
switch (varp->vltype()) {
|
||||
case VLVT_UINT8: return (*(reinterpret_cast<CData*>(datap)) >> lsb) & 1;
|
||||
case VLVT_UINT8: return (*(reinterpret_cast<CData*>(datap)) >> lsb) & 1;
|
||||
case VLVT_UINT16: return (*(reinterpret_cast<SData*>(datap)) >> lsb) & 1;
|
||||
case VLVT_UINT32: return (*(reinterpret_cast<IData*>(datap)) >> lsb) & 1;
|
||||
case VLVT_UINT64: return (*(reinterpret_cast<QData*>(datap)) >> static_cast<QData>(lsb)) & VL_ULL(1);
|
||||
case VLVT_UINT64:
|
||||
return (*(reinterpret_cast<QData*>(datap)) >> static_cast<QData>(lsb)) & VL_ULL(1);
|
||||
case VLVT_WDATA: {
|
||||
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(datap));
|
||||
return VL_BITRSHIFT_W(wdatap, lsb) & 1;
|
||||
}
|
||||
default:
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype());
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
|
||||
varp->vltype());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/// Update simulator open array from bit
|
||||
static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value,
|
||||
int nargs, int indx1, int indx2, int indx3, int indx4) VL_MT_SAFE {
|
||||
static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int nargs, int indx1,
|
||||
int indx2, int indx3, int indx4) VL_MT_SAFE {
|
||||
// One extra index supported, as need bit number
|
||||
value &= 1; // Make sure clean
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
void* datap;
|
||||
int lsb;
|
||||
if (varp->packed().elements()) {
|
||||
datap = _vl_sv_adjusted_datap(varp, nargs-1, indx1, indx2, indx3);
|
||||
lsb = _vl_sv_adjusted_bit(varp, ((nargs==1) ? indx1
|
||||
: (nargs==2) ? indx2
|
||||
: (nargs==3) ? indx3
|
||||
: indx4));
|
||||
datap = _vl_sv_adjusted_datap(varp, nargs - 1, indx1, indx2, indx3);
|
||||
lsb = _vl_sv_adjusted_bit(
|
||||
varp, ((nargs == 1) ? indx1 : (nargs == 2) ? indx2 : (nargs == 3) ? indx3 : indx4));
|
||||
} else {
|
||||
datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
|
||||
lsb = 0;
|
||||
}
|
||||
if (VL_UNLIKELY(!datap)) return;
|
||||
switch (varp->vltype()) {
|
||||
case VLVT_UINT8: VL_ASSIGNBIT_II(-1, lsb, *(reinterpret_cast<CData*>(datap)), value); return;
|
||||
case VLVT_UINT8: VL_ASSIGNBIT_II(-1, lsb, *(reinterpret_cast<CData*>(datap)), value); return;
|
||||
case VLVT_UINT16: VL_ASSIGNBIT_II(-1, lsb, *(reinterpret_cast<SData*>(datap)), value); return;
|
||||
case VLVT_UINT32: VL_ASSIGNBIT_II(-1, lsb, *(reinterpret_cast<IData*>(datap)), value); return;
|
||||
case VLVT_UINT64: VL_ASSIGNBIT_QI(-1, lsb, *(reinterpret_cast<QData*>(datap)), value); return;
|
||||
case VLVT_WDATA: VL_ASSIGNBIT_WI(-1, lsb, (reinterpret_cast<WDataOutP>(datap)), value); return;
|
||||
case VLVT_WDATA: VL_ASSIGNBIT_WI(-1, lsb, (reinterpret_cast<WDataOutP>(datap)), value); return;
|
||||
default:
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", varp->vltype());
|
||||
_VL_SVDPI_WARN("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
|
||||
varp->vltype());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -470,10 +468,17 @@ void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) {
|
|||
// va_arg is a macro, so need temporaries as used below
|
||||
switch (varp->udims()) {
|
||||
case 1: datap = _vl_svGetArrElemPtr(h, 1, indx1, 0, 0); break;
|
||||
case 2: { int indx2=va_arg(ap,int);
|
||||
datap = _vl_svGetArrElemPtr(h, 2, indx1, indx2, 0); break; }
|
||||
case 3: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int);
|
||||
datap = _vl_svGetArrElemPtr(h, 3, indx1, indx2, indx3); break; }
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
datap = _vl_svGetArrElemPtr(h, 2, indx1, indx2, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
datap = _vl_svGetArrElemPtr(h, 3, indx1, indx2, indx3);
|
||||
break;
|
||||
}
|
||||
default: datap = _vl_svGetArrElemPtr(h, -1, 0, 0, 0); break; // Will error
|
||||
}
|
||||
va_end(ap);
|
||||
|
|
@ -489,116 +494,134 @@ void* svGetArrElemPtr3(const svOpenArrayHandle h, int indx1, int indx2, int indx
|
|||
return _vl_svGetArrElemPtr(h, 3, indx1, indx2, indx3);
|
||||
}
|
||||
|
||||
void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s,
|
||||
int indx1, ...) {
|
||||
void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: _vl_svPutBitArrElemVecVal(d, s, 1, indx1, 0, 0); break;
|
||||
case 2: { int indx2=va_arg(ap,int);
|
||||
_vl_svPutBitArrElemVecVal(d, s, 2, indx1, indx2, 0); break; }
|
||||
case 3: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int);
|
||||
_vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3); break; }
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
break;
|
||||
}
|
||||
default: _vl_svPutBitArrElemVecVal(d, s, -1, 0, 0, 0); break; // Will error
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
void svPutBitArrElem1VecVal(const svOpenArrayHandle d, const svBitVecVal* s,
|
||||
int indx1) {
|
||||
void svPutBitArrElem1VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1) {
|
||||
_vl_svPutBitArrElemVecVal(d, s, 1, indx1, 0, 0);
|
||||
}
|
||||
void svPutBitArrElem2VecVal(const svOpenArrayHandle d, const svBitVecVal* s,
|
||||
int indx1, int indx2) {
|
||||
void svPutBitArrElem2VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1,
|
||||
int indx2) {
|
||||
_vl_svPutBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
|
||||
}
|
||||
void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s,
|
||||
int indx1, int indx2, int indx3) {
|
||||
void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, int indx2,
|
||||
int indx3) {
|
||||
_vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
}
|
||||
void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
|
||||
int indx1, ...) {
|
||||
void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(d);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: _vl_svPutLogicArrElemVecVal(d, s, 1, indx1, 0, 0); break;
|
||||
case 2: { int indx2=va_arg(ap,int);
|
||||
_vl_svPutLogicArrElemVecVal(d, s, 2, indx1, indx2, 0); break; }
|
||||
case 3: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int);
|
||||
_vl_svPutLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3); break; }
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
_vl_svPutLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
_vl_svPutLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
break;
|
||||
}
|
||||
default: _vl_svPutLogicArrElemVecVal(d, s, -1, 0, 0, 0); break; // Will error
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
void svPutLogicArrElem1VecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
|
||||
int indx1) {
|
||||
void svPutLogicArrElem1VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1) {
|
||||
_vl_svPutLogicArrElemVecVal(d, s, 1, indx1, 0, 0);
|
||||
}
|
||||
void svPutLogicArrElem2VecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
|
||||
int indx1, int indx2) {
|
||||
void svPutLogicArrElem2VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1,
|
||||
int indx2) {
|
||||
_vl_svPutLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
|
||||
}
|
||||
void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
|
||||
int indx1, int indx2, int indx3) {
|
||||
void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1,
|
||||
int indx2, int indx3) {
|
||||
_vl_svPutLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// From simulator storage into user space
|
||||
|
||||
void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s,
|
||||
int indx1, ...) {
|
||||
void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: _vl_svGetBitArrElemVecVal(d, s, 1, indx1, 0, 0); break;
|
||||
case 2: { int indx2=va_arg(ap,int);
|
||||
_vl_svGetBitArrElemVecVal(d, s, 2, indx1, indx2, 0); break; }
|
||||
case 3: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int);
|
||||
_vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3); break; }
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
_vl_svGetBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
_vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
break;
|
||||
}
|
||||
default: _vl_svGetBitArrElemVecVal(d, s, -1, 0, 0, 0); break; // Will error
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
void svGetBitArrElem1VecVal(svBitVecVal* d, const svOpenArrayHandle s,
|
||||
int indx1) {
|
||||
void svGetBitArrElem1VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1) {
|
||||
_vl_svGetBitArrElemVecVal(d, s, 1, indx1, 0, 0);
|
||||
}
|
||||
void svGetBitArrElem2VecVal(svBitVecVal* d, const svOpenArrayHandle s,
|
||||
int indx1, int indx2) {
|
||||
void svGetBitArrElem2VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, int indx2) {
|
||||
_vl_svGetBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
|
||||
}
|
||||
void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s,
|
||||
int indx1, int indx2, int indx3) {
|
||||
void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, int indx2,
|
||||
int indx3) {
|
||||
_vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
}
|
||||
void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s,
|
||||
int indx1, ...) {
|
||||
void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
|
||||
const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(s);
|
||||
va_list ap;
|
||||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: _vl_svGetLogicArrElemVecVal(d, s, 1, indx1, 0, 0); break;
|
||||
case 2: { int indx2=va_arg(ap,int);
|
||||
_vl_svGetLogicArrElemVecVal(d, s, 2, indx1, indx2, 0); break; }
|
||||
case 3: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int);
|
||||
_vl_svGetLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3); break; }
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
_vl_svGetLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
_vl_svGetLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
break;
|
||||
}
|
||||
default: _vl_svGetLogicArrElemVecVal(d, s, -1, 0, 0, 0); break; // Will error
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
void svGetLogicArrElem1VecVal(svLogicVecVal* d, const svOpenArrayHandle s,
|
||||
int indx1) {
|
||||
void svGetLogicArrElem1VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1) {
|
||||
_vl_svGetLogicArrElemVecVal(d, s, 1, indx1, 0, 0);
|
||||
}
|
||||
void svGetLogicArrElem2VecVal(svLogicVecVal* d, const svOpenArrayHandle s,
|
||||
int indx1, int indx2) {
|
||||
void svGetLogicArrElem2VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, int indx2) {
|
||||
_vl_svGetLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
|
||||
}
|
||||
void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s,
|
||||
int indx1, int indx2, int indx3) {
|
||||
void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, int indx2,
|
||||
int indx3) {
|
||||
_vl_svGetLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
|
||||
}
|
||||
|
||||
|
|
@ -609,12 +632,24 @@ svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...) {
|
|||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: out = _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); break;
|
||||
case 2: { int indx2=va_arg(ap,int);
|
||||
out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0); break; }
|
||||
case 3: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int);
|
||||
out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0); break; }
|
||||
case 4: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int); int indx4=va_arg(ap,int);
|
||||
out = _vl_svGetBitArrElem(s, 4, indx1, indx2, indx3, indx4); break; }
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
int indx4 = va_arg(ap, int);
|
||||
out = _vl_svGetBitArrElem(s, 4, indx1, indx2, indx3, indx4);
|
||||
break;
|
||||
}
|
||||
default: out = _vl_svGetBitArrElem(s, -1, 0, 0, 0, 0); break; // Will error
|
||||
}
|
||||
va_end(ap);
|
||||
|
|
@ -637,12 +672,24 @@ svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...) {
|
|||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: out = _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); break;
|
||||
case 2: { int indx2=va_arg(ap,int);
|
||||
out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0); break; }
|
||||
case 3: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int);
|
||||
out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0); break; }
|
||||
case 4: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int); int indx4=va_arg(ap,int);
|
||||
out = _vl_svGetBitArrElem(s, 4, indx1, indx2, indx3, indx4); break; }
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
int indx4 = va_arg(ap, int);
|
||||
out = _vl_svGetBitArrElem(s, 4, indx1, indx2, indx3, indx4);
|
||||
break;
|
||||
}
|
||||
default: out = _vl_svGetBitArrElem(s, -1, 0, 0, 0, 0); break; // Will error
|
||||
}
|
||||
va_end(ap);
|
||||
|
|
@ -667,12 +714,24 @@ void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...) {
|
|||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); break;
|
||||
case 2: { int indx2=va_arg(ap,int);
|
||||
_vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0); break; }
|
||||
case 3: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int);
|
||||
_vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0); break; }
|
||||
case 4: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int); int indx4=va_arg(ap,int);
|
||||
_vl_svPutBitArrElem(d, value, 4, indx1, indx2, indx3, indx4); break; }
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
int indx4 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElem(d, value, 4, indx1, indx2, indx3, indx4);
|
||||
break;
|
||||
}
|
||||
default: _vl_svPutBitArrElem(d, value, -1, 0, 0, 0, 0); break; // Will error
|
||||
}
|
||||
va_end(ap);
|
||||
|
|
@ -693,12 +752,24 @@ void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, ...)
|
|||
va_start(ap, indx1);
|
||||
switch (varp->udims()) {
|
||||
case 1: _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); break;
|
||||
case 2: { int indx2=va_arg(ap,int);
|
||||
_vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0); break; }
|
||||
case 3: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int);
|
||||
_vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0); break; }
|
||||
case 4: { int indx2=va_arg(ap,int); int indx3=va_arg(ap,int); int indx4=va_arg(ap,int);
|
||||
_vl_svPutBitArrElem(d, value, 4, indx1, indx2, indx3, indx4); break; }
|
||||
case 2: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
int indx2 = va_arg(ap, int);
|
||||
int indx3 = va_arg(ap, int);
|
||||
int indx4 = va_arg(ap, int);
|
||||
_vl_svPutBitArrElem(d, value, 4, indx1, indx2, indx3, indx4);
|
||||
break;
|
||||
}
|
||||
default: _vl_svPutBitArrElem(d, value, -1, 0, 0, 0, 0); break; // Will error
|
||||
}
|
||||
va_end(ap);
|
||||
|
|
@ -707,13 +778,12 @@ void svPutLogicArrElem1(const svOpenArrayHandle d, svLogic value, int indx1) {
|
|||
// Verilator doesn't support X/Z so can just call Bit version
|
||||
svPutBitArrElem1(d, value, indx1);
|
||||
}
|
||||
void svPutLogicArrElem2(const svOpenArrayHandle d, svLogic value,
|
||||
int indx1, int indx2) {
|
||||
void svPutLogicArrElem2(const svOpenArrayHandle d, svLogic value, int indx1, int indx2) {
|
||||
// Verilator doesn't support X/Z so can just call Bit version
|
||||
svPutBitArrElem2(d, value, indx1, indx2);
|
||||
}
|
||||
void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value,
|
||||
int indx1, int indx2, int indx3) {
|
||||
void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value, int indx1, int indx2,
|
||||
int indx3) {
|
||||
// Verilator doesn't support X/Z so can just call Bit version
|
||||
svPutBitArrElem3(d, value, indx1, indx2, indx3);
|
||||
}
|
||||
|
|
@ -722,7 +792,10 @@ void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value,
|
|||
// Functions for working with DPI context
|
||||
|
||||
svScope svGetScope() {
|
||||
if (VL_UNLIKELY(!Verilated::dpiInContext())) { _VL_SVDPI_CONTEXT_WARN(); return NULL; }
|
||||
if (VL_UNLIKELY(!Verilated::dpiInContext())) {
|
||||
_VL_SVDPI_CONTEXT_WARN();
|
||||
return NULL;
|
||||
}
|
||||
// NOLINTNEXTLINE(google-readability-casting)
|
||||
return (svScope)(Verilated::dpiScope());
|
||||
}
|
||||
|
|
@ -746,16 +819,19 @@ svScope svGetScopeFromName(const char* scopeName) {
|
|||
}
|
||||
|
||||
int svPutUserData(const svScope scope, void* userKey, void* userData) {
|
||||
VerilatedImp::userInsert(scope,userKey,userData);
|
||||
VerilatedImp::userInsert(scope, userKey, userData);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* svGetUserData(const svScope scope, void* userKey) {
|
||||
return VerilatedImp::userFind(scope,userKey);
|
||||
return VerilatedImp::userFind(scope, userKey);
|
||||
}
|
||||
|
||||
int svGetCallerInfo(const char** fileNamepp, int *lineNumberp) {
|
||||
if (VL_UNLIKELY(!Verilated::dpiInContext())) { _VL_SVDPI_CONTEXT_WARN(); return false; }
|
||||
int svGetCallerInfo(const char** fileNamepp, int* lineNumberp) {
|
||||
if (VL_UNLIKELY(!Verilated::dpiInContext())) {
|
||||
_VL_SVDPI_CONTEXT_WARN();
|
||||
return false;
|
||||
}
|
||||
if (VL_LIKELY(fileNamepp)) *fileNamepp = Verilated::dpiFilenamep(); // thread local
|
||||
if (VL_LIKELY(lineNumberp)) *lineNumberp = Verilated::dpiLineno(); // thread local
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -32,45 +32,59 @@
|
|||
//===================================================================
|
||||
// SETTING OPERATORS
|
||||
|
||||
/// Return WData from svBitVecVal
|
||||
/// Convert svBitVecVal to Verilator internal data
|
||||
static inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
int words = VL_WORDS_I(obits);
|
||||
for (int i=0; i<words-1; ++i) owp[i]=lwp[i];
|
||||
owp[words-1] = lwp[words-1] & VL_MASK_I(obits);
|
||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
|
||||
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
|
||||
}
|
||||
/// Return svBitVecVal from WData
|
||||
static inline QData VL_SET_Q_SVBV(const svBitVecVal* lwp) VL_MT_SAFE {
|
||||
return _VL_SET_QII(lwp[1], lwp[0]);
|
||||
}
|
||||
static inline IData VL_SET_I_SVBV(const svBitVecVal* lwp) VL_MT_SAFE { return lwp[0]; }
|
||||
|
||||
/// Convert Verilator internal data to svBitVecVal
|
||||
static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, WDataInP lwp) VL_MT_SAFE {
|
||||
int words = VL_WORDS_I(obits);
|
||||
for (int i=0; i<words-1; ++i) owp[i]=lwp[i];
|
||||
owp[words-1] = lwp[words-1] & VL_MASK_I(obits);
|
||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
|
||||
owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, IData ld) VL_MT_SAFE { owp[0] = ld; }
|
||||
static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, QData ld) VL_MT_SAFE {
|
||||
VL_SET_WQ(owp, ld);
|
||||
}
|
||||
|
||||
/// Convert svLogicVecVal to/from WData
|
||||
/// Convert svLogicVecVal to Verilator internal data
|
||||
/// Note these functions ignore X/Z in svLogicVecVal
|
||||
static inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
int words = VL_WORDS_I(obits);
|
||||
for (int i=0; i<words-1; ++i) owp[i]=lwp[i].aval;
|
||||
owp[words-1] = lwp[words-1].aval & VL_MASK_I(obits);
|
||||
for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i].aval;
|
||||
owp[words - 1] = lwp[words - 1].aval & VL_MASK_I(obits);
|
||||
}
|
||||
static inline QData VL_SET_Q_SVLV(const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
return _VL_SET_QII(lwp[1].aval, lwp[0].aval);
|
||||
}
|
||||
static inline IData VL_SET_I_SVLV(const svLogicVecVal* lwp) VL_MT_SAFE {
|
||||
return lwp[0].aval;
|
||||
}
|
||||
static inline IData VL_SET_I_SVLV(const svLogicVecVal* lwp) VL_MT_SAFE { return lwp[0].aval; }
|
||||
|
||||
/// Convert Verilator internal data to svLogicVecVal
|
||||
/// Note these functions never create X/Z in svLogicVecVal
|
||||
static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, WDataInP lwp) VL_MT_SAFE {
|
||||
int words = VL_WORDS_I(obits);
|
||||
for (int i=0; i<words; ++i) owp[i].bval=0;
|
||||
for (int i=0; i<words-1; ++i) owp[i].aval=lwp[i];
|
||||
owp[words-1].aval = lwp[words-1] & VL_MASK_I(obits);
|
||||
for (int i = 0; i < words; ++i) owp[i].bval = 0;
|
||||
for (int i = 0; i < words - 1; ++i) owp[i].aval = lwp[i];
|
||||
owp[words - 1].aval = lwp[words - 1] & VL_MASK_I(obits);
|
||||
}
|
||||
static inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, IData ld) VL_MT_SAFE {
|
||||
owp[0].aval=ld; owp[0].bval=0;
|
||||
owp[0].aval = ld;
|
||||
owp[0].bval = 0;
|
||||
}
|
||||
static inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, QData ld) VL_MT_SAFE {
|
||||
WData lwp[2]; VL_SET_WQ(lwp,ld);
|
||||
owp[0].aval=lwp[0]; owp[0].bval=0;
|
||||
owp[1].aval=lwp[1]; owp[1].bval=0;
|
||||
WData lwp[2];
|
||||
VL_SET_WQ(lwp, ld);
|
||||
owp[0].aval = lwp[0];
|
||||
owp[0].bval = 0;
|
||||
owp[1].aval = lwp[1];
|
||||
owp[1].bval = 0;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
|
|
|||
|
|
@ -17,13 +17,14 @@
|
|||
//=============================================================================
|
||||
// SPDIFF_OFF
|
||||
|
||||
// clang-format off
|
||||
|
||||
#define __STDC_LIMIT_MACROS // UINT64_MAX
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_fst_c.h"
|
||||
|
||||
// GTKWave configuration
|
||||
#ifdef VL_TRACE_THREADED
|
||||
#ifdef VL_TRACE_FST_WRITER_THREAD
|
||||
# define HAVE_LIBPTHREAD
|
||||
# define FST_WRITER_PARALLEL
|
||||
#endif
|
||||
|
|
@ -49,54 +50,41 @@
|
|||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// clang-format on
|
||||
|
||||
class VerilatedFstCallInfo {
|
||||
protected:
|
||||
friend class VerilatedFst;
|
||||
VerilatedFstCallback_t m_initcb; ///< Initialization Callback function
|
||||
VerilatedFstCallback_t m_fullcb; ///< Full Dumping Callback function
|
||||
VerilatedFstCallback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< Fake "this" for caller
|
||||
vluint32_t m_code; ///< Starting code number
|
||||
// CONSTRUCTORS
|
||||
VerilatedFstCallInfo(VerilatedFstCallback_t icb, VerilatedFstCallback_t fcb,
|
||||
VerilatedFstCallback_t changecb, void* ut)
|
||||
: m_initcb(icb)
|
||||
, m_fullcb(fcb)
|
||||
, m_changecb(changecb)
|
||||
, m_userthis(ut)
|
||||
, m_code(1) {}
|
||||
~VerilatedFstCallInfo() {}
|
||||
};
|
||||
//=============================================================================
|
||||
// Specialization of the generics for this trace format
|
||||
|
||||
#define VL_DERIVED_T VerilatedFst
|
||||
#include "verilated_trace_imp.cpp"
|
||||
#undef VL_DERIVED_T
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFst
|
||||
|
||||
VerilatedFst::VerilatedFst(void* fst)
|
||||
: m_fst(fst)
|
||||
, m_fullDump(true)
|
||||
, m_nextCode(1)
|
||||
, m_scopeEscape('.') {
|
||||
m_valueStrBuffer.reserve(64 + 1); // Need enough room for quad
|
||||
, m_symbolp(NULL)
|
||||
, m_strbuf(NULL) {}
|
||||
|
||||
VerilatedFst::~VerilatedFst() {
|
||||
if (m_fst) fstWriterClose(m_fst);
|
||||
if (m_symbolp) VL_DO_CLEAR(delete[] m_symbolp, m_symbolp = NULL);
|
||||
if (m_strbuf) VL_DO_CLEAR(delete[] m_strbuf, m_strbuf = NULL);
|
||||
}
|
||||
|
||||
void VerilatedFst::open(const char* filename) VL_MT_UNSAFE {
|
||||
m_assertOne.check();
|
||||
m_fst = fstWriterCreate(filename, 1);
|
||||
fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
|
||||
#ifdef VL_TRACE_THREADED
|
||||
fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref
|
||||
#ifdef VL_TRACE_FST_WRITER_THREAD
|
||||
fstWriterSetParallelMode(m_fst, 1);
|
||||
#endif
|
||||
m_curScope.clear();
|
||||
m_nextCode = 1;
|
||||
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ++ent) {
|
||||
VerilatedFstCallInfo* cip = m_callbacks[ent];
|
||||
cip->m_code = m_nextCode;
|
||||
// Initialize; callbacks will call decl* which update m_nextCode
|
||||
(cip->m_initcb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
m_curScope.clear();
|
||||
|
||||
VerilatedTrace<VerilatedFst>::traceInit();
|
||||
|
||||
// Clear the scope stack
|
||||
std::list<std::string>::iterator it = m_curScope.begin();
|
||||
|
|
@ -104,31 +92,50 @@ void VerilatedFst::open(const char* filename) VL_MT_UNSAFE {
|
|||
fstWriterSetUpscope(m_fst);
|
||||
it = m_curScope.erase(it);
|
||||
}
|
||||
|
||||
// convert m_code2symbol into an array for fast lookup
|
||||
if (!m_symbolp) {
|
||||
m_symbolp = new fstHandle[nextCode()];
|
||||
for (Code2SymbolType::iterator it = m_code2symbol.begin(); it != m_code2symbol.end();
|
||||
++it) {
|
||||
m_symbolp[it->first] = it->second;
|
||||
}
|
||||
}
|
||||
m_code2symbol.clear();
|
||||
|
||||
// Allocate string buffer for arrays
|
||||
if (!m_strbuf) { m_strbuf = new char[maxBits() + 32]; }
|
||||
}
|
||||
|
||||
void VerilatedFst::module(const std::string& name) {
|
||||
m_module = name;
|
||||
void VerilatedFst::close() {
|
||||
m_assertOne.check();
|
||||
VerilatedTrace<VerilatedFst>::close();
|
||||
fstWriterClose(m_fst);
|
||||
m_fst = NULL;
|
||||
}
|
||||
|
||||
void VerilatedFst::flush() {
|
||||
VerilatedTrace<VerilatedFst>::flush();
|
||||
fstWriterFlushContext(m_fst);
|
||||
}
|
||||
|
||||
void VerilatedFst::emitTimeChange(vluint64_t timeui) { fstWriterEmitTimeChange(m_fst, timeui); }
|
||||
|
||||
//=============================================================================
|
||||
// Decl
|
||||
|
||||
void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
|
||||
unsigned int minValbits,
|
||||
const char** itemNamesp, const char** itemValuesp) {
|
||||
fstEnumHandle enumNum = fstWriterCreateEnumTable(m_fst, name, elements,
|
||||
minValbits, itemNamesp, itemValuesp);
|
||||
unsigned int minValbits, const char** itemNamesp,
|
||||
const char** itemValuesp) {
|
||||
fstEnumHandle enumNum
|
||||
= fstWriterCreateEnumTable(m_fst, name, elements, minValbits, itemNamesp, itemValuesp);
|
||||
m_local2fstdtype[dtypenum] = enumNum;
|
||||
}
|
||||
|
||||
void VerilatedFst::declSymbol(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, vluint32_t len,
|
||||
vluint32_t bits) {
|
||||
|
||||
// Make sure deduplicate tracking increments for future declarations
|
||||
int codesNeeded = 1 + int(bits / 32);
|
||||
//Not supported: if (tri) codesNeeded *= 2; // Space in change array for __en signals
|
||||
m_nextCode = std::max(m_nextCode, code + codesNeeded);
|
||||
VerilatedTrace<VerilatedFst>::declCode(code, bits, false);
|
||||
|
||||
std::pair<Code2SymbolType::iterator, bool> p
|
||||
= m_code2symbol.insert(std::make_pair(code, static_cast<fstHandle>(NULL)));
|
||||
|
|
@ -137,14 +144,13 @@ void VerilatedFst::declSymbol(vluint32_t code, const char* name, int dtypenum, f
|
|||
std::list<std::string> tokens(beg, end); // Split name
|
||||
std::string symbol_name(tokens.back());
|
||||
tokens.pop_back(); // Remove symbol name from hierarchy
|
||||
tokens.insert(tokens.begin(), m_module); // Add current module to the hierarchy
|
||||
tokens.insert(tokens.begin(), moduleName()); // Add current module to the hierarchy
|
||||
|
||||
// Find point where current and new scope diverge
|
||||
std::list<std::string>::iterator cur_it = m_curScope.begin();
|
||||
std::list<std::string>::iterator new_it = tokens.begin();
|
||||
while (cur_it != m_curScope.end() && new_it != tokens.end()) {
|
||||
if (*cur_it != *new_it)
|
||||
break;
|
||||
if (*cur_it != *new_it) break;
|
||||
++cur_it;
|
||||
++new_it;
|
||||
}
|
||||
|
|
@ -179,41 +185,93 @@ void VerilatedFst::declSymbol(vluint32_t code, const char* name, int dtypenum, f
|
|||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Callbacks
|
||||
|
||||
void VerilatedFst::addCallback(VerilatedFstCallback_t initcb, VerilatedFstCallback_t fullcb,
|
||||
VerilatedFstCallback_t changecb, void* userthis) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(isOpen())) {
|
||||
std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
|
||||
+ " called with already open file");
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
}
|
||||
VerilatedFstCallInfo* cip = new VerilatedFstCallInfo(initcb, fullcb, changecb, userthis);
|
||||
m_callbacks.push_back(cip);
|
||||
void VerilatedFst::declBit(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, 1, 1);
|
||||
}
|
||||
void VerilatedFst::declBus(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, msb - lsb + 1,
|
||||
msb - lsb + 1);
|
||||
}
|
||||
void VerilatedFst::declQuad(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, msb - lsb + 1,
|
||||
msb - lsb + 1);
|
||||
}
|
||||
void VerilatedFst::declArray(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, msb - lsb + 1,
|
||||
msb - lsb + 1);
|
||||
}
|
||||
void VerilatedFst::declFloat(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, 1, 32);
|
||||
}
|
||||
void VerilatedFst::declDouble(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, 2, 64);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Dumping
|
||||
// Note: emit* are only ever called from one place (full* in
|
||||
// verilated_trace_imp.cpp, which is included in this file at the top),
|
||||
// so always inline them.
|
||||
|
||||
void VerilatedFst::dump(vluint64_t timeui) {
|
||||
if (!isOpen()) return;
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
m_fullDump = false; // No more need for next dump to be full
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ++ent) {
|
||||
VerilatedFstCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_fullcb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
return;
|
||||
}
|
||||
fstWriterEmitTimeChange(m_fst, timeui);
|
||||
for (vluint32_t ent = 0; ent< m_callbacks.size(); ++ent) {
|
||||
VerilatedFstCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_changecb)(this, cip->m_userthis, cip->m_code);
|
||||
}
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitBit(vluint32_t code, CData newval) {
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], newval ? "1" : "0");
|
||||
}
|
||||
|
||||
//********************************************************************
|
||||
// Local Variables:
|
||||
// End:
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitCData(vluint32_t code, CData newval, int bits) {
|
||||
char buf[VL_BYTESIZE];
|
||||
cvtCDataToStr(buf, newval << (VL_BYTESIZE - bits));
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitSData(vluint32_t code, SData newval, int bits) {
|
||||
char buf[VL_SHORTSIZE];
|
||||
cvtSDataToStr(buf, newval << (VL_SHORTSIZE - bits));
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitIData(vluint32_t code, IData newval, int bits) {
|
||||
char buf[VL_IDATASIZE];
|
||||
cvtIDataToStr(buf, newval << (VL_IDATASIZE - bits));
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitQData(vluint32_t code, QData newval, int bits) {
|
||||
char buf[VL_QUADSIZE];
|
||||
cvtQDataToStr(buf, newval << (VL_QUADSIZE - bits));
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitWData(vluint32_t code, const WData* newvalp, int bits) {
|
||||
int words = VL_WORDS_I(bits);
|
||||
char* wp = m_strbuf;
|
||||
// Convert the most significant word
|
||||
const int bitsInMSW = VL_BITBIT_E(bits) ? VL_BITBIT_E(bits) : VL_EDATASIZE;
|
||||
cvtEDataToStr(wp, newvalp[--words] << (VL_EDATASIZE - bitsInMSW));
|
||||
wp += bitsInMSW;
|
||||
// Convert the remaining words
|
||||
while (words > 0) {
|
||||
cvtEDataToStr(wp, newvalp[--words]);
|
||||
wp += VL_EDATASIZE;
|
||||
}
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], m_strbuf);
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitFloat(vluint32_t code, float newval) {
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], &newval);
|
||||
}
|
||||
|
||||
VL_ATTR_ALWINLINE
|
||||
void VerilatedFst::emitDouble(vluint32_t code, double newval) {
|
||||
fstWriterEmitValueChange(m_fst, m_symbolp[code], &newval);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
#ifndef _VERILATED_FST_C_H_
|
||||
#define _VERILATED_FST_C_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_trace.h"
|
||||
|
||||
#include "gtkwave/fstapi.h"
|
||||
|
||||
|
|
@ -30,163 +30,101 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedFst;
|
||||
class VerilatedFstCallInfo;
|
||||
typedef void (*VerilatedFstCallback_t)(VerilatedFst* vcdp, void* userthis, vluint32_t code);
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFst
|
||||
/// Base class to create a Verilator FST dump
|
||||
/// This is an internally used class - see VerilatedFstC for what to call from applications
|
||||
|
||||
class VerilatedFst {
|
||||
class VerilatedFst : public VerilatedTrace<VerilatedFst> {
|
||||
private:
|
||||
// Give the superclass access to private bits (to avoid virtual functions)
|
||||
friend class VerilatedTrace<VerilatedFst>;
|
||||
|
||||
//=========================================================================
|
||||
// FST specific internals
|
||||
|
||||
typedef std::map<vluint32_t, fstHandle> Code2SymbolType;
|
||||
typedef std::map<int, fstEnumHandle> Local2FstDtype;
|
||||
typedef std::vector<VerilatedFstCallInfo*> CallbackVec;
|
||||
private:
|
||||
|
||||
void* m_fst;
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
bool m_fullDump;
|
||||
vluint32_t m_nextCode; ///< Next code number to assign
|
||||
char m_scopeEscape;
|
||||
std::string m_module;
|
||||
CallbackVec m_callbacks; ///< Routines to perform dumping
|
||||
Code2SymbolType m_code2symbol;
|
||||
Local2FstDtype m_local2fstdtype;
|
||||
std::list<std::string> m_curScope;
|
||||
fstHandle* m_symbolp; ///< same as m_code2symbol, but as an array
|
||||
char* m_strbuf; ///< String buffer long enough to hold maxBits() chars
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedFst);
|
||||
void declSymbol(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
bool array, int arraynum, vluint32_t len, vluint32_t bits);
|
||||
// helpers
|
||||
std::vector<char> m_valueStrBuffer;
|
||||
void declSymbol(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, vluint32_t len, vluint32_t bits);
|
||||
|
||||
protected:
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTrace interface
|
||||
|
||||
// Implementations of protected virtual methods for VerilatedTrace
|
||||
void emitTimeChange(vluint64_t timeui) VL_OVERRIDE;
|
||||
|
||||
// Hooks called from VerilatedTrace
|
||||
bool preFullDump() VL_OVERRIDE { return isOpen(); }
|
||||
bool preChangeDump() VL_OVERRIDE { return isOpen(); }
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTrace. These are
|
||||
// called from only one place (namely full*) so always inline them.
|
||||
inline void emitBit(vluint32_t code, CData newval);
|
||||
inline void emitCData(vluint32_t code, CData newval, int bits);
|
||||
inline void emitSData(vluint32_t code, SData newval, int bits);
|
||||
inline void emitIData(vluint32_t code, IData newval, int bits);
|
||||
inline void emitQData(vluint32_t code, QData newval, int bits);
|
||||
inline void emitWData(vluint32_t code, const WData* newvalp, int bits);
|
||||
inline void emitFloat(vluint32_t code, float newval);
|
||||
inline void emitDouble(vluint32_t code, double newval);
|
||||
|
||||
public:
|
||||
explicit VerilatedFst(void* fst=NULL);
|
||||
~VerilatedFst() { if (m_fst == NULL) { fstWriterClose(m_fst); } }
|
||||
void changeThread() { m_assertOne.changeThread(); }
|
||||
bool isOpen() const { return m_fst != NULL; }
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
explicit VerilatedFst(void* fst = NULL);
|
||||
~VerilatedFst();
|
||||
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filename) VL_MT_UNSAFE;
|
||||
void flush() VL_MT_UNSAFE { fstWriterFlushContext(m_fst); }
|
||||
void close() VL_MT_UNSAFE {
|
||||
m_assertOne.check();
|
||||
fstWriterClose(m_fst);
|
||||
m_fst = NULL;
|
||||
}
|
||||
void set_time_unit(const char* unitp) { fstWriterSetTimescaleFromString(m_fst, unitp); }
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
/// Close the file
|
||||
void close() VL_MT_UNSAFE;
|
||||
/// Flush any remaining data to this file
|
||||
void flush() VL_MT_UNSAFE;
|
||||
/// Is file open?
|
||||
bool isOpen() const { return m_fst != NULL; }
|
||||
|
||||
void set_time_resolution(const char* unitp) { if (unitp) {} }
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
// double timescaleToDouble(const char* unitp);
|
||||
// std::string doubleToTimescale(double value);
|
||||
|
||||
/// Change character that splits scopes. Note whitespace are ALWAYS escapes.
|
||||
void scopeEscape(char flag) { m_scopeEscape = flag; }
|
||||
/// Is this an escape?
|
||||
bool isScopeEscape(char c) { return isspace(c) || c == m_scopeEscape; }
|
||||
/// Inside dumping routines, called each cycle to make the dump
|
||||
void dump(vluint64_t timeui);
|
||||
/// Inside dumping routines, declare callbacks for tracings
|
||||
void addCallback(VerilatedFstCallback_t initcb, VerilatedFstCallback_t fullcb,
|
||||
VerilatedFstCallback_t changecb,
|
||||
void* userthis) VL_MT_UNSAFE_ONE;
|
||||
|
||||
/// Inside dumping routines, declare a module
|
||||
void module(const std::string& name);
|
||||
/// Inside dumping routines, declare a data type
|
||||
void declDTypeEnum(int dtypenum, const char* name, vluint32_t elements,
|
||||
unsigned int minValbits,
|
||||
const char** itemNamesp, const char** itemValuesp);
|
||||
unsigned int minValbits, const char** itemNamesp, const char** itemValuesp);
|
||||
|
||||
/// Inside dumping routines, declare a signal
|
||||
void declBit(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
bool array, int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, 1, 1);
|
||||
}
|
||||
void declBus(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
bool array, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, msb - lsb + 1,
|
||||
msb - lsb + 1);
|
||||
}
|
||||
void declDouble(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
bool array, int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, 2, 64);
|
||||
}
|
||||
void declFloat(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
bool array, int arraynum) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, 1, 32);
|
||||
}
|
||||
void declQuad(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
bool array, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, msb - lsb + 1,
|
||||
msb - lsb + 1);
|
||||
}
|
||||
void declArray(vluint32_t code, const char* name,
|
||||
int dtypenum, fstVarDir vardir, fstVarType vartype,
|
||||
bool array, int arraynum, int msb, int lsb) {
|
||||
declSymbol(code, name, dtypenum, vardir, vartype, array, arraynum, msb - lsb + 1,
|
||||
msb - lsb + 1);
|
||||
}
|
||||
|
||||
/// Inside dumping routines, dump one signal if it has changed
|
||||
void chgBit(vluint32_t code, const vluint32_t newval) {
|
||||
fstWriterEmitValueChange(m_fst, m_code2symbol[code], newval ? "1" : "0");
|
||||
}
|
||||
void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
fstWriterEmitValueChange32(m_fst, m_code2symbol[code], bits, newval);
|
||||
}
|
||||
void chgDouble(vluint32_t code, const double newval) {
|
||||
double val = newval;
|
||||
fstWriterEmitValueChange(m_fst, m_code2symbol[code], &val);
|
||||
}
|
||||
void chgFloat(vluint32_t code, const float newval) {
|
||||
double val = (double)newval;
|
||||
fstWriterEmitValueChange(m_fst, m_code2symbol[code], &val);
|
||||
}
|
||||
void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
fstWriterEmitValueChange64(m_fst, m_code2symbol[code], bits, newval);
|
||||
}
|
||||
void chgArray(vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
fstWriterEmitValueChangeVec32(m_fst, m_code2symbol[code], bits, newval);
|
||||
}
|
||||
|
||||
void fullBit(vluint32_t code, const vluint32_t newval) {
|
||||
chgBit(code, newval); }
|
||||
void fullBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
chgBus(code, newval, bits); }
|
||||
void fullDouble(vluint32_t code, const double newval) {
|
||||
chgDouble(code, newval); }
|
||||
void fullFloat(vluint32_t code, const float newval) {
|
||||
chgFloat(code, newval); }
|
||||
void fullQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
chgQuad(code, newval, bits); }
|
||||
void fullArray(vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
chgArray(code, newval, bits); }
|
||||
|
||||
void declTriBit(vluint32_t code, const char* name, int arraynum);
|
||||
void declTriBus(vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void declTriQuad(vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void declTriArray(vluint32_t code, const char* name, int arraynum, int msb, int lsb);
|
||||
void fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri);
|
||||
void fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits);
|
||||
void fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits);
|
||||
void fullTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits);
|
||||
void fullBitX(vluint32_t code);
|
||||
void fullBusX(vluint32_t code, int bits);
|
||||
void fullQuadX(vluint32_t code, int bits);
|
||||
void fullArrayX(vluint32_t code, int bits);
|
||||
void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri);
|
||||
void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits);
|
||||
void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits);
|
||||
void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip, int bits);
|
||||
void declBit(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
void declBus(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum, int msb, int lsb);
|
||||
void declFloat(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
void declDouble(vluint32_t code, const char* name, int dtypenum, fstVarDir vardir,
|
||||
fstVarType vartype, bool array, int arraynum);
|
||||
};
|
||||
|
||||
// Declare specialization here as it's used in VerilatedFstC just below
|
||||
template <> void VerilatedTrace<VerilatedFst>::dump(vluint64_t timeui);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_unit(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedFst>::set_time_resolution(const std::string& unit);
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedFstC
|
||||
/// Create a FST dump file in C standalone (no SystemC) simulations.
|
||||
|
|
@ -198,12 +136,14 @@ class VerilatedFstC {
|
|||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedFstC);
|
||||
|
||||
public:
|
||||
explicit VerilatedFstC(void* filep=NULL) : m_sptrace(filep) {}
|
||||
explicit VerilatedFstC(void* filep = NULL)
|
||||
: m_sptrace(filep) {}
|
||||
~VerilatedFstC() { close(); }
|
||||
/// Routines can only be called from one thread; allow next call from different thread
|
||||
void changeThread() { spTrace()->changeThread(); }
|
||||
public:
|
||||
|
||||
// ACCESSORS
|
||||
/// Is file open?
|
||||
bool isOpen() const { return m_sptrace.isOpen(); }
|
||||
|
|
@ -222,13 +162,13 @@ public:
|
|||
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
/// Set time units (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
/// For Verilated models, these propage from the Verilated default --timeunit
|
||||
void set_time_unit(const char* unitp) { m_sptrace.set_time_unit(unitp); }
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
void set_time_unit(const std::string& unit) { m_sptrace.set_time_unit(unit); }
|
||||
/// Set time resolution (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
/// For Verilated models, these propage from the Verilated default --timeunit
|
||||
void set_time_resolution(const char* unitp) { m_sptrace.set_time_resolution(unitp); }
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
void set_time_resolution(const std::string& unit) { m_sptrace.set_time_resolution(unit); }
|
||||
|
||||
/// Internal class access
|
||||
inline VerilatedFst* spTrace() { return &m_sptrace; };
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
//===================================================================
|
||||
|
|
@ -82,8 +83,11 @@ public:
|
|||
|
||||
template <std::size_t T_Words> class VlWide {
|
||||
WData m_storage[T_Words];
|
||||
|
||||
public:
|
||||
// Default constructor/destructor/copy are fine
|
||||
// cppcheck-suppress uninitVar
|
||||
VlWide() {}
|
||||
~VlWide() {}
|
||||
const WData& at(size_t index) const { return m_storage[index]; }
|
||||
WData& at(size_t index) { return m_storage[index]; }
|
||||
WData* data() { return &m_storage[0]; }
|
||||
|
|
@ -95,13 +99,11 @@ public:
|
|||
|
||||
// Convert a C array to std::array reference by pointer magic, without copy.
|
||||
// Data type (second argument) is so the function template can automatically generate.
|
||||
template <std::size_t T_Words>
|
||||
VlWide<T_Words>& VL_CVT_W_A(WDataInP inp, const VlWide<T_Words>&) {
|
||||
template <std::size_t T_Words> VlWide<T_Words>& VL_CVT_W_A(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());
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +116,7 @@ template <class T_Key, class T_Value> class VlAssocArray {
|
|||
private:
|
||||
// TYPES
|
||||
typedef std::map<T_Key, T_Value> Map;
|
||||
|
||||
public:
|
||||
typedef typename Map::const_iterator const_iterator;
|
||||
|
||||
|
|
@ -187,8 +190,11 @@ public:
|
|||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(const T_Key& index) const {
|
||||
typename Map::iterator it = m_map.find(index);
|
||||
if (it == m_map.end()) return m_defaultValue;
|
||||
else return it->second;
|
||||
if (it == m_map.end()) {
|
||||
return m_defaultValue;
|
||||
} else {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_map.begin(); }
|
||||
|
|
@ -251,6 +257,7 @@ template <class T_Value, size_t T_MaxSize = 0> class VlQueue {
|
|||
private:
|
||||
// TYPES
|
||||
typedef std::deque<T_Value> Deque;
|
||||
|
||||
public:
|
||||
typedef typename Deque::const_iterator const_iterator;
|
||||
|
||||
|
|
@ -274,7 +281,9 @@ public:
|
|||
int size() const { return m_deque.size(); }
|
||||
// Clear array. Verilog: function void delete([input index])
|
||||
void clear() { m_deque.clear(); }
|
||||
void erase(size_t index) { if (VL_LIKELY(index < m_deque.size())) m_deque.erase(index); }
|
||||
void erase(size_t index) {
|
||||
if (VL_LIKELY(index < m_deque.size())) m_deque.erase(index);
|
||||
}
|
||||
|
||||
// Dynamic array new[] becomes a renew()
|
||||
void renew(size_t size) {
|
||||
|
|
@ -282,7 +291,7 @@ public:
|
|||
m_deque.resize(size, atDefault());
|
||||
}
|
||||
// Dynamic array new[]() becomes a renew_copy()
|
||||
void renew_copy(size_t size, const VlQueue<T_Value,T_MaxSize>& rhs) {
|
||||
void renew_copy(size_t size, const VlQueue<T_Value, T_MaxSize>& rhs) {
|
||||
if (size == 0) {
|
||||
clear();
|
||||
} else {
|
||||
|
|
@ -303,12 +312,16 @@ public:
|
|||
// function value_t q.pop_front();
|
||||
T_Value pop_front() {
|
||||
if (m_deque.empty()) return m_defaultValue;
|
||||
T_Value v = m_deque.front(); m_deque.pop_front(); return v;
|
||||
T_Value v = m_deque.front();
|
||||
m_deque.pop_front();
|
||||
return v;
|
||||
}
|
||||
// function value_t q.pop_back();
|
||||
T_Value pop_back() {
|
||||
if (m_deque.empty()) return m_defaultValue;
|
||||
T_Value v = m_deque.back(); m_deque.pop_back(); return v;
|
||||
T_Value v = m_deque.back();
|
||||
m_deque.pop_back();
|
||||
return v;
|
||||
}
|
||||
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
|
|
@ -320,15 +333,19 @@ public:
|
|||
if (VL_UNLIKELY(index >= m_deque.size())) {
|
||||
s_throwAway = atDefault();
|
||||
return s_throwAway;
|
||||
} else {
|
||||
return m_deque[index];
|
||||
}
|
||||
else return m_deque[index];
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(size_t index) const {
|
||||
static T_Value s_throwAway;
|
||||
// Needs to work for dynamic arrays, so does not use T_MaxSize
|
||||
if (VL_UNLIKELY(index >= m_deque.size())) return atDefault();
|
||||
else return m_deque[index];
|
||||
if (VL_UNLIKELY(index >= m_deque.size())) {
|
||||
return atDefault();
|
||||
} else {
|
||||
return m_deque[index];
|
||||
}
|
||||
}
|
||||
// function void q.insert(index, value);
|
||||
void insert(size_t index, const T_Value& value) {
|
||||
|
|
@ -352,36 +369,56 @@ 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();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog class reference container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
#if (defined(_MSC_VER) && _MSC_VER >= 1900) || (__cplusplus >= 201103L)
|
||||
# define VlClassRef std::shared_ptr
|
||||
#else
|
||||
# define VlClassRef VlClassRef__SystemVerilog_class_support_requires_a_C11_or_newer_compiler
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
template <class T> // T typically of type VlClassRef<x>
|
||||
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
|
||||
if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
|
||||
return t;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Conversion functions
|
||||
|
||||
extern std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE;
|
||||
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
|
||||
WData lw[VL_WQ_WORDS_E]; VL_SET_WQ(lw, lhs);
|
||||
WData lw[VL_WQ_WORDS_E];
|
||||
VL_SET_WQ(lw, lhs);
|
||||
return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw);
|
||||
}
|
||||
inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE {
|
||||
return lhs;
|
||||
}
|
||||
inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; }
|
||||
inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE {
|
||||
WData lw[VL_WQ_WORDS_E]; VL_SET_WI(lw, lhs);
|
||||
WData lw[VL_WQ_WORDS_E];
|
||||
VL_SET_WI(lw, lhs);
|
||||
return VL_CVT_PACK_STR_NW(1, lw);
|
||||
}
|
||||
inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE {
|
||||
return lhs + rhs;
|
||||
}
|
||||
inline std::string VL_REPLICATEN_NNQ(int,int,int, const std::string& lhs, IData rep) VL_PURE {
|
||||
std::string out; out.reserve(lhs.length() * rep);
|
||||
for (unsigned times=0; times<rep; ++times) out += lhs;
|
||||
inline std::string VL_REPLICATEN_NNQ(int, int, int, const std::string& lhs, IData rep) VL_PURE {
|
||||
std::string out;
|
||||
out.reserve(lhs.length() * rep);
|
||||
for (unsigned times = 0; times < rep; ++times) out += lhs;
|
||||
return out;
|
||||
}
|
||||
inline std::string VL_REPLICATEN_NNI(int obits,int lbits,int rbits,
|
||||
const std::string& lhs, IData rep) VL_PURE {
|
||||
inline std::string VL_REPLICATEN_NNI(int obits, int lbits, int rbits, const std::string& lhs,
|
||||
IData rep) VL_PURE {
|
||||
return VL_REPLICATEN_NNQ(obits, lbits, rbits, lhs, rep);
|
||||
}
|
||||
|
||||
|
|
@ -389,6 +426,7 @@ inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); }
|
|||
extern std::string VL_TOLOWER_NN(const std::string& ld);
|
||||
extern std::string VL_TOUPPER_NN(const std::string& ld);
|
||||
|
||||
extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE;
|
||||
extern IData VL_FOPEN_NI(const std::string& filename, IData mode) VL_MT_SAFE;
|
||||
extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb,
|
||||
const std::string& filename, void* memp, QData start,
|
||||
|
|
@ -396,11 +434,12 @@ extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb,
|
|||
extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb,
|
||||
const std::string& filename, const void* memp, QData start,
|
||||
QData end) VL_MT_SAFE;
|
||||
extern IData VL_SSCANF_INX(int lbits, const std::string& ld,
|
||||
const char* formatp, ...) VL_MT_SAFE;
|
||||
extern void VL_SFORMAT_X(int obits_ignored, std::string& output,
|
||||
const char* formatp, ...) VL_MT_SAFE;
|
||||
extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE;
|
||||
extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp,
|
||||
...) VL_MT_SAFE;
|
||||
extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE;
|
||||
extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix,
|
||||
int width) VL_MT_SAFE;
|
||||
extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE;
|
||||
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE {
|
||||
WData rwp[2]; // WData must always be at least 2
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#ifndef _VERILATED_IMP_H_
|
||||
#define _VERILATED_IMP_H_ 1 ///< Header Guard
|
||||
|
||||
// clang-format off
|
||||
#if !defined(_VERILATED_CPP_) && !defined(_VERILATED_DPI_CPP_) && !defined(_VERILATED_VPI_CPP_)
|
||||
# error "verilated_imp.h only to be included by verilated*.cpp internals"
|
||||
#endif
|
||||
|
|
@ -35,6 +36,7 @@
|
|||
# include <functional>
|
||||
# include <queue>
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
class VerilatedScope;
|
||||
|
||||
|
|
@ -47,9 +49,11 @@ class VerilatedMsg {
|
|||
public:
|
||||
// TYPES
|
||||
struct Cmp {
|
||||
bool operator() (const VerilatedMsg& a, const VerilatedMsg& b) const {
|
||||
return a.mtaskId() < b.mtaskId(); }
|
||||
bool operator()(const VerilatedMsg& a, const VerilatedMsg& b) const {
|
||||
return a.mtaskId() < b.mtaskId();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
vluint32_t m_mtaskId; ///< MTask that did enqueue
|
||||
|
|
@ -86,6 +90,7 @@ public:
|
|||
|
||||
private:
|
||||
VL_UNCOPYABLE(VerilatedEvalMsgQueue);
|
||||
|
||||
public:
|
||||
// METHODS
|
||||
//// Add message to queue (called by producer)
|
||||
|
|
@ -122,6 +127,7 @@ public:
|
|||
/// Each thread has a local queue to build up messages until the end of the eval() call
|
||||
class VerilatedThreadMsgQueue {
|
||||
std::queue<VerilatedMsg> m_queue;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedThreadMsgQueue() {}
|
||||
|
|
@ -129,6 +135,7 @@ public:
|
|||
// 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.
|
||||
}
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(VerilatedThreadMsgQueue);
|
||||
// METHODS
|
||||
|
|
@ -136,6 +143,7 @@ private:
|
|||
static VL_THREAD_LOCAL VerilatedThreadMsgQueue t_s;
|
||||
return t_s;
|
||||
}
|
||||
|
||||
public:
|
||||
/// Add message to queue, called by producer
|
||||
static void post(const VerilatedMsg& msg) VL_MT_SAFE {
|
||||
|
|
@ -165,6 +173,8 @@ public:
|
|||
|
||||
class VerilatedImp {
|
||||
// Whole class is internal use only - Global information shared between verilated*.cpp files.
|
||||
protected:
|
||||
friend class Verilated;
|
||||
|
||||
// TYPES
|
||||
typedef std::vector<std::string> ArgVec;
|
||||
|
|
@ -174,30 +184,52 @@ class VerilatedImp {
|
|||
// MEMBERS
|
||||
static VerilatedImp s_s; ///< Static Singleton; One and only static this
|
||||
|
||||
// Nothing here is save-restored; users expected to re-register appropriately
|
||||
struct Serialized { // All these members serialized/deserialized
|
||||
int m_timeFormatUnits; // $timeformat units
|
||||
int m_timeFormatPrecision; // $timeformat number of decimal places
|
||||
int m_timeFormatWidth; // $timeformat character width
|
||||
enum { UNITS_NONE = 99 }; // Default based on precision
|
||||
Serialized()
|
||||
: m_timeFormatUnits(UNITS_NONE)
|
||||
, m_timeFormatPrecision(0)
|
||||
, m_timeFormatWidth(20) {}
|
||||
~Serialized() {}
|
||||
} m_ser;
|
||||
|
||||
VerilatedMutex m_argMutex; ///< Protect m_argVec, m_argVecLoaded
|
||||
ArgVec m_argVec VL_GUARDED_BY(m_argMutex); ///< Argument list (NOT save-restored, may want different results)
|
||||
bool m_argVecLoaded VL_GUARDED_BY(m_argMutex); ///< Ever loaded argument list
|
||||
VerilatedMutex m_sergMutex; ///< Protect m_ser
|
||||
struct SerializedG { // All these members serialized/deserialized and guarded
|
||||
std::string m_timeFormatSuffix; // $timeformat printf format
|
||||
} m_serg VL_GUARDED_BY(m_sergMutex);
|
||||
|
||||
VerilatedMutex m_userMapMutex; ///< Protect m_userMap
|
||||
UserMap m_userMap VL_GUARDED_BY(m_userMapMutex); ///< Map of <(scope,userkey), userData>
|
||||
// Nothing below here is save-restored; users expected to re-register appropriately
|
||||
|
||||
VerilatedMutex m_nameMutex; ///< Protect m_nameMap
|
||||
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex); ///< Map of <scope_name, scope pointer>
|
||||
VerilatedMutex m_argMutex; ///< Protect m_argVec, m_argVecLoaded
|
||||
/// Argument list (NOT save-restored, may want different results)
|
||||
ArgVec m_argVec VL_GUARDED_BY(m_argMutex);
|
||||
bool m_argVecLoaded VL_GUARDED_BY(m_argMutex); ///< Ever loaded argument list
|
||||
|
||||
VerilatedMutex m_hierMapMutex; ///< Protect m_hierMap
|
||||
VerilatedHierarchyMap m_hierMap VL_GUARDED_BY(m_hierMapMutex); ///< Map the represents scope hierarchy
|
||||
VerilatedMutex m_userMapMutex; ///< Protect m_userMap
|
||||
UserMap m_userMap VL_GUARDED_BY(m_userMapMutex); ///< Map of <(scope,userkey), userData>
|
||||
|
||||
VerilatedMutex m_nameMutex; ///< Protect m_nameMap
|
||||
/// Map of <scope_name, scope pointer>
|
||||
VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex);
|
||||
|
||||
VerilatedMutex m_hierMapMutex; ///< Protect m_hierMap
|
||||
/// Map the represents scope hierarchy
|
||||
VerilatedHierarchyMap m_hierMap VL_GUARDED_BY(m_hierMapMutex);
|
||||
|
||||
// Slow - somewhat static:
|
||||
VerilatedMutex m_exportMutex; ///< Protect m_nameMap
|
||||
ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex); ///< Map of <export_func_proto, func number>
|
||||
int m_exportNext VL_GUARDED_BY(m_exportMutex); ///< Next export funcnum
|
||||
VerilatedMutex m_exportMutex; ///< Protect m_nameMap
|
||||
/// Map of <export_func_proto, func number>
|
||||
ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex);
|
||||
int m_exportNext VL_GUARDED_BY(m_exportMutex); ///< Next export funcnum
|
||||
|
||||
// File I/O
|
||||
VerilatedMutex m_fdMutex; ///< Protect m_fdps, m_fdFree
|
||||
std::vector<FILE*> m_fdps VL_GUARDED_BY(m_fdMutex); ///< File descriptors
|
||||
std::deque<IData> m_fdFree VL_GUARDED_BY(m_fdMutex); ///< List of free descriptors (SLOW - FOPEN/CLOSE only)
|
||||
VerilatedMutex m_fdMutex; ///< Protect m_fdps, m_fdFree
|
||||
std::vector<FILE*> m_fdps VL_GUARDED_BY(m_fdMutex); ///< File descriptors
|
||||
/// List of free descriptors (SLOW - FOPEN/CLOSE only)
|
||||
std::deque<IData> m_fdFree VL_GUARDED_BY(m_fdMutex);
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -210,8 +242,10 @@ public: // But only for verilated*.cpp
|
|||
m_fdps[2] = stderr;
|
||||
}
|
||||
~VerilatedImp() {}
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(VerilatedImp);
|
||||
|
||||
public:
|
||||
// METHODS - debug
|
||||
static void internalsDump() VL_MT_SAFE;
|
||||
|
|
@ -238,11 +272,12 @@ public:
|
|||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
static void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.m_argMutex);
|
||||
static void commandArgVl(const std::string& arg);
|
||||
static bool commandArgVlValue(const std::string& arg,
|
||||
const std::string& prefix, std::string& valuer);
|
||||
static bool commandArgVlValue(const std::string& arg, const std::string& prefix,
|
||||
std::string& valuer);
|
||||
|
||||
public:
|
||||
// METHODS - user scope tracking
|
||||
|
|
@ -252,16 +287,19 @@ public:
|
|||
static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_userMapMutex);
|
||||
UserMap::iterator it = s_s.m_userMap.find(std::make_pair(scopep, userKey));
|
||||
if (it != s_s.m_userMap.end()) it->second = userData;
|
||||
// When we support VL_THREADs, we need a lock around this insert, as it's runtime
|
||||
else s_s.m_userMap.insert(it, std::make_pair(std::make_pair(scopep, userKey), userData));
|
||||
if (it != s_s.m_userMap.end()) {
|
||||
it->second = userData;
|
||||
} else {
|
||||
s_s.m_userMap.insert(it, std::make_pair(std::make_pair(scopep, userKey), userData));
|
||||
}
|
||||
}
|
||||
static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_userMapMutex);
|
||||
UserMap::const_iterator it=s_s.m_userMap.find(std::make_pair(scopep, userKey));
|
||||
if (VL_LIKELY(it != s_s.m_userMap.end())) return it->second;
|
||||
else return NULL;
|
||||
UserMap::const_iterator it = s_s.m_userMap.find(std::make_pair(scopep, userKey));
|
||||
if (VL_UNLIKELY(it == s_s.m_userMap.end())) return NULL;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Symbol table destruction cleans up the entries for each scope.
|
||||
static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
|
|
@ -278,10 +316,13 @@ private:
|
|||
static void userDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_userMapMutex); // Avoid it changing in middle of dump
|
||||
bool first = true;
|
||||
for (UserMap::const_iterator it=s_s.m_userMap.begin(); it!=s_s.m_userMap.end(); ++it) {
|
||||
if (first) { VL_PRINTF_MT(" userDump:\n"); first=false; }
|
||||
VL_PRINTF_MT(" DPI_USER_DATA scope %p key %p: %p\n",
|
||||
it->first.first, it->first.second, it->second);
|
||||
for (UserMap::const_iterator it = s_s.m_userMap.begin(); it != s_s.m_userMap.end(); ++it) {
|
||||
if (first) {
|
||||
VL_PRINTF_MT(" userDump:\n");
|
||||
first = false;
|
||||
}
|
||||
VL_PRINTF_MT(" DPI_USER_DATA scope %p key %p: %p\n", it->first.first,
|
||||
it->first.second, it->second);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -298,9 +339,9 @@ public: // But only for verilated*.cpp
|
|||
static inline const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_nameMutex);
|
||||
// If too slow, can assume this is only VL_MT_SAFE_POSINIT
|
||||
VerilatedScopeNameMap::const_iterator it=s_s.m_nameMap.find(namep);
|
||||
if (VL_LIKELY(it != s_s.m_nameMap.end())) return it->second;
|
||||
else return NULL;
|
||||
VerilatedScopeNameMap::const_iterator it = s_s.m_nameMap.find(namep);
|
||||
if (VL_UNLIKELY(it == s_s.m_nameMap.end())) return NULL;
|
||||
return it->second;
|
||||
}
|
||||
static void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE {
|
||||
// Slow ok - called once/scope at destruction
|
||||
|
|
@ -360,8 +401,8 @@ public: // But only for verilated*.cpp
|
|||
VerilatedLockGuard lock(s_s.m_exportMutex);
|
||||
ExportNameMap::const_iterator it = s_s.m_exportMap.find(namep);
|
||||
if (VL_LIKELY(it != s_s.m_exportMap.end())) return it->second;
|
||||
std::string msg = (std::string("%Error: Testbench C called ")+namep
|
||||
+" but no such DPI export function name exists in ANY model");
|
||||
std::string msg = (std::string("%Error: Testbench C called ") + namep
|
||||
+ " but no such DPI export function name exists in ANY model");
|
||||
VL_FATAL_MT("unknown", 0, "", msg.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -377,15 +418,34 @@ public: // But only for verilated*.cpp
|
|||
static void exportsDump() VL_MT_SAFE {
|
||||
VerilatedLockGuard lock(s_s.m_exportMutex);
|
||||
bool first = true;
|
||||
for (ExportNameMap::const_iterator it=s_s.m_exportMap.begin();
|
||||
it!=s_s.m_exportMap.end(); ++it) {
|
||||
if (first) { VL_PRINTF_MT(" exportDump:\n"); first=false; }
|
||||
for (ExportNameMap::const_iterator it = s_s.m_exportMap.begin();
|
||||
it != s_s.m_exportMap.end(); ++it) {
|
||||
if (first) {
|
||||
VL_PRINTF_MT(" exportDump:\n");
|
||||
first = false;
|
||||
}
|
||||
VL_PRINTF_MT(" DPI_EXPORT_NAME %05d: %s\n", it->second, it->first);
|
||||
}
|
||||
}
|
||||
// We don't free up m_exportMap until the end, because we can't be sure
|
||||
// what other models are using the assigned funcnum's.
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - timeformat
|
||||
static std::string timeFormatSuffix() VL_MT_SAFE;
|
||||
static void timeFormatSuffix(const std::string& value) VL_MT_SAFE;
|
||||
static int timeFormatUnits() VL_MT_SAFE {
|
||||
if (s_s.m_ser.m_timeFormatUnits == Serialized::UNITS_NONE) {
|
||||
return Verilated::timeprecision();
|
||||
}
|
||||
return s_s.m_ser.m_timeFormatUnits;
|
||||
}
|
||||
static int timeFormatPrecision() VL_MT_SAFE { return s_s.m_ser.m_timeFormatPrecision; }
|
||||
static int timeFormatWidth() VL_MT_SAFE { return s_s.m_ser.m_timeFormatWidth; }
|
||||
static void timeFormatUnits(int value) VL_MT_SAFE;
|
||||
static void timeFormatPrecision(int value) VL_MT_SAFE;
|
||||
static void timeFormatWidth(int value) VL_MT_SAFE;
|
||||
|
||||
public: // But only for verilated*.cpp
|
||||
// METHODS - file IO
|
||||
static IData fdNew(FILE* fp) VL_MT_SAFE {
|
||||
|
|
@ -395,12 +455,15 @@ public: // But only for verilated*.cpp
|
|||
if (s_s.m_fdFree.empty()) {
|
||||
// Need to create more space in m_fdps and m_fdFree
|
||||
size_t start = s_s.m_fdps.size();
|
||||
s_s.m_fdps.resize(start*2);
|
||||
for (size_t i=start; i<start*2; ++i) s_s.m_fdFree.push_back(static_cast<IData>(i));
|
||||
s_s.m_fdps.resize(start * 2);
|
||||
for (size_t i = start; i < start * 2; ++i) {
|
||||
s_s.m_fdFree.push_back(static_cast<IData>(i));
|
||||
}
|
||||
}
|
||||
IData idx = s_s.m_fdFree.back(); s_s.m_fdFree.pop_back();
|
||||
IData idx = s_s.m_fdFree.back();
|
||||
s_s.m_fdFree.pop_back();
|
||||
s_s.m_fdps[idx] = fp;
|
||||
return (idx | (1UL<<31)); // bit 31 indicates not MCD
|
||||
return (idx | (1UL << 31)); // bit 31 indicates not MCD
|
||||
}
|
||||
static void fdDelete(IData fdi) VL_MT_SAFE {
|
||||
IData idx = VL_MASK_I(31) & fdi;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2020 by Wilson Snyder. This program is free software; you can
|
||||
// redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilator: Common include for target specific intrinsics.
|
||||
///
|
||||
/// Code using machine specific intrinsics for optimization should
|
||||
/// include this header rather than directly including he target
|
||||
/// specific headers. We provide macros to check for availability
|
||||
/// of instruction sets, and a common mechanism to disable them.
|
||||
///
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _VERILATED_INTRINSICS_H_
|
||||
#define _VERILATED_INTRINSICS_H_ 1 ///< Header Guard
|
||||
|
||||
// clang-format off
|
||||
|
||||
// Use VL_DISABLE_INTRINSICS to disable all intrinsics based optimization
|
||||
#if !defined(VL_DISABLE_INTRINSICS) && !defined(VL_PORTABLE_ONLY)
|
||||
# if defined(__SSE2__) && !defined(VL_DISABLE_SSE2)
|
||||
# define VL_HAVE_SSE2 1
|
||||
# include <emmintrin.h>
|
||||
# endif
|
||||
# if defined(__AVX2__) && defined(VL_HAVE_SSE2) && !defined(VL_DISABLE_AVX2)
|
||||
# define VL_HAVE_AVX2 1
|
||||
# include <immintrin.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
#include <cerrno>
|
||||
#include <fcntl.h>
|
||||
|
||||
// clang-format off
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
|
||||
# include <io.h>
|
||||
#else
|
||||
|
|
@ -38,9 +39,11 @@
|
|||
#ifndef O_CLOEXEC
|
||||
# define O_CLOEXEC 0
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
// CONSTANTS
|
||||
static const char* const VLTSAVE_HEADER_STR = "verilatorsave01\n"; ///< Value of first bytes of each file
|
||||
static const char* const VLTSAVE_HEADER_STR
|
||||
= "verilatorsave01\n"; ///< Value of first bytes of each file
|
||||
static const char* const VLTSAVE_TRAILER_STR = "vltsaved"; ///< Value of last bytes of each file
|
||||
|
||||
//=============================================================================
|
||||
|
|
@ -48,22 +51,21 @@ static const char* const VLTSAVE_TRAILER_STR = "vltsaved"; ///< Value of last b
|
|||
//=============================================================================
|
||||
// Searalization
|
||||
|
||||
bool VerilatedDeserialize::readDiffers(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
bool VerilatedDeserialize::readDiffers(const void* __restrict datap,
|
||||
size_t size) VL_MT_UNSAFE_ONE {
|
||||
bufferCheck();
|
||||
const vluint8_t* __restrict dp = static_cast<const vluint8_t* __restrict>(datap);
|
||||
vluint8_t miss = 0;
|
||||
while (size--) {
|
||||
miss |= (*dp++ ^ *m_cp++);
|
||||
}
|
||||
return (miss!=0);
|
||||
while (size--) miss |= (*dp++ ^ *m_cp++);
|
||||
return (miss != 0);
|
||||
}
|
||||
|
||||
VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap,
|
||||
size_t size) VL_MT_UNSAFE_ONE {
|
||||
if (VL_UNLIKELY(readDiffers(datap, size))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = "Can't deserialize save-restore file as was made from different model:"
|
||||
+filename();
|
||||
std::string msg
|
||||
= "Can't deserialize save-restore file as was made from different model:" + filename();
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
|
|
@ -77,19 +79,21 @@ void VerilatedSerialize::header() VL_MT_UNSAFE_ONE {
|
|||
|
||||
// Verilated doesn't do it itself, as if we're not using save/restore
|
||||
// it doesn't need to compile this stuff in
|
||||
os.write(Verilated::serializedPtr(), Verilated::serializedSize());
|
||||
os.write(Verilated::serialized1Ptr(), Verilated::serialized1Size());
|
||||
os.write(Verilated::serialized2Ptr(), Verilated::serialized2Size());
|
||||
}
|
||||
|
||||
void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
|
||||
VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below
|
||||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize; file has wrong header signature: ")
|
||||
+filename();
|
||||
std::string msg
|
||||
= std::string("Can't deserialize; file has wrong header signature: ") + filename();
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
os.read(Verilated::serializedPtr(), Verilated::serializedSize());
|
||||
os.read(Verilated::serialized1Ptr(), Verilated::serialized1Size());
|
||||
os.read(Verilated::serialized2Ptr(), Verilated::serialized2Size());
|
||||
}
|
||||
|
||||
void VerilatedSerialize::trailer() VL_MT_UNSAFE_ONE {
|
||||
|
|
@ -103,7 +107,7 @@ void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
|
|||
if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR)))) {
|
||||
std::string fn = filename();
|
||||
std::string msg = std::string("Can't deserialize; file has wrong end-of-file signature: ")
|
||||
+filename();
|
||||
+ filename();
|
||||
VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
|
||||
close();
|
||||
}
|
||||
|
|
@ -119,13 +123,13 @@ void VerilatedSave::open(const char* filenamep) VL_MT_UNSAFE_ONE {
|
|||
if (isOpen()) return;
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("- save: opening save file %s\n", filenamep););
|
||||
|
||||
if (filenamep[0]=='|') {
|
||||
if (filenamep[0] == '|') {
|
||||
assert(0); // Not supported yet.
|
||||
} else {
|
||||
// cppcheck-suppress duplicateExpression
|
||||
m_fd = ::open(filenamep, O_CREAT|O_WRONLY|O_TRUNC|O_LARGEFILE|O_NONBLOCK|O_CLOEXEC
|
||||
, 0666);
|
||||
if (m_fd<0) {
|
||||
m_fd = ::open(filenamep,
|
||||
O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE | O_NONBLOCK | O_CLOEXEC, 0666);
|
||||
if (m_fd < 0) {
|
||||
// User code can check isOpen()
|
||||
m_isOpen = false;
|
||||
return;
|
||||
|
|
@ -142,13 +146,12 @@ void VerilatedRestore::open(const char* filenamep) VL_MT_UNSAFE_ONE {
|
|||
if (isOpen()) return;
|
||||
VL_DEBUG_IF(VL_DBG_MSGF("- restore: opening restore file %s\n", filenamep););
|
||||
|
||||
if (filenamep[0]=='|') {
|
||||
if (filenamep[0] == '|') {
|
||||
assert(0); // Not supported yet.
|
||||
} else {
|
||||
// cppcheck-suppress duplicateExpression
|
||||
m_fd = ::open(filenamep, O_CREAT|O_RDONLY|O_LARGEFILE|O_CLOEXEC
|
||||
, 0666);
|
||||
if (m_fd<0) {
|
||||
m_fd = ::open(filenamep, O_CREAT | O_RDONLY | O_LARGEFILE | O_CLOEXEC, 0666);
|
||||
if (m_fd < 0) {
|
||||
// User code can check isOpen()
|
||||
m_isOpen = false;
|
||||
return;
|
||||
|
|
@ -186,15 +189,15 @@ void VerilatedSave::flush() VL_MT_UNSAFE_ONE {
|
|||
vluint8_t* wp = m_bufp;
|
||||
while (true) {
|
||||
ssize_t remaining = (m_cp - wp);
|
||||
if (remaining==0) break;
|
||||
if (remaining == 0) break;
|
||||
errno = 0;
|
||||
ssize_t got = ::write(m_fd, wp, remaining);
|
||||
if (got>0) {
|
||||
if (got > 0) {
|
||||
wp += got;
|
||||
} else if (got < 0) {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string(__FUNCTION__)+": "+strerror(errno);
|
||||
std::string msg = std::string(__FUNCTION__) + ": " + strerror(errno);
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
close();
|
||||
break;
|
||||
|
|
@ -209,21 +212,21 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
|
|||
if (VL_UNLIKELY(!isOpen())) return;
|
||||
// Move remaining characters down to start of buffer. (No memcpy, overlaps allowed)
|
||||
vluint8_t* rp = m_bufp;
|
||||
for (vluint8_t* sp=m_cp; sp < m_endp;) *rp++ = *sp++; // Overlaps
|
||||
for (vluint8_t* sp = m_cp; sp < m_endp; *rp++ = *sp++) {} // Overlaps
|
||||
m_endp = m_bufp + (m_endp - m_cp);
|
||||
m_cp = m_bufp; // Reset buffer
|
||||
// Read into buffer starting at m_endp
|
||||
while (true) {
|
||||
ssize_t remaining = (m_bufp+bufferSize() - m_endp);
|
||||
if (remaining==0) break;
|
||||
ssize_t remaining = (m_bufp + bufferSize() - m_endp);
|
||||
if (remaining == 0) break;
|
||||
errno = 0;
|
||||
ssize_t got = ::read(m_fd, m_endp, remaining);
|
||||
if (got>0) {
|
||||
if (got > 0) {
|
||||
m_endp += got;
|
||||
} else if (got < 0) {
|
||||
if (errno != EAGAIN && errno != EINTR) {
|
||||
// write failed, presume error (perhaps out of disk space)
|
||||
std::string msg = std::string(__FUNCTION__)+": "+strerror(errno);
|
||||
std::string msg = std::string(__FUNCTION__) + ": " + strerror(errno);
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
close();
|
||||
break;
|
||||
|
|
@ -231,7 +234,7 @@ void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
|
|||
} else { // got==0, EOF
|
||||
// Fill buffer from here to end with NULLs so reader's don't
|
||||
// need to check eof each character.
|
||||
while (m_endp < m_bufp+bufferSize()) *m_endp++ = '\0';
|
||||
while (m_endp < m_bufp + bufferSize()) *m_endp++ = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ protected:
|
|||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedSerialize);
|
||||
|
||||
public:
|
||||
VerilatedSerialize() {
|
||||
m_isOpen = false;
|
||||
|
|
@ -54,7 +55,7 @@ public:
|
|||
}
|
||||
virtual ~VerilatedSerialize() {
|
||||
close();
|
||||
if (m_bufp) { delete m_bufp; m_bufp=NULL; }
|
||||
if (m_bufp) VL_DO_CLEAR(delete m_bufp, m_bufp = NULL);
|
||||
}
|
||||
// METHODS
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
|
|
@ -65,20 +66,20 @@ public:
|
|||
const vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap;
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
size_t blk = size; if (blk>bufferInsertSize()) blk = bufferInsertSize();
|
||||
size_t blk = size;
|
||||
if (blk > bufferInsertSize()) blk = bufferInsertSize();
|
||||
const vluint8_t* __restrict maxp = dp + blk;
|
||||
while (dp < maxp) *m_cp++ = *dp++;
|
||||
for (; dp < maxp; *m_cp++ = *dp++) {}
|
||||
size -= blk;
|
||||
}
|
||||
return *this; // For function chaining
|
||||
}
|
||||
|
||||
private:
|
||||
VerilatedSerialize& bufferCheck() VL_MT_UNSAFE_ONE {
|
||||
// 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_cp > (m_bufp+(bufferSize()-bufferInsertSize())))) {
|
||||
flush();
|
||||
}
|
||||
if (VL_UNLIKELY(m_cp > (m_bufp + (bufferSize() - bufferInsertSize())))) flush();
|
||||
return *this; // For function chaining
|
||||
}
|
||||
};
|
||||
|
|
@ -107,6 +108,7 @@ protected:
|
|||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedDeserialize);
|
||||
|
||||
public:
|
||||
VerilatedDeserialize() {
|
||||
m_isOpen = false;
|
||||
|
|
@ -116,7 +118,7 @@ public:
|
|||
}
|
||||
virtual ~VerilatedDeserialize() {
|
||||
close();
|
||||
if (m_bufp) { delete m_bufp; m_bufp=NULL; }
|
||||
if (m_bufp) VL_DO_CLEAR(delete m_bufp, m_bufp = NULL);
|
||||
}
|
||||
// METHODS
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
|
|
@ -124,12 +126,13 @@ public:
|
|||
virtual void close() VL_MT_UNSAFE_ONE { flush(); }
|
||||
virtual void flush() VL_MT_UNSAFE_ONE {}
|
||||
inline VerilatedDeserialize& read(void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
||||
vluint8_t* __restrict dp = (vluint8_t* __restrict)datap;
|
||||
vluint8_t* __restrict dp = static_cast<vluint8_t* __restrict>(datap);
|
||||
while (size) {
|
||||
bufferCheck();
|
||||
size_t blk = size; if (blk>bufferInsertSize()) blk = bufferInsertSize();
|
||||
size_t blk = size;
|
||||
if (blk > bufferInsertSize()) blk = bufferInsertSize();
|
||||
const vluint8_t* __restrict maxp = dp + blk;
|
||||
while (dp < maxp) *dp++ = *m_cp++;
|
||||
for (; dp < maxp; *dp++ = *m_cp++) {}
|
||||
size -= blk;
|
||||
}
|
||||
return *this; // For function chaining
|
||||
|
|
@ -137,15 +140,15 @@ public:
|
|||
// Read a datum and compare with expected value
|
||||
VerilatedDeserialize& readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
|
||||
VerilatedDeserialize& readAssert(vluint64_t data) VL_MT_UNSAFE_ONE {
|
||||
return readAssert(&data, sizeof(data)); }
|
||||
return readAssert(&data, sizeof(data));
|
||||
}
|
||||
|
||||
private:
|
||||
bool readDiffers(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
|
||||
VerilatedDeserialize& bufferCheck() VL_MT_UNSAFE_ONE {
|
||||
// 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_cp+bufferInsertSize()) > m_endp)) {
|
||||
fill();
|
||||
}
|
||||
if (VL_UNLIKELY((m_cp + bufferInsertSize()) > m_endp)) fill();
|
||||
return *this; // For function chaining
|
||||
}
|
||||
};
|
||||
|
|
@ -160,10 +163,12 @@ private:
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedSave() { m_fd = -1; }
|
||||
VerilatedSave()
|
||||
: m_fd(-1) {}
|
||||
virtual ~VerilatedSave() VL_OVERRIDE { close(); }
|
||||
// METHODS
|
||||
void open(const char* filenamep) VL_MT_UNSAFE_ONE; ///< Open the file; call isOpen() to see if errors
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filenamep) VL_MT_UNSAFE_ONE;
|
||||
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
|
||||
virtual void close() VL_OVERRIDE VL_MT_UNSAFE_ONE;
|
||||
virtual void flush() VL_OVERRIDE VL_MT_UNSAFE_ONE;
|
||||
|
|
@ -179,11 +184,13 @@ private:
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VerilatedRestore() { m_fd = -1; }
|
||||
VerilatedRestore()
|
||||
: m_fd(-1) {}
|
||||
virtual ~VerilatedRestore() VL_OVERRIDE { close(); }
|
||||
|
||||
// METHODS
|
||||
void open(const char* filenamep) VL_MT_UNSAFE_ONE; ///< Open the file; call isOpen() to see if errors
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filenamep) VL_MT_UNSAFE_ONE;
|
||||
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
|
||||
virtual void close() VL_OVERRIDE VL_MT_UNSAFE_ONE;
|
||||
virtual void flush() VL_OVERRIDE VL_MT_UNSAFE_ONE {}
|
||||
|
|
@ -250,8 +257,8 @@ VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Val
|
|||
os << rhs.atDefault();
|
||||
vluint32_t len = rhs.size();
|
||||
os << len;
|
||||
for (typename VlAssocArray<T_Key, T_Value>::const_iterator it = rhs.begin();
|
||||
it != rhs.end(); ++it) {
|
||||
for (typename VlAssocArray<T_Key, T_Value>::const_iterator it = rhs.begin(); it != rhs.end();
|
||||
++it) {
|
||||
T_Key index = it->first; // Copy to get around const_iterator
|
||||
T_Value value = it->second;
|
||||
os << index << value;
|
||||
|
|
|
|||
|
|
@ -35,14 +35,23 @@
|
|||
|
||||
// See also V3Ast::VNumRange
|
||||
class VerilatedRange {
|
||||
int m_left;
|
||||
int m_right;
|
||||
int m_left;
|
||||
int m_right;
|
||||
|
||||
protected:
|
||||
friend class VerilatedVarProps;
|
||||
friend class VerilatedScope;
|
||||
VerilatedRange() : m_left(0), m_right(0) {}
|
||||
VerilatedRange(int left, int right) : m_left(left), m_right(right) {}
|
||||
void init(int left, int right) { m_left=left; m_right=right; }
|
||||
VerilatedRange()
|
||||
: m_left(0)
|
||||
, m_right(0) {}
|
||||
VerilatedRange(int left, int right)
|
||||
: m_left(left)
|
||||
, m_right(right) {}
|
||||
void init(int left, int right) {
|
||||
m_left = left;
|
||||
m_right = right;
|
||||
}
|
||||
|
||||
public:
|
||||
~VerilatedRange() {}
|
||||
int left() const { return m_left; }
|
||||
|
|
@ -50,7 +59,8 @@ public:
|
|||
int low() const { return (m_left < m_right) ? m_left : m_right; }
|
||||
int high() const { return (m_left > m_right) ? m_left : m_right; }
|
||||
int elements() const {
|
||||
return (VL_LIKELY(m_left>=m_right) ? (m_left-m_right+1) : (m_right-m_left+1)); }
|
||||
return (VL_LIKELY(m_left >= m_right) ? (m_left - m_right + 1) : (m_right - m_left + 1));
|
||||
}
|
||||
int increment() const { return (m_left >= m_right) ? 1 : -1; }
|
||||
};
|
||||
|
||||
|
|
@ -62,71 +72,112 @@ class VerilatedVarProps {
|
|||
// TYPES
|
||||
enum { MAGIC = 0xddc4f829 };
|
||||
// MEMBERS
|
||||
const vluint32_t m_magic; // Magic number
|
||||
const VerilatedVarType m_vltype; // Data type
|
||||
const VerilatedVarFlags m_vlflags; // Direction
|
||||
const int m_pdims; // Packed dimensions
|
||||
const int m_udims; // Unpacked dimensions
|
||||
VerilatedRange m_packed; // Packed array range
|
||||
VerilatedRange m_unpacked[3]; // Unpacked array range
|
||||
const vluint32_t m_magic; // Magic number
|
||||
const VerilatedVarType m_vltype; // Data type
|
||||
const VerilatedVarFlags m_vlflags; // Direction
|
||||
const int m_pdims; // Packed dimensions
|
||||
const int m_udims; // Unpacked dimensions
|
||||
VerilatedRange m_packed; // Packed array range
|
||||
VerilatedRange m_unpacked[3]; // Unpacked array range
|
||||
// CONSTRUCTORS
|
||||
protected:
|
||||
friend class VerilatedScope;
|
||||
VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags,
|
||||
int pdims, int udims)
|
||||
: m_magic(MAGIC), m_vltype(vltype), m_vlflags(vlflags), m_pdims(pdims), m_udims(udims) {}
|
||||
VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int pdims, int udims)
|
||||
: m_magic(MAGIC)
|
||||
, m_vltype(vltype)
|
||||
, m_vlflags(vlflags)
|
||||
, m_pdims(pdims)
|
||||
, m_udims(udims) {}
|
||||
|
||||
public:
|
||||
class Unpacked {};
|
||||
// Without packed
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags)
|
||||
: m_magic(MAGIC), m_vltype(vltype),
|
||||
m_vlflags(VerilatedVarFlags(vlflags)), m_pdims(0), m_udims(0) { }
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags,
|
||||
Unpacked, int u0l, int u0r)
|
||||
: m_magic(MAGIC), m_vltype(vltype),
|
||||
m_vlflags(VerilatedVarFlags(vlflags)), m_pdims(0), m_udims(1) {
|
||||
m_unpacked[0].init(u0l, u0r); }
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags,
|
||||
Unpacked, int u0l, int u0r, int u1l, int u1r)
|
||||
: m_magic(MAGIC), m_vltype(vltype),
|
||||
m_vlflags(VerilatedVarFlags(vlflags)), m_pdims(0), m_udims(2) {
|
||||
m_unpacked[0].init(u0l, u0r); m_unpacked[1].init(u1l, u1r); }
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags,
|
||||
Unpacked, int u0l, int u0r, int u1l, int u1r, int u2l, int u2r)
|
||||
: m_magic(MAGIC), m_vltype(vltype),
|
||||
m_vlflags(VerilatedVarFlags(vlflags)), m_pdims(0), m_udims(3) {
|
||||
m_unpacked[0].init(u0l, u0r); m_unpacked[1].init(u1l, u1r); m_unpacked[2].init(u2l, u2r); }
|
||||
: m_magic(MAGIC)
|
||||
, m_vltype(vltype)
|
||||
, m_vlflags(VerilatedVarFlags(vlflags))
|
||||
, m_pdims(0)
|
||||
, m_udims(0) {}
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int u0l, int u0r)
|
||||
: m_magic(MAGIC)
|
||||
, m_vltype(vltype)
|
||||
, m_vlflags(VerilatedVarFlags(vlflags))
|
||||
, m_pdims(0)
|
||||
, m_udims(1) {
|
||||
m_unpacked[0].init(u0l, u0r);
|
||||
}
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int u0l, int u0r, int u1l,
|
||||
int u1r)
|
||||
: m_magic(MAGIC)
|
||||
, m_vltype(vltype)
|
||||
, m_vlflags(VerilatedVarFlags(vlflags))
|
||||
, m_pdims(0)
|
||||
, m_udims(2) {
|
||||
m_unpacked[0].init(u0l, u0r);
|
||||
m_unpacked[1].init(u1l, u1r);
|
||||
}
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int u0l, int u0r, int u1l,
|
||||
int u1r, int u2l, int u2r)
|
||||
: m_magic(MAGIC)
|
||||
, m_vltype(vltype)
|
||||
, m_vlflags(VerilatedVarFlags(vlflags))
|
||||
, m_pdims(0)
|
||||
, m_udims(3) {
|
||||
m_unpacked[0].init(u0l, u0r);
|
||||
m_unpacked[1].init(u1l, u1r);
|
||||
m_unpacked[2].init(u2l, u2r);
|
||||
}
|
||||
// With packed
|
||||
class Packed {};
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags,
|
||||
Packed, int pl, int pr)
|
||||
: m_magic(MAGIC), m_vltype(vltype),
|
||||
m_vlflags(VerilatedVarFlags(vlflags)), m_pdims(1), m_udims(0), m_packed(pl,pr) { }
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags,
|
||||
Packed, int pl, int pr,
|
||||
Unpacked, int u0l, int u0r)
|
||||
: m_magic(MAGIC), m_vltype(vltype),
|
||||
m_vlflags(VerilatedVarFlags(vlflags)), m_pdims(1), m_udims(1), m_packed(pl,pr) {
|
||||
m_unpacked[0].init(u0l, u0r); }
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags,
|
||||
Packed, int pl, int pr,
|
||||
Unpacked, int u0l, int u0r, int u1l, int u1r)
|
||||
: m_magic(MAGIC), m_vltype(vltype), m_vlflags(VerilatedVarFlags(vlflags)),
|
||||
m_pdims(1), m_udims(2), m_packed(pl,pr) {
|
||||
m_unpacked[0].init(u0l, u0r); m_unpacked[1].init(u1l, u1r); }
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags,
|
||||
Packed, int pl, int pr,
|
||||
Unpacked, int u0l, int u0r, int u1l, int u1r, int u2l, int u2r)
|
||||
: m_magic(MAGIC), m_vltype(vltype),
|
||||
m_vlflags(VerilatedVarFlags(vlflags)), m_pdims(1), m_udims(3), m_packed(pl,pr) {
|
||||
m_unpacked[0].init(u0l, u0r); m_unpacked[1].init(u1l, u1r); m_unpacked[2].init(u2l, u2r); }
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pl, int pr)
|
||||
: m_magic(MAGIC)
|
||||
, m_vltype(vltype)
|
||||
, m_vlflags(VerilatedVarFlags(vlflags))
|
||||
, m_pdims(1)
|
||||
, m_udims(0)
|
||||
, m_packed(pl, pr) {}
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pl, int pr, Unpacked,
|
||||
int u0l, int u0r)
|
||||
: m_magic(MAGIC)
|
||||
, m_vltype(vltype)
|
||||
, m_vlflags(VerilatedVarFlags(vlflags))
|
||||
, m_pdims(1)
|
||||
, m_udims(1)
|
||||
, m_packed(pl, pr) {
|
||||
m_unpacked[0].init(u0l, u0r);
|
||||
}
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pl, int pr, Unpacked,
|
||||
int u0l, int u0r, int u1l, int u1r)
|
||||
: m_magic(MAGIC)
|
||||
, m_vltype(vltype)
|
||||
, m_vlflags(VerilatedVarFlags(vlflags))
|
||||
, m_pdims(1)
|
||||
, m_udims(2)
|
||||
, m_packed(pl, pr) {
|
||||
m_unpacked[0].init(u0l, u0r);
|
||||
m_unpacked[1].init(u1l, u1r);
|
||||
}
|
||||
VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pl, int pr, Unpacked,
|
||||
int u0l, int u0r, int u1l, int u1r, int u2l, int u2r)
|
||||
: m_magic(MAGIC)
|
||||
, m_vltype(vltype)
|
||||
, m_vlflags(VerilatedVarFlags(vlflags))
|
||||
, m_pdims(1)
|
||||
, m_udims(3)
|
||||
, m_packed(pl, pr) {
|
||||
m_unpacked[0].init(u0l, u0r);
|
||||
m_unpacked[1].init(u1l, u1r);
|
||||
m_unpacked[2].init(u2l, u2r);
|
||||
}
|
||||
|
||||
public:
|
||||
~VerilatedVarProps() {}
|
||||
// METHODS
|
||||
bool magicOk() const { return m_magic == MAGIC; }
|
||||
VerilatedVarType vltype() const { return m_vltype; }
|
||||
VerilatedVarFlags vldir() const {
|
||||
return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR); }
|
||||
return static_cast<VerilatedVarFlags>(static_cast<int>(m_vlflags) & VLVF_MASK_DIR);
|
||||
}
|
||||
vluint32_t entSize() const;
|
||||
bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); }
|
||||
/// DPI compatible C standard layout
|
||||
|
|
@ -137,28 +188,28 @@ public:
|
|||
const VerilatedRange& unpacked() const { return m_unpacked[0]; }
|
||||
// DPI accessors
|
||||
int left(int dim) const {
|
||||
return dim==0 ? m_packed.left()
|
||||
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].left() : 0;
|
||||
return dim == 0 ? m_packed.left()
|
||||
: VL_LIKELY(dim >= 1 && dim <= 3) ? m_unpacked[dim - 1].left() : 0;
|
||||
}
|
||||
int right(int dim) const {
|
||||
return dim==0 ? m_packed.right()
|
||||
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].right() : 0;
|
||||
return dim == 0 ? m_packed.right()
|
||||
: VL_LIKELY(dim >= 1 && dim <= 3) ? m_unpacked[dim - 1].right() : 0;
|
||||
}
|
||||
int low(int dim) const {
|
||||
return dim==0 ? m_packed.low()
|
||||
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].low() : 0;
|
||||
return dim == 0 ? m_packed.low()
|
||||
: VL_LIKELY(dim >= 1 && dim <= 3) ? m_unpacked[dim - 1].low() : 0;
|
||||
}
|
||||
int high(int dim) const {
|
||||
return dim==0 ? m_packed.high()
|
||||
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].high() : 0;
|
||||
return dim == 0 ? m_packed.high()
|
||||
: VL_LIKELY(dim >= 1 && dim <= 3) ? m_unpacked[dim - 1].high() : 0;
|
||||
}
|
||||
int increment(int dim) const {
|
||||
return dim==0 ? m_packed.increment()
|
||||
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].increment() : 0;
|
||||
return dim == 0 ? m_packed.increment()
|
||||
: VL_LIKELY(dim >= 1 && dim <= 3) ? m_unpacked[dim - 1].increment() : 0;
|
||||
}
|
||||
int elements(int dim) const {
|
||||
return dim==0 ? m_packed.elements()
|
||||
: VL_LIKELY(dim>=1 && dim<=3) ? m_unpacked[dim-1].elements() : 0;
|
||||
return dim == 0 ? m_packed.elements()
|
||||
: VL_LIKELY(dim >= 1 && dim <= 3) ? m_unpacked[dim - 1].elements() : 0;
|
||||
}
|
||||
/// Total size in bytes (note DPI limited to 4GB)
|
||||
size_t totalSize() const;
|
||||
|
|
@ -199,7 +250,8 @@ public:
|
|||
int elements(int dim) const { return m_propsp->elements(dim); }
|
||||
size_t totalSize() const { return m_propsp->totalSize(); }
|
||||
void* datapAdjustIndex(void* datap, int dim, int indx) const {
|
||||
return m_propsp->datapAdjustIndex(datap, dim, indx); }
|
||||
return m_propsp->datapAdjustIndex(datap, dim, indx);
|
||||
}
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
|
|
@ -213,10 +265,12 @@ class VerilatedVar : public VerilatedVarProps {
|
|||
protected:
|
||||
friend class VerilatedScope;
|
||||
// CONSTRUCTORS
|
||||
VerilatedVar(const char* namep, void* datap,
|
||||
VerilatedVarType vltype, VerilatedVarFlags vlflags, int dims)
|
||||
: VerilatedVarProps(vltype, vlflags, (dims>0?1:0), ((dims>1)?dims-1:0))
|
||||
, m_datap(datap), m_namep(namep) {}
|
||||
VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
|
||||
VerilatedVarFlags vlflags, int dims)
|
||||
: VerilatedVarProps(vltype, vlflags, (dims > 0 ? 1 : 0), ((dims > 1) ? dims - 1 : 0))
|
||||
, m_datap(datap)
|
||||
, m_namep(namep) {}
|
||||
|
||||
public:
|
||||
~VerilatedVar() {}
|
||||
// ACCESSORS
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ VL_THREAD_LOCAL VlThreadPool::ProfileTrace* VlThreadPool::t_profilep = NULL;
|
|||
// VlMTaskVertex
|
||||
|
||||
VlMTaskVertex::VlMTaskVertex(vluint32_t upstreamDepCount)
|
||||
: m_upstreamDepsDone(0),
|
||||
m_upstreamDepCount(upstreamDepCount) {
|
||||
: m_upstreamDepsDone(0)
|
||||
, m_upstreamDepCount(upstreamDepCount) {
|
||||
assert(atomic_is_lock_free(&m_upstreamDepsDone));
|
||||
}
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ VlWorkerThread::VlWorkerThread(VlThreadPool* poolp, bool profiling)
|
|||
, m_poolp(poolp)
|
||||
, m_profiling(profiling)
|
||||
, m_exiting(false)
|
||||
// Must init this last -- after setting up fields that it might read:
|
||||
// Must init this last -- after setting up fields that it might read:
|
||||
, m_cthread(startWorker, this) {}
|
||||
|
||||
VlWorkerThread::~VlWorkerThread() {
|
||||
|
|
@ -52,21 +52,16 @@ VlWorkerThread::~VlWorkerThread() {
|
|||
}
|
||||
|
||||
void VlWorkerThread::workerLoop() {
|
||||
if (VL_UNLIKELY(m_profiling)) {
|
||||
m_poolp->setupProfilingClientThread();
|
||||
}
|
||||
if (VL_UNLIKELY(m_profiling)) m_poolp->setupProfilingClientThread();
|
||||
|
||||
ExecRec work;
|
||||
work.m_fnp = NULL;
|
||||
|
||||
while (true) {
|
||||
if (VL_LIKELY(!work.m_fnp)) {
|
||||
dequeWork(&work);
|
||||
}
|
||||
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_UNLIKELY(m_exiting.load(std::memory_order_acquire))) break;
|
||||
|
||||
if (VL_LIKELY(work.m_fnp)) {
|
||||
work.m_fnp(work.m_evenCycle, work.m_sym);
|
||||
|
|
@ -74,14 +69,10 @@ void VlWorkerThread::workerLoop() {
|
|||
}
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(m_profiling)) {
|
||||
m_poolp->tearDownProfilingClientThread();
|
||||
}
|
||||
if (VL_UNLIKELY(m_profiling)) m_poolp->tearDownProfilingClientThread();
|
||||
}
|
||||
|
||||
void VlWorkerThread::startWorker(VlWorkerThread* workerp) {
|
||||
workerp->workerLoop();
|
||||
}
|
||||
void VlWorkerThread::startWorker(VlWorkerThread* workerp) { workerp->workerLoop(); }
|
||||
|
||||
//=============================================================================
|
||||
// VlThreadPool
|
||||
|
|
@ -90,23 +81,22 @@ VlThreadPool::VlThreadPool(int nThreads, bool profiling)
|
|||
: m_profiling(profiling) {
|
||||
// --threads N passes nThreads=N-1, as the "main" threads counts as 1
|
||||
unsigned cpus = std::thread::hardware_concurrency();
|
||||
if (cpus < nThreads+1) {
|
||||
if (cpus < nThreads + 1) {
|
||||
static int warnedOnce = 0;
|
||||
if (!warnedOnce++) {
|
||||
VL_PRINTF_MT("%%Warning: System has %u CPUs but model Verilated with"
|
||||
" --threads %d; may run slow.\n", cpus, nThreads+1);
|
||||
" --threads %d; may run slow.\n",
|
||||
cpus, nThreads + 1);
|
||||
}
|
||||
}
|
||||
// Create'em
|
||||
for (int i=0; i<nThreads; ++i) {
|
||||
for (int i = 0; i < nThreads; ++i) {
|
||||
m_workers.push_back(new VlWorkerThread(this, profiling));
|
||||
}
|
||||
// Set up a profile buffer for the current thread too -- on the
|
||||
// assumption that it's the same thread that calls eval and may be
|
||||
// donated to run mtasks during the eval.
|
||||
if (VL_UNLIKELY(m_profiling)) {
|
||||
setupProfilingClientThread();
|
||||
}
|
||||
if (VL_UNLIKELY(m_profiling)) setupProfilingClientThread();
|
||||
}
|
||||
|
||||
VlThreadPool::~VlThreadPool() {
|
||||
|
|
@ -114,9 +104,7 @@ VlThreadPool::~VlThreadPool() {
|
|||
// Each ~WorkerThread will wait for its thread to exit.
|
||||
delete m_workers[i];
|
||||
}
|
||||
if (VL_UNLIKELY(m_profiling)) {
|
||||
tearDownProfilingClientThread();
|
||||
}
|
||||
if (VL_UNLIKELY(m_profiling)) tearDownProfilingClientThread();
|
||||
}
|
||||
|
||||
void VlThreadPool::tearDownProfilingClientThread() {
|
||||
|
|
@ -139,8 +127,7 @@ void VlThreadPool::setupProfilingClientThread() {
|
|||
|
||||
void VlThreadPool::profileAppendAll(const VlProfileRec& rec) {
|
||||
VerilatedLockGuard lk(m_mutex);
|
||||
for (ProfileSet::iterator it = m_allProfiles.begin();
|
||||
it != m_allProfiles.end(); ++it) {
|
||||
for (ProfileSet::iterator it = m_allProfiles.begin(); it != m_allProfiles.end(); ++it) {
|
||||
// Every thread's profile trace gets a copy of rec.
|
||||
(*it)->emplace_back(rec);
|
||||
}
|
||||
|
|
@ -153,52 +140,45 @@ void VlThreadPool::profileDump(const char* filenamep, vluint64_t ticksElapsed) {
|
|||
FILE* fp = fopen(filenamep, "w");
|
||||
if (VL_UNLIKELY(!fp)) {
|
||||
VL_FATAL_MT(filenamep, 0, "", "+prof+threads+file file not writable");
|
||||
// cppcheck-suppress resourceLeak // bug, doesn't realize fp is nullptr
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO Perhaps merge with verilated_coverage output format, so can
|
||||
// have a common merging and reporting tool, etc.
|
||||
fprintf(fp, "VLPROFTHREAD 1.0 # Verilator thread profile dump version 1.0\n");
|
||||
fprintf(fp, "VLPROF arg --threads %" VL_PRI64 "u\n",
|
||||
vluint64_t(m_workers.size()+1));
|
||||
fprintf(fp, "VLPROF arg --threads %" VL_PRI64 "u\n", vluint64_t(m_workers.size() + 1));
|
||||
fprintf(fp, "VLPROF arg +verilator+prof+threads+start+%" VL_PRI64 "u\n",
|
||||
Verilated::profThreadsStart());
|
||||
fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n",
|
||||
Verilated::profThreadsWindow());
|
||||
fprintf(fp, "VLPROF stat yields %" VL_PRI64 "u\n",
|
||||
VlMTaskVertex::yields());
|
||||
fprintf(fp, "VLPROF arg +verilator+prof+threads+window+%u\n", Verilated::profThreadsWindow());
|
||||
fprintf(fp, "VLPROF stat yields %" VL_PRI64 "u\n", VlMTaskVertex::yields());
|
||||
|
||||
vluint32_t thread_id = 0;
|
||||
for (ProfileSet::const_iterator pit = m_allProfiles.begin();
|
||||
pit != m_allProfiles.end(); ++pit) {
|
||||
for (ProfileSet::const_iterator pit = m_allProfiles.begin(); pit != m_allProfiles.end();
|
||||
++pit) {
|
||||
++thread_id;
|
||||
|
||||
bool printing = false; // False while in warmup phase
|
||||
for (ProfileTrace::const_iterator eit = (*pit)->begin();
|
||||
eit != (*pit)->end(); ++eit) {
|
||||
for (ProfileTrace::const_iterator eit = (*pit)->begin(); eit != (*pit)->end(); ++eit) {
|
||||
switch (eit->m_type) {
|
||||
case VlProfileRec::TYPE_BARRIER:
|
||||
case VlProfileRec::TYPE_BARRIER: //
|
||||
printing = true;
|
||||
break;
|
||||
case VlProfileRec::TYPE_MTASK_RUN:
|
||||
if (!printing) break;
|
||||
fprintf(fp, "VLPROF mtask %d"
|
||||
" start %" VL_PRI64"u end %" VL_PRI64"u elapsed %" VL_PRI64 "u"
|
||||
fprintf(fp,
|
||||
"VLPROF mtask %d"
|
||||
" start %" VL_PRI64 "u end %" VL_PRI64 "u elapsed %" VL_PRI64 "u"
|
||||
" predict_time %u cpu %u on thread %u\n",
|
||||
eit->m_mtaskId,
|
||||
eit->m_startTime,
|
||||
eit->m_endTime,
|
||||
(eit->m_endTime - eit->m_startTime),
|
||||
eit->m_predictTime,
|
||||
eit->m_cpu,
|
||||
eit->m_mtaskId, eit->m_startTime, eit->m_endTime,
|
||||
(eit->m_endTime - eit->m_startTime), eit->m_predictTime, eit->m_cpu,
|
||||
thread_id);
|
||||
break;
|
||||
default: assert(false); break; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(fp, "VLPROF stat ticks %" VL_PRI64 "u\n",
|
||||
ticksElapsed);
|
||||
fprintf(fp, "VLPROF stat ticks %" VL_PRI64 "u\n", ticksElapsed);
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,12 +25,15 @@
|
|||
#include <condition_variable>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
// clang-format off
|
||||
#if defined(__linux)
|
||||
#include <sched.h> // For sched_getcpu()
|
||||
# include <sched.h> // For sched_getcpu()
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
# include <cpuid.h> // For __cpuid_count()
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
// VlMTaskVertex and VlThreadpool will work with multiple symbol table types.
|
||||
// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
|
||||
|
|
@ -115,10 +118,7 @@ public:
|
|||
class VlProfileRec {
|
||||
protected:
|
||||
friend class VlThreadPool;
|
||||
enum VlProfileE {
|
||||
TYPE_MTASK_RUN,
|
||||
TYPE_BARRIER
|
||||
};
|
||||
enum VlProfileE { TYPE_MTASK_RUN, TYPE_BARRIER };
|
||||
VlProfileE m_type; // Record type
|
||||
vluint32_t m_mtaskId; // Mtask we're logging
|
||||
vluint32_t m_predictTime; // How long scheduler predicted would take
|
||||
|
|
@ -173,9 +173,14 @@ private:
|
|||
VlExecFnp m_fnp; // Function to execute
|
||||
VlThrSymTab m_sym; // Symbol table to execute
|
||||
bool m_evenCycle; // Even/odd for flag alternation
|
||||
ExecRec() : m_fnp(NULL), m_sym(NULL), m_evenCycle(false) {}
|
||||
ExecRec()
|
||||
: m_fnp(NULL)
|
||||
, m_sym(NULL)
|
||||
, m_evenCycle(false) {}
|
||||
ExecRec(VlExecFnp fnp, bool evenCycle, VlThrSymTab sym)
|
||||
: m_fnp(fnp), m_sym(sym), m_evenCycle(evenCycle) {}
|
||||
: m_fnp(fnp)
|
||||
, m_sym(sym)
|
||||
, m_evenCycle(evenCycle) {}
|
||||
};
|
||||
|
||||
// MEMBERS
|
||||
|
|
@ -208,7 +213,7 @@ public:
|
|||
inline void dequeWork(ExecRec* workp) {
|
||||
// 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))) {
|
||||
if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) { //
|
||||
break;
|
||||
}
|
||||
VL_CPU_RELAX();
|
||||
|
|
@ -284,6 +289,7 @@ public:
|
|||
// this once to setup profiling state:
|
||||
void setupProfilingClientThread();
|
||||
void tearDownProfilingClientThread();
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(VlThreadPool);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,388 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2001-2020 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Tracing functionality common to all formats
|
||||
///
|
||||
//=============================================================================
|
||||
// SPDIFF_OFF
|
||||
|
||||
#ifndef _VERILATED_TRACE_H_
|
||||
#define _VERILATED_TRACE_H_ 1
|
||||
|
||||
// clang-format off
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
# include <condition_variable>
|
||||
# include <deque>
|
||||
# include <thread>
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
//=============================================================================
|
||||
// Threaded tracing
|
||||
|
||||
// A simple synchronized first in first out queue
|
||||
template <class T> class VerilatedThreadQueue {
|
||||
private:
|
||||
VerilatedMutex m_mutex; // Protects m_queue
|
||||
std::condition_variable_any m_cv;
|
||||
std::deque<T> m_queue VL_GUARDED_BY(m_mutex);
|
||||
|
||||
public:
|
||||
// Put an element at the back of the queue
|
||||
void put(T value) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
m_queue.push_back(value);
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
// Put an element at the front of the queue
|
||||
void put_front(T value) {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
m_queue.push_front(value);
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
// Get an element from the front of the queue. Blocks if none available
|
||||
T get() {
|
||||
VerilatedLockGuard lock(m_mutex);
|
||||
m_cv.wait(lock, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
|
||||
assert(!m_queue.empty());
|
||||
T value = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
return value;
|
||||
}
|
||||
|
||||
// Non blocking get
|
||||
bool tryGet(T& result) {
|
||||
VerilatedLockGuard lockGuard(m_mutex);
|
||||
if (m_queue.empty()) { return false; }
|
||||
result = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Commands used by thread tracing. Anonymous enum in class, as we want
|
||||
// it scoped, but we also want the automatic conversion to integer types.
|
||||
class VerilatedTraceCommand {
|
||||
public:
|
||||
// These must all fit in 4 bit at the moment, as the tracing routines
|
||||
// pack parameters in the top bits.
|
||||
enum {
|
||||
CHG_BIT_0 = 0x0,
|
||||
CHG_BIT_1 = 0x1,
|
||||
CHG_CDATA = 0x2,
|
||||
CHG_SDATA = 0x3,
|
||||
CHG_IDATA = 0x4,
|
||||
CHG_QDATA = 0x5,
|
||||
CHG_WDATA = 0x6,
|
||||
CHG_FLOAT = 0x7,
|
||||
CHG_DOUBLE = 0x8,
|
||||
// TODO: full..
|
||||
TIME_CHANGE = 0xd,
|
||||
END = 0xe, // End of buffer
|
||||
SHUTDOWN = 0xf // Shutdown worker thread, also marks end of buffer
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
||||
class VerilatedTraceCallInfo;
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
// VerilatedTrace uses F-bounded polymorphism to access duck-typed
|
||||
// implementations in the format specific derived class, which must be passed
|
||||
// as the type parameter T_Derived
|
||||
template <class T_Derived> class VerilatedTrace {
|
||||
private:
|
||||
//=========================================================================
|
||||
// Generic tracing internals
|
||||
|
||||
vluint32_t* m_sigs_oldvalp; ///< Old value store
|
||||
vluint64_t m_timeLastDump; ///< Last time we did a dump
|
||||
std::vector<VerilatedTraceCallInfo*> m_callbacks; ///< Routines to perform dumping
|
||||
bool m_fullDump; ///< Whether a full dump is required on the next call to 'dump'
|
||||
vluint32_t m_nextCode; ///< Next code number to assign
|
||||
vluint32_t m_numSignals; ///< Number of distinct signals
|
||||
vluint32_t m_maxBits; ///< Number of bits in the widest signal
|
||||
std::string m_moduleName; ///< Name of module being trace initialized now
|
||||
char m_scopeEscape;
|
||||
double m_timeRes; ///< Time resolution (ns/ms etc)
|
||||
double m_timeUnit; ///< Time units (ns/ms etc)
|
||||
|
||||
// Equivalent to 'this' but is of the sub-type 'T_Derived*'. Use 'self()->'
|
||||
// to access duck-typed functions to avoid a virtual function call.
|
||||
T_Derived* self() { return static_cast<T_Derived*>(this); }
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Number of total trace buffers that have been allocated
|
||||
vluint32_t m_numTraceBuffers;
|
||||
|
||||
// Size of trace buffers
|
||||
size_t m_traceBufferSize;
|
||||
|
||||
// Buffers handed to worker for processing
|
||||
VerilatedThreadQueue<vluint32_t*> m_buffersToWorker;
|
||||
// Buffers returned from worker after processing
|
||||
VerilatedThreadQueue<vluint32_t*> m_buffersFromWorker;
|
||||
|
||||
// Get a new trace buffer that can be populated. May block if none available
|
||||
vluint32_t* getTraceBuffer();
|
||||
|
||||
// Write pointer into current buffer
|
||||
vluint32_t* m_traceBufferWritep;
|
||||
|
||||
// End of trace buffer
|
||||
vluint32_t* m_traceBufferEndp;
|
||||
|
||||
// The worker thread itself
|
||||
std::unique_ptr<std::thread> m_workerThread;
|
||||
|
||||
// The function executed by the worker thread
|
||||
void workerThreadMain();
|
||||
|
||||
// Wait until given buffer is placed in m_buffersFromWorker
|
||||
void waitForBuffer(const vluint32_t* bufferp);
|
||||
|
||||
// Shut down and join worker, if it's running, otherwise do nothing
|
||||
void shutdownWorker();
|
||||
#endif
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedTrace);
|
||||
|
||||
protected:
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
vluint32_t nextCode() const { return m_nextCode; }
|
||||
vluint32_t numSignals() const { return m_numSignals; }
|
||||
vluint32_t maxBits() const { return m_maxBits; }
|
||||
const std::string& moduleName() const { return m_moduleName; }
|
||||
void fullDump(bool value) { m_fullDump = value; }
|
||||
vluint64_t timeLastDump() { return m_timeLastDump; }
|
||||
|
||||
double timeRes() const { return m_timeRes; }
|
||||
double timeUnit() const { return m_timeUnit; }
|
||||
std::string timeResStr() const;
|
||||
std::string timeUnitStr() const;
|
||||
|
||||
void traceInit() VL_MT_UNSAFE;
|
||||
|
||||
void declCode(vluint32_t code, vluint32_t bits, bool tri);
|
||||
|
||||
/// Is this an escape?
|
||||
bool isScopeEscape(char c) { return isspace(c) || c == m_scopeEscape; }
|
||||
/// Character that splits scopes. Note whitespace are ALWAYS escapes.
|
||||
char scopeEscape() { return m_scopeEscape; }
|
||||
|
||||
void close();
|
||||
void flush();
|
||||
|
||||
//=========================================================================
|
||||
// Virtual functions to be provided by the format specific implementation
|
||||
|
||||
// Called when the trace moves forward to a new time point
|
||||
virtual void emitTimeChange(vluint64_t timeui) = 0;
|
||||
|
||||
// These hooks are called before a full or change based dump is produced.
|
||||
// The return value indicates whether to proceed with the dump.
|
||||
virtual bool preFullDump() { return true; }
|
||||
virtual bool preChangeDump() { return true; }
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
explicit VerilatedTrace();
|
||||
~VerilatedTrace();
|
||||
|
||||
// Set time units (s/ms, defaults to ns)
|
||||
void set_time_unit(const char* unitp);
|
||||
void set_time_unit(const std::string& unit);
|
||||
// Set time resolution (s/ms, defaults to ns)
|
||||
void set_time_resolution(const char* unitp);
|
||||
void set_time_resolution(const std::string& unit);
|
||||
|
||||
// Call
|
||||
void dump(vluint64_t timeui);
|
||||
|
||||
//=========================================================================
|
||||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
typedef void (*callback_t)(T_Derived* tracep, void* userthis, vluint32_t code);
|
||||
|
||||
void changeThread() { m_assertOne.changeThread(); }
|
||||
|
||||
void addCallback(callback_t initcb, callback_t fullcb, callback_t changecb,
|
||||
void* userthis) VL_MT_UNSAFE_ONE;
|
||||
|
||||
void module(const std::string& name) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
m_moduleName = name;
|
||||
}
|
||||
|
||||
void scopeEscape(char flag) { m_scopeEscape = flag; }
|
||||
|
||||
//=========================================================================
|
||||
// Hot path internal interface to Verilator generated code
|
||||
|
||||
// Implementation note: We rely on the following duck-typed implementations
|
||||
// in the derived class T_Derived. These emit* functions record a format
|
||||
// specific trace entry. Normally one would use pure virtual functions for
|
||||
// these here, but we cannot afford dynamic dispatch for calling these as
|
||||
// this is very hot code during tracing.
|
||||
|
||||
// duck-typed void emitBit(vluint32_t code, CData newval) = 0;
|
||||
// duck-typed void emitCData(vluint32_t code, CData newval, int bits) = 0;
|
||||
// duck-typed void emitSData(vluint32_t code, SData newval, int bits) = 0;
|
||||
// duck-typed void emitIData(vluint32_t code, IData newval, int bits) = 0;
|
||||
// duck-typed void emitQData(vluint32_t code, QData newval, int bits) = 0;
|
||||
// duck-typed void emitWData(vluint32_t code, const WData* newvalp, int bits) = 0;
|
||||
// duck-typed void emitFloat(vluint32_t code, float newval) = 0;
|
||||
// duck-typed void emitDouble(vluint32_t code, double newval) = 0;
|
||||
|
||||
vluint32_t* oldp(vluint32_t code) { return m_sigs_oldvalp + code; }
|
||||
|
||||
// Write to previous value buffer value and emit trace entry.
|
||||
void fullBit(vluint32_t* oldp, CData newval);
|
||||
void fullCData(vluint32_t* oldp, CData newval, int bits);
|
||||
void fullSData(vluint32_t* oldp, SData newval, int bits);
|
||||
void fullIData(vluint32_t* oldp, IData newval, int bits);
|
||||
void fullQData(vluint32_t* oldp, QData newval, int bits);
|
||||
void fullWData(vluint32_t* oldp, const WData* newvalp, int bits);
|
||||
void fullFloat(vluint32_t* oldp, float newval);
|
||||
void fullDouble(vluint32_t* oldp, double newval);
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Threaded tracing. Just dump everything in the trace buffer
|
||||
inline void chgBit(vluint32_t code, CData newval) {
|
||||
m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_BIT_0 | newval;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep += 2;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgCData(vluint32_t code, CData newval, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_CDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep[2] = newval;
|
||||
m_traceBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgSData(vluint32_t code, SData newval, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_SDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep[2] = newval;
|
||||
m_traceBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgIData(vluint32_t code, IData newval, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_IDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep[2] = newval;
|
||||
m_traceBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgQData(vluint32_t code, QData newval, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_QDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
*reinterpret_cast<QData*>(m_traceBufferWritep + 2) = newval;
|
||||
m_traceBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgWData(vluint32_t code, const WData* newvalp, int bits) {
|
||||
m_traceBufferWritep[0] = (bits << 4) | VerilatedTraceCommand::CHG_WDATA;
|
||||
m_traceBufferWritep[1] = code;
|
||||
m_traceBufferWritep += 2;
|
||||
for (int i = 0; i < (bits + 31) / 32; ++i) { *m_traceBufferWritep++ = newvalp[i]; }
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgFloat(vluint32_t code, float newval) {
|
||||
m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_FLOAT;
|
||||
m_traceBufferWritep[1] = code;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<float*>(m_traceBufferWritep + 2) = newval;
|
||||
m_traceBufferWritep += 3;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
inline void chgDouble(vluint32_t code, double newval) {
|
||||
m_traceBufferWritep[0] = VerilatedTraceCommand::CHG_DOUBLE;
|
||||
m_traceBufferWritep[1] = code;
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<double*>(m_traceBufferWritep + 2) = newval;
|
||||
m_traceBufferWritep += 4;
|
||||
VL_DEBUG_IF(assert(m_traceBufferWritep <= m_traceBufferEndp););
|
||||
}
|
||||
|
||||
#define CHG(name) chg##name##Impl
|
||||
#else
|
||||
#define CHG(name) chg##name
|
||||
#endif
|
||||
|
||||
// In non-threaded mode, these are called directly by the trace callbacks,
|
||||
// and are called chg*. In threaded mode, they are called by the worker
|
||||
// thread and are called chg*Impl
|
||||
|
||||
// Check previous dumped value of signal. If changed, then emit trace entry
|
||||
inline void CHG(Bit)(vluint32_t* oldp, CData newval) {
|
||||
const vluint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
|
||||
}
|
||||
inline void CHG(CData)(vluint32_t* oldp, CData newval, int bits) {
|
||||
const vluint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
|
||||
}
|
||||
inline void CHG(SData)(vluint32_t* oldp, SData newval, int bits) {
|
||||
const vluint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
|
||||
}
|
||||
inline void CHG(IData)(vluint32_t* oldp, IData newval, int bits) {
|
||||
const vluint32_t diff = *oldp ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
|
||||
}
|
||||
inline void CHG(QData)(vluint32_t* oldp, QData newval, int bits) {
|
||||
const vluint64_t diff = *reinterpret_cast<QData*>(oldp) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
|
||||
}
|
||||
inline void CHG(WData)(vluint32_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void CHG(Float)(vluint32_t* oldp, float newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY(*reinterpret_cast<float*>(oldp) != newval)) fullFloat(oldp, newval);
|
||||
}
|
||||
inline void CHG(Double)(vluint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY(*reinterpret_cast<double*>(oldp) != newval)) fullDouble(oldp, newval);
|
||||
}
|
||||
|
||||
#undef CHG
|
||||
};
|
||||
#endif // guard
|
||||
|
|
@ -0,0 +1,640 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2001-2020 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Implementation of tracing functionality common to all trace formats
|
||||
///
|
||||
//=============================================================================
|
||||
// SPDIFF_OFF
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef VL_DERIVED_T
|
||||
# error "This file should be included in trace format implementations"
|
||||
#endif
|
||||
|
||||
#include "verilated_intrinsics.h"
|
||||
#include "verilated_trace.h"
|
||||
|
||||
#if 0
|
||||
# include <iostream>
|
||||
# define VL_TRACE_THREAD_DEBUG(msg) std::cout << "TRACE THREAD: " << msg << std::endl
|
||||
#else
|
||||
# define VL_TRACE_THREAD_DEBUG(msg)
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
|
||||
//=============================================================================
|
||||
// Static utility functions
|
||||
|
||||
static double timescaleToDouble(const char* unitp) {
|
||||
char* endp;
|
||||
double value = strtod(unitp, &endp);
|
||||
// On error so we allow just "ns" to return 1e-9.
|
||||
if (value == 0.0 && endp == unitp) value = 1;
|
||||
unitp = endp;
|
||||
for (; *unitp && isspace(*unitp); unitp++) {}
|
||||
switch (*unitp) {
|
||||
case 's': value *= 1e1; break;
|
||||
case 'm': value *= 1e-3; break;
|
||||
case 'u': value *= 1e-6; break;
|
||||
case 'n': value *= 1e-9; break;
|
||||
case 'p': value *= 1e-12; break;
|
||||
case 'f': value *= 1e-15; break;
|
||||
case 'a': value *= 1e-18; break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::string doubleToTimescale(double value) {
|
||||
const char* suffixp = "s";
|
||||
// clang-format off
|
||||
if (value >= 1e0) { suffixp = "s"; value *= 1e0; }
|
||||
else if (value >= 1e-3 ) { suffixp = "ms"; value *= 1e3; }
|
||||
else if (value >= 1e-6 ) { suffixp = "us"; value *= 1e6; }
|
||||
else if (value >= 1e-9 ) { suffixp = "ns"; value *= 1e9; }
|
||||
else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
|
||||
else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
|
||||
else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
|
||||
// clang-format on
|
||||
char valuestr[100];
|
||||
sprintf(valuestr, "%3.0f%s", value, suffixp);
|
||||
return valuestr; // Gets converted to string, so no ref to stack
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Internal callback routines for each module being traced.
|
||||
|
||||
// Each module that wishes to be traced registers a set of callbacks stored in
|
||||
// this class. When the trace file is being constructed, this class provides
|
||||
// the callback routines to be executed.
|
||||
class VerilatedTraceCallInfo {
|
||||
public: // This is in .cpp file so is not widely visible
|
||||
typedef VerilatedTrace<VL_DERIVED_T>::callback_t callback_t;
|
||||
|
||||
callback_t m_initcb; ///< Initialization Callback function
|
||||
callback_t m_fullcb; ///< Full Dumping Callback function
|
||||
callback_t m_changecb; ///< Incremental Dumping Callback function
|
||||
void* m_userthis; ///< User data pointer for callback
|
||||
vluint32_t m_code; ///< Starting code number (set later by traceInit)
|
||||
// CONSTRUCTORS
|
||||
VerilatedTraceCallInfo(callback_t icb, callback_t fcb, callback_t changecb, void* ut)
|
||||
: m_initcb(icb)
|
||||
, m_fullcb(fcb)
|
||||
, m_changecb(changecb)
|
||||
, m_userthis(ut)
|
||||
, m_code(1) {}
|
||||
~VerilatedTraceCallInfo() {}
|
||||
};
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
//=========================================================================
|
||||
// Buffer management
|
||||
|
||||
template <> vluint32_t* VerilatedTrace<VL_DERIVED_T>::getTraceBuffer() {
|
||||
vluint32_t* bufferp;
|
||||
// Some jitter is expected, so some number of alternative trace buffers are
|
||||
// required, but don't allocate more than 8 buffers.
|
||||
if (m_numTraceBuffers < 8) {
|
||||
// Allocate a new buffer if none is available
|
||||
if (!m_buffersFromWorker.tryGet(bufferp)) {
|
||||
++m_numTraceBuffers;
|
||||
// Note: over allocate a bit so pointer comparison is well defined
|
||||
// if we overflow only by a small amount
|
||||
bufferp = new vluint32_t[m_traceBufferSize + 16];
|
||||
}
|
||||
} else {
|
||||
// Block until a buffer becomes available
|
||||
bufferp = m_buffersFromWorker.get();
|
||||
}
|
||||
return bufferp;
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::waitForBuffer(const vluint32_t* buffp) {
|
||||
// Slow path code only called on flush/shutdown, so use a simple algorithm.
|
||||
// Collect buffers from worker and stash them until we get the one we want.
|
||||
std::deque<vluint32_t*> stash;
|
||||
do { stash.push_back(m_buffersFromWorker.get()); } while (stash.back() != buffp);
|
||||
// Now put them back in the queue, in the original order.
|
||||
while (!stash.empty()) {
|
||||
m_buffersFromWorker.put_front(stash.back());
|
||||
stash.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Worker thread
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::workerThreadMain() {
|
||||
bool shutdown = false;
|
||||
|
||||
do {
|
||||
vluint32_t* const bufferp = m_buffersToWorker.get();
|
||||
|
||||
VL_TRACE_THREAD_DEBUG("");
|
||||
VL_TRACE_THREAD_DEBUG("Got buffer: " << bufferp);
|
||||
|
||||
const vluint32_t* readp = bufferp;
|
||||
|
||||
while (true) {
|
||||
const vluint32_t cmd = readp[0];
|
||||
const vluint32_t top = cmd >> 4;
|
||||
// Always set this up, as it is almost always needed
|
||||
vluint32_t* const oldp = m_sigs_oldvalp + readp[1];
|
||||
// Note this increment needs to be undone on commands which do not
|
||||
// actually contain a code, but those are the rare cases.
|
||||
readp += 2;
|
||||
|
||||
switch (cmd & 0xF) {
|
||||
//===
|
||||
// CHG_* commands
|
||||
case VerilatedTraceCommand::CHG_BIT_0:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_BIT_0 " << top);
|
||||
chgBitImpl(oldp, 0);
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_BIT_1:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_BIT_1 " << top);
|
||||
chgBitImpl(oldp, 1);
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_CDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_CDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
chgCDataImpl(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_SDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_SDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
chgSDataImpl(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_IDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_IDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
chgIDataImpl(oldp, *readp, top);
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_QDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_QDATA " << top);
|
||||
// Bits stored in bottom byte of command
|
||||
chgQDataImpl(oldp, *reinterpret_cast<const QData*>(readp), top);
|
||||
readp += 2;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_WDATA:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_WDATA " << top);
|
||||
chgWDataImpl(oldp, readp, top);
|
||||
readp += VL_WORDS_I(top);
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_FLOAT:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_FLOAT " << top);
|
||||
chgFloatImpl(oldp, *reinterpret_cast<const float*>(readp));
|
||||
readp += 1;
|
||||
continue;
|
||||
case VerilatedTraceCommand::CHG_DOUBLE:
|
||||
VL_TRACE_THREAD_DEBUG("Command CHG_DOUBLE " << top);
|
||||
chgDoubleImpl(oldp, *reinterpret_cast<const double*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
//===
|
||||
// Rare commands
|
||||
case VerilatedTraceCommand::TIME_CHANGE:
|
||||
VL_TRACE_THREAD_DEBUG("Command TIME_CHANGE " << top);
|
||||
readp -= 1; // No code in this command, undo increment
|
||||
emitTimeChange(*reinterpret_cast<const vluint64_t*>(readp));
|
||||
readp += 2;
|
||||
continue;
|
||||
|
||||
//===
|
||||
// Commands ending this buffer
|
||||
case VerilatedTraceCommand::END: VL_TRACE_THREAD_DEBUG("Command END"); break;
|
||||
case VerilatedTraceCommand::SHUTDOWN:
|
||||
VL_TRACE_THREAD_DEBUG("Command SHUTDOWN");
|
||||
shutdown = true;
|
||||
break;
|
||||
|
||||
//===
|
||||
// Unknown command
|
||||
default:
|
||||
VL_TRACE_THREAD_DEBUG("Command UNKNOWN");
|
||||
VL_PRINTF_MT("Trace command: 0x%08x\n", cmd);
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Unknown trace command");
|
||||
break;
|
||||
}
|
||||
|
||||
// The above switch will execute 'continue' when necessary,
|
||||
// so if we ever reach here, we are done with the buffer.
|
||||
break;
|
||||
}
|
||||
|
||||
VL_TRACE_THREAD_DEBUG("Returning buffer");
|
||||
|
||||
// Return buffer
|
||||
m_buffersFromWorker.put(bufferp);
|
||||
} while (VL_LIKELY(!shutdown));
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::shutdownWorker() {
|
||||
// If the worker thread is not running, done..
|
||||
if (!m_workerThread) return;
|
||||
|
||||
// Hand an buffer with a shutdown command to the worker thread
|
||||
vluint32_t* const bufferp = getTraceBuffer();
|
||||
bufferp[0] = VerilatedTraceCommand::SHUTDOWN;
|
||||
m_buffersToWorker.put(bufferp);
|
||||
// Wait for it to return
|
||||
waitForBuffer(bufferp);
|
||||
// Join the thread and delete it
|
||||
m_workerThread->join();
|
||||
m_workerThread.reset(nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// Life cycle
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::close() {
|
||||
#ifdef VL_TRACE_THREADED
|
||||
shutdownWorker();
|
||||
while (m_numTraceBuffers) {
|
||||
delete[] m_buffersFromWorker.get();
|
||||
m_numTraceBuffers--;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::flush() {
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Hand an empty buffer to the worker thread
|
||||
vluint32_t* const bufferp = getTraceBuffer();
|
||||
*bufferp = VerilatedTraceCommand::END;
|
||||
m_buffersToWorker.put(bufferp);
|
||||
// Wait for it to be returned. As the processing is in-order,
|
||||
// this ensures all previous buffers have been processed.
|
||||
waitForBuffer(bufferp);
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedTrace
|
||||
|
||||
template <>
|
||||
VerilatedTrace<VL_DERIVED_T>::VerilatedTrace()
|
||||
: m_sigs_oldvalp(NULL)
|
||||
, m_timeLastDump(0)
|
||||
, m_fullDump(true)
|
||||
, m_nextCode(0)
|
||||
, m_numSignals(0)
|
||||
, m_maxBits(0)
|
||||
, m_scopeEscape('.')
|
||||
, m_timeRes(1e-9)
|
||||
, m_timeUnit(1e-9)
|
||||
#ifdef VL_TRACE_THREADED
|
||||
, m_numTraceBuffers(0)
|
||||
#endif
|
||||
{
|
||||
set_time_unit(Verilated::timeunitString());
|
||||
set_time_resolution(Verilated::timeprecisionString());
|
||||
}
|
||||
|
||||
template <> VerilatedTrace<VL_DERIVED_T>::~VerilatedTrace() {
|
||||
if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = NULL);
|
||||
while (!m_callbacks.empty()) {
|
||||
delete m_callbacks.back();
|
||||
m_callbacks.pop_back();
|
||||
}
|
||||
#ifdef VL_TRACE_THREADED
|
||||
close();
|
||||
#endif
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::traceInit() VL_MT_UNSAFE {
|
||||
m_assertOne.check();
|
||||
|
||||
// Note: It is possible to re-open a trace file (VCD in particular),
|
||||
// so we must reset the next code here, but it must have the same number
|
||||
// of codes on re-open
|
||||
const vluint32_t expectedCodes = nextCode();
|
||||
m_nextCode = 1;
|
||||
m_numSignals = 0;
|
||||
m_maxBits = 0;
|
||||
|
||||
// Call all initialize callbacks, which will call decl* for each signal.
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ++ent) {
|
||||
VerilatedTraceCallInfo* cip = m_callbacks[ent];
|
||||
cip->m_code = nextCode();
|
||||
(cip->m_initcb)(self(), cip->m_userthis, cip->m_code);
|
||||
}
|
||||
|
||||
if (expectedCodes && nextCode() != expectedCodes) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "",
|
||||
"Reopening trace file with different number of signals");
|
||||
}
|
||||
|
||||
// Now that we know the number of codes, allocate space for the buffer
|
||||
// holding previous signal values.
|
||||
if (!m_sigs_oldvalp) m_sigs_oldvalp = new vluint32_t[nextCode()];
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Compute trace buffer size. we need to be able to store a new value for
|
||||
// each signal, which is 'nextCode()' entries after the init callbacks
|
||||
// above have been run, plus up to 2 more words of metadata per signal,
|
||||
// plus fixed overhead of 1 for a termination flag and 3 for a time stamp
|
||||
// update.
|
||||
m_traceBufferSize = nextCode() + numSignals() * 2 + 4;
|
||||
|
||||
// Start the worker thread
|
||||
m_workerThread.reset(new std::thread(&VerilatedTrace<VL_DERIVED_T>::workerThreadMain, this));
|
||||
#endif
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::declCode(vluint32_t code, vluint32_t bits, bool tri) {
|
||||
if (!code) {
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal");
|
||||
}
|
||||
// Note: The tri-state flag is not used by Verilator, but is here for
|
||||
// compatibility with some foreign code.
|
||||
int codesNeeded = VL_WORDS_I(bits);
|
||||
if (tri) codesNeeded *= 2;
|
||||
m_nextCode = std::max(m_nextCode, code + codesNeeded);
|
||||
++m_numSignals;
|
||||
m_maxBits = std::max(m_maxBits, bits);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Internals available to format specific implementations
|
||||
|
||||
template <> std::string VerilatedTrace<VL_DERIVED_T>::timeResStr() const {
|
||||
return doubleToTimescale(m_timeRes);
|
||||
}
|
||||
|
||||
template <> std::string VerilatedTrace<VL_DERIVED_T>::timeUnitStr() const {
|
||||
return doubleToTimescale(m_timeUnit);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const char* unitp) {
|
||||
m_timeUnit = timescaleToDouble(unitp);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_unit(const std::string& unit) {
|
||||
set_time_unit(unit.c_str());
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const char* unitp) {
|
||||
m_timeRes = timescaleToDouble(unitp);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::set_time_resolution(const std::string& unit) {
|
||||
set_time_resolution(unit.c_str());
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::dump(vluint64_t timeui) {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(m_timeLastDump && timeui <= m_timeLastDump)) {
|
||||
VL_PRINTF_MT("%%Warning: previous dump at t=%" VL_PRI64 "u, requesting t=%" VL_PRI64
|
||||
"u, dump call ignored\n",
|
||||
m_timeLastDump, timeui);
|
||||
return;
|
||||
}
|
||||
m_timeLastDump = timeui;
|
||||
|
||||
Verilated::quiesce();
|
||||
|
||||
// Call hook for format specific behaviour
|
||||
if (VL_UNLIKELY(m_fullDump)) {
|
||||
if (!preFullDump()) return;
|
||||
} else {
|
||||
if (!preChangeDump()) return;
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
// Currently only incremental dumps run on the worker thread
|
||||
vluint32_t* bufferp = nullptr;
|
||||
if (VL_LIKELY(!m_fullDump)) {
|
||||
// Get the trace buffer we are about to fill
|
||||
bufferp = getTraceBuffer();
|
||||
m_traceBufferWritep = bufferp;
|
||||
m_traceBufferEndp = bufferp + m_traceBufferSize;
|
||||
|
||||
// Tell worker to update time point
|
||||
m_traceBufferWritep[0] = VerilatedTraceCommand::TIME_CHANGE;
|
||||
*reinterpret_cast<vluint64_t*>(m_traceBufferWritep + 1) = timeui;
|
||||
m_traceBufferWritep += 3;
|
||||
} else {
|
||||
// Update time point
|
||||
flush();
|
||||
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
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ++ent) {
|
||||
VerilatedTraceCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_fullcb)(self(), cip->m_userthis, cip->m_code);
|
||||
}
|
||||
} else {
|
||||
for (vluint32_t ent = 0; ent < m_callbacks.size(); ++ent) {
|
||||
VerilatedTraceCallInfo* cip = m_callbacks[ent];
|
||||
(cip->m_changecb)(self(), cip->m_userthis, cip->m_code);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VL_TRACE_THREADED
|
||||
if (VL_LIKELY(bufferp)) {
|
||||
// Mark end of the trace buffer we just filled
|
||||
*m_traceBufferWritep++ = VerilatedTraceCommand::END;
|
||||
|
||||
// Assert no buffer overflow
|
||||
assert(m_traceBufferWritep - bufferp <= m_traceBufferSize);
|
||||
|
||||
// Pass it to the worker thread
|
||||
m_buffersToWorker.put(bufferp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Non-hot path internal interface to Verilator generated code
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::addCallback(callback_t initcb, callback_t fullcb,
|
||||
callback_t changecb,
|
||||
void* userthis) VL_MT_UNSAFE_ONE {
|
||||
m_assertOne.check();
|
||||
if (VL_UNLIKELY(timeLastDump() != 0)) {
|
||||
std::string msg = (std::string("Internal: ") + __FILE__ + "::" + __FUNCTION__
|
||||
+ " called with already open file");
|
||||
VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
|
||||
}
|
||||
VerilatedTraceCallInfo* cip = new VerilatedTraceCallInfo(initcb, fullcb, changecb, userthis);
|
||||
m_callbacks.push_back(cip);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Hot path internal interface to Verilator generated code
|
||||
|
||||
// These functions must write the new value back into the old value store,
|
||||
// and subsequently call the format specific emit* implementations. Note
|
||||
// that this file must be included in the format specific implementation, so
|
||||
// the emit* functions can be inlined for performance.
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::fullBit(vluint32_t* oldp, CData newval) {
|
||||
*oldp = newval;
|
||||
self()->emitBit(oldp - m_sigs_oldvalp, newval);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullCData(vluint32_t* oldp, CData newval, int bits) {
|
||||
*oldp = newval;
|
||||
self()->emitCData(oldp - m_sigs_oldvalp, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullSData(vluint32_t* oldp, SData newval, int bits) {
|
||||
*oldp = newval;
|
||||
self()->emitSData(oldp - m_sigs_oldvalp, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullIData(vluint32_t* oldp, IData newval, int bits) {
|
||||
*oldp = newval;
|
||||
self()->emitIData(oldp - m_sigs_oldvalp, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullQData(vluint32_t* oldp, QData newval, int bits) {
|
||||
*reinterpret_cast<QData*>(oldp) = newval;
|
||||
self()->emitQData(oldp - m_sigs_oldvalp, newval, bits);
|
||||
}
|
||||
|
||||
template <>
|
||||
void VerilatedTrace<VL_DERIVED_T>::fullWData(vluint32_t* oldp, const WData* newvalp, int bits) {
|
||||
for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
|
||||
self()->emitWData(oldp - m_sigs_oldvalp, newvalp, bits);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::fullFloat(vluint32_t* oldp, float newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<float*>(oldp) = newval;
|
||||
self()->emitFloat(oldp - m_sigs_oldvalp, newval);
|
||||
}
|
||||
|
||||
template <> void VerilatedTrace<VL_DERIVED_T>::fullDouble(vluint32_t* oldp, double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
*reinterpret_cast<double*>(oldp) = newval;
|
||||
self()->emitDouble(oldp - m_sigs_oldvalp, newval);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Primitives converting binary values to strings...
|
||||
|
||||
// All of these take a destination pointer where the string will be emitted,
|
||||
// and a value to convert. There are a couple of variants for efficiency.
|
||||
|
||||
inline static void cvtCDataToStr(char* dstp, CData value) {
|
||||
#ifdef VL_HAVE_SSE2
|
||||
// Similar to cvtSDataToStr but only the bottom 8 byte lanes are used
|
||||
const __m128i a = _mm_cvtsi32_si128(value);
|
||||
const __m128i b = _mm_unpacklo_epi8(a, a);
|
||||
const __m128i c = _mm_shufflelo_epi16(b, 0);
|
||||
const __m128i m = _mm_set1_epi64x(0x0102040810204080);
|
||||
const __m128i d = _mm_cmpeq_epi8(_mm_and_si128(c, m), m);
|
||||
const __m128i result = _mm_sub_epi8(_mm_set1_epi8('0'), d);
|
||||
_mm_storel_epi64(reinterpret_cast<__m128i*>(dstp), result);
|
||||
#else
|
||||
dstp[0] = '0' | static_cast<char>((value >> 7) & 1);
|
||||
dstp[1] = '0' | static_cast<char>((value >> 6) & 1);
|
||||
dstp[2] = '0' | static_cast<char>((value >> 5) & 1);
|
||||
dstp[3] = '0' | static_cast<char>((value >> 4) & 1);
|
||||
dstp[4] = '0' | static_cast<char>((value >> 3) & 1);
|
||||
dstp[5] = '0' | static_cast<char>((value >> 2) & 1);
|
||||
dstp[6] = '0' | static_cast<char>((value >> 1) & 1);
|
||||
dstp[7] = '0' | static_cast<char>(value & 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline static void cvtSDataToStr(char* dstp, SData value) {
|
||||
#ifdef VL_HAVE_SSE2
|
||||
// We want each bit in the 16-bit input value to end up in a byte lane
|
||||
// within the 128-bit XMM register. Note that x86 is little-endian and we
|
||||
// want the MSB of the input at the low address, so we will bit-reverse
|
||||
// at the same time.
|
||||
|
||||
// Put value in bottom of 128-bit register a[15:0] = value
|
||||
const __m128i a = _mm_cvtsi32_si128(value);
|
||||
// Interleave bytes with themselves
|
||||
// b[15: 0] = {2{a[ 7:0]}} == {2{value[ 7:0]}}
|
||||
// b[31:16] = {2{a[15:8]}} == {2{value[15:8]}}
|
||||
const __m128i b = _mm_unpacklo_epi8(a, a);
|
||||
// Shuffle bottom 64 bits, note swapping high bytes with low bytes
|
||||
// c[31: 0] = {2{b[31:16]}} == {4{value[15:8}}
|
||||
// c[63:32] = {2{b[15: 0]}} == {4{value[ 7:0}}
|
||||
const __m128i c = _mm_shufflelo_epi16(b, 0x05);
|
||||
// Shuffle whole register
|
||||
// d[ 63: 0] = {2{c[31: 0]}} == {8{value[15:8}}
|
||||
// d[126:54] = {2{c[63:32]}} == {8{value[ 7:0}}
|
||||
const __m128i d = _mm_shuffle_epi32(c, 0x50);
|
||||
// Test each bit within the bytes, this sets each byte lane to 0
|
||||
// if the bit for that lane is 0 and to 0xff if the bit is 1.
|
||||
const __m128i m = _mm_set1_epi64x(0x0102040810204080);
|
||||
const __m128i e = _mm_cmpeq_epi8(_mm_and_si128(d, m), m);
|
||||
// Convert to ASCII by subtracting the masks from ASCII '0':
|
||||
// '0' - 0 is '0', '0' - -1 is '1'
|
||||
const __m128i result = _mm_sub_epi8(_mm_set1_epi8('0'), e);
|
||||
// Store the 16 characters to the un-aligned buffer
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i*>(dstp), result);
|
||||
#else
|
||||
cvtCDataToStr(dstp, value >> 8);
|
||||
cvtCDataToStr(dstp + 8, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline static void cvtIDataToStr(char* dstp, IData value) {
|
||||
#ifdef VL_HAVE_AVX2
|
||||
// Similar to cvtSDataToStr but the bottom 16-bits are processed in the
|
||||
// top half of the YMM registerss
|
||||
const __m256i a = _mm256_insert_epi32(_mm256_undefined_si256(), value, 0);
|
||||
const __m256i b = _mm256_permute4x64_epi64(a, 0);
|
||||
const __m256i s = _mm256_set_epi8(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3);
|
||||
const __m256i c = _mm256_shuffle_epi8(b, s);
|
||||
const __m256i m = _mm256_set1_epi64x(0x0102040810204080);
|
||||
const __m256i d = _mm256_cmpeq_epi8(_mm256_and_si256(c, m), m);
|
||||
const __m256i result = _mm256_sub_epi8(_mm256_set1_epi8('0'), d);
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp), result);
|
||||
#else
|
||||
cvtSDataToStr(dstp, value >> 16);
|
||||
cvtSDataToStr(dstp + 16, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline static void cvtQDataToStr(char* dstp, QData value) {
|
||||
cvtIDataToStr(dstp, value >> 32);
|
||||
cvtIDataToStr(dstp + 32, value);
|
||||
}
|
||||
|
||||
#define cvtEDataToStr cvtIDataToStr
|
||||
|
|
@ -41,78 +41,64 @@
|
|||
#include <string>
|
||||
|
||||
// Abstract 'vl_hash' and 'vl_equal_to' templates.
|
||||
template <typename T> struct vl_hash {
|
||||
size_t operator()(const T& k) const;
|
||||
};
|
||||
template <typename T> struct vl_hash { size_t operator()(const T& k) const; };
|
||||
|
||||
template <typename T> struct vl_equal_to {
|
||||
bool operator()(const T& a, const T& b) const;
|
||||
};
|
||||
template <typename T> struct vl_equal_to { bool operator()(const T& a, const T& b) const; };
|
||||
|
||||
// Specializations of 'vl_hash' and 'vl_equal_to'.
|
||||
inline size_t vl_hash_bytes(const void* vbufp, size_t nbytes) {
|
||||
const vluint8_t* bufp = static_cast<const vluint8_t*>(vbufp);
|
||||
size_t hash = 0;
|
||||
for (size_t i = 0; i < nbytes; i++) {
|
||||
hash = bufp[i] + 31u * hash; // the K&R classic!
|
||||
hash = bufp[i] + 31U * hash; // the K&R classic!
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
template <> inline size_t
|
||||
vl_hash<unsigned int>::operator()(const unsigned int& k) const {
|
||||
template <> inline size_t vl_hash<unsigned int>::operator()(const unsigned int& k) const {
|
||||
return k;
|
||||
}
|
||||
|
||||
template <> inline bool
|
||||
vl_equal_to<unsigned int>::operator()(const unsigned int& a,
|
||||
const unsigned int& b) const {
|
||||
template <>
|
||||
inline bool vl_equal_to<unsigned int>::operator()(const unsigned int& a,
|
||||
const unsigned int& b) const {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
template <> inline size_t
|
||||
vl_hash<std::string>::operator()(const std::string& k) const {
|
||||
template <> inline size_t vl_hash<std::string>::operator()(const std::string& k) const {
|
||||
return vl_hash_bytes(k.data(), k.size());
|
||||
}
|
||||
|
||||
template <> inline bool
|
||||
vl_equal_to<std::string>::operator()(const std::string& a,
|
||||
const std::string& b) const {
|
||||
template <>
|
||||
inline bool vl_equal_to<std::string>::operator()(const std::string& a,
|
||||
const std::string& b) const {
|
||||
// Don't scan the strings if the sizes are different.
|
||||
if (a.size() != b.size()) {
|
||||
return false;
|
||||
}
|
||||
if (a.size() != b.size()) return false;
|
||||
return (0 == a.compare(b)); // Must scan.
|
||||
}
|
||||
|
||||
template <typename T> struct vl_hash<T*> {
|
||||
size_t operator()(T* kp) const {
|
||||
return ((sizeof(size_t) == sizeof(kp))
|
||||
? reinterpret_cast<size_t>(kp)
|
||||
: vl_hash_bytes(&kp, sizeof(kp)));
|
||||
return ((sizeof(size_t) == sizeof(kp)) ? reinterpret_cast<size_t>(kp)
|
||||
: vl_hash_bytes(&kp, sizeof(kp)));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct vl_equal_to<T*> {
|
||||
bool operator()(T* ap, T* bp) const {
|
||||
return ap == bp;
|
||||
}
|
||||
bool operator()(T* ap, T* bp) const { return ap == bp; }
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
//
|
||||
/// Functional clone of the std::unordered_set hash table.
|
||||
template <class T_Key,
|
||||
class T_Hash = vl_hash<T_Key>,
|
||||
class T_Equal = vl_equal_to<T_Key> >
|
||||
template <class T_Key, class T_Hash = vl_hash<T_Key>, class T_Equal = vl_equal_to<T_Key> >
|
||||
class vl_unordered_set {
|
||||
public:
|
||||
// TYPES
|
||||
typedef std::list<T_Key> Bucket;
|
||||
enum RehashType { GROW, SHRINK };
|
||||
|
||||
template <class KK, class VV,
|
||||
class HH, class EQ> friend class vl_unordered_map;
|
||||
template <class KK, class VV, class HH, class EQ> friend class vl_unordered_map;
|
||||
|
||||
class iterator {
|
||||
protected:
|
||||
|
|
@ -123,29 +109,23 @@ public:
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
iterator(size_t bucketIdx, typename Bucket::iterator bit,
|
||||
const vl_unordered_set* setp)
|
||||
: m_bucketIdx(bucketIdx), m_bit(bit), m_setp(setp) {}
|
||||
iterator(size_t bucketIdx, typename Bucket::iterator bit, const vl_unordered_set* setp)
|
||||
: m_bucketIdx(bucketIdx)
|
||||
, m_bit(bit)
|
||||
, m_setp(setp) {}
|
||||
|
||||
// METHODS
|
||||
const T_Key& operator*() const {
|
||||
return *m_bit;
|
||||
}
|
||||
const T_Key& operator*() const { return *m_bit; }
|
||||
// This should really be 'const T_Key*' type for unordered_set,
|
||||
// however this iterator is shared with unordered_map whose
|
||||
// operator-> returns a non-const ValueType*, so keep this
|
||||
// non-const to avoid having to define a whole separate iterator
|
||||
// for unordered_map.
|
||||
T_Key* operator->() const {
|
||||
return &(*m_bit);
|
||||
}
|
||||
T_Key* operator->() const { return &(*m_bit); }
|
||||
bool operator==(const iterator& other) const {
|
||||
return ((m_bucketIdx == other.m_bucketIdx)
|
||||
&& (m_bit == other.m_bit));
|
||||
}
|
||||
bool operator!=(const iterator& other) const {
|
||||
return (!this->operator==(other));
|
||||
return ((m_bucketIdx == other.m_bucketIdx) && (m_bit == other.m_bit));
|
||||
}
|
||||
bool operator!=(const iterator& other) const { return (!this->operator==(other)); }
|
||||
void advanceUntilValid() {
|
||||
while (true) {
|
||||
if (m_bit != m_setp->m_bucketsp[m_bucketIdx].end()) {
|
||||
|
|
@ -176,14 +156,14 @@ public:
|
|||
|
||||
private:
|
||||
// MEMBERS
|
||||
size_t m_numElements; // Number of entries present.
|
||||
size_t m_log2Buckets; // Log-base-2 of the number of buckets.
|
||||
mutable Bucket* m_bucketsp; // Hash table buckets. May be NULL;
|
||||
size_t m_numElements; // Number of entries present.
|
||||
size_t m_log2Buckets; // Log-base-2 of the number of buckets.
|
||||
mutable Bucket* m_bucketsp; // Hash table buckets. May be NULL;
|
||||
// // we'll allocate it on the fly when
|
||||
// // the first entries are created.
|
||||
Bucket m_emptyBucket; // A fake bucket, used to construct end().
|
||||
T_Hash m_hash; // Hash function provider.
|
||||
T_Equal m_equal; // Equal-to function provider.
|
||||
Bucket m_emptyBucket; // A fake bucket, used to construct end().
|
||||
T_Hash m_hash; // Hash function provider.
|
||||
T_Equal m_equal; // Equal-to function provider.
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
|
|
@ -202,26 +182,20 @@ public:
|
|||
, m_equal() {
|
||||
if (other.m_bucketsp) {
|
||||
m_bucketsp = new Bucket[numBuckets()];
|
||||
for (size_t i = 0; i < numBuckets(); i++) {
|
||||
m_bucketsp[i] = other.m_bucketsp[i];
|
||||
}
|
||||
for (size_t i = 0; i < numBuckets(); i++) m_bucketsp[i] = other.m_bucketsp[i];
|
||||
}
|
||||
}
|
||||
~vl_unordered_set() {
|
||||
VL_DO_DANGLING(delete [] m_bucketsp, m_bucketsp);
|
||||
}
|
||||
~vl_unordered_set() { VL_DO_DANGLING(delete[] m_bucketsp, m_bucketsp); }
|
||||
|
||||
vl_unordered_set& operator=(const vl_unordered_set& other) {
|
||||
if (this != &other) {
|
||||
clear();
|
||||
delete [] m_bucketsp;
|
||||
delete[] m_bucketsp;
|
||||
m_numElements = other.m_numElements;
|
||||
m_log2Buckets = other.m_log2Buckets;
|
||||
if (other.m_bucketsp) {
|
||||
m_bucketsp = new Bucket[numBuckets()];
|
||||
for (size_t i = 0; i < numBuckets(); i++) {
|
||||
m_bucketsp[i] = other.m_bucketsp[i];
|
||||
}
|
||||
for (size_t i = 0; i < numBuckets(); i++) m_bucketsp[i] = other.m_bucketsp[i];
|
||||
} else {
|
||||
m_bucketsp = NULL;
|
||||
}
|
||||
|
|
@ -251,24 +225,21 @@ public:
|
|||
return end();
|
||||
}
|
||||
const_iterator end() const {
|
||||
return iterator(VL_ULL(0xFFFFFFFFFFFFFFFF),
|
||||
const_cast<Bucket&>(m_emptyBucket).begin(), this);
|
||||
return iterator(VL_ULL(0xFFFFFFFFFFFFFFFF), const_cast<Bucket&>(m_emptyBucket).begin(),
|
||||
this);
|
||||
}
|
||||
|
||||
bool empty() const { return m_numElements == 0; }
|
||||
|
||||
size_t size() const { return m_numElements; }
|
||||
|
||||
size_t count(const T_Key& key) const {
|
||||
return (find(key) == end()) ? 0 : 1;
|
||||
}
|
||||
size_t count(const T_Key& key) const { return (find(key) == end()) ? 0 : 1; }
|
||||
|
||||
size_t hashToBucket(size_t hashVal) const {
|
||||
return hashToBucket(hashVal, m_log2Buckets);
|
||||
}
|
||||
size_t hashToBucket(size_t hashVal) const { return hashToBucket(hashVal, m_log2Buckets); }
|
||||
static size_t hashToBucket(size_t hashVal, unsigned log2Buckets) {
|
||||
// Fibonacci hashing
|
||||
// See https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/
|
||||
// Fibonacci hashing, see
|
||||
// https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization
|
||||
// -that-the-world-forgot-or-a-better-alternative-to-integer-modulo/
|
||||
//
|
||||
// * The magic numbers below are UINT_MAX/phi where phi is the
|
||||
// golden ratio number (1.618...) for either 64- or 32-bit
|
||||
|
|
@ -278,11 +249,9 @@ public:
|
|||
// function further. This permits the use of very fast client
|
||||
// hash funcs (like just returning the int or pointer value as
|
||||
// is!) and tolerates crappy client hash functions pretty well.
|
||||
size_t mult = hashVal * ((sizeof(size_t) == 8)
|
||||
? VL_ULL(11400714819323198485)
|
||||
: 2654435769lu);
|
||||
size_t result = (mult >> (((sizeof(size_t) == 8)
|
||||
? 64 : 32) - log2Buckets));
|
||||
size_t mult
|
||||
= hashVal * ((sizeof(size_t) == 8) ? VL_ULL(11400714819323198485) : 2654435769lu);
|
||||
size_t result = (mult >> (((sizeof(size_t) == 8) ? 64 : 32) - log2Buckets));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -292,19 +261,15 @@ public:
|
|||
initBuckets();
|
||||
Bucket* bucketp = &m_bucketsp[bucketIdxOut];
|
||||
|
||||
for (typename Bucket::iterator it = bucketp->begin();
|
||||
it != bucketp->end(); ++it) {
|
||||
if (m_equal.operator()(*it, key)) {
|
||||
return iterator(bucketIdxOut, it, this);
|
||||
}
|
||||
for (typename Bucket::iterator it = bucketp->begin(); it != bucketp->end(); ++it) {
|
||||
if (m_equal.operator()(*it, key)) return iterator(bucketIdxOut, it, this);
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
const_iterator find(const T_Key& key) const {
|
||||
size_t bucketIdx;
|
||||
return const_cast<vl_unordered_set*>(this)->find_internal(key,
|
||||
bucketIdx);
|
||||
return const_cast<vl_unordered_set*>(this)->find_internal(key, bucketIdx);
|
||||
}
|
||||
|
||||
iterator find(const T_Key& key) {
|
||||
|
|
@ -358,9 +323,7 @@ public:
|
|||
// for the Scoreboard in V3Partition, which begins tracking
|
||||
// a huge number of vertices and then tracks a successively
|
||||
// smaller number over time.
|
||||
if (needToRehash(SHRINK)) {
|
||||
rehash(SHRINK);
|
||||
}
|
||||
if (needToRehash(SHRINK)) rehash(SHRINK);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -419,8 +382,7 @@ private:
|
|||
size_t new_idx = hashToBucket(hash, new_log2Buckets);
|
||||
// Avoid mallocing one list elem and freeing another;
|
||||
// splice just moves it over.
|
||||
new_bucketsp[new_idx].splice(new_bucketsp[new_idx].begin(),
|
||||
m_bucketsp[i], bit);
|
||||
new_bucketsp[new_idx].splice(new_bucketsp[new_idx].begin(), m_bucketsp[i], bit);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -433,9 +395,7 @@ private:
|
|||
//===================================================================
|
||||
//
|
||||
/// Functional clone of the std::unordered_map hash table.
|
||||
template <class T_Key,
|
||||
class T_Value,
|
||||
class T_Hash = vl_hash<T_Key>,
|
||||
template <class T_Key, class T_Value, class T_Hash = vl_hash<T_Key>,
|
||||
class T_Equal = vl_equal_to<T_Key> >
|
||||
class vl_unordered_map {
|
||||
private:
|
||||
|
|
@ -445,6 +405,7 @@ private:
|
|||
class KeyHash {
|
||||
private:
|
||||
T_Hash key_hash;
|
||||
|
||||
public:
|
||||
KeyHash() {}
|
||||
size_t operator()(const KeyValPair& kv_pair) const {
|
||||
|
|
@ -455,6 +416,7 @@ private:
|
|||
class KeyEqual {
|
||||
private:
|
||||
T_Equal key_eq;
|
||||
|
||||
public:
|
||||
KeyEqual() {}
|
||||
bool operator()(const KeyValPair& kv_a, const KeyValPair& kv_b) const {
|
||||
|
|
@ -491,24 +453,19 @@ public:
|
|||
size_t bucketIdxOut = m_set.hashToBucket(hash);
|
||||
typename MapSet::Bucket* bucketp = m_set.getBucket(bucketIdxOut);
|
||||
|
||||
for (typename MapSet::Bucket::iterator it = bucketp->begin();
|
||||
it != bucketp->end(); ++it) {
|
||||
if (mapEq.operator()(it->first, k)) {
|
||||
return iterator(bucketIdxOut, it, &m_set);
|
||||
}
|
||||
for (typename MapSet::Bucket::iterator it = bucketp->begin(); it != bucketp->end(); ++it) {
|
||||
if (mapEq.operator()(it->first, k)) return iterator(bucketIdxOut, it, &m_set);
|
||||
}
|
||||
return end();
|
||||
}
|
||||
const_iterator find(const T_Key& k) const {
|
||||
return const_cast<vl_unordered_map*>(this)->find(k);
|
||||
}
|
||||
std::pair<iterator, bool> insert(const KeyValPair& val) {
|
||||
return m_set.insert(val);
|
||||
}
|
||||
std::pair<iterator, bool> insert(const KeyValPair& val) { return m_set.insert(val); }
|
||||
iterator erase(iterator it) { return m_set.erase(it); }
|
||||
size_t erase(const T_Key& k) {
|
||||
iterator it = find(k);
|
||||
if (it == end()) { return 0; }
|
||||
if (it == end()) return 0;
|
||||
m_set.erase(it);
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -517,9 +474,7 @@ public:
|
|||
// std::unordered_map::operator[] relies on it too.
|
||||
KeyValPair dummy = std::make_pair(k, T_Value());
|
||||
iterator it = m_set.find(dummy);
|
||||
if (it == m_set.end()) {
|
||||
it = m_set.insert(dummy).first;
|
||||
}
|
||||
if (it == m_set.end()) it = m_set.insert(dummy).first;
|
||||
// For the 'set', it's generally not safe to modify
|
||||
// the value after deref. For the 'map' though, we know
|
||||
// it's safe to modify the value field and we can allow it:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -20,15 +20,14 @@
|
|||
#ifndef _VERILATED_VCD_C_H_
|
||||
#define _VERILATED_VCD_C_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_trace.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class VerilatedVcd;
|
||||
class VerilatedVcdCallInfo;
|
||||
|
||||
// SPDIFF_ON
|
||||
//=============================================================================
|
||||
|
|
@ -48,64 +47,39 @@ public:
|
|||
virtual ssize_t write(const char* bufp, ssize_t len) VL_MT_UNSAFE;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdSig
|
||||
/// Internal data on one signal being traced.
|
||||
|
||||
class VerilatedVcdSig {
|
||||
protected:
|
||||
friend class VerilatedVcd;
|
||||
vluint32_t m_code; ///< VCD file code number
|
||||
int m_bits; ///< Size of value in bits
|
||||
VerilatedVcdSig(vluint32_t code, int bits)
|
||||
: m_code(code)
|
||||
, m_bits(bits) {}
|
||||
public:
|
||||
~VerilatedVcdSig() {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
||||
typedef void (*VerilatedVcdCallback_t)(VerilatedVcd* vcdp, void* userthis, vluint32_t code);
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcd
|
||||
/// Base class to create a Verilator VCD dump
|
||||
/// This is an internally used class - see VerilatedVcdC for what to call from applications
|
||||
|
||||
class VerilatedVcd {
|
||||
class VerilatedVcd : public VerilatedTrace<VerilatedVcd> {
|
||||
private:
|
||||
VerilatedVcdFile* m_filep; ///< File we're writing to
|
||||
bool m_fileNewed; ///< m_filep needs destruction
|
||||
bool m_isOpen; ///< True indicates open file
|
||||
bool m_evcd; ///< True for evcd format
|
||||
std::string m_filename; ///< Filename we're writing to (if open)
|
||||
vluint64_t m_rolloverMB; ///< MB of file size to rollover at
|
||||
char m_scopeEscape; ///< Character to separate scope components
|
||||
int m_modDepth; ///< Depth of module hierarchy
|
||||
bool m_fullDump; ///< True indicates dump ignoring if changed
|
||||
vluint32_t m_nextCode; ///< Next code number to assign
|
||||
std::string m_modName; ///< Module name being traced now
|
||||
double m_timeRes; ///< Time resolution (ns/ms etc)
|
||||
double m_timeUnit; ///< Time units (ns/ms etc)
|
||||
vluint64_t m_timeLastDump; ///< Last time we did a dump
|
||||
// Give the superclass access to private bits (to avoid virtual functions)
|
||||
friend class VerilatedTrace<VerilatedVcd>;
|
||||
|
||||
char* m_wrBufp; ///< Output buffer
|
||||
char* m_wrFlushp; ///< Output buffer flush trigger location
|
||||
char* m_writep; ///< Write pointer into output buffer
|
||||
vluint64_t m_wrChunkSize; ///< Output buffer size
|
||||
vluint64_t m_wroteBytes; ///< Number of bytes written to this file
|
||||
//=========================================================================
|
||||
// VCD specific internals
|
||||
|
||||
vluint32_t* m_sigs_oldvalp; ///< Pointer to old signal values
|
||||
typedef std::vector<VerilatedVcdSig> SigVec;
|
||||
SigVec m_sigs; ///< Pointer to signal information
|
||||
typedef std::vector<VerilatedVcdCallInfo*> CallbackVec;
|
||||
CallbackVec m_callbacks; ///< Routines to perform dumping
|
||||
typedef std::map<std::string,std::string> NameMap;
|
||||
VerilatedVcdFile* m_filep; ///< File we're writing to
|
||||
bool m_fileNewed; ///< m_filep needs destruction
|
||||
bool m_isOpen; ///< True indicates open file
|
||||
bool m_evcd; ///< True for evcd format
|
||||
std::string m_filename; ///< Filename we're writing to (if open)
|
||||
vluint64_t m_rolloverMB; ///< MB of file size to rollover at
|
||||
int m_modDepth; ///< Depth of module hierarchy
|
||||
|
||||
char* m_wrBufp; ///< Output buffer
|
||||
char* m_wrFlushp; ///< Output buffer flush trigger location
|
||||
char* m_writep; ///< Write pointer into output buffer
|
||||
vluint64_t m_wrChunkSize; ///< Output buffer size
|
||||
vluint64_t m_wroteBytes; ///< Number of bytes written to this file
|
||||
|
||||
std::vector<char> m_suffixes; ///< VCD line end string codes + metadata
|
||||
const char* m_suffixesp; ///< Pointer to first element of above
|
||||
|
||||
typedef std::map<std::string, std::string> NameMap;
|
||||
NameMap* m_namemapp; ///< List of names for the header
|
||||
|
||||
VerilatedAssertOneThread m_assertOne; ///< Assert only called from single thread
|
||||
|
||||
void bufferResize(vluint64_t minsize);
|
||||
void bufferFlush() VL_MT_UNSAFE_ONE;
|
||||
inline void bufferCheck() {
|
||||
|
|
@ -126,185 +100,139 @@ private:
|
|||
bool tri, bool bussed, int msb, int lsb);
|
||||
|
||||
void dumpHeader();
|
||||
void dumpPrep(vluint64_t timeui);
|
||||
void dumpFull(vluint64_t timeui);
|
||||
// cppcheck-suppress functionConst
|
||||
void dumpDone();
|
||||
inline void printCode(vluint32_t code) {
|
||||
*m_writep++ = static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
while (code) {
|
||||
code--;
|
||||
*m_writep++ = static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
}
|
||||
}
|
||||
static std::string stringCode(vluint32_t code) VL_PURE {
|
||||
std::string out;
|
||||
out += static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
while (code) {
|
||||
code--;
|
||||
out += static_cast<char>('!' + code % 94);
|
||||
code /= 94;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
char* writeCode(char* writep, vluint32_t code);
|
||||
|
||||
void finishLine(vluint32_t code, char* writep);
|
||||
|
||||
/// Flush any remaining data from all files
|
||||
static void flush_all() VL_MT_UNSAFE_ONE;
|
||||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcd);
|
||||
|
||||
protected:
|
||||
//=========================================================================
|
||||
// Implementation of VerilatedTrace interface
|
||||
|
||||
// Implementations of protected virtual methods for VerilatedTrace
|
||||
void emitTimeChange(vluint64_t timeui) VL_OVERRIDE;
|
||||
|
||||
// Hooks called from VerilatedTrace
|
||||
bool preFullDump() VL_OVERRIDE { return isOpen(); }
|
||||
bool preChangeDump() VL_OVERRIDE;
|
||||
|
||||
// Implementations of duck-typed methods for VerilatedTrace. These are
|
||||
// called from only one place (namely full*) so always inline them.
|
||||
inline void emitBit(vluint32_t code, CData newval);
|
||||
inline void emitCData(vluint32_t code, CData newval, int bits);
|
||||
inline void emitSData(vluint32_t code, SData newval, int bits);
|
||||
inline void emitIData(vluint32_t code, IData newval, int bits);
|
||||
inline void emitQData(vluint32_t code, QData newval, int bits);
|
||||
inline void emitWData(vluint32_t code, const WData* newvalp, int bits);
|
||||
inline void emitFloat(vluint32_t code, float newval);
|
||||
inline void emitDouble(vluint32_t code, double newval);
|
||||
|
||||
public:
|
||||
//=========================================================================
|
||||
// External interface to client code
|
||||
|
||||
explicit VerilatedVcd(VerilatedVcdFile* filep = NULL);
|
||||
~VerilatedVcd();
|
||||
/// Routines can only be called from one thread; allow next call from different thread
|
||||
void changeThread() { m_assertOne.changeThread(); }
|
||||
|
||||
// ACCESSORS
|
||||
/// Set size in megabytes after which new file should be created
|
||||
void rolloverMB(vluint64_t rolloverMB) { m_rolloverMB = rolloverMB; }
|
||||
/// Is file open?
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
/// Change character that splits scopes. Note whitespace are ALWAYS escapes.
|
||||
void scopeEscape(char flag) { m_scopeEscape = flag; }
|
||||
/// Is this an escape?
|
||||
inline bool isScopeEscape(char c) { return isspace(c) || c == m_scopeEscape; }
|
||||
|
||||
// METHODS
|
||||
void open(const char* filename) VL_MT_UNSAFE_ONE; ///< Open the file; call isOpen() to see if errors
|
||||
void openNext(bool incFilename); ///< Open next data-only file
|
||||
void close() VL_MT_UNSAFE_ONE; ///< Close the file
|
||||
/// Open the file; call isOpen() to see if errors
|
||||
void open(const char* filename) VL_MT_UNSAFE_ONE;
|
||||
/// Open next data-only file
|
||||
void openNext(bool incFilename) VL_MT_UNSAFE_ONE;
|
||||
/// Close the file
|
||||
void close() VL_MT_UNSAFE_ONE;
|
||||
/// Flush any remaining data to this file
|
||||
void flush() VL_MT_UNSAFE_ONE { bufferFlush(); }
|
||||
/// Flush any remaining data from all files
|
||||
static void flush_all() VL_MT_UNSAFE_ONE;
|
||||
void flush() VL_MT_UNSAFE_ONE;
|
||||
/// Is file open?
|
||||
bool isOpen() const { return m_isOpen; }
|
||||
|
||||
void set_time_unit(const char* unitp); ///< Set time units (s/ms, defaults to ns)
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
//=========================================================================
|
||||
// Internal interface to Verilator generated code
|
||||
|
||||
void set_time_resolution(const char* unitp); ///< Set time resolution (s/ms, defaults to ns)
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
void declBit(vluint32_t code, const char* name, bool array, int arraynum);
|
||||
void declBus(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declArray(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declFloat(vluint32_t code, const char* name, bool array, int arraynum);
|
||||
void declDouble(vluint32_t code, const char* name, bool array, int arraynum);
|
||||
|
||||
double timescaleToDouble(const char* unitp);
|
||||
std::string doubleToTimescale(double value);
|
||||
#ifdef VL_TRACE_VCD_OLD_API
|
||||
//=========================================================================
|
||||
// Note: These are only for testing for backward compatibility with foreign
|
||||
// code and is not used by Verilator. Do not use these as there is no
|
||||
// guarantee of functionality.
|
||||
|
||||
/// Inside dumping routines, called each cycle to make the dump
|
||||
void dump(vluint64_t timeui);
|
||||
/// Call dump with a absolute unscaled time in seconds
|
||||
void dumpSeconds(double secs) { dump(static_cast<vluint64_t>(secs * m_timeRes)); }
|
||||
void declTriBit(vluint32_t code, const char* name, bool array, int arraynum);
|
||||
void declTriBus(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declTriQuad(vluint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb);
|
||||
void declTriArray(vluint32_t code, const char* name, bool array, int arraynum, int msb,
|
||||
int lsb);
|
||||
|
||||
/// Inside dumping routines, declare callbacks for tracings
|
||||
void addCallback(VerilatedVcdCallback_t initcb, VerilatedVcdCallback_t fullcb,
|
||||
VerilatedVcdCallback_t changecb,
|
||||
void* userthis) VL_MT_UNSAFE_ONE;
|
||||
void fullBit(vluint32_t* oldp, CData newval) { fullBit(oldp - this->oldp(0), newval); }
|
||||
void fullCData(vluint32_t* oldp, CData newval, int bits) {
|
||||
fullBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullSData(vluint32_t* oldp, SData newval, int bits) {
|
||||
fullBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullIData(vluint32_t* oldp, IData newval, int bits) {
|
||||
fullBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullQData(vluint32_t* oldp, QData newval, int bits) {
|
||||
fullQuad(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
void fullWData(vluint32_t* oldp, const WData* newvalp, int bits) {
|
||||
fullArray(oldp - this->oldp(0), newvalp, bits);
|
||||
}
|
||||
void fullFloat(vluint32_t* oldp, float newval) { fullFloat(oldp - this->oldp(0), newval); }
|
||||
void fullDouble(vluint32_t* oldp, double newval) { fullDouble(oldp - this->oldp(0), newval); }
|
||||
|
||||
/// Inside dumping routines, declare a module
|
||||
void module(const std::string& name);
|
||||
/// Inside dumping routines, declare a signal
|
||||
void declBit( vluint32_t code, const char* name, bool array, int arraynum);
|
||||
void declBus( vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declQuad( vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declArray( vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declTriBit( vluint32_t code, const char* name, bool array, int arraynum);
|
||||
void declTriBus( vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declTriQuad( vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declTriArray(vluint32_t code, const char* name, bool array, int arraynum, int msb, int lsb);
|
||||
void declDouble( vluint32_t code, const char* name, bool array, int arraynum);
|
||||
void declFloat( vluint32_t code, const char* name, bool array, int arraynum);
|
||||
// ... other module_start for submodules (based on cell name)
|
||||
inline void chgBit(vluint32_t* oldp, CData newval) { chgBit(oldp - this->oldp(0), newval); }
|
||||
inline void chgCData(vluint32_t* oldp, CData newval, int bits) {
|
||||
chgBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
inline void chgSData(vluint32_t* oldp, SData newval, int bits) {
|
||||
chgBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
inline void chgIData(vluint32_t* oldp, IData newval, int bits) {
|
||||
chgBus(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
inline void chgQData(vluint32_t* oldp, QData newval, int bits) {
|
||||
chgQuad(oldp - this->oldp(0), newval, bits);
|
||||
}
|
||||
inline void chgWData(vluint32_t* oldp, const WData* newvalp, int bits) {
|
||||
chgArray(oldp - this->oldp(0), newvalp, bits);
|
||||
}
|
||||
inline void chgFloat(vluint32_t* oldp, float newval) {
|
||||
chgFloat(oldp - this->oldp(0), newval);
|
||||
}
|
||||
inline void chgDouble(vluint32_t* oldp, double newval) {
|
||||
chgDouble(oldp - this->oldp(0), newval);
|
||||
}
|
||||
|
||||
/// Inside dumping routines, dump one signal
|
||||
void fullBit(vluint32_t code, const vluint32_t newval) {
|
||||
// Note the &1, so we don't require clean input -- makes more common no change case faster
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
*m_writep++=('0'+static_cast<char>(newval&1)); printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++=((newval&(1L<<bit))?'1':'0');
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++ = ((newval & (VL_ULL(1) << bit)) ? '1' : '0');
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullArray(vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
m_sigs_oldvalp[code + word] = newval[word];
|
||||
}
|
||||
*m_writep++ = 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval[(bit / 32)] & (1L << (bit & 0x1f))) ? '1' : '0');
|
||||
}
|
||||
*m_writep ++= ' '; printCode(code); *m_writep ++= '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullArray(vluint32_t code, const vluint64_t* newval, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) {
|
||||
m_sigs_oldvalp[code + word] = newval[word];
|
||||
}
|
||||
*m_writep ++= 'b';
|
||||
for (int bit = bits - 1; bit >= 0; --bit) {
|
||||
*m_writep++ = ((newval[(bit / 64)] & (VL_ULL(1) << (bit & 0x3f))) ? '1' : '0');
|
||||
}
|
||||
*m_writep ++= ' '; printCode(code); *m_writep ++= '\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
m_sigs_oldvalp[code+1] = newtri;
|
||||
*m_writep++ = "01zz"[m_sigs_oldvalp[code]
|
||||
| (m_sigs_oldvalp[code+1]<<1)];
|
||||
printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits) {
|
||||
m_sigs_oldvalp[code] = newval;
|
||||
m_sigs_oldvalp[code+1] = newtri;
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++ = "01zz"[((newval >> bit)&1)
|
||||
| (((newtri >> bit)&1)<<1)];
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullTriQuad(vluint32_t code, const vluint64_t newval,
|
||||
const vluint32_t newtri, int bits) {
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) = newval;
|
||||
(*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) = newtri;
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++ = "01zz"[((newval >> bit) & VL_ULL(1))
|
||||
| (((newtri >> bit) & VL_ULL(1)) << VL_ULL(1))];
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
void fullTriArray(vluint32_t code, const vluint32_t* newvalp,
|
||||
const vluint32_t* newtrip, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
m_sigs_oldvalp[code+word*2] = newvalp[word];
|
||||
m_sigs_oldvalp[code+word*2+1] = newtrip[word];
|
||||
}
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
vluint32_t valbit = (newvalp[(bit/32)]>>(bit&0x1f)) & 1;
|
||||
vluint32_t tribit = (newtrip[(bit/32)]>>(bit&0x1f)) & 1;
|
||||
*m_writep++ = "01zz"[valbit | (tribit<<1)];
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
/// Inside dumping routines, dump one signal, faster when not inlined
|
||||
/// due to code size reduction.
|
||||
void fullBit(vluint32_t code, const vluint32_t newval);
|
||||
void fullBus(vluint32_t code, const vluint32_t newval, int bits);
|
||||
void fullQuad(vluint32_t code, const vluint64_t newval, int bits);
|
||||
void fullArray(vluint32_t code, const vluint32_t* newvalp, int bits);
|
||||
void fullArray(vluint32_t code, const vluint64_t* newvalp, int bits);
|
||||
void fullTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri);
|
||||
void fullTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri, int bits);
|
||||
void fullTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri, int bits);
|
||||
void fullTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip,
|
||||
int bits);
|
||||
void fullDouble(vluint32_t code, const double newval);
|
||||
void fullFloat(vluint32_t code, const float newval);
|
||||
|
||||
|
|
@ -312,34 +240,19 @@ public:
|
|||
/// Presently this code doesn't change the oldval vector.
|
||||
/// Thus this is for special standalone applications that after calling
|
||||
/// fullBitX, must when then value goes non-X call fullBit.
|
||||
inline void fullBitX(vluint32_t code) {
|
||||
*m_writep++='x'; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
inline void fullBusX(vluint32_t code, int bits) {
|
||||
*m_writep++='b';
|
||||
for (int bit=bits-1; bit>=0; --bit) {
|
||||
*m_writep++='x';
|
||||
}
|
||||
*m_writep++=' '; printCode(code); *m_writep++='\n';
|
||||
bufferCheck();
|
||||
}
|
||||
inline void fullQuadX(vluint32_t code, int bits) { fullBusX(code, bits); }
|
||||
inline void fullArrayX(vluint32_t code, int bits) { fullBusX(code, bits); }
|
||||
void fullBitX(vluint32_t code);
|
||||
void fullBusX(vluint32_t code, int bits);
|
||||
void fullQuadX(vluint32_t code, int bits);
|
||||
void fullArrayX(vluint32_t code, int bits);
|
||||
|
||||
/// Inside dumping routines, dump one signal if it has changed
|
||||
/// Inside dumping routines, dump one signal if it has changed.
|
||||
/// We do want to inline these to avoid calls when the value did not change.
|
||||
inline void chgBit(vluint32_t code, const vluint32_t newval) {
|
||||
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below
|
||||
// is only for back compatibility
|
||||
if (VL_UNLIKELY(diff & 1)) { // Change after clean?
|
||||
fullBit(code, newval);
|
||||
}
|
||||
}
|
||||
vluint32_t diff = oldp(code)[0] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) fullBit(code, newval);
|
||||
}
|
||||
inline void chgBus(vluint32_t code, const vluint32_t newval, int bits) {
|
||||
vluint32_t diff = m_sigs_oldvalp[code] ^ newval;
|
||||
vluint32_t diff = oldp(code)[0] ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
|
||||
fullBus(code, newval, bits);
|
||||
|
|
@ -347,33 +260,32 @@ public:
|
|||
}
|
||||
}
|
||||
inline void chgQuad(vluint32_t code, const vluint64_t newval, int bits) {
|
||||
vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval;
|
||||
vluint64_t diff = (*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval;
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 64 || (diff & ((VL_ULL(1) << bits) - 1)))) {
|
||||
fullQuad(code, newval, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgArray(vluint32_t code, const vluint32_t* newval, int bits) {
|
||||
inline void chgArray(vluint32_t code, const vluint32_t* newvalp, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
if (VL_UNLIKELY(m_sigs_oldvalp[code + word] ^ newval[word])) {
|
||||
fullArray(code, newval, bits);
|
||||
if (VL_UNLIKELY(oldp(code)[word] ^ newvalp[word])) {
|
||||
fullArray(code, newvalp, bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgArray(vluint32_t code, const vluint64_t* newval, int bits) {
|
||||
inline void chgArray(vluint32_t code, const vluint64_t* newvalp, int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 64) + 1); ++word) {
|
||||
if (VL_UNLIKELY(m_sigs_oldvalp[code + word] ^ newval[word])) {
|
||||
fullArray(code, newval, bits);
|
||||
if (VL_UNLIKELY(*(reinterpret_cast<vluint64_t*>(oldp(code + 2 * word)))
|
||||
^ newvalp[word])) {
|
||||
fullArray(code, newvalp, bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriBit(vluint32_t code, const vluint32_t newval,
|
||||
const vluint32_t newtri) {
|
||||
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
|
||||
| (m_sigs_oldvalp[code+1] ^ newtri));
|
||||
inline void chgTriBit(vluint32_t code, const vluint32_t newval, const vluint32_t newtri) {
|
||||
vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
// Verilator 3.510 and newer provide clean input, so the below
|
||||
// is only for back compatibility
|
||||
|
|
@ -382,45 +294,44 @@ public:
|
|||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriBus(vluint32_t code, const vluint32_t newval,
|
||||
const vluint32_t newtri, int bits) {
|
||||
vluint32_t diff = ((m_sigs_oldvalp[code] ^ newval)
|
||||
| (m_sigs_oldvalp[code+1] ^ newtri));
|
||||
inline void chgTriBus(vluint32_t code, const vluint32_t newval, const vluint32_t newtri,
|
||||
int bits) {
|
||||
vluint32_t diff = ((oldp(code)[0] ^ newval) | (oldp(code)[1] ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits==32 || (diff & ((1U<<bits)-1) ))) {
|
||||
if (VL_UNLIKELY(bits == 32 || (diff & ((1U << bits) - 1)))) {
|
||||
fullTriBus(code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriQuad(vluint32_t code, const vluint64_t newval,
|
||||
const vluint32_t newtri, int bits) {
|
||||
vluint64_t diff = ( ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code]))) ^ newval)
|
||||
| ((*(reinterpret_cast<vluint64_t*>(&m_sigs_oldvalp[code+1]))) ^ newtri));
|
||||
inline void chgTriQuad(vluint32_t code, const vluint64_t newval, const vluint32_t newtri,
|
||||
int bits) {
|
||||
vluint64_t diff = (((*(reinterpret_cast<vluint64_t*>(oldp(code)))) ^ newval)
|
||||
| ((*(reinterpret_cast<vluint64_t*>(oldp(code + 1)))) ^ newtri));
|
||||
if (VL_UNLIKELY(diff)) {
|
||||
if (VL_UNLIKELY(bits == 64 || (diff & ((VL_ULL(1) << bits) - 1)))) {
|
||||
fullTriQuad(code, newval, newtri, bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgTriArray(vluint32_t code, const vluint32_t* newvalp,
|
||||
const vluint32_t* newtrip, int bits) {
|
||||
for (int word=0; word<(((bits-1)/32)+1); ++word) {
|
||||
if (VL_UNLIKELY((m_sigs_oldvalp[code+word*2] ^ newvalp[word])
|
||||
| (m_sigs_oldvalp[code+word*2+1] ^ newtrip[word]))) {
|
||||
fullTriArray(code,newvalp,newtrip,bits);
|
||||
inline void chgTriArray(vluint32_t code, const vluint32_t* newvalp, const vluint32_t* newtrip,
|
||||
int bits) {
|
||||
for (int word = 0; word < (((bits - 1) / 32) + 1); ++word) {
|
||||
if (VL_UNLIKELY((oldp(code)[word * 2] ^ newvalp[word])
|
||||
| (oldp(code)[word * 2 + 1] ^ newtrip[word]))) {
|
||||
fullTriArray(code, newvalp, newtrip, bits);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void chgDouble(vluint32_t code, const double newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<double*>(&m_sigs_oldvalp[code]))) != newval)) {
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<double*>(oldp(code)))) != newval)) {
|
||||
fullDouble(code, newval);
|
||||
}
|
||||
}
|
||||
inline void chgFloat(vluint32_t code, const float newval) {
|
||||
// cppcheck-suppress invalidPointerCast
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<float*>(&m_sigs_oldvalp[code]))) != newval)) {
|
||||
if (VL_UNLIKELY((*(reinterpret_cast<float*>(oldp(code)))) != newval)) {
|
||||
fullFloat(code, newval);
|
||||
}
|
||||
}
|
||||
|
|
@ -428,8 +339,16 @@ public:
|
|||
protected:
|
||||
// METHODS
|
||||
void evcd(bool flag) { m_evcd = flag; }
|
||||
#endif // VL_TRACE_VCD_OLD_API
|
||||
};
|
||||
|
||||
// Declare specializations here they are used in VerilatedVcdC just below
|
||||
template <> void VerilatedTrace<VerilatedVcd>::dump(vluint64_t timeui);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_unit(const std::string& unit);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const char* unitp);
|
||||
template <> void VerilatedTrace<VerilatedVcd>::set_time_resolution(const std::string& unit);
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedVcdC
|
||||
/// Create a VCD dump file in C standalone (no SystemC) simulations.
|
||||
|
|
@ -441,12 +360,14 @@ class VerilatedVcdC {
|
|||
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcdC);
|
||||
|
||||
public:
|
||||
explicit VerilatedVcdC(VerilatedVcdFile* filep = NULL)
|
||||
: m_sptrace(filep) {}
|
||||
~VerilatedVcdC() { close(); }
|
||||
/// Routines can only be called from one thread; allow next call from different thread
|
||||
void changeThread() { spTrace()->changeThread(); }
|
||||
|
||||
public:
|
||||
// ACCESSORS
|
||||
/// Is file open?
|
||||
|
|
@ -474,13 +395,13 @@ public:
|
|||
void dump(vluint32_t timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
void dump(int timestamp) { dump(static_cast<vluint64_t>(timestamp)); }
|
||||
/// Set time units (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
/// For Verilated models, these propage from the Verilated default --timeunit
|
||||
void set_time_unit(const char* unit) { m_sptrace.set_time_unit(unit); }
|
||||
void set_time_unit(const std::string& unit) { set_time_unit(unit.c_str()); }
|
||||
void set_time_unit(const std::string& unit) { m_sptrace.set_time_unit(unit); }
|
||||
/// Set time resolution (s/ms, defaults to ns)
|
||||
/// See also VL_TIME_PRECISION, and VL_TIME_MULTIPLIER in verilated.h
|
||||
/// For Verilated models, these propage from the Verilated default --timeunit
|
||||
void set_time_resolution(const char* unit) { m_sptrace.set_time_resolution(unit); }
|
||||
void set_time_resolution(const std::string& unit) { set_time_resolution(unit.c_str()); }
|
||||
void set_time_resolution(const std::string& unit) { m_sptrace.set_time_resolution(unit); }
|
||||
|
||||
/// Internal class access
|
||||
inline VerilatedVcd* spTrace() { return &m_sptrace; }
|
||||
|
|
|
|||
|
|
@ -25,21 +25,22 @@
|
|||
//======================================================================
|
||||
|
||||
//--------------------------------------------------
|
||||
#if (SYSTEMC_VERSION>=20050714)
|
||||
// SystemC 2.1.v1
|
||||
#if (SYSTEMC_VERSION >= 20050714)
|
||||
// SystemC 2.1.v1
|
||||
// cppcheck-suppress unusedFunction
|
||||
void VerilatedVcdSc::write_comment(const std::string &) {}
|
||||
void VerilatedVcdSc::trace(const unsigned int &, const std::string &, const char**) {}
|
||||
void VerilatedVcdSc::write_comment(const std::string&) {}
|
||||
void VerilatedVcdSc::trace(const unsigned int&, const std::string&, const char**) {}
|
||||
|
||||
// clang-format off
|
||||
# define DECL_TRACE_METHOD_A(tp) \
|
||||
void VerilatedVcdSc::trace( const tp& object, const std::string& name ) {}
|
||||
void VerilatedVcdSc::trace(const tp& object, const std::string& name) {}
|
||||
# define DECL_TRACE_METHOD_B(tp) \
|
||||
void VerilatedVcdSc::trace( const tp& object, const std::string& name, int width ) {}
|
||||
void VerilatedVcdSc::trace(const tp& object, const std::string& name, int width) {}
|
||||
|
||||
#if (SYSTEMC_VERSION>=20171012)
|
||||
# if (SYSTEMC_VERSION >= 20171012)
|
||||
DECL_TRACE_METHOD_A( sc_event )
|
||||
DECL_TRACE_METHOD_A( sc_time )
|
||||
#endif
|
||||
# endif
|
||||
|
||||
DECL_TRACE_METHOD_A( bool )
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_bit )
|
||||
|
|
@ -49,9 +50,9 @@ 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
|
||||
# ifdef SYSTEMC_64BIT_PATCHES
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
#endif
|
||||
# endif
|
||||
DECL_TRACE_METHOD_B( char )
|
||||
DECL_TRACE_METHOD_B( short )
|
||||
DECL_TRACE_METHOD_B( int )
|
||||
|
|
@ -73,19 +74,21 @@ void VerilatedVcdSc::trace(const unsigned int &, const std::string &, const char
|
|||
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_bv_base )
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_lv_base )
|
||||
// clang-format on
|
||||
|
||||
//--------------------------------------------------
|
||||
#elif (SYSTEMC_VERSION>20011000)
|
||||
// SystemC 2.0.1
|
||||
#elif (SYSTEMC_VERSION > 20011000)
|
||||
// SystemC 2.0.1
|
||||
// cppcheck-suppress unusedFunction
|
||||
void VerilatedVcdSc::write_comment(const sc_string &) {}
|
||||
void VerilatedVcdSc::write_comment(const sc_string&) {}
|
||||
void VerilatedVcdSc::trace(const unsigned int&, const sc_string&, const char**) {}
|
||||
|
||||
#define DECL_TRACE_METHOD_A(tp) \
|
||||
void VerilatedVcdSc::trace( const tp& object, const sc_string& name ) {}
|
||||
void VerilatedVcdSc::trace(const tp& object, const sc_string& name) {}
|
||||
#define DECL_TRACE_METHOD_B(tp) \
|
||||
void VerilatedVcdSc::trace( const tp& object, const sc_string& name, int width ) {}
|
||||
void VerilatedVcdSc::trace(const tp& object, const sc_string& name, int width) {}
|
||||
|
||||
// clang-format off
|
||||
DECL_TRACE_METHOD_A( bool )
|
||||
DECL_TRACE_METHOD_A( sc_bit )
|
||||
DECL_TRACE_METHOD_A( sc_logic )
|
||||
|
|
@ -96,7 +99,7 @@ void VerilatedVcdSc::trace(const unsigned int&, const sc_string&, const char**)
|
|||
#ifdef SYSTEMC_64BIT_PATCHES
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
#endif
|
||||
#if (SYSTEMC_VERSION>20041000)
|
||||
#if (SYSTEMC_VERSION > 20041000)
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
DECL_TRACE_METHOD_B( long long)
|
||||
#endif
|
||||
|
|
@ -116,19 +119,21 @@ void VerilatedVcdSc::trace(const unsigned int&, const sc_string&, const char**)
|
|||
DECL_TRACE_METHOD_A( sc_fxnum_fast )
|
||||
DECL_TRACE_METHOD_A( sc_bv_base )
|
||||
DECL_TRACE_METHOD_A( sc_lv_base )
|
||||
// clang-format on
|
||||
|
||||
//--------------------------------------------------
|
||||
#else
|
||||
// SystemC 1.2.1beta
|
||||
// SystemC 1.2.1beta
|
||||
// cppcheck-suppress unusedFunction
|
||||
void VerilatedVcdSc::write_comment(const sc_string &) {}
|
||||
void VerilatedVcdSc::write_comment(const sc_string&) {}
|
||||
void VerilatedVcdSc::trace(const unsigned int&, const sc_string&, const char**) {}
|
||||
|
||||
#define DECL_TRACE_METHOD_A(tp) \
|
||||
void VerilatedVcdSc::trace( const tp& object, const sc_string& name ) {}
|
||||
void VerilatedVcdSc::trace(const tp& object, const sc_string& name) {}
|
||||
#define DECL_TRACE_METHOD_B(tp) \
|
||||
void VerilatedVcdSc::trace( const tp& object, const sc_string& name, int width ) {}
|
||||
void VerilatedVcdSc::trace(const tp& object, const sc_string& name, int width) {}
|
||||
|
||||
// clang-format off
|
||||
DECL_TRACE_METHOD_A( bool )
|
||||
DECL_TRACE_METHOD_B( unsigned char )
|
||||
DECL_TRACE_METHOD_B( short unsigned int )
|
||||
|
|
@ -154,6 +159,7 @@ void VerilatedVcdSc::trace(const unsigned int&, const sc_string&, const char**)
|
|||
DECL_TRACE_METHOD_A( sc_signal_resolved_vector )
|
||||
DECL_TRACE_METHOD_A( sc_bv_ns::sc_bv_base )
|
||||
DECL_TRACE_METHOD_A( sc_bv_ns::sc_lv_base )
|
||||
// clang-format on
|
||||
#endif
|
||||
|
||||
#undef DECL_TRACE_METHOD_A
|
||||
|
|
|
|||
|
|
@ -31,44 +31,42 @@
|
|||
/// This class is passed to the SystemC simulation kernel, just like a
|
||||
/// documented SystemC trace format.
|
||||
|
||||
class VerilatedVcdSc
|
||||
: sc_trace_file
|
||||
, public VerilatedVcdC
|
||||
{
|
||||
class VerilatedVcdSc : sc_trace_file, public VerilatedVcdC {
|
||||
// CONSTRUCTORS
|
||||
VL_UNCOPYABLE(VerilatedVcdSc);
|
||||
|
||||
public:
|
||||
VerilatedVcdSc() {
|
||||
sc_get_curr_simcontext()->add_trace_file(this);
|
||||
# if (SYSTEMC_VERSION>=20060505)
|
||||
#if (SYSTEMC_VERSION >= 20060505)
|
||||
// We want to avoid a depreciated warning, but still be back compatible.
|
||||
// Turning off the message just for this still results in an
|
||||
// annoying "to turn off" message.
|
||||
sc_time t1sec(1,SC_SEC);
|
||||
if (t1sec.to_default_time_units()!=0) {
|
||||
sc_time tunits(1.0/t1sec.to_default_time_units(),SC_SEC);
|
||||
sc_time t1sec(1, SC_SEC);
|
||||
if (t1sec.to_default_time_units() != 0) {
|
||||
sc_time tunits(1.0 / t1sec.to_default_time_units(), SC_SEC);
|
||||
spTrace()->set_time_unit(tunits.to_string());
|
||||
}
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
# elif (SYSTEMC_VERSION>20011000)
|
||||
#elif (SYSTEMC_VERSION > 20011000)
|
||||
// To confuse matters 2.1.beta returns a char* here, while 2.1.v1 returns a std::string
|
||||
// we allow both flavors with overloaded set_time_* functions.
|
||||
spTrace()->set_time_unit(sc_get_default_time_unit().to_string());
|
||||
spTrace()->set_time_resolution(sc_get_time_resolution().to_string());
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
virtual ~VerilatedVcdSc() { close(); }
|
||||
|
||||
// METHODS
|
||||
/// Called by SystemC simulate()
|
||||
virtual void cycle(bool delta_cycle) {
|
||||
# if (SYSTEMC_VERSION>20011000)
|
||||
#if (SYSTEMC_VERSION > 20011000)
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
# else
|
||||
#else
|
||||
// VCD files must have integer timestamps, so we write all times in
|
||||
// increments of time_resolution
|
||||
if (!delta_cycle) { this->dump(sc_time_stamp().to_double()); }
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -78,26 +76,26 @@ private:
|
|||
// Cadence Incisive has these as abstract functions so we must create them
|
||||
virtual void set_time_unit(int exponent10_seconds) {} // deprecated
|
||||
#endif
|
||||
#if defined(NC_SYSTEMC) || (SYSTEMC_VERSION>=20111100)
|
||||
#if defined(NC_SYSTEMC) || (SYSTEMC_VERSION >= 20111100)
|
||||
virtual void set_time_unit(double v, sc_time_unit tu) {}
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
# if (SYSTEMC_VERSION>=20050714)
|
||||
#if (SYSTEMC_VERSION >= 20050714)
|
||||
// SystemC 2.1.v1
|
||||
# define DECL_TRACE_METHOD_A(tp) \
|
||||
virtual void trace(const tp& object, const std::string& name);
|
||||
// clang-format off
|
||||
# define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const std::string& name);
|
||||
# define DECL_TRACE_METHOD_B(tp) \
|
||||
virtual void trace(const tp& object, const std::string& name, int width);
|
||||
|
||||
virtual void write_comment(const std::string&);
|
||||
virtual void trace(const unsigned int&, const std::string&, const char**);
|
||||
|
||||
#if (SYSTEMC_VERSION>=20171012)
|
||||
// Formatting matches that of sc_trace.h
|
||||
# if (SYSTEMC_VERSION >= 20171012)
|
||||
DECL_TRACE_METHOD_A( sc_event )
|
||||
DECL_TRACE_METHOD_A( sc_time )
|
||||
#endif
|
||||
# endif
|
||||
|
||||
DECL_TRACE_METHOD_A( bool )
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_bit )
|
||||
|
|
@ -107,9 +105,9 @@ private:
|
|||
DECL_TRACE_METHOD_B( unsigned short )
|
||||
DECL_TRACE_METHOD_B( unsigned int )
|
||||
DECL_TRACE_METHOD_B( unsigned long )
|
||||
#ifdef SYSTEMC_64BIT_PATCHES
|
||||
# ifdef SYSTEMC_64BIT_PATCHES
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
#endif
|
||||
# endif
|
||||
DECL_TRACE_METHOD_B( char )
|
||||
DECL_TRACE_METHOD_B( short )
|
||||
DECL_TRACE_METHOD_B( int )
|
||||
|
|
@ -131,12 +129,13 @@ private:
|
|||
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_bv_base )
|
||||
DECL_TRACE_METHOD_A( sc_dt::sc_lv_base )
|
||||
// clang-format on
|
||||
|
||||
//--------------------------------------------------
|
||||
# elif (SYSTEMC_VERSION>20011000)
|
||||
#elif (SYSTEMC_VERSION > 20011000)
|
||||
// SystemC 2.0.1
|
||||
# define DECL_TRACE_METHOD_A(tp) \
|
||||
virtual void trace(const tp& object, const sc_string& name);
|
||||
// clang-format off
|
||||
# define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const sc_string& name);
|
||||
# define DECL_TRACE_METHOD_B(tp) \
|
||||
virtual void trace(const tp& object, const sc_string& name, int width);
|
||||
|
||||
|
|
@ -152,13 +151,13 @@ private:
|
|||
DECL_TRACE_METHOD_B( unsigned short )
|
||||
DECL_TRACE_METHOD_B( unsigned int )
|
||||
DECL_TRACE_METHOD_B( unsigned long )
|
||||
#ifdef SYSTEMC_64BIT_PATCHES
|
||||
# ifdef SYSTEMC_64BIT_PATCHES
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
#endif
|
||||
#if (SYSTEMC_VERSION>20041000)
|
||||
# endif
|
||||
# if (SYSTEMC_VERSION > 20041000)
|
||||
DECL_TRACE_METHOD_B( unsigned long long)
|
||||
DECL_TRACE_METHOD_B( long long)
|
||||
#endif
|
||||
# endif
|
||||
DECL_TRACE_METHOD_B( char )
|
||||
DECL_TRACE_METHOD_B( short )
|
||||
DECL_TRACE_METHOD_B( int )
|
||||
|
|
@ -175,12 +174,13 @@ private:
|
|||
DECL_TRACE_METHOD_A( sc_fxnum_fast )
|
||||
DECL_TRACE_METHOD_A( sc_bv_base )
|
||||
DECL_TRACE_METHOD_A( sc_lv_base )
|
||||
// clang-format on
|
||||
|
||||
//--------------------------------------------------
|
||||
# else
|
||||
#else
|
||||
// SystemC 1.2.1beta
|
||||
# define DECL_TRACE_METHOD_A(tp) \
|
||||
virtual void trace(const tp& object, const sc_string& name);
|
||||
// clang-format off
|
||||
# define DECL_TRACE_METHOD_A(tp) virtual void trace(const tp& object, const sc_string& name);
|
||||
# define DECL_TRACE_METHOD_B(tp) \
|
||||
virtual void trace(const tp& object, const sc_string& name, int width);
|
||||
|
||||
|
|
@ -213,9 +213,10 @@ private:
|
|||
DECL_TRACE_METHOD_A( sc_bv_ns::sc_bv_base )
|
||||
DECL_TRACE_METHOD_A( sc_bv_ns::sc_lv_base )
|
||||
# endif
|
||||
// clang-format on
|
||||
|
||||
# undef DECL_TRACE_METHOD_A
|
||||
# undef DECL_TRACE_METHOD_B
|
||||
#undef DECL_TRACE_METHOD_A
|
||||
#undef DECL_TRACE_METHOD_B
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -22,99 +22,100 @@
|
|||
///
|
||||
//*************************************************************************
|
||||
|
||||
|
||||
#ifndef _VERILATEDOS_H_
|
||||
#define _VERILATEDOS_H_ 1 ///< Header Guard
|
||||
|
||||
// Current clang-format versions botch #ifdef inclusion, so
|
||||
// clang-format off
|
||||
//=========================================================================
|
||||
// Compiler pragma abstraction
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define VL_ATTR_ALIGNED(alignment) __attribute__ ((aligned (alignment)))
|
||||
# define VL_ATTR_ALWINLINE __attribute__ ((always_inline))
|
||||
# define VL_ATTR_COLD __attribute__ ((cold))
|
||||
# define VL_ATTR_HOT __attribute__ ((hot))
|
||||
# define VL_ATTR_NORETURN __attribute__ ((noreturn))
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__ ((format (printf, (fmtArgNum), (fmtArgNum)+1)))
|
||||
# define VL_ATTR_PURE __attribute__ ((pure))
|
||||
# define VL_ATTR_UNUSED __attribute__ ((unused))
|
||||
# define VL_FUNC __func__
|
||||
# define VL_ATTR_ALIGNED(alignment) __attribute__((aligned(alignment)))
|
||||
# define VL_ATTR_ALWINLINE __attribute__((always_inline))
|
||||
# define VL_ATTR_COLD __attribute__((cold))
|
||||
# define VL_ATTR_HOT __attribute__((hot))
|
||||
# define VL_ATTR_NORETURN __attribute__((noreturn))
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) __attribute__((format(printf, (fmtArgNum), (fmtArgNum) + 1)))
|
||||
# define VL_ATTR_PURE __attribute__((pure))
|
||||
# define VL_ATTR_UNUSED __attribute__((unused))
|
||||
# define VL_FUNC __func__
|
||||
# if defined(__clang__) && defined(VL_THREADED)
|
||||
# define VL_ACQUIRE(...) __attribute__ ((acquire_capability(__VA_ARGS__)))
|
||||
# define VL_ACQUIRE_SHARED(...) __attribute__ ((acquire_shared_capability(__VA_ARGS__)))
|
||||
# define VL_RELEASE(...) __attribute__ ((release_capability(__VA_ARGS__)))
|
||||
# define VL_RELEASE_SHARED(...) __attribute__ ((release_shared_capability(__VA_ARGS__)))
|
||||
# define VL_TRY_ACQUIRE(...) __attribute__ ((try_acquire_capability(__VA_ARGS__)))
|
||||
# define VL_TRY_ACQUIRE_SHARED(...) __attribute__ ((try_acquire_shared_capability(__VA_ARGS__)))
|
||||
# define VL_CAPABILITY(x) __attribute__ ((capability(x)))
|
||||
# define VL_REQUIRES(x) __attribute__ ((requires_capability(x)))
|
||||
# define VL_GUARDED_BY(x) __attribute__ ((guarded_by(x)))
|
||||
# define VL_EXCLUDES(x) __attribute__ ((locks_excluded(x)))
|
||||
# define VL_SCOPED_CAPABILITY __attribute__ ((scoped_lockable))
|
||||
# define VL_ACQUIRE(...) __attribute__((acquire_capability(__VA_ARGS__)))
|
||||
# define VL_ACQUIRE_SHARED(...) __attribute__((acquire_shared_capability(__VA_ARGS__)))
|
||||
# define VL_RELEASE(...) __attribute__((release_capability(__VA_ARGS__)))
|
||||
# define VL_RELEASE_SHARED(...) __attribute__((release_shared_capability(__VA_ARGS__)))
|
||||
# define VL_TRY_ACQUIRE(...) __attribute__((try_acquire_capability(__VA_ARGS__)))
|
||||
# define VL_TRY_ACQUIRE_SHARED(...) __attribute__((try_acquire_shared_capability(__VA_ARGS__)))
|
||||
# define VL_CAPABILITY(x) __attribute__((capability(x)))
|
||||
# define VL_REQUIRES(x) __attribute__((requires_capability(x)))
|
||||
# define VL_GUARDED_BY(x) __attribute__((guarded_by(x)))
|
||||
# define VL_EXCLUDES(x) __attribute__((locks_excluded(x)))
|
||||
# define VL_SCOPED_CAPABILITY __attribute__((scoped_lockable))
|
||||
# endif
|
||||
# define VL_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
# define VL_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
# define VL_UNREACHABLE __builtin_unreachable();
|
||||
# define VL_PREFETCH_RD(p) __builtin_prefetch((p),0)
|
||||
# define VL_PREFETCH_RW(p) __builtin_prefetch((p),1)
|
||||
# define VL_PREFETCH_RD(p) __builtin_prefetch((p), 0)
|
||||
# define VL_PREFETCH_RW(p) __builtin_prefetch((p), 1)
|
||||
#elif defined(_MSC_VER)
|
||||
# define VL_FUNC __FUNCTION__
|
||||
# define VL_FUNC __FUNCTION__
|
||||
#endif
|
||||
|
||||
// Defaults for unsupported compiler features
|
||||
#ifndef VL_ATTR_ALIGNED
|
||||
# define VL_ATTR_ALIGNED(alignment) ///< Align structure to specified byte alignment
|
||||
# define VL_ATTR_ALIGNED(alignment) ///< Align structure to specified byte alignment
|
||||
#endif
|
||||
#ifndef VL_ATTR_ALWINLINE
|
||||
# define VL_ATTR_ALWINLINE ///< Inline, even when not optimizing
|
||||
# define VL_ATTR_ALWINLINE ///< Inline, even when not optimizing
|
||||
#endif
|
||||
#ifndef VL_ATTR_COLD
|
||||
# define VL_ATTR_COLD ///< Function is rarely executed
|
||||
# define VL_ATTR_COLD ///< Function is rarely executed
|
||||
#endif
|
||||
#ifndef VL_ATTR_HOT
|
||||
# define VL_ATTR_HOT ///< Function is highly executed
|
||||
# define VL_ATTR_HOT ///< Function is highly executed
|
||||
#endif
|
||||
#ifndef VL_ATTR_NORETURN
|
||||
# define VL_ATTR_NORETURN ///< Function does not ever return
|
||||
# define VL_ATTR_NORETURN ///< Function does not ever return
|
||||
#endif
|
||||
#ifndef VL_ATTR_PRINTF
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) ///< Function with printf format checking
|
||||
# define VL_ATTR_PRINTF(fmtArgNum) ///< Function with printf format checking
|
||||
#endif
|
||||
#ifndef VL_ATTR_PURE
|
||||
# define VL_ATTR_PURE ///< Function is pure (and thus also VL_MT_SAFE)
|
||||
# define VL_ATTR_PURE ///< Function is pure (and thus also VL_MT_SAFE)
|
||||
#endif
|
||||
#ifndef VL_ATTR_UNUSED
|
||||
# define VL_ATTR_UNUSED ///< Function that may be never used
|
||||
# define VL_ATTR_UNUSED ///< Function that may be never used
|
||||
#endif
|
||||
#ifndef VL_FUNC
|
||||
# define VL_FUNC "__func__" ///< Name of current function for error macros
|
||||
# define VL_FUNC "__func__" ///< Name of current function for error macros
|
||||
#endif
|
||||
#ifndef VL_CAPABILITY
|
||||
# define VL_ACQUIRE(...) ///< Function requires a capability/lock (-fthread-safety)
|
||||
# define VL_ACQUIRE_SHARED(...) ///< Function aquires a shared capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE(...) ///< Function releases a capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE_SHARED(...) ///< Function releases a shared capability/lock (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE(...) ///< Function returns bool if aquired a capability (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE_SHARED(...) ///< Function returns bool if aquired a shared capability (-fthread-safety)
|
||||
# define VL_REQUIRES(x) ///< Function requires a capability inbound (-fthread-safety)
|
||||
# define VL_EXCLUDES(x) ///< Function requires not having a capability inbound (-fthread-safety)
|
||||
# define VL_CAPABILITY(x) ///< Name of capability/lock (-fthread-safety)
|
||||
# define VL_GUARDED_BY(x) ///< Name of mutex protecting this variable (-fthread-safety)
|
||||
# define VL_SCOPED_CAPABILITY ///< Scoped threaded capability/lock (-fthread-safety)
|
||||
# define VL_ACQUIRE(...) ///< Function requires a capability/lock (-fthread-safety)
|
||||
# define VL_ACQUIRE_SHARED(...) ///< Function aquires a shared capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE(...) ///< Function releases a capability/lock (-fthread-safety)
|
||||
# define VL_RELEASE_SHARED(...) ///< Function releases a shared capability/lock (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE(...) ///< Function returns bool if aquired a capability (-fthread-safety)
|
||||
# define VL_TRY_ACQUIRE_SHARED(...) ///< Function returns bool if aquired shared (-fthread-safety)
|
||||
# define VL_REQUIRES(x) ///< Function requires a capability inbound (-fthread-safety)
|
||||
# define VL_EXCLUDES(x) ///< Function requires not having a capability inbound (-fthread-safety)
|
||||
# define VL_CAPABILITY(x) ///< Name of capability/lock (-fthread-safety)
|
||||
# define VL_GUARDED_BY(x) ///< Name of mutex protecting this variable (-fthread-safety)
|
||||
# define VL_SCOPED_CAPABILITY ///< Scoped threaded capability/lock (-fthread-safety)
|
||||
#endif
|
||||
#ifndef VL_LIKELY
|
||||
# define VL_LIKELY(x) (!!(x)) ///< Boolean expression more often true than false
|
||||
# define VL_UNLIKELY(x) (!!(x)) ///< Boolean expression more often false than true
|
||||
# define VL_LIKELY(x) (!!(x)) ///< Boolean expression more often true than false
|
||||
# define VL_UNLIKELY(x) (!!(x)) ///< Boolean expression more often false than true
|
||||
#endif
|
||||
#define VL_UNCOVERABLE(x) VL_UNLIKELY(x) ///< Boolean expression never hit by users (no coverage)
|
||||
# define VL_UNCOVERABLE(x) VL_UNLIKELY(x) ///< Boolean expression never hit by users (no coverage)
|
||||
#ifndef VL_UNREACHABLE
|
||||
# define VL_UNREACHABLE ///< Point that may never be reached
|
||||
# define VL_UNREACHABLE ///< Point that may never be reached
|
||||
#endif
|
||||
#ifndef VL_PREFETCH_RD
|
||||
# define VL_PREFETCH_RD(p) ///< Prefetch data with read intent
|
||||
# define VL_PREFETCH_RD(p) ///< Prefetch data with read intent
|
||||
#endif
|
||||
#ifndef VL_PREFETCH_RW
|
||||
# define VL_PREFETCH_RW(p) ///< Prefetch data with read/write intent
|
||||
# define VL_PREFETCH_RW(p) ///< Prefetch data with read/write intent
|
||||
#endif
|
||||
|
||||
#ifdef VL_THREADED
|
||||
|
|
@ -122,28 +123,34 @@
|
|||
# define VL_THREAD_LOCAL thread_local
|
||||
# elif defined(__GNUC__)
|
||||
# if (__cplusplus < 201103L) && !defined(VL_THREADED_NO_C11_WARNING)
|
||||
# error "VL_THREADED/--threads support requires C++-11 or newer only; use newer compiler"
|
||||
# error "VL_THREADED/--threads support requires C++-11 or newer only; use newer compiler"
|
||||
# endif
|
||||
# else
|
||||
# error "Unsupported compiler for VL_THREADED: No thread-local declarator"
|
||||
# endif
|
||||
# define VL_THREAD_LOCAL thread_local ///< Use new C++ static local thread
|
||||
# define VL_THREAD_LOCAL thread_local ///< Use new C++ static local thread
|
||||
#else
|
||||
# define VL_THREAD_LOCAL ///< Use new C++ static local thread
|
||||
# define VL_THREAD_LOCAL ///< Use new C++ static local thread
|
||||
#endif
|
||||
#define VL_THREAD ///< Deprecated
|
||||
#define VL_STATIC_OR_THREAD static ///< Deprecated
|
||||
|
||||
#define VL_THREAD ///< Deprecated
|
||||
#define VL_STATIC_OR_THREAD static ///< Deprecated
|
||||
|
||||
#define VL_PURE ///< Comment tag that Function is pure (and thus also VL_MT_SAFE)
|
||||
#define VL_MT_SAFE ///< Comment tag that function is threadsafe when VL_THREADED
|
||||
#define VL_MT_SAFE_POSTINIT ///< Comment tag that function is threadsafe when VL_THREADED, only during normal operation (post-init)
|
||||
#define VL_MT_SAFE_POSTINIT ///< Comment tag that function is threadsafe when VL_THREADED, only
|
||||
///< during normal operation (post-init)
|
||||
#define VL_MT_UNSAFE ///< Comment tag that function is not threadsafe when VL_THREADED
|
||||
#define VL_MT_UNSAFE_ONE ///< Comment tag that function is not threadsafe when VL_THREADED, protected to make sure single-caller
|
||||
#define VL_MT_UNSAFE_ONE ///< Comment tag that function is not threadsafe when VL_THREADED,
|
||||
///< protected to make sure single-caller
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define VL_ULL(c) (c##ui64) ///< Add appropriate suffix to 64-bit constant
|
||||
# define VL_ULL(c) (c##ULL) ///< Add appropriate suffix to 64-bit constant
|
||||
// Was "(c##ui64)". C++11 has standardized on ULL, and MSVC now supports this.
|
||||
// We propose to no longer require using this macro no sooner than June 2020.
|
||||
// File an issue ASAP if this breaks anything.
|
||||
#else
|
||||
# define VL_ULL(c) (c##ULL) ///< Add appropriate suffix to 64-bit constant
|
||||
# define VL_ULL(c) (c##ULL) ///< Add appropriate suffix to 64-bit constant
|
||||
#endif
|
||||
|
||||
// This is not necessarily the same as #UL, depending on what the IData typedef is.
|
||||
|
|
@ -198,6 +205,7 @@
|
|||
# define VL_INCLUDE_UNORDERED_SET <unordered_set>
|
||||
# endif
|
||||
# define VL_FINAL final
|
||||
# define VL_MUTABLE mutable
|
||||
# define VL_OVERRIDE override
|
||||
#else
|
||||
# define VL_EQ_DELETE
|
||||
|
|
@ -205,6 +213,7 @@
|
|||
# define VL_INCLUDE_UNORDERED_MAP "verilated_unordered_set_map.h"
|
||||
# define VL_INCLUDE_UNORDERED_SET "verilated_unordered_set_map.h"
|
||||
# define VL_FINAL
|
||||
# define VL_MUTABLE
|
||||
# define VL_OVERRIDE
|
||||
#endif
|
||||
|
||||
|
|
@ -247,47 +256,47 @@
|
|||
# include <stdint.h>
|
||||
# include <sys/types.h> // __WORDSIZE
|
||||
# include <unistd.h> // ssize_t
|
||||
typedef unsigned char uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
typedef unsigned short int uint16_t; ///< 16-bit unsigned type (backward compatibility)
|
||||
typedef char vlsint8_t; ///< 8-bit signed type
|
||||
typedef unsigned char vluint8_t; ///< 8-bit unsigned type
|
||||
typedef short int vlsint16_t; ///< 16-bit signed type
|
||||
typedef unsigned short int vluint16_t; ///< 16-bit unsigned type
|
||||
typedef unsigned char uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
typedef unsigned short int uint16_t; ///< 16-bit unsigned type (backward compatibility)
|
||||
typedef char vlsint8_t; ///< 8-bit signed type
|
||||
typedef unsigned char vluint8_t; ///< 8-bit unsigned type
|
||||
typedef short int vlsint16_t; ///< 16-bit signed type
|
||||
typedef unsigned short int vluint16_t; ///< 16-bit unsigned type
|
||||
# if defined(__uint32_t_defined) || defined(___int32_t_defined) // Newer Cygwin uint32_t in stdint.h as an unsigned int
|
||||
typedef int32_t vlsint32_t; ///< 32-bit signed type
|
||||
typedef uint32_t vluint32_t; ///< 32-bit unsigned type
|
||||
typedef int32_t vlsint32_t; ///< 32-bit signed type
|
||||
typedef uint32_t vluint32_t; ///< 32-bit unsigned type
|
||||
# else // Older Cygwin has long==uint32_t
|
||||
typedef unsigned long uint32_t; ///< 32-bit unsigned type (backward compatibility)
|
||||
typedef long vlsint32_t; ///< 32-bit signed type
|
||||
typedef unsigned long vluint32_t; ///< 32-bit unsigned type
|
||||
typedef unsigned long uint32_t; ///< 32-bit unsigned type (backward compatibility)
|
||||
typedef long vlsint32_t; ///< 32-bit signed type
|
||||
typedef unsigned long vluint32_t; ///< 32-bit unsigned type
|
||||
# endif
|
||||
# if defined(__WORDSIZE) && (__WORDSIZE == 64)
|
||||
typedef long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long vluint64_t; ///< 64-bit unsigned type
|
||||
typedef long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long vluint64_t; ///< 64-bit unsigned type
|
||||
# else
|
||||
typedef long long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
typedef long long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
# endif
|
||||
|
||||
#elif defined(_WIN32) && defined(_MSC_VER)
|
||||
|
||||
typedef unsigned __int8 uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
typedef unsigned __int16 uint16_t; ///< 16-bit unsigned type (backward compatibility)
|
||||
typedef unsigned __int32 uint32_t; ///< 32-bit unsigned type (backward compatibility)
|
||||
typedef signed __int8 vlsint8_t; ///< 8-bit signed type
|
||||
typedef unsigned __int8 vluint8_t; ///< 8-bit unsigned type
|
||||
typedef signed __int16 vlsint16_t; ///< 16-bit signed type
|
||||
typedef unsigned __int16 vluint16_t; ///< 16-bit unsigned type
|
||||
typedef signed __int32 vlsint32_t; ///< 32-bit signed type
|
||||
typedef unsigned __int32 vluint32_t; ///< 32-bit unsigned type
|
||||
typedef signed __int64 vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned __int64 vluint64_t; ///< 64-bit unsigned type
|
||||
typedef unsigned __int8 uint8_t; ///< 8-bit unsigned type (backward compatibility)
|
||||
typedef unsigned __int16 uint16_t; ///< 16-bit unsigned type (backward compatibility)
|
||||
typedef unsigned __int32 uint32_t; ///< 32-bit unsigned type (backward compatibility)
|
||||
typedef signed __int8 vlsint8_t; ///< 8-bit signed type
|
||||
typedef unsigned __int8 vluint8_t; ///< 8-bit unsigned type
|
||||
typedef signed __int16 vlsint16_t; ///< 16-bit signed type
|
||||
typedef unsigned __int16 vluint16_t; ///< 16-bit unsigned type
|
||||
typedef signed __int32 vlsint32_t; ///< 32-bit signed type
|
||||
typedef unsigned __int32 vluint32_t; ///< 32-bit unsigned type
|
||||
typedef signed __int64 vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned __int64 vluint64_t; ///< 64-bit unsigned type
|
||||
|
||||
# ifndef _SSIZE_T_DEFINED
|
||||
# ifdef _WIN64
|
||||
typedef signed __int64 ssize_t; ///< signed size_t; returned from read()
|
||||
# ifdef _WIN64
|
||||
typedef signed __int64 ssize_t; ///< signed size_t; returned from read()
|
||||
# else
|
||||
typedef signed __int32 ssize_t; ///< signed size_t; returned from read()
|
||||
typedef signed __int32 ssize_t; ///< signed size_t; returned from read()
|
||||
# endif
|
||||
# endif
|
||||
|
||||
|
|
@ -297,18 +306,18 @@ typedef signed __int32 ssize_t; ///< signed size_t; returned fro
|
|||
# include <stdint.h> // Linux and most flavors
|
||||
# include <sys/types.h> // __WORDSIZE
|
||||
# include <unistd.h> // ssize_t
|
||||
typedef char vlsint8_t; ///< 8-bit signed type
|
||||
typedef uint8_t vluint8_t; ///< 8-bit unsigned type
|
||||
typedef short vlsint16_t; ///< 16-bit signed type
|
||||
typedef uint16_t vluint16_t; ///< 16-bit unsigned type
|
||||
typedef int vlsint32_t; ///< 32-bit signed type
|
||||
typedef uint32_t vluint32_t; ///< 32-bit unsigned type
|
||||
typedef char vlsint8_t; ///< 8-bit signed type
|
||||
typedef uint8_t vluint8_t; ///< 8-bit unsigned type
|
||||
typedef short vlsint16_t; ///< 16-bit signed type
|
||||
typedef uint16_t vluint16_t; ///< 16-bit unsigned type
|
||||
typedef int vlsint32_t; ///< 32-bit signed type
|
||||
typedef uint32_t vluint32_t; ///< 32-bit unsigned type
|
||||
# if defined(__WORDSIZE) && (__WORDSIZE == 64)
|
||||
typedef long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long vluint64_t; ///< 64-bit unsigned type
|
||||
typedef long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long vluint64_t; ///< 64-bit unsigned type
|
||||
# else
|
||||
typedef long long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
typedef long long vlsint64_t; ///< 64-bit signed type
|
||||
typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
|
@ -351,14 +360,14 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
|||
//=========================================================================
|
||||
// Integer size macros
|
||||
|
||||
#define VL_BYTESIZE 8 ///< Bits in a CData / byte
|
||||
#define VL_SHORTSIZE 16 ///< Bits in a SData / short
|
||||
#define VL_IDATASIZE 32 ///< Bits in a IData / word
|
||||
#define VL_WORDSIZE IDATASIZE ///< Legacy define
|
||||
#define VL_QUADSIZE 64 ///< Bits in a QData / quadword
|
||||
#define VL_EDATASIZE 32 ///< Bits in a EData (WData entry)
|
||||
#define VL_EDATASIZE_LOG2 5 ///< log2(VL_EDATASIZE)
|
||||
#define VL_CACHE_LINE_BYTES 64 ///< Bytes in a cache line (for alignment)
|
||||
#define VL_BYTESIZE 8 ///< Bits in a CData / byte
|
||||
#define VL_SHORTSIZE 16 ///< Bits in a SData / short
|
||||
#define VL_IDATASIZE 32 ///< Bits in a IData / word
|
||||
#define VL_WORDSIZE VL_IDATASIZE ///< Legacy define
|
||||
#define VL_QUADSIZE 64 ///< Bits in a QData / quadword
|
||||
#define VL_EDATASIZE 32 ///< Bits in a EData (WData entry)
|
||||
#define VL_EDATASIZE_LOG2 5 ///< log2(VL_EDATASIZE)
|
||||
#define VL_CACHE_LINE_BYTES 64 ///< Bytes in a cache line (for alignment)
|
||||
|
||||
/// Bytes this number of bits needs (1 bit=1 byte)
|
||||
#define VL_BYTES_I(nbits) (((nbits) + (VL_BYTESIZE - 1)) / VL_BYTESIZE)
|
||||
|
|
@ -371,15 +380,15 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
|||
// Class definition helpers
|
||||
|
||||
// Used to declare a class as uncopyable; put after a private:
|
||||
#define VL_UNCOPYABLE(Type) \
|
||||
Type(const Type& other) VL_EQ_DELETE; \
|
||||
Type& operator= (const Type&) VL_EQ_DELETE
|
||||
#define VL_UNCOPYABLE(Type) \
|
||||
Type(const Type& other) VL_EQ_DELETE; \
|
||||
Type& operator=(const Type&) VL_EQ_DELETE
|
||||
|
||||
//=========================================================================
|
||||
// Verilated function size macros
|
||||
|
||||
#define VL_MULS_MAX_WORDS 16 ///< Max size in words of MULS operation
|
||||
#define VL_TO_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
|
||||
#define VL_MULS_MAX_WORDS 16 ///< Max size in words of MULS operation
|
||||
#define VL_TO_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation
|
||||
|
||||
//=========================================================================
|
||||
// Base macros
|
||||
|
|
@ -389,11 +398,10 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
|||
#define VL_SIZEBITS_E (VL_EDATASIZE - 1) ///< Bit mask for bits in a quad
|
||||
|
||||
/// Mask for words with 1's where relevant bits are (0=all bits)
|
||||
#define VL_MASK_I(nbits) (((nbits) & VL_SIZEBITS_I) \
|
||||
? ((1U << ((nbits) & VL_SIZEBITS_I) )-1) : ~0)
|
||||
#define VL_MASK_I(nbits) (((nbits) & VL_SIZEBITS_I) ? ((1U << ((nbits) & VL_SIZEBITS_I)) - 1) : ~0)
|
||||
/// Mask for quads with 1's where relevant bits are (0=all bits)
|
||||
#define VL_MASK_Q(nbits) (((nbits) & VL_SIZEBITS_Q) \
|
||||
? ((VL_ULL(1) << ((nbits) & VL_SIZEBITS_Q) )-VL_ULL(1)) : VL_ULL(~0))
|
||||
#define VL_MASK_Q(nbits) \
|
||||
(((nbits) & VL_SIZEBITS_Q) ? ((VL_ULL(1) << ((nbits) & VL_SIZEBITS_Q)) - VL_ULL(1)) : VL_ULL(~0))
|
||||
/// Mask for EData with 1's where relevant bits are (0=all bits)
|
||||
#define VL_MASK_E(nbits) VL_MASK_I(nbits)
|
||||
#define VL_EUL(n) VL_UL(n) ///< Make constant number EData sized
|
||||
|
|
@ -409,8 +417,8 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
|||
// #defines, to avoid requiring math.h on all compile runs
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define VL_TRUNC(n) (((n)<0) ? ceil((n)) : floor((n)))
|
||||
# define VL_ROUND(n) (((n)<0) ? ceil((n)-0.5) : floor((n)+0.5))
|
||||
# define VL_TRUNC(n) (((n) < 0) ? ceil((n)) : floor((n)))
|
||||
# define VL_ROUND(n) (((n) < 0) ? ceil((n)-0.5) : floor((n) + 0.5))
|
||||
#else
|
||||
# define VL_TRUNC(n) trunc(n)
|
||||
# define VL_ROUND(n) round(n)
|
||||
|
|
@ -422,13 +430,14 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
|||
/// The vluint64_t argument is loaded with a high-performance counter for profiling
|
||||
/// or 0x0 if not implemeted on this platform
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
# define VL_RDTSC(val) { \
|
||||
vluint32_t hi, lo; \
|
||||
asm volatile("rdtsc" : "=a" (lo), "=d" (hi)); \
|
||||
(val) = ((vluint64_t)lo) | (((vluint64_t)hi)<<32); \
|
||||
}
|
||||
#define VL_RDTSC(val) \
|
||||
{ \
|
||||
vluint32_t hi, lo; \
|
||||
asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); \
|
||||
(val) = ((vluint64_t)lo) | (((vluint64_t)hi) << 32); \
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
# define VL_RDTSC(val) asm volatile("mrs %[rt],PMCCNTR_EL0" : [rt] "=r" (val));
|
||||
# define VL_RDTSC(val) asm volatile("mrs %[rt],PMCCNTR_EL0" : [rt] "=r"(val));
|
||||
#else
|
||||
// We just silently ignore unknown OSes, as only leads to missing statistics
|
||||
# define VL_RDTSC(val) (val) = 0;
|
||||
|
|
@ -467,6 +476,17 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type
|
|||
# define VL_STRCASECMP strcasecmp
|
||||
#endif
|
||||
|
||||
//=========================================================================
|
||||
// Macros controlling target specific optimizations
|
||||
|
||||
// Define VL_PORTABLE_ONLY to disable all target specific optimizations
|
||||
#ifndef VL_PORTABLE_ONLY
|
||||
# ifdef __x86_64__
|
||||
# define VL_X86_64 1
|
||||
# endif
|
||||
#endif // VL_PORTABLE_ONLY
|
||||
// clang-format on
|
||||
|
||||
//=========================================================================
|
||||
// Stringify macros
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ LEX = @LEX@
|
|||
LFLAGS = -d
|
||||
PERL = @PERL@
|
||||
YACC = @YACC@
|
||||
OBJCACHE ?= @OBJCACHE@
|
||||
|
||||
prefix = @prefix@
|
||||
|
||||
|
|
@ -167,6 +168,7 @@ RAW_OBJS = \
|
|||
V3Cast.o \
|
||||
V3Cdc.o \
|
||||
V3Changed.o \
|
||||
V3Class.o \
|
||||
V3Clean.o \
|
||||
V3Clock.o \
|
||||
V3Combine.o \
|
||||
|
|
@ -183,6 +185,7 @@ RAW_OBJS = \
|
|||
V3EmitCInlines.o \
|
||||
V3EmitCSyms.o \
|
||||
V3EmitCMake.o \
|
||||
V3EmitCMain.o \
|
||||
V3EmitMk.o \
|
||||
V3EmitV.o \
|
||||
V3EmitXml.o \
|
||||
|
|
|
|||
136
src/V3Active.cpp
136
src/V3Active.cpp
|
|
@ -50,11 +50,11 @@ protected:
|
|||
|
||||
class ActiveNamer : public ActiveBaseVisitor {
|
||||
private:
|
||||
typedef std::map<string,AstActive*> ActiveNameMap;
|
||||
typedef std::map<string, AstActive*> ActiveNameMap;
|
||||
// STATE
|
||||
AstScope* m_scopep; // Current scope to add statement to
|
||||
AstActive* m_iActivep; // For current scope, the IActive we're building
|
||||
AstActive* m_cActivep; // For current scope, the SActive(combo) we're building
|
||||
AstScope* m_scopep; // Current scope to add statement to
|
||||
AstActive* m_iActivep; // For current scope, the IActive we're building
|
||||
AstActive* m_cActivep; // For current scope, the SActive(combo) we're building
|
||||
|
||||
SenTreeSet m_activeSens; // Sen lists for each active we've made
|
||||
typedef vl_unordered_map<AstSenTree*, AstActive*> ActiveMap;
|
||||
|
|
@ -89,8 +89,7 @@ public:
|
|||
AstActive* getCActive(FileLine* fl) {
|
||||
if (!m_cActivep) {
|
||||
m_cActivep = new AstActive(
|
||||
fl, "combo",
|
||||
new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Combo())));
|
||||
fl, "combo", new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Combo())));
|
||||
m_cActivep->sensesStorep(m_cActivep->sensesp());
|
||||
addActive(m_cActivep);
|
||||
}
|
||||
|
|
@ -99,8 +98,7 @@ public:
|
|||
AstActive* getIActive(FileLine* fl) {
|
||||
if (!m_iActivep) {
|
||||
m_iActivep = new AstActive(
|
||||
fl, "initial",
|
||||
new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Initial())));
|
||||
fl, "initial", new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Initial())));
|
||||
m_iActivep->sensesStorep(m_iActivep->sensesp());
|
||||
addActive(m_iActivep);
|
||||
}
|
||||
|
|
@ -122,7 +120,7 @@ public:
|
|||
AstSenTree* newsenp = sensesp->cloneTree(false);
|
||||
activep = new AstActive(fl, "sequent", newsenp);
|
||||
activep->sensesStorep(activep->sensesp());
|
||||
UINFO(8," New ACTIVE "<<activep<<endl);
|
||||
UINFO(8, " New ACTIVE " << activep << endl);
|
||||
// Form the sensitivity list
|
||||
addActive(activep);
|
||||
m_activeMap[newsenp] = activep;
|
||||
|
|
@ -131,6 +129,7 @@ public:
|
|||
}
|
||||
return activep;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
ActiveNamer() {
|
||||
|
|
@ -139,9 +138,7 @@ public:
|
|||
m_cActivep = NULL;
|
||||
}
|
||||
virtual ~ActiveNamer() {}
|
||||
void main(AstScope* nodep) {
|
||||
iterate(nodep);
|
||||
}
|
||||
void main(AstScope* nodep) { iterate(nodep); }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -150,27 +147,29 @@ public:
|
|||
class ActiveDlyVisitor : public ActiveBaseVisitor {
|
||||
public:
|
||||
enum CheckType { CT_SEQ, CT_COMBO, CT_INITIAL, CT_LATCH };
|
||||
|
||||
private:
|
||||
CheckType m_check; // Combo logic or other
|
||||
AstNode* m_alwaysp; // Always we're under
|
||||
AstNode* m_assignp; // In assign
|
||||
CheckType m_check; // Combo logic or other
|
||||
AstNode* m_alwaysp; // Always we're under
|
||||
AstNode* m_assignp; // In assign
|
||||
// VISITORS
|
||||
virtual void visit(AstAssignDly* nodep) VL_OVERRIDE {
|
||||
if (m_check != CT_SEQ) {
|
||||
// Convert to a non-delayed assignment
|
||||
UINFO(5," ASSIGNDLY "<<nodep<<endl);
|
||||
UINFO(5, " ASSIGNDLY " << nodep << endl);
|
||||
if (m_check == CT_INITIAL) {
|
||||
nodep->v3warn(INITIALDLY, "Delayed assignments (<=) in initial or final block\n"
|
||||
<<nodep->warnMore()<<"... Suggest blocking assignments (=)");
|
||||
<< nodep->warnMore()
|
||||
<< "... Suggest blocking assignments (=)");
|
||||
} else if (m_check == CT_LATCH) {
|
||||
// Suppress. Shouldn't matter that the interior of the latch races
|
||||
} else {
|
||||
nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked"
|
||||
" (non flop or latch) block\n"
|
||||
<<nodep->warnMore()<<"... Suggest blocking assignments (=)");
|
||||
" (non flop or latch) block\n"
|
||||
<< nodep->warnMore()
|
||||
<< "... Suggest blocking assignments (=)");
|
||||
}
|
||||
AstNode* newp = new AstAssign(nodep->fileline(),
|
||||
nodep->lhsp()->unlinkFrBack(),
|
||||
AstNode* newp = new AstAssign(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
|
||||
nodep->rhsp()->unlinkFrBack());
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
|
|
@ -186,17 +185,18 @@ private:
|
|||
}
|
||||
virtual void visit(AstVarRef* nodep) VL_OVERRIDE {
|
||||
AstVar* varp = nodep->varp();
|
||||
if (m_check == CT_SEQ
|
||||
&& m_assignp
|
||||
&& !varp->isUsedLoopIdx() // Ignore loop indices
|
||||
if (m_check == CT_SEQ && m_assignp && !varp->isUsedLoopIdx() // Ignore loop indices
|
||||
&& !varp->isTemp()) {
|
||||
// Allow turning off warnings on the always, or the variable also
|
||||
if (!m_alwaysp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||
&& !m_assignp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)
|
||||
&& !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ)) {
|
||||
m_assignp->v3warn(BLKSEQ, "Blocking assignments (=) in sequential (flop or latch) block\n"
|
||||
<<m_assignp->warnMore()<<"... Suggest delayed assignments (<=)");
|
||||
m_alwaysp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always
|
||||
m_assignp->v3warn(BLKSEQ,
|
||||
"Blocking assignments (=) in sequential (flop or latch) block\n"
|
||||
<< m_assignp->warnMore()
|
||||
<< "... Suggest delayed assignments (<=)");
|
||||
m_alwaysp->fileline()->modifyWarnOff(
|
||||
V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always
|
||||
varp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -225,15 +225,15 @@ private:
|
|||
// AstNode::user4() Used by V3Const::constify, called below
|
||||
|
||||
// STATE
|
||||
ActiveNamer m_namer; // Tracking of active names
|
||||
AstCFunc* m_scopeFinalp; // Final function for this scope
|
||||
bool m_itemCombo; // Found a SenItem combo
|
||||
bool m_itemSequent; // Found a SenItem sequential
|
||||
ActiveNamer m_namer; // Tracking of active names
|
||||
AstCFunc* m_scopeFinalp; // Final function for this scope
|
||||
bool m_itemCombo; // Found a SenItem combo
|
||||
bool m_itemSequent; // Found a SenItem sequential
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstScope* nodep) VL_OVERRIDE {
|
||||
// Create required actives and add to scope
|
||||
UINFO(4," SCOPE "<<nodep<<endl);
|
||||
UINFO(4, " SCOPE " << nodep << endl);
|
||||
// Clear last scope's names, and collect this scope's existing names
|
||||
m_namer.main(nodep);
|
||||
m_scopeFinalp = NULL;
|
||||
|
|
@ -244,48 +244,47 @@ private:
|
|||
}
|
||||
virtual void visit(AstInitial* nodep) VL_OVERRIDE {
|
||||
// Relink to IACTIVE, unless already under it
|
||||
UINFO(4," INITIAL "<<nodep<<endl);
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
UINFO(4, " INITIAL " << nodep << endl);
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
AstActive* wantactivep = m_namer.getIActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) VL_OVERRIDE {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4," ASSIGNW "<<nodep<<endl);
|
||||
UINFO(4, " ASSIGNW " << nodep << endl);
|
||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) VL_OVERRIDE {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4," ASSIGNW "<<nodep<<endl);
|
||||
UINFO(4, " ASSIGNW " << nodep << endl);
|
||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) VL_OVERRIDE {
|
||||
// Relink to CACTIVE, unless already under it
|
||||
UINFO(4," COVERTOGGLE "<<nodep<<endl);
|
||||
UINFO(4, " COVERTOGGLE " << nodep << endl);
|
||||
AstActive* wantactivep = m_namer.getCActive(nodep->fileline());
|
||||
nodep->unlinkFrBack();
|
||||
wantactivep->addStmtsp(nodep);
|
||||
}
|
||||
virtual void visit(AstFinal* nodep) VL_OVERRIDE {
|
||||
// Relink to CFUNC for the final
|
||||
UINFO(4," FINAL "<<nodep<<endl);
|
||||
UINFO(4, " FINAL " << nodep << endl);
|
||||
if (!nodep->bodysp()) { // Empty, Kill it.
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_INITIAL);
|
||||
if (!m_scopeFinalp) {
|
||||
m_scopeFinalp = new AstCFunc(
|
||||
nodep->fileline(), "_final_"+m_namer.scopep()->nameDotless(),
|
||||
m_namer.scopep());
|
||||
nodep->fileline(), "_final_" + m_namer.scopep()->nameDotless(), m_namer.scopep());
|
||||
m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_scopeFinalp->addInitsp(
|
||||
new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n"));
|
||||
new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign() + "\n"));
|
||||
m_scopeFinalp->dontCombine(true);
|
||||
m_scopeFinalp->formCallTree(true);
|
||||
m_scopeFinalp->slow(true);
|
||||
|
|
@ -300,9 +299,7 @@ private:
|
|||
// METHODS
|
||||
void visitAlways(AstNode* nodep, AstSenTree* oldsensesp, VAlwaysKwd kwd) {
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
if (oldsensesp
|
||||
&& oldsensesp->sensesp()
|
||||
&& VN_IS(oldsensesp->sensesp(), SenItem)
|
||||
if (oldsensesp && oldsensesp->sensesp() && VN_IS(oldsensesp->sensesp(), SenItem)
|
||||
&& VN_CAST(oldsensesp->sensesp(), SenItem)->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
UASSERT_OBJ(!oldsensesp->sensesp()->nextp(), nodep,
|
||||
|
|
@ -318,10 +315,11 @@ private:
|
|||
bool combo = m_itemCombo;
|
||||
bool sequent = m_itemSequent;
|
||||
|
||||
if (!combo && !sequent) combo=true; // If no list, Verilog 2000: always @ (*)
|
||||
if (!combo && !sequent) combo = true; // If no list, Verilog 2000: always @ (*)
|
||||
if (combo && sequent) {
|
||||
if (!v3Global.opt.bboxUnsup()) {
|
||||
nodep->v3error("Unsupported: Mixed edge (pos/negedge) and activity (no edge) sensitive activity list");
|
||||
nodep->v3error("Unsupported: Mixed edge (pos/negedge) and activity "
|
||||
"(no edge) sensitive activity list");
|
||||
}
|
||||
sequent = false;
|
||||
}
|
||||
|
|
@ -336,8 +334,9 @@ private:
|
|||
// always (posedge RESET) { if (RESET).... } we know RESET is true.
|
||||
// Summarize a long list of combo inputs as just "combo"
|
||||
#ifndef __COVERITY__ // Else dead code on next line.
|
||||
if (combo) oldsensesp->addSensesp
|
||||
(new AstSenItem(nodep->fileline(), AstSenItem::Combo()));
|
||||
if (combo) {
|
||||
oldsensesp->addSensesp(new AstSenItem(nodep->fileline(), AstSenItem::Combo()));
|
||||
}
|
||||
#endif
|
||||
wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp);
|
||||
}
|
||||
|
|
@ -354,19 +353,18 @@ private:
|
|||
// Warn and/or convert any delayed assignments
|
||||
if (combo && !sequent) {
|
||||
if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_LATCH);
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_LATCH);
|
||||
} else {
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_COMBO);
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_COMBO);
|
||||
}
|
||||
}
|
||||
else if (!combo && sequent) {
|
||||
ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_SEQ);
|
||||
} else if (!combo && sequent) {
|
||||
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_SEQ);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) VL_OVERRIDE {
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
UINFO(4," ALW "<<nodep<<endl);
|
||||
//if (debug()>=9) nodep->dumpTree(cout, " Alw: ");
|
||||
UINFO(4, " ALW " << nodep << endl);
|
||||
// if (debug() >= 9) nodep->dumpTree(cout, " Alw: ");
|
||||
|
||||
if (!nodep->bodysp()) {
|
||||
// Empty always. Kill it.
|
||||
|
|
@ -377,19 +375,29 @@ private:
|
|||
}
|
||||
virtual void visit(AstAlwaysPublic* nodep) VL_OVERRIDE {
|
||||
// Move always to appropriate ACTIVE based on its sense list
|
||||
UINFO(4," ALWPub "<<nodep<<endl);
|
||||
//if (debug()>=9) nodep->dumpTree(cout, " Alw: ");
|
||||
UINFO(4, " ALWPub " << nodep << endl);
|
||||
// if (debug() >= 9) nodep->dumpTree(cout, " Alw: ");
|
||||
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
|
||||
}
|
||||
virtual void visit(AstSenGate* nodep) VL_OVERRIDE {
|
||||
AstSenItem* subitemp = nodep->sensesp();
|
||||
UASSERT_OBJ(subitemp->edgeType() == VEdgeType::ET_ANYEDGE
|
||||
|| subitemp->edgeType() == VEdgeType::ET_POSEDGE
|
||||
|| subitemp->edgeType() == VEdgeType::ET_NEGEDGE,
|
||||
|| subitemp->edgeType() == VEdgeType::ET_POSEDGE
|
||||
|| subitemp->edgeType() == VEdgeType::ET_NEGEDGE,
|
||||
nodep, "Strange activity type under SenGate");
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstSenItem* nodep) VL_OVERRIDE {
|
||||
if (nodep->varrefp()) {
|
||||
if (AstBasicDType* basicp = nodep->varrefp()->dtypep()->basicp()) {
|
||||
if (basicp->isEventValue()) {
|
||||
// Events need to be treated as active high so we only activate on event being
|
||||
// 1
|
||||
UINFO(8, "Demote event to HIGHEDGE " << nodep << endl);
|
||||
nodep->edgeType(VEdgeType::ET_HIGHEDGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nodep->edgeType() == VEdgeType::ET_ANYEDGE) {
|
||||
m_itemCombo = true;
|
||||
// Delete the sensitivity
|
||||
|
|
@ -399,7 +407,7 @@ private:
|
|||
// V3LinkResolve should have cleaned most of these up
|
||||
if (!nodep->varrefp()->width1()) {
|
||||
nodep->v3error("Unsupported: Non-single bit wide signal pos/negedge sensitivity: "
|
||||
<<nodep->varrefp()->prettyNameQ());
|
||||
<< nodep->varrefp()->prettyNameQ());
|
||||
}
|
||||
m_itemSequent = true;
|
||||
nodep->varrefp()->varp()->usedClock(true);
|
||||
|
|
@ -426,9 +434,7 @@ public:
|
|||
// Active class functions
|
||||
|
||||
void V3Active::activeAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
ActiveVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ActiveVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("active", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,11 +42,11 @@ private:
|
|||
// AstNode::user() bool. True if processed
|
||||
// Each call to V3Const::constify
|
||||
// AstNode::user4() Used by V3Const::constify, called below
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
AstTopScope* m_topscopep; // Top scope for adding sentrees under
|
||||
SenTreeFinder m_finder; // Find global sentree's and add them
|
||||
AstTopScope* m_topscopep; // Top scope for adding sentrees under
|
||||
SenTreeFinder m_finder; // Find global sentree's and add them
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
|
@ -61,16 +61,16 @@ private:
|
|||
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
||||
// Create required actives and add to module
|
||||
// We can start ordering at a module, or a scope
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstActive* nodep) VL_OVERRIDE {
|
||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||
V3Const::constifyExpensiveEdit(nodep); // Remove duplicate clocks and such; sensesp() may change!
|
||||
UINFO(4, " ACTIVE " << nodep << endl);
|
||||
// Remove duplicate clocks and such; sensesp() may change!
|
||||
V3Const::constifyExpensiveEdit(nodep);
|
||||
AstSenTree* sensesp = nodep->sensesp();
|
||||
UASSERT_OBJ(sensesp, nodep, "NULL");
|
||||
if (sensesp->sensesp()
|
||||
&& VN_IS(sensesp->sensesp(), SenItem)
|
||||
if (sensesp->sensesp() && VN_IS(sensesp->sensesp(), SenItem)
|
||||
&& VN_CAST(sensesp->sensesp(), SenItem)->isNever()) {
|
||||
// Never executing. Kill it.
|
||||
UASSERT_OBJ(!sensesp->sensesp()->nextp(), nodep,
|
||||
|
|
@ -80,10 +80,9 @@ private:
|
|||
}
|
||||
// Copy combo tree to settlement tree with duplicated statements
|
||||
if (sensesp->hasCombo()) {
|
||||
AstSenTree* newsentreep
|
||||
= new AstSenTree(nodep->fileline(),
|
||||
new AstSenItem(nodep->fileline(), AstSenItem::Settle()));
|
||||
AstActive* newp = new AstActive(nodep->fileline(),"settle", newsentreep);
|
||||
AstSenTree* newsentreep = new AstSenTree(
|
||||
nodep->fileline(), new AstSenItem(nodep->fileline(), AstSenItem::Settle()));
|
||||
AstActive* newp = new AstActive(nodep->fileline(), "settle", newsentreep);
|
||||
newp->sensesStorep(newsentreep);
|
||||
if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true));
|
||||
nodep->addNextHere(newp);
|
||||
|
|
@ -91,10 +90,10 @@ private:
|
|||
// Move the SENTREE for each active up to the global level.
|
||||
// This way we'll easily see what clock domains are identical
|
||||
AstSenTree* wantp = m_finder.getSenTree(nodep->fileline(), sensesp);
|
||||
UINFO(4," lookdone\n");
|
||||
UINFO(4, " lookdone\n");
|
||||
if (wantp != sensesp) {
|
||||
// Move the active's contents to the other active
|
||||
UINFO(4," merge active "<<sensesp<<" into "<<wantp<<endl);
|
||||
UINFO(4, " merge active " << sensesp << " into " << wantp << endl);
|
||||
if (nodep->sensesStorep()) {
|
||||
UASSERT_OBJ(sensesp == nodep->sensesStorep(), nodep,
|
||||
"sensesStore should have been deleted earlier if different");
|
||||
|
|
@ -107,7 +106,7 @@ private:
|
|||
nodep->sensesp(wantp);
|
||||
}
|
||||
// No need to do statements under it, they're already moved.
|
||||
//iterateChildren(nodep);
|
||||
// iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) VL_OVERRIDE { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Node should have been under ACTIVE");
|
||||
|
|
@ -145,9 +144,7 @@ public:
|
|||
// Active class functions
|
||||
|
||||
void V3ActiveTop::activeTopAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
ActiveTopVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ActiveTopVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("activetop", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
129
src/V3Assert.cpp
129
src/V3Assert.cpp
|
|
@ -34,30 +34,28 @@ private:
|
|||
// NODE STATE/TYPES
|
||||
// Cleared on netlist
|
||||
// AstNode::user() -> bool. True if processed
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Last module
|
||||
AstBegin* m_beginp; // Last begin
|
||||
unsigned m_modPastNum; // Module past numbering
|
||||
VDouble0 m_statCover; // Statistic tracking
|
||||
VDouble0 m_statAsNotImm; // Statistic tracking
|
||||
VDouble0 m_statAsImm; // Statistic tracking
|
||||
VDouble0 m_statAsFull; // Statistic tracking
|
||||
AstNodeModule* m_modp; // Last module
|
||||
AstBegin* m_beginp; // Last begin
|
||||
unsigned m_modPastNum; // Module past numbering
|
||||
VDouble0 m_statCover; // Statistic tracking
|
||||
VDouble0 m_statAsNotImm; // Statistic tracking
|
||||
VDouble0 m_statAsImm; // Statistic tracking
|
||||
VDouble0 m_statAsFull; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message) {
|
||||
return (string("[%0t] "+prefix+": ")+nodep->fileline()->filebasename()
|
||||
+":"+cvtToStr(nodep->fileline()->lineno())
|
||||
+": Assertion failed in %m"
|
||||
+((message != "")?": ":"")+message
|
||||
+"\n");
|
||||
return (string("[%0t] " + prefix + ": ") + nodep->fileline()->filebasename() + ":"
|
||||
+ cvtToStr(nodep->fileline()->lineno()) + ": Assertion failed in %m"
|
||||
+ ((message != "") ? ": " : "") + message + "\n");
|
||||
}
|
||||
void replaceDisplay(AstDisplay* nodep, const string& prefix) {
|
||||
nodep->displayType(AstDisplayType::DT_WRITE);
|
||||
nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text()));
|
||||
// cppcheck-suppress nullPointer
|
||||
AstNode* timenewp = new AstTime(nodep->fileline());
|
||||
AstNode* timenewp = new AstTime(nodep->fileline(), m_modp->timeunit());
|
||||
if (AstNode* timesp = nodep->fmtp()->exprsp()) {
|
||||
timesp->unlinkFrBackWithNext();
|
||||
timenewp->addNext(timesp);
|
||||
|
|
@ -77,8 +75,8 @@ private:
|
|||
// If assertions are off, have constant propagation rip them out later
|
||||
// This allows syntax errors and such to be detected normally.
|
||||
(v3Global.opt.assertOn()
|
||||
? static_cast<AstNode*>(new AstCMath(fl, "Verilated::assertOn()", 1))
|
||||
: static_cast<AstNode*>(new AstConst(fl, AstConst::LogicFalse()))),
|
||||
? static_cast<AstNode*>(new AstCMath(fl, "Verilated::assertOn()", 1))
|
||||
: static_cast<AstNode*>(new AstConst(fl, AstConst::LogicFalse()))),
|
||||
nodep, NULL);
|
||||
newp->user1(true); // Don't assert/cover this if
|
||||
return newp;
|
||||
|
|
@ -86,8 +84,8 @@ private:
|
|||
|
||||
AstNode* newFireAssertUnchecked(AstNode* nodep, const string& message) {
|
||||
// Like newFireAssert() but omits the asserts-on check
|
||||
AstDisplay* dispp = new AstDisplay(nodep->fileline(),
|
||||
AstDisplayType::DT_ERROR, message, NULL, NULL);
|
||||
AstDisplay* dispp
|
||||
= new AstDisplay(nodep->fileline(), AstDisplayType::DT_ERROR, message, NULL, NULL);
|
||||
AstNode* bodysp = dispp;
|
||||
replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
|
||||
bodysp->addNext(new AstStop(nodep->fileline(), true));
|
||||
|
|
@ -129,7 +127,7 @@ private:
|
|||
AstCoverInc* covincp = VN_CAST(snodep->coverincp(), CoverInc);
|
||||
UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
|
||||
covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
|
||||
if (message!="") covincp->declp()->comment(message);
|
||||
if (message != "") covincp->declp()->comment(message);
|
||||
bodysp = covincp;
|
||||
}
|
||||
|
||||
|
|
@ -137,8 +135,11 @@ private:
|
|||
ifp = new AstIf(nodep->fileline(), propp, bodysp, NULL);
|
||||
bodysp = ifp;
|
||||
} else if (VN_IS(nodep, Assert)) {
|
||||
if (nodep->immediate()) ++m_statAsImm;
|
||||
else ++m_statAsNotImm;
|
||||
if (nodep->immediate()) {
|
||||
++m_statAsImm;
|
||||
} else {
|
||||
++m_statAsNotImm;
|
||||
}
|
||||
if (passsp) passsp = newIfAssertOn(passsp);
|
||||
if (failsp) failsp = newIfAssertOn(failsp);
|
||||
if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
|
||||
|
|
@ -153,9 +154,10 @@ private:
|
|||
|
||||
AstNode* newp;
|
||||
if (sentreep) {
|
||||
newp = new AstAlways(nodep->fileline(),
|
||||
VAlwaysKwd::ALWAYS, sentreep, bodysp);
|
||||
} else { newp = bodysp; }
|
||||
newp = new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, bodysp);
|
||||
} else {
|
||||
newp = bodysp;
|
||||
}
|
||||
// Install it
|
||||
if (selfDestruct) {
|
||||
// Delete it after making the tree. This way we can tell the user
|
||||
|
|
@ -186,7 +188,7 @@ private:
|
|||
iterateAndNextNull(ifp->ifsp());
|
||||
|
||||
// If the last else is not an else if, recurse into that too.
|
||||
if (ifp->elsesp() && !nextifp) {
|
||||
if (ifp->elsesp() && !nextifp) { //
|
||||
iterateAndNextNull(ifp->elsesp());
|
||||
}
|
||||
|
||||
|
|
@ -199,14 +201,12 @@ private:
|
|||
}
|
||||
|
||||
// Record if this ends with an 'else' that does not have an if
|
||||
if (ifp->elsesp() && !nextifp) {
|
||||
hasDefaultElse = true;
|
||||
}
|
||||
if (ifp->elsesp() && !nextifp) hasDefaultElse = true;
|
||||
|
||||
ifp = nextifp;
|
||||
} while (ifp);
|
||||
|
||||
AstNode *newifp = nodep->cloneTree(false);
|
||||
AstNode* newifp = nodep->cloneTree(false);
|
||||
bool allow_none = nodep->unique0Pragma();
|
||||
|
||||
// Empty case means no property
|
||||
|
|
@ -215,12 +215,11 @@ private:
|
|||
// Note: if this ends with an 'else', then we don't need to validate that one of the
|
||||
// predicates evaluates to true.
|
||||
AstNode* ohot = ((allow_none || hasDefaultElse)
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||
AstIf* checkifp = new AstIf(nodep->fileline(),
|
||||
new AstLogNot(nodep->fileline(), ohot),
|
||||
newFireAssert(nodep, "'unique if' statement violated"),
|
||||
newifp);
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||
AstIf* checkifp
|
||||
= new AstIf(nodep->fileline(), new AstLogNot(nodep->fileline(), ohot),
|
||||
newFireAssert(nodep, "'unique if' statement violated"), newifp);
|
||||
checkifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
nodep->replaceWith(checkifp);
|
||||
pushDeletep(nodep);
|
||||
|
|
@ -234,16 +233,17 @@ private:
|
|||
iterateChildren(nodep);
|
||||
if (!nodep->user1SetOnce()) {
|
||||
bool has_default = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp();
|
||||
itemp; itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) has_default = true;
|
||||
}
|
||||
if (nodep->fullPragma() || nodep->priorityPragma()) {
|
||||
// Simply need to add a default if there isn't one already
|
||||
++m_statAsFull;
|
||||
if (!has_default) {
|
||||
nodep->addItemsp(new AstCaseItem(nodep->fileline(), NULL/*DEFAULT*/,
|
||||
newFireAssert(nodep, "synthesis full_case, but non-match found")));
|
||||
nodep->addItemsp(new AstCaseItem(
|
||||
nodep->fileline(), NULL /*DEFAULT*/,
|
||||
newFireAssert(nodep, "synthesis full_case, but non-match found")));
|
||||
}
|
||||
}
|
||||
if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) {
|
||||
|
|
@ -271,8 +271,11 @@ private:
|
|||
nodep->exprp()->cloneTree(false),
|
||||
icondp->cloneTree(false));
|
||||
}
|
||||
if (propp) propp = new AstConcat(icondp->fileline(), onep, propp);
|
||||
else propp = onep;
|
||||
if (propp) {
|
||||
propp = new AstConcat(icondp->fileline(), onep, propp);
|
||||
} else {
|
||||
propp = onep;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Empty case means no property
|
||||
|
|
@ -281,12 +284,13 @@ private:
|
|||
bool allow_none = has_default || nodep->unique0Pragma();
|
||||
AstNode* ohot
|
||||
= (allow_none
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||
AstIf* ifp = new AstIf(nodep->fileline(),
|
||||
new AstLogNot(nodep->fileline(), ohot),
|
||||
newFireAssert(nodep, "synthesis parallel_case, but multiple matches found"),
|
||||
NULL);
|
||||
? static_cast<AstNode*>(new AstOneHot0(nodep->fileline(), propp))
|
||||
: static_cast<AstNode*>(new AstOneHot(nodep->fileline(), propp)));
|
||||
AstIf* ifp = new AstIf(
|
||||
nodep->fileline(), new AstLogNot(nodep->fileline(), ohot),
|
||||
newFireAssert(nodep,
|
||||
"synthesis parallel_case, but multiple matches found"),
|
||||
NULL);
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
nodep->addNotParallelp(ifp);
|
||||
}
|
||||
|
|
@ -303,23 +307,22 @@ private:
|
|||
"Expected constant ticks, checked in V3Width");
|
||||
ticks = VN_CAST(nodep->ticksp(), Const)->toUInt();
|
||||
}
|
||||
UASSERT_OBJ(ticks>=1, nodep, "0 tick should have been checked in V3Width");
|
||||
UASSERT_OBJ(ticks >= 1, nodep, "0 tick should have been checked in V3Width");
|
||||
AstNode* inp = nodep->exprp()->unlinkFrBack();
|
||||
AstVar* invarp = NULL;
|
||||
AstSenTree* sentreep = nodep->sentreep(); sentreep->unlinkFrBack();
|
||||
AstAlways* alwaysp = new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS,
|
||||
sentreep, NULL);
|
||||
AstSenTree* sentreep = nodep->sentreep();
|
||||
sentreep->unlinkFrBack();
|
||||
AstAlways* alwaysp = new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, NULL);
|
||||
m_modp->addStmtp(alwaysp);
|
||||
for (uint32_t i=0; i<ticks; ++i) {
|
||||
for (uint32_t i = 0; i < ticks; ++i) {
|
||||
AstVar* outvarp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP,
|
||||
"_Vpast_"+cvtToStr(m_modPastNum++)+"_"+cvtToStr(i),
|
||||
"_Vpast_" + cvtToStr(m_modPastNum++) + "_" + cvtToStr(i),
|
||||
inp->dtypep());
|
||||
m_modp->addStmtp(outvarp);
|
||||
AstNode* assp = new AstAssignDly(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), outvarp, true),
|
||||
inp);
|
||||
new AstVarRef(nodep->fileline(), outvarp, true), inp);
|
||||
alwaysp->addStmtp(assp);
|
||||
//if (debug()>-9) assp->dumpTree(cout, "-ass: ");
|
||||
// if (debug() >= 9) assp->dumpTree(cout, "-ass: ");
|
||||
invarp = outvarp;
|
||||
inp = new AstVarRef(nodep->fileline(), invarp, false);
|
||||
}
|
||||
|
|
@ -334,12 +337,12 @@ private:
|
|||
virtual void visit(AstDisplay* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
// Replace the special types with standard text
|
||||
if (nodep->displayType()==AstDisplayType::DT_INFO) {
|
||||
if (nodep->displayType() == AstDisplayType::DT_INFO) {
|
||||
replaceDisplay(nodep, "-Info");
|
||||
} else if (nodep->displayType()==AstDisplayType::DT_WARNING) {
|
||||
} else if (nodep->displayType() == AstDisplayType::DT_WARNING) {
|
||||
replaceDisplay(nodep, "%%Warning");
|
||||
} else if (nodep->displayType()==AstDisplayType::DT_ERROR
|
||||
|| nodep->displayType()==AstDisplayType::DT_FATAL) {
|
||||
} else if (nodep->displayType() == AstDisplayType::DT_ERROR
|
||||
|| nodep->displayType() == AstDisplayType::DT_FATAL) {
|
||||
replaceDisplay(nodep, "%%Error");
|
||||
}
|
||||
}
|
||||
|
|
@ -403,9 +406,7 @@ public:
|
|||
// Top Assert class
|
||||
|
||||
void V3Assert::assertAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
AssertVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ AssertVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("assert", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,14 +61,12 @@ private:
|
|||
}
|
||||
return newp;
|
||||
}
|
||||
void clearAssertInfo() {
|
||||
m_senip = NULL;
|
||||
}
|
||||
void clearAssertInfo() { m_senip = NULL; }
|
||||
|
||||
// VISITORS
|
||||
//========== Statements
|
||||
virtual void visit(AstClocking* nodep) VL_OVERRIDE {
|
||||
UINFO(8," CLOCKING"<<nodep<<endl);
|
||||
UINFO(8, " CLOCKING" << nodep << endl);
|
||||
// Store the new default clock, reset on new module
|
||||
m_seniDefaultp = nodep->sensesp();
|
||||
// Trash it, keeping children
|
||||
|
|
@ -81,9 +79,7 @@ private:
|
|||
}
|
||||
virtual void visit(AstAlways* nodep) VL_OVERRIDE {
|
||||
iterateAndNextNull(nodep->sensesp());
|
||||
if (nodep->sensesp()) {
|
||||
m_seniAlwaysp = nodep->sensesp()->sensesp();
|
||||
}
|
||||
if (nodep->sensesp()) m_seniAlwaysp = nodep->sensesp()->sensesp();
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
m_seniAlwaysp = NULL;
|
||||
}
|
||||
|
|
@ -93,9 +89,7 @@ private:
|
|||
clearAssertInfo();
|
||||
// Find Clocking's buried under nodep->exprsp
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->immediate()) {
|
||||
nodep->sentreep(newSenTree(nodep));
|
||||
}
|
||||
if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep));
|
||||
clearAssertInfo();
|
||||
}
|
||||
virtual void visit(AstPast* nodep) VL_OVERRIDE {
|
||||
|
|
@ -106,21 +100,18 @@ private:
|
|||
virtual void visit(AstPropClocked* nodep) VL_OVERRIDE {
|
||||
// No need to iterate the body, once replace will get iterated
|
||||
iterateAndNextNull(nodep->sensesp());
|
||||
if (m_senip) {
|
||||
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
|
||||
}
|
||||
if (m_senip) nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
|
||||
// Block is the new expression to evaluate
|
||||
AstNode* blockp = nodep->propp()->unlinkFrBack();
|
||||
if (nodep->disablep()) {
|
||||
if (VN_IS(nodep->backp(), Cover)) {
|
||||
blockp = new AstAnd(nodep->disablep()->fileline(),
|
||||
new AstNot(nodep->disablep()->fileline(),
|
||||
nodep->disablep()->unlinkFrBack()),
|
||||
blockp);
|
||||
blockp = new AstAnd(
|
||||
nodep->disablep()->fileline(),
|
||||
new AstNot(nodep->disablep()->fileline(), nodep->disablep()->unlinkFrBack()),
|
||||
blockp);
|
||||
} else {
|
||||
blockp = new AstOr(nodep->disablep()->fileline(),
|
||||
nodep->disablep()->unlinkFrBack(),
|
||||
blockp);
|
||||
nodep->disablep()->unlinkFrBack(), blockp);
|
||||
}
|
||||
}
|
||||
// Unlink and just keep a pointer to it, convert to sentree as needed
|
||||
|
|
@ -151,9 +142,7 @@ public:
|
|||
// Top Assert class
|
||||
|
||||
void V3AssertPre::assertPreAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
AssertPreVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ AssertPreVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("assertpre", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
499
src/V3Ast.cpp
499
src/V3Ast.cpp
|
|
@ -51,7 +51,6 @@ bool AstUser5InUse::s_userBusy = false;
|
|||
|
||||
int AstNodeDType::s_uniqueNum = 0;
|
||||
|
||||
|
||||
//######################################################################
|
||||
// V3AstType
|
||||
|
||||
|
|
@ -102,15 +101,16 @@ AstNode* AstNode::abovep() const {
|
|||
string AstNode::encodeName(const string& namein) {
|
||||
// Encode signal name raw from parser, then not called again on same signal
|
||||
string out;
|
||||
for (string::const_iterator pos = namein.begin(); pos!=namein.end(); ++pos) {
|
||||
if ((pos==namein.begin()) ? isalpha(pos[0]) // digits can't lead identifiers
|
||||
: isalnum(pos[0])) {
|
||||
for (string::const_iterator pos = namein.begin(); pos != namein.end(); ++pos) {
|
||||
if ((pos == namein.begin()) ? isalpha(pos[0]) // digits can't lead identifiers
|
||||
: isalnum(pos[0])) {
|
||||
out += pos[0];
|
||||
} else if (pos[0]=='_') {
|
||||
if (pos[1]=='_') {
|
||||
out += "_"; out += "__05F"; // hex(_) = 0x5F
|
||||
} else if (pos[0] == '_') {
|
||||
if (pos[1] == '_') {
|
||||
out += "_";
|
||||
out += "__05F"; // hex(_) = 0x5F
|
||||
++pos;
|
||||
if (pos==namein.end()) break;
|
||||
if (pos == namein.end()) break;
|
||||
} else {
|
||||
out += pos[0];
|
||||
}
|
||||
|
|
@ -120,7 +120,8 @@ string AstNode::encodeName(const string& namein) {
|
|||
// We also do *NOT* use __DOT__ etc, as we search for those
|
||||
// in some replacements, and don't want to mangle the user's names.
|
||||
unsigned val = pos[0] & 0xff; // Mask to avoid sign extension
|
||||
char hex[10]; sprintf(hex, "__0%02X", val);
|
||||
char hex[10];
|
||||
sprintf(hex, "__0%02X", val);
|
||||
out += hex;
|
||||
}
|
||||
}
|
||||
|
|
@ -133,34 +134,26 @@ string AstNode::encodeName(const string& namein) {
|
|||
|
||||
string AstNode::encodeNumber(vlsint64_t num) {
|
||||
if (num < 0) {
|
||||
return "__02D"+cvtToStr(-num); // 2D=-
|
||||
return "__02D" + cvtToStr(-num); // 2D=-
|
||||
} else {
|
||||
return cvtToStr(num);
|
||||
}
|
||||
}
|
||||
|
||||
string AstNode::nameProtect() const {
|
||||
return VIdProtect::protectIf(name(), protect());
|
||||
}
|
||||
string AstNode::origNameProtect() const {
|
||||
return VIdProtect::protectIf(origName(), protect());
|
||||
}
|
||||
string AstNode::nameProtect() const { return VIdProtect::protectIf(name(), protect()); }
|
||||
string AstNode::origNameProtect() const { return VIdProtect::protectIf(origName(), protect()); }
|
||||
|
||||
string AstNode::shortName() const {
|
||||
string pretty = name();
|
||||
string::size_type pos;
|
||||
while ((pos = pretty.find("__PVT__")) != string::npos) {
|
||||
pretty.replace(pos, 7, "");
|
||||
}
|
||||
while ((pos = pretty.find("__PVT__")) != string::npos) pretty.replace(pos, 7, "");
|
||||
return pretty;
|
||||
}
|
||||
|
||||
string AstNode::dedotName(const string& namein) {
|
||||
string pretty = namein;
|
||||
string::size_type pos;
|
||||
while ((pos = pretty.find("__DOT__")) != string::npos) {
|
||||
pretty.replace(pos, 7, ".");
|
||||
}
|
||||
while ((pos = pretty.find("__DOT__")) != string::npos) pretty.replace(pos, 7, ".");
|
||||
if (pretty.substr(0, 4) == "TOP.") pretty.replace(0, 4, "");
|
||||
return pretty;
|
||||
}
|
||||
|
|
@ -170,12 +163,8 @@ string AstNode::vcdName(const string& namein) {
|
|||
// Dots are reserved for dots the user put in the name
|
||||
string pretty = namein;
|
||||
string::size_type pos;
|
||||
while ((pos = pretty.find("__DOT__")) != string::npos) {
|
||||
pretty.replace(pos, 7, " ");
|
||||
}
|
||||
while ((pos = pretty.find('.')) != string::npos) {
|
||||
pretty.replace(pos, 1, " ");
|
||||
}
|
||||
while ((pos = pretty.find("__DOT__")) != string::npos) pretty.replace(pos, 7, " ");
|
||||
while ((pos = pretty.find('.')) != string::npos) pretty.replace(pos, 1, " ");
|
||||
// Now convert escaped special characters, etc
|
||||
return prettyName(pretty);
|
||||
}
|
||||
|
|
@ -185,38 +174,38 @@ string AstNode::prettyName(const string& namein) {
|
|||
string pretty;
|
||||
pretty = "";
|
||||
pretty.reserve(namein.length());
|
||||
for (const char* pos = namein.c_str(); *pos; ) {
|
||||
if (pos[0]=='-' && pos[1]=='>') { // ->
|
||||
for (const char* pos = namein.c_str(); *pos;) {
|
||||
if (pos[0] == '-' && pos[1] == '>') { // ->
|
||||
pretty += ".";
|
||||
pos += 2;
|
||||
continue;
|
||||
}
|
||||
if (pos[0]=='_' && pos[1]=='_') { // Short-circuit
|
||||
if (0==strncmp(pos, "__BRA__", 7)) {
|
||||
if (pos[0] == '_' && pos[1] == '_') { // Short-circuit
|
||||
if (0 == strncmp(pos, "__BRA__", 7)) {
|
||||
pretty += "[";
|
||||
pos += 7;
|
||||
continue;
|
||||
}
|
||||
if (0==strncmp(pos, "__KET__", 7)) {
|
||||
if (0 == strncmp(pos, "__KET__", 7)) {
|
||||
pretty += "]";
|
||||
pos += 7;
|
||||
continue;
|
||||
}
|
||||
if (0==strncmp(pos, "__DOT__", 7)) {
|
||||
if (0 == strncmp(pos, "__DOT__", 7)) {
|
||||
pretty += ".";
|
||||
pos += 7;
|
||||
continue;
|
||||
}
|
||||
if (0==strncmp(pos, "__PVT__", 7)) {
|
||||
if (0 == strncmp(pos, "__PVT__", 7)) {
|
||||
pretty += "";
|
||||
pos += 7;
|
||||
continue;
|
||||
}
|
||||
if (pos[0]=='_' && pos[1]=='_' && pos[2]=='0'
|
||||
&& isxdigit(pos[3]) && isxdigit(pos[4])) {
|
||||
if (pos[0] == '_' && pos[1] == '_' && pos[2] == '0' && isxdigit(pos[3])
|
||||
&& isxdigit(pos[4])) {
|
||||
char value = 0;
|
||||
value += 16*(isdigit(pos[3]) ? (pos[3]-'0') : (tolower(pos[3])-'a'+10));
|
||||
value += (isdigit(pos[4]) ? (pos[4]-'0') : (tolower(pos[4])-'a'+10));
|
||||
value += 16 * (isdigit(pos[3]) ? (pos[3] - '0') : (tolower(pos[3]) - 'a' + 10));
|
||||
value += (isdigit(pos[4]) ? (pos[4] - '0') : (tolower(pos[4]) - 'a' + 10));
|
||||
pretty += value;
|
||||
pos += 5;
|
||||
continue;
|
||||
|
|
@ -226,37 +215,37 @@ string AstNode::prettyName(const string& namein) {
|
|||
pretty += pos[0];
|
||||
++pos;
|
||||
}
|
||||
if (pretty[0]=='T' && pretty.substr(0, 4) == "TOP.") pretty.replace(0, 4, "");
|
||||
if (pretty[0]=='T' && pretty.substr(0, 5) == "TOP->") pretty.replace(0, 5, "");
|
||||
if (pretty[0] == 'T' && pretty.substr(0, 4) == "TOP.") pretty.replace(0, 4, "");
|
||||
if (pretty[0] == 'T' && pretty.substr(0, 5) == "TOP->") pretty.replace(0, 5, "");
|
||||
return pretty;
|
||||
}
|
||||
|
||||
string AstNode::prettyTypeName() const {
|
||||
if (name()=="") return typeName();
|
||||
return string(typeName())+" '"+prettyName()+"'";
|
||||
if (name() == "") return typeName();
|
||||
return string(typeName()) + " '" + prettyName() + "'";
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// Insertion
|
||||
|
||||
inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next) {
|
||||
inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next){
|
||||
#ifdef VL_DEBUG
|
||||
// Called on all major tree changers.
|
||||
// Only for use for those really nasty bugs relating to internals
|
||||
// Note this may be null.
|
||||
//if (debug()) cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "
|
||||
// <<prefix<<": "<<cvtToHex(this)<<" <e"<<AstNode::s_editCntGbl<<">"<<endl;
|
||||
//if (debug()) {
|
||||
// cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl;
|
||||
// // Commenting out the section below may crash, as the tree state
|
||||
// // between edits is not always consistent for printing
|
||||
// cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl;
|
||||
// v3Global.rootp()->dumpTree(cout, "-treeChange: ");
|
||||
// if (next||1) this->dumpTreeAndNext(cout, prefix);
|
||||
// else this->dumpTree(cout, prefix);
|
||||
// this->checkTree();
|
||||
// v3Global.rootp()->checkTree();
|
||||
//}
|
||||
// Called on all major tree changers.
|
||||
// Only for use for those really nasty bugs relating to internals
|
||||
// Note this may be null.
|
||||
// if (debug()) cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "
|
||||
// <<prefix<<": "<<cvtToHex(this)<<" <e"<<AstNode::s_editCntGbl<<">"<<endl;
|
||||
// if (debug()) {
|
||||
// cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl;
|
||||
// // Commenting out the section below may crash, as the tree state
|
||||
// // between edits is not always consistent for printing
|
||||
// cout<<"-treeChange: V3Ast.cpp:"<<lineno<<" Tree Change for "<<prefix<<endl;
|
||||
// v3Global.rootp()->dumpTree(cout, "-treeChange: ");
|
||||
// if (next||1) this->dumpTreeAndNext(cout, prefix);
|
||||
// else this->dumpTree(cout, prefix);
|
||||
// this->checkTree();
|
||||
// v3Global.rootp()->checkTree();
|
||||
//}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -276,8 +265,9 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) {
|
|||
UDEBUGONLY(UASSERT_OBJ(!oldtailp->m_nextp, nodep,
|
||||
"Node had next, but headtail says it shouldn't"););
|
||||
} else {
|
||||
// Though inefficient, we are occasionally passed a addNext in the middle of a list.
|
||||
while (oldtailp->m_nextp != NULL) oldtailp = oldtailp->m_nextp;
|
||||
// Though inefficient, we are occasionally passed an
|
||||
// addNext in the middle of a list.
|
||||
while (oldtailp->m_nextp) oldtailp = oldtailp->m_nextp;
|
||||
}
|
||||
}
|
||||
// Link it in
|
||||
|
|
@ -328,15 +318,15 @@ void AstNode::addNextHere(AstNode* newp) {
|
|||
AstNode* oldheadtailp = this->m_headtailp;
|
||||
// (!oldheadtailp) // this was&is middle of list
|
||||
// (oldheadtailp==this && !oldnext)// this was head AND tail (one node long list)
|
||||
// (oldheadtailp && oldnextp) // this was&is head of list of not just one node, not tail
|
||||
// (oldheadtailp && !oldnextp) // this was tail of list, might also
|
||||
// (oldheadtailp && oldnextp) // this was&is head of list of not just one node, not
|
||||
// tail (oldheadtailp && !oldnextp) // this was tail of list, might also
|
||||
// be head of one-node list
|
||||
//
|
||||
newp->m_headtailp = NULL; // Not at head any longer
|
||||
addlastp->m_headtailp = NULL; // Presume middle of list
|
||||
// newp might happen to be head/tail after all, if so will be set again below
|
||||
if (oldheadtailp) { // else in middle of list, no change
|
||||
if (oldheadtailp==this) { // this was one node
|
||||
if (oldheadtailp == this) { // this was one node
|
||||
this->m_headtailp = addlastp; // Was head/tail, now a tail
|
||||
addlastp->m_headtailp = oldheadtailp; // Tail needs to remember head (or NULL)
|
||||
} else if (!oldnextp) { // this was tail
|
||||
|
|
@ -404,26 +394,38 @@ void AstNode::setOp4p(AstNode* newp) {
|
|||
|
||||
void AstNode::addOp1p(AstNode* newp) {
|
||||
UASSERT(newp, "Null item passed to addOp1p");
|
||||
if (!m_op1p) { op1p(newp); }
|
||||
else { m_op1p->addNext(newp); }
|
||||
if (!m_op1p) {
|
||||
op1p(newp);
|
||||
} else {
|
||||
m_op1p->addNext(newp);
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::addOp2p(AstNode* newp) {
|
||||
UASSERT(newp, "Null item passed to addOp2p");
|
||||
if (!m_op2p) { op2p(newp); }
|
||||
else { m_op2p->addNext(newp); }
|
||||
if (!m_op2p) {
|
||||
op2p(newp);
|
||||
} else {
|
||||
m_op2p->addNext(newp);
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::addOp3p(AstNode* newp) {
|
||||
UASSERT(newp, "Null item passed to addOp3p");
|
||||
if (!m_op3p) { op3p(newp); }
|
||||
else { m_op3p->addNext(newp); }
|
||||
if (!m_op3p) {
|
||||
op3p(newp);
|
||||
} else {
|
||||
m_op3p->addNext(newp);
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::addOp4p(AstNode* newp) {
|
||||
UASSERT(newp, "Null item passed to addOp4p");
|
||||
if (!m_op4p) { op4p(newp); }
|
||||
else { m_op4p->addNext(newp); }
|
||||
if (!m_op4p) {
|
||||
op4p(newp);
|
||||
} else {
|
||||
m_op4p->addNext(newp);
|
||||
}
|
||||
}
|
||||
|
||||
void AstNode::replaceWith(AstNode* newp) {
|
||||
|
|
@ -435,13 +437,13 @@ void AstNode::replaceWith(AstNode* newp) {
|
|||
}
|
||||
|
||||
void AstNRelinker::dump(std::ostream& str) const {
|
||||
str<<" BK="<<reinterpret_cast<uint32_t*>(m_backp);
|
||||
str<<" ITER="<<reinterpret_cast<uint32_t*>(m_iterpp);
|
||||
str<<" CHG="<<(m_chg==RELINK_NEXT?"[NEXT] ":"");
|
||||
str<<(m_chg==RELINK_OP1?"[OP1] ":"");
|
||||
str<<(m_chg==RELINK_OP2?"[OP2] ":"");
|
||||
str<<(m_chg==RELINK_OP3?"[OP3] ":"");
|
||||
str<<(m_chg==RELINK_OP4?"[OP4] ":"");
|
||||
str << " BK=" << reinterpret_cast<uint32_t*>(m_backp);
|
||||
str << " ITER=" << reinterpret_cast<uint32_t*>(m_iterpp);
|
||||
str << " CHG=" << (m_chg == RELINK_NEXT ? "[NEXT] " : "");
|
||||
str << (m_chg == RELINK_OP1 ? "[OP1] " : "");
|
||||
str << (m_chg == RELINK_OP2 ? "[OP2] " : "");
|
||||
str << (m_chg == RELINK_OP3 ? "[OP3] " : "");
|
||||
str << (m_chg == RELINK_OP4 ? "[OP4] " : "");
|
||||
}
|
||||
|
||||
AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
|
||||
|
|
@ -452,17 +454,24 @@ AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
|
|||
AstNode* backp = oldp->m_backp;
|
||||
if (linkerp) {
|
||||
linkerp->m_oldp = oldp;
|
||||
linkerp->m_backp = backp;
|
||||
linkerp->m_backp = backp;
|
||||
linkerp->m_iterpp = oldp->m_iterpp;
|
||||
if (backp->m_nextp == oldp) linkerp->m_chg = AstNRelinker::RELINK_NEXT;
|
||||
else if (backp->m_op1p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP1;
|
||||
else if (backp->m_op2p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP2;
|
||||
else if (backp->m_op3p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP3;
|
||||
else if (backp->m_op4p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP4;
|
||||
else oldp->v3fatalSrc("Unlink of node with back not pointing to it.");
|
||||
if (backp->m_nextp == oldp) {
|
||||
linkerp->m_chg = AstNRelinker::RELINK_NEXT;
|
||||
} else if (backp->m_op1p == oldp) {
|
||||
linkerp->m_chg = AstNRelinker::RELINK_OP1;
|
||||
} else if (backp->m_op2p == oldp) {
|
||||
linkerp->m_chg = AstNRelinker::RELINK_OP2;
|
||||
} else if (backp->m_op3p == oldp) {
|
||||
linkerp->m_chg = AstNRelinker::RELINK_OP3;
|
||||
} else if (backp->m_op4p == oldp) {
|
||||
linkerp->m_chg = AstNRelinker::RELINK_OP4;
|
||||
} else {
|
||||
oldp->v3fatalSrc("Unlink of node with back not pointing to it.");
|
||||
}
|
||||
}
|
||||
if (backp->m_nextp== oldp) {
|
||||
backp->m_nextp= NULL;
|
||||
if (backp->m_nextp == oldp) {
|
||||
backp->m_nextp = NULL;
|
||||
// Old list gets truncated
|
||||
// New list becomes a list upon itself
|
||||
// Most common case is unlinking a entire operand tree
|
||||
|
|
@ -477,12 +486,17 @@ AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) {
|
|||
// Create new head/tail of extracted list
|
||||
oldp->m_headtailp = oldtailp;
|
||||
oldp->m_headtailp->m_headtailp = oldp;
|
||||
} else if (backp->m_op1p == oldp) {
|
||||
backp->m_op1p = NULL;
|
||||
} else if (backp->m_op2p == oldp) {
|
||||
backp->m_op2p = NULL;
|
||||
} else if (backp->m_op3p == oldp) {
|
||||
backp->m_op3p = NULL;
|
||||
} else if (backp->m_op4p == oldp) {
|
||||
backp->m_op4p = NULL;
|
||||
} else {
|
||||
this->v3fatalSrc("Unlink of node with back not pointing to it.");
|
||||
}
|
||||
else if (backp->m_op1p == oldp) backp->m_op1p = NULL;
|
||||
else if (backp->m_op2p == oldp) backp->m_op2p = NULL;
|
||||
else if (backp->m_op3p == oldp) backp->m_op3p = NULL;
|
||||
else if (backp->m_op4p == oldp) backp->m_op4p = NULL;
|
||||
else this->v3fatalSrc("Unlink of node with back not pointing to it.");
|
||||
// Relink
|
||||
oldp->m_backp = NULL;
|
||||
// Iterator fixup
|
||||
|
|
@ -500,32 +514,44 @@ AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
|
|||
AstNode* backp = oldp->m_backp;
|
||||
if (linkerp) {
|
||||
linkerp->m_oldp = oldp;
|
||||
linkerp->m_backp = backp;
|
||||
linkerp->m_backp = backp;
|
||||
linkerp->m_iterpp = oldp->m_iterpp;
|
||||
if (backp->m_nextp == oldp) linkerp->m_chg = AstNRelinker::RELINK_NEXT;
|
||||
else if (backp->m_op1p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP1;
|
||||
else if (backp->m_op2p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP2;
|
||||
else if (backp->m_op3p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP3;
|
||||
else if (backp->m_op4p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP4;
|
||||
else this->v3fatalSrc("Unlink of node with back not pointing to it.");
|
||||
if (backp->m_nextp == oldp) {
|
||||
linkerp->m_chg = AstNRelinker::RELINK_NEXT;
|
||||
} else if (backp->m_op1p == oldp) {
|
||||
linkerp->m_chg = AstNRelinker::RELINK_OP1;
|
||||
} else if (backp->m_op2p == oldp) {
|
||||
linkerp->m_chg = AstNRelinker::RELINK_OP2;
|
||||
} else if (backp->m_op3p == oldp) {
|
||||
linkerp->m_chg = AstNRelinker::RELINK_OP3;
|
||||
} else if (backp->m_op4p == oldp) {
|
||||
linkerp->m_chg = AstNRelinker::RELINK_OP4;
|
||||
} else {
|
||||
this->v3fatalSrc("Unlink of node with back not pointing to it.");
|
||||
}
|
||||
}
|
||||
if (backp->m_nextp== oldp) {
|
||||
if (backp->m_nextp == oldp) {
|
||||
// This node gets removed from middle (or tail) of list
|
||||
// Not head, since then oldp wouldn't be a next of backp...
|
||||
backp->m_nextp= oldp->m_nextp;
|
||||
backp->m_nextp = oldp->m_nextp;
|
||||
if (backp->m_nextp) backp->m_nextp->m_backp = backp;
|
||||
// If it was a tail, back becomes new tail
|
||||
if (oldp->m_headtailp) {
|
||||
backp->m_headtailp = oldp->m_headtailp;
|
||||
backp->m_headtailp->m_headtailp = backp;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (backp->m_op1p == oldp) backp->m_op1p = oldp->m_nextp;
|
||||
else if (backp->m_op2p == oldp) backp->m_op2p = oldp->m_nextp;
|
||||
else if (backp->m_op3p == oldp) backp->m_op3p = oldp->m_nextp;
|
||||
else if (backp->m_op4p == oldp) backp->m_op4p = oldp->m_nextp;
|
||||
else this->v3fatalSrc("Unlink of node with back not pointing to it.");
|
||||
} else {
|
||||
if (backp->m_op1p == oldp) {
|
||||
backp->m_op1p = oldp->m_nextp;
|
||||
} else if (backp->m_op2p == oldp) {
|
||||
backp->m_op2p = oldp->m_nextp;
|
||||
} else if (backp->m_op3p == oldp) {
|
||||
backp->m_op3p = oldp->m_nextp;
|
||||
} else if (backp->m_op4p == oldp) {
|
||||
backp->m_op4p = oldp->m_nextp;
|
||||
} else {
|
||||
this->v3fatalSrc("Unlink of node with back not pointing to it.");
|
||||
}
|
||||
if (oldp->m_nextp) {
|
||||
AstNode* newheadp = oldp->m_nextp;
|
||||
newheadp->m_backp = backp;
|
||||
|
|
@ -545,13 +571,19 @@ AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) {
|
|||
}
|
||||
|
||||
void AstNode::relink(AstNRelinker* linkerp) {
|
||||
if (debug()>8) { UINFO(0," EDIT: relink: "); dumpPtrs(); }
|
||||
if (debug() > 8) {
|
||||
UINFO(0, " EDIT: relink: ");
|
||||
dumpPtrs();
|
||||
}
|
||||
AstNode* newp = this;
|
||||
UASSERT(linkerp && linkerp->m_backp, "Need non-empty linker");
|
||||
UASSERT(!newp->backp(), "New node already linked?");
|
||||
newp->editCountInc();
|
||||
|
||||
if (debug()>8) { linkerp->dump(cout); cout<<endl; }
|
||||
if (debug() > 8) {
|
||||
linkerp->dump(cout);
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
AstNode* backp = linkerp->m_backp;
|
||||
this->debugTreeChange("-relinkNew: ", __LINE__, true);
|
||||
|
|
@ -563,9 +595,7 @@ void AstNode::relink(AstNRelinker* linkerp) {
|
|||
case AstNRelinker::RELINK_OP2: relinkOneLink(backp->m_op2p /*ref*/, newp); break;
|
||||
case AstNRelinker::RELINK_OP3: relinkOneLink(backp->m_op3p /*ref*/, newp); break;
|
||||
case AstNRelinker::RELINK_OP4: relinkOneLink(backp->m_op4p /*ref*/, newp); break;
|
||||
default:
|
||||
this->v3fatalSrc("Relink of node without any link to change.");
|
||||
break;
|
||||
default: this->v3fatalSrc("Relink of node without any link to change."); break;
|
||||
}
|
||||
// Relink
|
||||
newp->m_backp = backp;
|
||||
|
|
@ -595,10 +625,10 @@ void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set
|
|||
// Insert the whole old list following the new node's list.
|
||||
// Thus a unlink without next, followed by relink, gives the same list.
|
||||
AstNode* newlistlastp = newp->m_headtailp;
|
||||
UASSERT_OBJ(!(newlistlastp->m_nextp && newlistlastp!=newp), newp,
|
||||
UASSERT_OBJ(!(newlistlastp->m_nextp && newlistlastp != newp), newp,
|
||||
"Headtailp tail isn't at the tail");
|
||||
AstNode* oldlistlastp = pointpr->m_headtailp;
|
||||
UASSERT_OBJ(!(oldlistlastp->m_nextp && oldlistlastp!=pointpr), newp,
|
||||
UASSERT_OBJ(!(oldlistlastp->m_nextp && oldlistlastp != pointpr), newp,
|
||||
"Old headtailp tail isn't at the tail");
|
||||
// Next links
|
||||
newlistlastp->m_nextp = pointpr;
|
||||
|
|
@ -650,7 +680,7 @@ AstNode* AstNode::cloneTreeIterList() {
|
|||
AstNode* newheadp = NULL;
|
||||
AstNode* newtailp = NULL;
|
||||
// Audited to make sure this is never NULL
|
||||
for (AstNode* oldp = this; oldp; oldp=oldp->m_nextp) {
|
||||
for (AstNode* oldp = this; oldp; oldp = oldp->m_nextp) {
|
||||
AstNode* newp = oldp->cloneTreeIter();
|
||||
newp->m_headtailp = NULL;
|
||||
newp->m_backp = newtailp;
|
||||
|
|
@ -701,7 +731,7 @@ void AstNode::deleteNode() {
|
|||
#else
|
||||
!v3Global.opt.debugLeak()
|
||||
#endif
|
||||
) {
|
||||
) {
|
||||
delete this;
|
||||
}
|
||||
// Else leak massively, so each pointer is unique
|
||||
|
|
@ -711,7 +741,7 @@ void AstNode::deleteNode() {
|
|||
void AstNode::deleteTreeIter() {
|
||||
// private: Delete list of nodes. Publicly call deleteTree() instead.
|
||||
// Audited to make sure this is never NULL
|
||||
for (AstNode* nodep=this, *nnextp; nodep; nodep=nnextp) {
|
||||
for (AstNode *nodep = this, *nnextp; nodep; nodep = nnextp) {
|
||||
nnextp = nodep->m_nextp;
|
||||
// MUST be depth first!
|
||||
if (nodep->m_op1p) nodep->m_op1p->deleteTreeIter();
|
||||
|
|
@ -789,26 +819,25 @@ void AstNode::iterateAndNext(AstNVisitor& v) {
|
|||
// there's no lower level reason yet though the back must exist.
|
||||
AstNode* nodep = this;
|
||||
#ifdef VL_DEBUG // Otherwise too hot of a function for debug
|
||||
UASSERT_OBJ(!(nodep && !nodep->m_backp), nodep,
|
||||
"iterateAndNext node has no back");
|
||||
UASSERT_OBJ(!(nodep && !nodep->m_backp), nodep, "iterateAndNext node has no back");
|
||||
#endif
|
||||
if (nodep) ASTNODE_PREFETCH(nodep->m_nextp);
|
||||
while (nodep) { // effectively: if (!this) return; // Callers rely on this
|
||||
while (nodep) { // effectively: if (!this) return; // Callers rely on this
|
||||
if (nodep->m_nextp) ASTNODE_PREFETCH(nodep->m_nextp->m_nextp);
|
||||
AstNode* niterp = nodep; // This address may get stomped via m_iterpp if the node is edited
|
||||
AstNode* niterp = nodep; // Pointer may get stomped via m_iterpp if the node is edited
|
||||
// Desirable check, but many places where multiple iterations are OK
|
||||
// UASSERT_OBJ(!niterp->m_iterpp, niterp, "IterateAndNext under iterateAndNext may miss edits");
|
||||
// Optimization note: Doing PREFETCH_RW on m_iterpp is a net even
|
||||
// UASSERT_OBJ(!niterp->m_iterpp, niterp, "IterateAndNext under iterateAndNext may miss
|
||||
// edits"); Optimization note: Doing PREFETCH_RW on m_iterpp is a net even
|
||||
// cppcheck-suppress nullPointer
|
||||
niterp->m_iterpp = &niterp;
|
||||
niterp->accept(v);
|
||||
// accept may do a replaceNode and change niterp on us...
|
||||
// niterp maybe NULL, so need cast if printing
|
||||
//if (niterp != nodep) UINFO(1,"iterateAndNext edited "<<cvtToHex(nodep)
|
||||
// if (niterp != nodep) UINFO(1,"iterateAndNext edited "<<cvtToHex(nodep)
|
||||
// <<" now into "<<cvtToHex(niterp)<<endl);
|
||||
if (!niterp) return; // Perhaps node deleted inside accept
|
||||
niterp->m_iterpp = NULL;
|
||||
if (VL_UNLIKELY(niterp!=nodep)) { // Edited node inside accept
|
||||
if (VL_UNLIKELY(niterp != nodep)) { // Edited node inside accept
|
||||
nodep = niterp;
|
||||
} else { // Unchanged node, just continue loop
|
||||
nodep = niterp->m_nextp;
|
||||
|
|
@ -822,8 +851,11 @@ void AstNode::iterateListBackwards(AstNVisitor& v) {
|
|||
while (nodep) {
|
||||
// Edits not supported: nodep->m_iterpp = &nodep;
|
||||
nodep->accept(v);
|
||||
if (nodep->backp()->m_nextp == nodep) nodep = nodep->backp();
|
||||
else nodep = NULL; // else: backp points up the tree.
|
||||
if (nodep->backp()->m_nextp == nodep) {
|
||||
nodep = nodep->backp();
|
||||
} else {
|
||||
nodep = NULL;
|
||||
} // else: backp points up the tree.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -861,7 +893,8 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
|
|||
// track, then delete it on completion
|
||||
AstBegin* tempp = new AstBegin(nodep->fileline(), "[EditWrapper]", nodep);
|
||||
{
|
||||
VL_DO_DANGLING(tempp->stmtsp()->accept(v), nodep); // nodep to null as may be replaced
|
||||
VL_DO_DANGLING(tempp->stmtsp()->accept(v),
|
||||
nodep); // nodep to null as may be replaced
|
||||
}
|
||||
nodep = tempp->stmtsp()->unlinkFrBackWithNext();
|
||||
VL_DO_DANGLING(tempp->deleteTree(), tempp);
|
||||
|
|
@ -869,11 +902,17 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
|
|||
// Use back to determine who's pointing at us (IE assume new node
|
||||
// grafts into same place as old one)
|
||||
AstNode** nextnodepp = NULL;
|
||||
if (this->m_backp->m_op1p == this) nextnodepp = &(this->m_backp->m_op1p);
|
||||
else if (this->m_backp->m_op2p == this) nextnodepp = &(this->m_backp->m_op2p);
|
||||
else if (this->m_backp->m_op3p == this) nextnodepp = &(this->m_backp->m_op3p);
|
||||
else if (this->m_backp->m_op4p == this) nextnodepp = &(this->m_backp->m_op4p);
|
||||
else if (this->m_backp->m_nextp == this) nextnodepp = &(this->m_backp->m_nextp);
|
||||
if (this->m_backp->m_op1p == this) {
|
||||
nextnodepp = &(this->m_backp->m_op1p);
|
||||
} else if (this->m_backp->m_op2p == this) {
|
||||
nextnodepp = &(this->m_backp->m_op2p);
|
||||
} else if (this->m_backp->m_op3p == this) {
|
||||
nextnodepp = &(this->m_backp->m_op3p);
|
||||
} else if (this->m_backp->m_op4p == this) {
|
||||
nextnodepp = &(this->m_backp->m_op4p);
|
||||
} else if (this->m_backp->m_nextp == this) {
|
||||
nextnodepp = &(this->m_backp->m_nextp);
|
||||
}
|
||||
UASSERT_OBJ(nextnodepp, this, "Node's back doesn't point to forward to node itself");
|
||||
{
|
||||
VL_DO_DANGLING(nodep->accept(v), nodep); // nodep to null as may be replaced
|
||||
|
|
@ -887,10 +926,8 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) {
|
|||
|
||||
void AstNode::cloneRelinkTree() {
|
||||
// private: Cleanup clone() operation on whole tree. Publicly call cloneTree() instead.
|
||||
for (AstNode* nodep=this; nodep; nodep = nodep->m_nextp) {
|
||||
if (m_dtypep && m_dtypep->clonep()) {
|
||||
m_dtypep = m_dtypep->clonep();
|
||||
}
|
||||
for (AstNode* nodep = this; nodep; nodep = nodep->m_nextp) {
|
||||
if (m_dtypep && m_dtypep->clonep()) m_dtypep = m_dtypep->clonep();
|
||||
nodep->cloneRelink();
|
||||
if (nodep->m_op1p) nodep->m_op1p->cloneRelinkTree();
|
||||
if (nodep->m_op2p) nodep->m_op2p->cloneRelinkTree();
|
||||
|
|
@ -912,37 +949,34 @@ bool AstNode::gateTreeIter() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p,
|
||||
bool ignNext, bool gateOnly) {
|
||||
bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext,
|
||||
bool gateOnly) {
|
||||
// private: Return true if the two trees are identical
|
||||
if (!node1p && !node2p) return true;
|
||||
if (!node1p || !node2p) return false;
|
||||
if (node1p->type() != node2p->type()
|
||||
|| node1p->dtypep() != node2p->dtypep()
|
||||
|| !node1p->same(node2p)
|
||||
|| (gateOnly && !node1p->isGateOptimizable())) {
|
||||
if (node1p->type() != node2p->type() || node1p->dtypep() != node2p->dtypep()
|
||||
|| !node1p->same(node2p) || (gateOnly && !node1p->isGateOptimizable())) {
|
||||
return false;
|
||||
}
|
||||
return (sameTreeIter(node1p->m_op1p, node2p->m_op1p, false, gateOnly)
|
||||
&& sameTreeIter(node1p->m_op2p, node2p->m_op2p, false, gateOnly)
|
||||
&& sameTreeIter(node1p->m_op3p, node2p->m_op3p, false, gateOnly)
|
||||
&& sameTreeIter(node1p->m_op4p, node2p->m_op4p, false, gateOnly)
|
||||
&& (ignNext || sameTreeIter(node1p->m_nextp, node2p->m_nextp, false, gateOnly))
|
||||
);
|
||||
&& (ignNext || sameTreeIter(node1p->m_nextp, node2p->m_nextp, false, gateOnly)));
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Static utilities
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) {
|
||||
return os<<std::hex<<std::setw(2)<<std::setfill('0')<<rhs.depth()
|
||||
<<"_"<<std::setw(6)<<std::setfill('0')<<rhs.hshval();
|
||||
return os << std::hex << std::setw(2) << std::setfill('0') << rhs.depth() << "_"
|
||||
<< std::setw(6) << std::setfill('0') << rhs.hshval();
|
||||
}
|
||||
|
||||
V3Hash::V3Hash(const string& name) {
|
||||
uint32_t val = 0;
|
||||
for (string::const_iterator it = name.begin(); it!=name.end(); ++it) {
|
||||
val = val*31 + *it;
|
||||
for (string::const_iterator it = name.begin(); it != name.end(); ++it) {
|
||||
val = val * 31 + *it;
|
||||
}
|
||||
setBoth(1, val);
|
||||
}
|
||||
|
|
@ -955,7 +989,7 @@ void AstNode::checkTreeIter(AstNode* backp) {
|
|||
UASSERT_OBJ(backp == this->backp(), this, "Back node inconsistent");
|
||||
if (VN_IS(this, NodeTermop) || VN_IS(this, NodeVarRef)) {
|
||||
// Termops have a short-circuited iterateChildren, so check usage
|
||||
UASSERT_OBJ(!(op1p()||op2p()||op3p()||op4p()), this,
|
||||
UASSERT_OBJ(!(op1p() || op2p() || op3p() || op4p()), this,
|
||||
"Terminal operation with non-terminals");
|
||||
}
|
||||
if (m_op1p) m_op1p->checkTreeIterList(this);
|
||||
|
|
@ -969,9 +1003,9 @@ void AstNode::checkTreeIterList(AstNode* backp) {
|
|||
// Audited to make sure this is never NULL
|
||||
AstNode* headp = this;
|
||||
AstNode* tailp = this;
|
||||
for (AstNode* nodep=headp; nodep; nodep=nodep->nextp()) {
|
||||
for (AstNode* nodep = headp; nodep; nodep = nodep->nextp()) {
|
||||
nodep->checkTreeIter(backp);
|
||||
UASSERT_OBJ(headp==this || !nextp(), this,
|
||||
UASSERT_OBJ(headp == this || !nextp(), this,
|
||||
"Headtailp should be null in middle of lists");
|
||||
tailp = nodep;
|
||||
backp = nodep;
|
||||
|
|
@ -993,12 +1027,14 @@ void AstNode::checkTree() {
|
|||
// cppcheck-suppress unusedFunction // Debug only
|
||||
void AstNode::dumpGdb() { // For GDB only // LCOV_EXCL_LINE
|
||||
dumpGdbHeader(); // LCOV_EXCL_LINE
|
||||
cout<<" "; dump(cout); cout<<endl; // LCOV_EXCL_LINE
|
||||
cout << " ";
|
||||
dump(cout);
|
||||
cout << endl; // LCOV_EXCL_LINE
|
||||
}
|
||||
// cppcheck-suppress unusedFunction // Debug only
|
||||
void AstNode::dumpGdbHeader() const { // For GDB only // LCOV_EXCL_LINE
|
||||
dumpPtrs(cout); // LCOV_EXCL_LINE
|
||||
cout<<" Fileline = "<<fileline()<<endl; // LCOV_EXCL_LINE
|
||||
cout << " Fileline = " << fileline() << endl; // LCOV_EXCL_LINE
|
||||
}
|
||||
// cppcheck-suppress unusedFunction // Debug only
|
||||
void AstNode::dumpTreeGdb() { // For GDB only // LCOV_EXCL_LINE
|
||||
|
|
@ -1007,7 +1043,8 @@ void AstNode::dumpTreeGdb() { // For GDB only // LCOV_EXCL_LINE
|
|||
}
|
||||
// cppcheck-suppress unusedFunction // Debug only
|
||||
void AstNode::dumpTreeFileGdb(const char* filenamep) { // For GDB only // LCOV_EXCL_LINE
|
||||
string filename = filenamep ? filenamep : v3Global.debugFilename("debug.tree", 98); // LCOV_EXCL_LINE
|
||||
string filename
|
||||
= filenamep ? filenamep : v3Global.debugFilename("debug.tree", 98); // LCOV_EXCL_LINE
|
||||
v3Global.rootp()->dumpTreeFile(filename); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
|
|
@ -1021,45 +1058,53 @@ void AstNode::checkIter() const {
|
|||
}
|
||||
|
||||
void AstNode::dumpPtrs(std::ostream& os) const {
|
||||
os<<"This="<<typeName()<<" "<<cvtToHex(this);
|
||||
os<<" back="<<cvtToHex(backp());
|
||||
if (nextp()) os<<" next="<<cvtToHex(nextp());
|
||||
if (m_headtailp==this) os<<" headtail=this";
|
||||
else os<<" headtail="<<cvtToHex(m_headtailp);
|
||||
if (op1p()) os<<" op1p="<<cvtToHex(op1p());
|
||||
if (op2p()) os<<" op2p="<<cvtToHex(op2p());
|
||||
if (op3p()) os<<" op3p="<<cvtToHex(op3p());
|
||||
if (op4p()) os<<" op4p="<<cvtToHex(op4p());
|
||||
if (user1p()) os<<" user1p="<<cvtToHex(user1p());
|
||||
if (user2p()) os<<" user2p="<<cvtToHex(user2p());
|
||||
if (user3p()) os<<" user3p="<<cvtToHex(user3p());
|
||||
if (user4p()) os<<" user4p="<<cvtToHex(user4p());
|
||||
if (user5p()) os<<" user5p="<<cvtToHex(user5p());
|
||||
if (m_iterpp) {
|
||||
os<<" iterpp="<<cvtToHex(m_iterpp);
|
||||
os<<"*="<<cvtToHex(*m_iterpp);
|
||||
os << "This=" << typeName() << " " << cvtToHex(this);
|
||||
os << " back=" << cvtToHex(backp());
|
||||
if (nextp()) os << " next=" << cvtToHex(nextp());
|
||||
if (m_headtailp == this) {
|
||||
os << " headtail=this";
|
||||
} else {
|
||||
os << " headtail=" << cvtToHex(m_headtailp);
|
||||
}
|
||||
os<<endl;
|
||||
if (op1p()) os << " op1p=" << cvtToHex(op1p());
|
||||
if (op2p()) os << " op2p=" << cvtToHex(op2p());
|
||||
if (op3p()) os << " op3p=" << cvtToHex(op3p());
|
||||
if (op4p()) os << " op4p=" << cvtToHex(op4p());
|
||||
if (user1p()) os << " user1p=" << cvtToHex(user1p());
|
||||
if (user2p()) os << " user2p=" << cvtToHex(user2p());
|
||||
if (user3p()) os << " user3p=" << cvtToHex(user3p());
|
||||
if (user4p()) os << " user4p=" << cvtToHex(user4p());
|
||||
if (user5p()) os << " user5p=" << cvtToHex(user5p());
|
||||
if (m_iterpp) {
|
||||
os << " iterpp=" << cvtToHex(m_iterpp);
|
||||
os << "*=" << cvtToHex(*m_iterpp);
|
||||
}
|
||||
os << endl;
|
||||
}
|
||||
|
||||
void AstNode::dumpTree(std::ostream& os, const string& indent, int maxDepth) const {
|
||||
static int s_debugFileline = v3Global.opt.debugSrcLevel("fileline"); // --debugi-fileline 9
|
||||
os<<indent<<" "<<this<<endl;
|
||||
if (debug()>8) { os<<indent<<" "; dumpPtrs(os); }
|
||||
if (s_debugFileline >= 9) {
|
||||
os<<fileline()->warnContextSecondary();
|
||||
os << indent << " " << this << endl;
|
||||
if (debug() > 8) {
|
||||
os << indent << " ";
|
||||
dumpPtrs(os);
|
||||
}
|
||||
if (maxDepth==1) {
|
||||
if (op1p()||op2p()||op3p()||op4p()) { os<<indent<<"1: ...(maxDepth)"<<endl; }
|
||||
if (s_debugFileline >= 9) { os << fileline()->warnContextSecondary(); }
|
||||
if (maxDepth == 1) {
|
||||
if (op1p() || op2p() || op3p() || op4p()) { os << indent << "1: ...(maxDepth)" << endl; }
|
||||
} else {
|
||||
for (const AstNode* nodep=op1p(); nodep; nodep=nodep->nextp()) {
|
||||
nodep->dumpTree(os, indent+"1:", maxDepth-1); }
|
||||
for (const AstNode* nodep=op2p(); nodep; nodep=nodep->nextp()) {
|
||||
nodep->dumpTree(os, indent+"2:", maxDepth-1); }
|
||||
for (const AstNode* nodep=op3p(); nodep; nodep=nodep->nextp()) {
|
||||
nodep->dumpTree(os, indent+"3:", maxDepth-1); }
|
||||
for (const AstNode* nodep=op4p(); nodep; nodep=nodep->nextp()) {
|
||||
nodep->dumpTree(os, indent+"4:", maxDepth-1); }
|
||||
for (const AstNode* nodep = op1p(); nodep; nodep = nodep->nextp()) {
|
||||
nodep->dumpTree(os, indent + "1:", maxDepth - 1);
|
||||
}
|
||||
for (const AstNode* nodep = op2p(); nodep; nodep = nodep->nextp()) {
|
||||
nodep->dumpTree(os, indent + "2:", maxDepth - 1);
|
||||
}
|
||||
for (const AstNode* nodep = op3p(); nodep; nodep = nodep->nextp()) {
|
||||
nodep->dumpTree(os, indent + "3:", maxDepth - 1);
|
||||
}
|
||||
for (const AstNode* nodep = op4p(); nodep; nodep = nodep->nextp()) {
|
||||
nodep->dumpTree(os, indent + "4:", maxDepth - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1073,16 +1118,15 @@ void AstNode::dumpTreeAndNext(std::ostream& os, const string& indent, int maxDep
|
|||
void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump) {
|
||||
// Not const function as calls checkTree
|
||||
if (doDump) {
|
||||
{ // Write log & close
|
||||
UINFO(2,"Dumping "<<filename<<endl);
|
||||
const vl_unique_ptr<std::ofstream> logsp (V3File::new_ofstream(filename, append));
|
||||
if (logsp->fail()) v3fatal("Can't write "<<filename);
|
||||
*logsp<<"Verilator Tree Dump (format 0x3900) from <e"<<std::dec<<editCountLast()<<">";
|
||||
*logsp<<" to <e"<<std::dec<<editCountGbl()<<">"<<endl;
|
||||
if (editCountGbl()==editCountLast()
|
||||
&& !(v3Global.opt.dumpTree()>=9)) {
|
||||
*logsp<<endl;
|
||||
*logsp<<"No changes since last dump!\n";
|
||||
{ // Write log & close
|
||||
UINFO(2, "Dumping " << filename << endl);
|
||||
const vl_unique_ptr<std::ofstream> logsp(V3File::new_ofstream(filename, append));
|
||||
if (logsp->fail()) v3fatal("Can't write " << filename);
|
||||
*logsp << "Verilator Tree Dump (format 0x3900) from <e" << std::dec << editCountLast();
|
||||
*logsp << "> to <e" << std::dec << editCountGbl() << ">" << endl;
|
||||
if (editCountGbl() == editCountLast() && !(v3Global.opt.dumpTree() >= 9)) {
|
||||
*logsp << endl;
|
||||
*logsp << "No changes since last dump!\n";
|
||||
} else {
|
||||
dumpTree(*logsp);
|
||||
}
|
||||
|
|
@ -1100,7 +1144,9 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump) {
|
|||
}
|
||||
|
||||
void AstNode::v3errorEndFatal(std::ostringstream& str) const {
|
||||
v3errorEnd(str); assert(0); VL_UNREACHABLE
|
||||
v3errorEnd(str);
|
||||
assert(0);
|
||||
VL_UNREACHABLE
|
||||
}
|
||||
|
||||
string AstNode::locationStr() const {
|
||||
|
|
@ -1148,12 +1194,12 @@ void AstNode::v3errorEnd(std::ostringstream& str) const {
|
|||
V3Error::v3errorEnd(str, locationStr());
|
||||
} else {
|
||||
std::ostringstream nsstr;
|
||||
nsstr<<str.str();
|
||||
nsstr << str.str();
|
||||
if (debug()) {
|
||||
nsstr<<endl;
|
||||
nsstr<<"-node: ";
|
||||
nsstr << endl;
|
||||
nsstr << "-node: ";
|
||||
const_cast<AstNode*>(this)->dump(nsstr);
|
||||
nsstr<<endl;
|
||||
nsstr << endl;
|
||||
}
|
||||
m_fileline->v3errorEnd(nsstr, locationStr());
|
||||
}
|
||||
|
|
@ -1164,8 +1210,7 @@ void AstNode::v3errorEnd(std::ostringstream& str) const {
|
|||
|
||||
void AstNode::dtypeChgSigned(bool flag) {
|
||||
UASSERT_OBJ(dtypep(), this, "No dtype when changing to (un)signed");
|
||||
dtypeChgWidthSigned(dtypep()->width(), dtypep()->widthMin(),
|
||||
AstNumeric::fromBool(flag));
|
||||
dtypeChgWidthSigned(dtypep()->width(), dtypep()->widthMin(), VSigning::fromBool(flag));
|
||||
}
|
||||
void AstNode::dtypeChgWidth(int width, int widthMin) {
|
||||
UASSERT_OBJ(dtypep(), this,
|
||||
|
|
@ -1173,14 +1218,14 @@ void AstNode::dtypeChgWidth(int width, int widthMin) {
|
|||
dtypeChgWidthSigned(width, widthMin, dtypep()->numeric());
|
||||
}
|
||||
|
||||
void AstNode::dtypeChgWidthSigned(int width, int widthMin, AstNumeric numeric) {
|
||||
void AstNode::dtypeChgWidthSigned(int width, int widthMin, VSigning numeric) {
|
||||
if (!dtypep()) {
|
||||
// We allow dtypep() to be null, as before/during widthing dtypes are not resolved
|
||||
dtypeSetLogicUnsized(width, widthMin, numeric);
|
||||
} else {
|
||||
if (width==dtypep()->width()
|
||||
&& widthMin==dtypep()->widthMin()
|
||||
&& numeric==dtypep()->numeric()) return; // Correct already
|
||||
if (width == dtypep()->width() && widthMin == dtypep()->widthMin()
|
||||
&& numeric == dtypep()->numeric())
|
||||
return; // Correct already
|
||||
// FUTURE: We may be pointing at a two state data type, and this may
|
||||
// convert it to logic. Since the AstVar remains correct, we
|
||||
// work OK but this assumption may break in the future.
|
||||
|
|
@ -1193,29 +1238,31 @@ void AstNode::dtypeChgWidthSigned(int width, int widthMin, AstNumeric numeric) {
|
|||
AstNodeDType* AstNode::findBasicDType(AstBasicDTypeKwd kwd) const {
|
||||
// For 'simple' types we use the global directory. These are all unsized.
|
||||
// More advanced types land under the module/task/etc
|
||||
return v3Global.rootp()->typeTablep()
|
||||
->findBasicDType(fileline(), kwd);
|
||||
return v3Global.rootp()->typeTablep()->findBasicDType(fileline(), kwd);
|
||||
}
|
||||
AstNodeDType* AstNode::findBitDType(int width, int widthMin, AstNumeric numeric) const {
|
||||
return v3Global.rootp()->typeTablep()
|
||||
->findLogicBitDType(fileline(), AstBasicDTypeKwd::BIT, width, widthMin, numeric);
|
||||
AstNodeDType* AstNode::findBitDType(int width, int widthMin, VSigning numeric) const {
|
||||
return v3Global.rootp()->typeTablep()->findLogicBitDType(fileline(), AstBasicDTypeKwd::BIT,
|
||||
width, widthMin, numeric);
|
||||
}
|
||||
AstNodeDType* AstNode::findLogicDType(int width, int widthMin, AstNumeric numeric) const {
|
||||
return v3Global.rootp()->typeTablep()
|
||||
->findLogicBitDType(fileline(), AstBasicDTypeKwd::LOGIC, width, widthMin, numeric);
|
||||
AstNodeDType* AstNode::findLogicDType(int width, int widthMin, VSigning numeric) const {
|
||||
return v3Global.rootp()->typeTablep()->findLogicBitDType(fileline(), AstBasicDTypeKwd::LOGIC,
|
||||
width, widthMin, numeric);
|
||||
}
|
||||
AstNodeDType* AstNode::findLogicRangeDType(const VNumRange& range, int widthMin,
|
||||
AstNumeric numeric) const {
|
||||
return v3Global.rootp()->typeTablep()
|
||||
->findLogicBitDType(fileline(), AstBasicDTypeKwd::LOGIC, range, widthMin, numeric);
|
||||
VSigning numeric) const {
|
||||
return v3Global.rootp()->typeTablep()->findLogicBitDType(fileline(), AstBasicDTypeKwd::LOGIC,
|
||||
range, widthMin, numeric);
|
||||
}
|
||||
AstNodeDType* AstNode::findBitRangeDType(const VNumRange& range, int widthMin,
|
||||
VSigning numeric) const {
|
||||
return v3Global.rootp()->typeTablep()->findLogicBitDType(fileline(), AstBasicDTypeKwd::BIT,
|
||||
range, widthMin, numeric);
|
||||
}
|
||||
AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) {
|
||||
return v3Global.rootp()->typeTablep()
|
||||
->findInsertSameDType(nodep);
|
||||
return v3Global.rootp()->typeTablep()->findInsertSameDType(nodep);
|
||||
}
|
||||
AstNodeDType* AstNode::findVoidDType() const {
|
||||
return v3Global.rootp()->typeTablep()
|
||||
->findVoidDType(fileline());
|
||||
return v3Global.rootp()->typeTablep()->findVoidDType(fileline());
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
1747
src/V3Ast.h
1747
src/V3Ast.h
File diff suppressed because it is too large
Load Diff
|
|
@ -18,12 +18,12 @@
|
|||
#define _V3ASTCONSTONLY_H_ 1
|
||||
|
||||
// Include only in visitors that do not not edit nodes, so should use constant iterators
|
||||
#define iterateAndNext error_use_iterateAndNextConst
|
||||
#define iterateChildren error_use_iterateChildrenConst
|
||||
#define iterateAndNext error_use_iterateAndNextConst
|
||||
#define iterateChildren error_use_iterateChildrenConst
|
||||
|
||||
#define addNext error_no_addNext_in_ConstOnlyVisitor
|
||||
#define replaceWith error_no_replaceWith_in_ConstOnlyVisitor
|
||||
#define deleteTree error_no_deleteTree_in_ConstOnlyVisitor
|
||||
#define unlinkFrBack error_no_unlinkFrBack_in_ConstOnlyVisitor
|
||||
#define addNext error_no_addNext_in_ConstOnlyVisitor
|
||||
#define replaceWith error_no_replaceWith_in_ConstOnlyVisitor
|
||||
#define deleteTree error_no_deleteTree_in_ConstOnlyVisitor
|
||||
#define unlinkFrBack error_no_unlinkFrBack_in_ConstOnlyVisitor
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
3690
src/V3AstNodes.h
3690
src/V3AstNodes.h
File diff suppressed because it is too large
Load Diff
117
src/V3Begin.cpp
117
src/V3Begin.cpp
|
|
@ -42,14 +42,14 @@
|
|||
class BeginState {
|
||||
private:
|
||||
// NODE STATE
|
||||
//Entire netlist:
|
||||
// Entire netlist:
|
||||
// AstNodeFTask::user1 -> bool, 1=processed
|
||||
AstUser1InUse m_inuser1;
|
||||
bool m_anyFuncInBegin;
|
||||
AstUser1InUse m_inuser1;
|
||||
bool m_anyFuncInBegin;
|
||||
|
||||
public:
|
||||
BeginState() {
|
||||
m_anyFuncInBegin = false;
|
||||
}
|
||||
BeginState()
|
||||
: m_anyFuncInBegin(false) {}
|
||||
~BeginState() {}
|
||||
void userMarkChanged(AstNode* nodep) {
|
||||
nodep->user1(true);
|
||||
|
|
@ -63,12 +63,12 @@ public:
|
|||
class BeginVisitor : public AstNVisitor {
|
||||
private:
|
||||
// STATE
|
||||
BeginState* m_statep; // Current global state
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstNodeFTask* m_ftaskp; // Current function/task
|
||||
string m_namedScope; // Name of begin blocks above us
|
||||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||
int m_ifDepth; // Current if depth
|
||||
BeginState* m_statep; // Current global state
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstNodeFTask* m_ftaskp; // Current function/task
|
||||
string m_namedScope; // Name of begin blocks above us
|
||||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||
int m_ifDepth; // Current if depth
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
|
@ -83,11 +83,11 @@ private:
|
|||
m_modp = origModp;
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep) VL_OVERRIDE {
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
UINFO(8, " " << nodep << endl);
|
||||
// Rename it
|
||||
if (m_unnamedScope != "") {
|
||||
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
|
||||
UINFO(8," rename to "<<nodep->name()<<endl);
|
||||
nodep->name(m_unnamedScope + "__DOT__" + nodep->name());
|
||||
UINFO(8, " rename to " << nodep->name() << endl);
|
||||
m_statep->userMarkChanged(nodep);
|
||||
}
|
||||
// BEGIN wrapping a function rename that function, but don't affect
|
||||
|
|
@ -109,28 +109,34 @@ private:
|
|||
}
|
||||
virtual void visit(AstBegin* nodep) VL_OVERRIDE {
|
||||
// Begin blocks were only useful in variable creation, change names and delete
|
||||
UINFO(8," "<<nodep<<endl);
|
||||
UINFO(8, " " << nodep << endl);
|
||||
string oldScope = m_namedScope;
|
||||
string oldUnnamed = m_unnamedScope;
|
||||
{
|
||||
UINFO(8,"nname "<<m_namedScope<<endl);
|
||||
UINFO(8, "nname " << m_namedScope << endl);
|
||||
if (nodep->name() != "") { // Else unneeded unnamed block
|
||||
// Create data for dotted variable resolution
|
||||
string dottedname = nodep->name() + "__DOT__"; // So always found
|
||||
string::size_type pos;
|
||||
while ((pos=dottedname.find("__DOT__")) != string::npos) {
|
||||
while ((pos = dottedname.find("__DOT__")) != string::npos) {
|
||||
string ident = dottedname.substr(0, pos);
|
||||
dottedname = dottedname.substr(pos+strlen("__DOT__"));
|
||||
dottedname = dottedname.substr(pos + strlen("__DOT__"));
|
||||
if (nodep->name() != "") {
|
||||
if (m_namedScope=="") m_namedScope = ident;
|
||||
else m_namedScope = m_namedScope + "__DOT__"+ident;
|
||||
if (m_namedScope == "") {
|
||||
m_namedScope = ident;
|
||||
} else {
|
||||
m_namedScope = m_namedScope + "__DOT__" + ident;
|
||||
}
|
||||
}
|
||||
if (m_unnamedScope == "") {
|
||||
m_unnamedScope = ident;
|
||||
} else {
|
||||
m_unnamedScope = m_unnamedScope + "__DOT__" + ident;
|
||||
}
|
||||
if (m_unnamedScope=="") m_unnamedScope = ident;
|
||||
else m_unnamedScope = m_unnamedScope + "__DOT__"+ident;
|
||||
// Create CellInline for dotted var resolution
|
||||
if (!m_ftaskp) {
|
||||
AstCellInline* inlinep = new AstCellInline(nodep->fileline(),
|
||||
m_unnamedScope, "__BEGIN__");
|
||||
AstCellInline* inlinep = new AstCellInline(
|
||||
nodep->fileline(), m_unnamedScope, "__BEGIN__", m_modp->timeunit());
|
||||
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
|
||||
}
|
||||
}
|
||||
|
|
@ -147,7 +153,11 @@ private:
|
|||
AstNode* addsp = NULL;
|
||||
if (AstNode* stmtsp = nodep->stmtsp()) {
|
||||
stmtsp->unlinkFrBackWithNext();
|
||||
if (addsp) { addsp = addsp->addNextNull(stmtsp); } else { addsp = stmtsp; }
|
||||
if (addsp) {
|
||||
addsp = addsp->addNextNull(stmtsp);
|
||||
} else {
|
||||
addsp = stmtsp;
|
||||
}
|
||||
}
|
||||
if (addsp) {
|
||||
nodep->replaceWith(addsp);
|
||||
|
|
@ -159,12 +169,15 @@ private:
|
|||
virtual void visit(AstVar* nodep) VL_OVERRIDE {
|
||||
if (m_unnamedScope != "") {
|
||||
// Rename it
|
||||
nodep->name(m_unnamedScope+"__DOT__"+nodep->name());
|
||||
nodep->name(m_unnamedScope + "__DOT__" + nodep->name());
|
||||
m_statep->userMarkChanged(nodep);
|
||||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
if (m_ftaskp) m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
|
||||
else m_modp->addStmtp(nodep);
|
||||
if (m_ftaskp) {
|
||||
m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func
|
||||
} else {
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstTypedef* nodep) VL_OVERRIDE {
|
||||
|
|
@ -175,17 +188,20 @@ private:
|
|||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
// Begins under funcs just move into the func
|
||||
if (m_ftaskp) m_ftaskp->addStmtsp(nodep);
|
||||
else m_modp->addStmtp(nodep);
|
||||
if (m_ftaskp) {
|
||||
m_ftaskp->addStmtsp(nodep);
|
||||
} else {
|
||||
m_modp->addStmtp(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep) VL_OVERRIDE {
|
||||
UINFO(8," CELL "<<nodep<<endl);
|
||||
UINFO(8, " CELL " << nodep << endl);
|
||||
if (m_namedScope != "") {
|
||||
m_statep->userMarkChanged(nodep);
|
||||
// Rename it
|
||||
nodep->name(m_namedScope+"__DOT__"+nodep->name());
|
||||
UINFO(8," rename to "<<nodep->name()<<endl);
|
||||
nodep->name(m_namedScope + "__DOT__" + nodep->name());
|
||||
UINFO(8, " rename to " << nodep->name() << endl);
|
||||
// Move to module
|
||||
nodep->unlinkFrBack();
|
||||
m_modp->addStmtp(nodep);
|
||||
|
|
@ -193,10 +209,10 @@ private:
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarXRef* nodep) VL_OVERRIDE {
|
||||
UINFO(9, " VARXREF "<<nodep<<endl);
|
||||
UINFO(9, " VARXREF " << nodep << endl);
|
||||
if (m_namedScope != "" && nodep->inlinedDots() == "") {
|
||||
nodep->inlinedDots(m_namedScope);
|
||||
UINFO(9, " rescope to "<<nodep<<endl);
|
||||
UINFO(9, " rescope to " << nodep << endl);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScopeName* nodep) VL_OVERRIDE {
|
||||
|
|
@ -207,7 +223,7 @@ private:
|
|||
// To keep correct visual order, must add before other Text's
|
||||
AstNode* afterp = nodep->scopeAttrp();
|
||||
if (afterp) afterp->unlinkFrBackWithNext();
|
||||
nodep->scopeAttrp(new AstText(nodep->fileline(), string("__DOT__")+m_namedScope));
|
||||
nodep->scopeAttrp(new AstText(nodep->fileline(), string("__DOT__") + m_namedScope));
|
||||
if (afterp) nodep->scopeAttrp(afterp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -218,14 +234,15 @@ private:
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
// VISITORS - LINT CHECK
|
||||
virtual void visit(AstIf* nodep) VL_OVERRIDE { // Note not AstNodeIf; other types don't get covered
|
||||
virtual void visit(AstIf* nodep) VL_OVERRIDE { // not AstNodeIf; other types not covered
|
||||
// Check IFDEPTH warning - could be in other transform files if desire
|
||||
int prevIfDepth = m_ifDepth;
|
||||
if (m_ifDepth == -1 || v3Global.opt.ifDepth()<1) { // Turned off
|
||||
if (m_ifDepth == -1 || v3Global.opt.ifDepth() < 1) { // Turned off
|
||||
} else if (nodep->uniquePragma() || nodep->unique0Pragma() || nodep->priorityPragma()) {
|
||||
m_ifDepth = -1;
|
||||
} else if (++m_ifDepth > v3Global.opt.ifDepth()) {
|
||||
nodep->v3warn(IFDEPTH,"Deep 'if' statement; suggest unique/priority to avoid slow logic");
|
||||
nodep->v3warn(IFDEPTH,
|
||||
"Deep 'if' statement; suggest unique/priority to avoid slow logic");
|
||||
nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once
|
||||
m_ifDepth = -1;
|
||||
}
|
||||
|
|
@ -258,14 +275,14 @@ private:
|
|||
// VISITORS
|
||||
virtual void visit(AstNodeFTaskRef* nodep) VL_OVERRIDE {
|
||||
if (nodep->taskp()->user1()) { // It was converted
|
||||
UINFO(9, " relinkFTask "<<nodep<<endl);
|
||||
UINFO(9, " relinkFTask " << nodep << endl);
|
||||
nodep->name(nodep->taskp()->name());
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) VL_OVERRIDE {
|
||||
if (nodep->varp()->user1()) { // It was converted
|
||||
UINFO(9, " relinVarRef "<<nodep<<endl);
|
||||
UINFO(9, " relinVarRef " << nodep << endl);
|
||||
nodep->name(nodep->varp()->name());
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -273,9 +290,9 @@ private:
|
|||
virtual void visit(AstIfaceRefDType* nodep) VL_OVERRIDE {
|
||||
// May have changed cell names
|
||||
// TypeTable is always after all modules, so names are stable
|
||||
UINFO(8," IFACEREFDTYPE "<<nodep<<endl);
|
||||
UINFO(8, " IFACEREFDTYPE " << nodep << endl);
|
||||
if (nodep->cellp()) nodep->cellName(nodep->cellp()->name());
|
||||
UINFO(8," rename to "<<nodep<<endl);
|
||||
UINFO(8, " rename to " << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//--------------------
|
||||
|
|
@ -283,9 +300,7 @@ private:
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
BeginRelinkVisitor(AstNetlist* nodep, BeginState*) {
|
||||
iterate(nodep);
|
||||
}
|
||||
BeginRelinkVisitor(AstNetlist* nodep, BeginState*) { iterate(nodep); }
|
||||
virtual ~BeginRelinkVisitor() {}
|
||||
};
|
||||
|
||||
|
|
@ -293,13 +308,11 @@ public:
|
|||
// Task class functions
|
||||
|
||||
void V3Begin::debeginAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{
|
||||
BeginState state;
|
||||
{ BeginVisitor bvisitor (nodep,&state); }
|
||||
if (state.anyFuncInBegin()) {
|
||||
BeginRelinkVisitor brvisitor (nodep,&state);
|
||||
}
|
||||
{ BeginVisitor bvisitor(nodep, &state); }
|
||||
if (state.anyFuncInBegin()) { BeginRelinkVisitor brvisitor(nodep, &state); }
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("begin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,15 +41,15 @@ private:
|
|||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstFTask::user1() -> int. Number of references
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// TYPES
|
||||
typedef std::vector<AstCFunc*> CFuncVec;
|
||||
|
||||
// STATE
|
||||
int m_likely; // Excuses for branch likely taken
|
||||
int m_unlikely; // Excuses for branch likely not taken
|
||||
CFuncVec m_cfuncsp; // List of all tasks
|
||||
int m_likely; // Excuses for branch likely taken
|
||||
int m_unlikely; // Excuses for branch likely not taken
|
||||
CFuncVec m_cfuncsp; // List of all tasks
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
|
@ -60,14 +60,14 @@ private:
|
|||
}
|
||||
void checkUnlikely(AstNode* nodep) {
|
||||
if (nodep->isUnlikely()) {
|
||||
UINFO(4," UNLIKELY: "<<nodep<<endl);
|
||||
UINFO(4, " UNLIKELY: " << nodep << endl);
|
||||
m_unlikely++;
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeIf* nodep) VL_OVERRIDE {
|
||||
UINFO(4," IF: "<<nodep<<endl);
|
||||
UINFO(4, " IF: " << nodep << endl);
|
||||
int lastLikely = m_likely;
|
||||
int lastUnlikely = m_unlikely;
|
||||
{
|
||||
|
|
@ -109,11 +109,9 @@ private:
|
|||
|
||||
// METHODS
|
||||
void calc_tasks() {
|
||||
for (CFuncVec::iterator it=m_cfuncsp.begin(); it!=m_cfuncsp.end(); ++it) {
|
||||
for (CFuncVec::iterator it = m_cfuncsp.begin(); it != m_cfuncsp.end(); ++it) {
|
||||
AstCFunc* nodep = *it;
|
||||
if (!nodep->dontInline()) {
|
||||
nodep->isInline(true);
|
||||
}
|
||||
if (!nodep->dontInline()) nodep->isInline(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,6 +129,6 @@ public:
|
|||
// Branch class functions
|
||||
|
||||
void V3Branch::branchAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
BranchVisitor visitor (nodep);
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
BranchVisitor visitor(nodep);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,35 +42,35 @@ class BrokenTable : public AstNVisitor {
|
|||
private:
|
||||
// MEMBERS
|
||||
// For each node, we keep if it exists or not.
|
||||
typedef vl_unordered_map<const AstNode*,int> NodeMap; // Performance matters (when --debug)
|
||||
typedef vl_unordered_map<const AstNode*, int> NodeMap; // Performance matters (when --debug)
|
||||
static NodeMap s_nodes; // Set of all nodes that exist
|
||||
// BITMASK
|
||||
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
|
||||
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
|
||||
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
|
||||
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
|
||||
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
|
||||
enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed
|
||||
enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree
|
||||
enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to
|
||||
enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked
|
||||
enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node
|
||||
public:
|
||||
// METHODS
|
||||
static void deleted(const AstNode* nodep) {
|
||||
// Called by operator delete on any node - only if VL_LEAK_CHECKS
|
||||
if (debug()>=9) cout<<"-nodeDel: "<<cvtToHex(nodep)<<endl;
|
||||
if (debug() >= 9) cout << "-nodeDel: " << cvtToHex(nodep) << endl;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
UASSERT_OBJ(!(iter==s_nodes.end() || !(iter->second & FLAG_ALLOCATED)),
|
||||
UASSERT_OBJ(!(iter == s_nodes.end() || !(iter->second & FLAG_ALLOCATED)),
|
||||
reinterpret_cast<const AstNode*>(nodep),
|
||||
"Deleting AstNode object that was never tracked or already deleted");
|
||||
if (iter!=s_nodes.end()) s_nodes.erase(iter);
|
||||
if (iter != s_nodes.end()) s_nodes.erase(iter);
|
||||
}
|
||||
#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4
|
||||
// GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390
|
||||
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
static void addNewed(const AstNode* nodep) {
|
||||
// Called by operator new on any node - only if VL_LEAK_CHECKS
|
||||
if (debug()>=9) cout<<"-nodeNew: "<<cvtToHex(nodep)<<endl;
|
||||
if (debug() >= 9) cout << "-nodeNew: " << cvtToHex(nodep) << endl;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
UASSERT_OBJ(!(iter !=s_nodes.end() && (iter->second & FLAG_ALLOCATED)),
|
||||
nodep, "Newing AstNode object that is already allocated");
|
||||
UASSERT_OBJ(!(iter != s_nodes.end() && (iter->second & FLAG_ALLOCATED)), nodep,
|
||||
"Newing AstNode object that is already allocated");
|
||||
if (iter == s_nodes.end()) {
|
||||
int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2
|
||||
s_nodes.insert(make_pair(nodep, flags));
|
||||
|
|
@ -80,13 +80,14 @@ public:
|
|||
// Called by BrokenCheckVisitor when each node entered/exited
|
||||
if (!okIfLinkedTo(nodep)) return;
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
if (iter!=s_nodes.end()) {
|
||||
if (iter != s_nodes.end()) {
|
||||
iter->second &= ~FLAG_UNDER_NOW;
|
||||
if (flag) iter->second |= FLAG_UNDER_NOW;
|
||||
if (flag) iter->second |= FLAG_UNDER_NOW;
|
||||
}
|
||||
}
|
||||
static void addInTree(AstNode* nodep, bool linkable) {
|
||||
#ifndef VL_LEAK_CHECKS
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (!linkable) return; // save some time, else the map will get huge!
|
||||
#endif
|
||||
NodeMap::iterator iter = s_nodes.find(nodep);
|
||||
|
|
@ -102,7 +103,7 @@ public:
|
|||
UASSERT_OBJ(!(iter->second & FLAG_IN_TREE), nodep,
|
||||
"AstNode is already in tree at another location");
|
||||
}
|
||||
int or_flags = FLAG_IN_TREE | (linkable?FLAG_LINKABLE:0);
|
||||
int or_flags = FLAG_IN_TREE | (linkable ? FLAG_LINKABLE : 0);
|
||||
if (iter == s_nodes.end()) {
|
||||
s_nodes.insert(make_pair(nodep, or_flags));
|
||||
} else {
|
||||
|
|
@ -148,12 +149,12 @@ public:
|
|||
}
|
||||
}
|
||||
static void doneWithTree() {
|
||||
for (int backs=0; backs<2; backs++) { // Those with backp() are probably under one leaking without
|
||||
for (int backs = 0; backs < 2;
|
||||
backs++) { // Those with backp() are probably under one leaking without
|
||||
for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) {
|
||||
if ((it->second & FLAG_ALLOCATED)
|
||||
&& !(it->second & FLAG_IN_TREE)
|
||||
if ((it->second & FLAG_ALLOCATED) && !(it->second & FLAG_IN_TREE)
|
||||
&& !(it->second & FLAG_LEAKED)
|
||||
&& (it->first->backp() ? backs==1 : backs==0)) {
|
||||
&& (it->first->backp() ? backs == 1 : backs == 0)) {
|
||||
// Use only AstNode::dump instead of the virtual one, as there
|
||||
// may be varp() and other cross links that are bad.
|
||||
if (v3Global.opt.debugCheck()) {
|
||||
|
|
@ -162,11 +163,12 @@ public:
|
|||
// watch AstNode::s_editCntGbl==####
|
||||
// run
|
||||
// bt
|
||||
std::cerr<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": ");
|
||||
AstNode* rawp = const_cast<AstNode*>
|
||||
(static_cast<const AstNode*>(it->first));
|
||||
std::cerr << "%Error: LeakedNode"
|
||||
<< (it->first->backp() ? "Back: " : ": ");
|
||||
AstNode* rawp
|
||||
= const_cast<AstNode*>(static_cast<const AstNode*>(it->first));
|
||||
rawp->AstNode::dump(std::cerr);
|
||||
std::cerr<<endl;
|
||||
std::cerr << endl;
|
||||
V3Error::incErrors();
|
||||
}
|
||||
it->second |= FLAG_LEAKED;
|
||||
|
|
@ -174,6 +176,7 @@ public:
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
BrokenTable() {}
|
||||
|
|
@ -212,9 +215,7 @@ private:
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit BrokenMarkVisitor(AstNetlist* nodep) {
|
||||
iterate(nodep);
|
||||
}
|
||||
explicit BrokenMarkVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~BrokenMarkVisitor() {}
|
||||
};
|
||||
|
||||
|
|
@ -225,27 +226,27 @@ class BrokenCheckVisitor : public AstNVisitor {
|
|||
private:
|
||||
void checkWidthMin(const AstNode* nodep) {
|
||||
UASSERT_OBJ(nodep->width() == nodep->widthMin()
|
||||
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
|
||||
|| v3Global.widthMinUsage() != VWidthMinUsage::MATCHES_WIDTH,
|
||||
nodep, "Width != WidthMin");
|
||||
}
|
||||
void processAndIterate(AstNode* nodep) {
|
||||
BrokenTable::setUnder(nodep, true);
|
||||
const char* whyp = nodep->broken();
|
||||
UASSERT_OBJ(!whyp, nodep,
|
||||
"Broken link in node (or something without maybePointedTo): "<<whyp);
|
||||
"Broken link in node (or something without maybePointedTo): " << whyp);
|
||||
if (nodep->dtypep()) {
|
||||
UASSERT_OBJ(nodep->dtypep()->brokeExists(), nodep,
|
||||
"Broken link in node->dtypep() to "<<cvtToHex(nodep->dtypep()));
|
||||
"Broken link in node->dtypep() to " << cvtToHex(nodep->dtypep()));
|
||||
UASSERT_OBJ(VN_IS(nodep->dtypep(), NodeDType), nodep,
|
||||
"Non-dtype link in node->dtypep() to "<<cvtToHex(nodep->dtypep()));
|
||||
"Non-dtype link in node->dtypep() to " << cvtToHex(nodep->dtypep()));
|
||||
}
|
||||
if (v3Global.assertDTypesResolved()) {
|
||||
if (nodep->hasDType()) {
|
||||
UASSERT_OBJ(nodep->dtypep(), nodep,
|
||||
"No dtype on node with hasDType(): "<<nodep->prettyTypeName());
|
||||
"No dtype on node with hasDType(): " << nodep->prettyTypeName());
|
||||
} else {
|
||||
UASSERT_OBJ(!nodep->dtypep(), nodep,
|
||||
"DType on node without hasDType(): "<<nodep->prettyTypeName());
|
||||
"DType on node without hasDType(): " << nodep->prettyTypeName());
|
||||
}
|
||||
UASSERT_OBJ(!nodep->getChildDTypep(), nodep,
|
||||
"childDTypep() non-null on node after should have removed");
|
||||
|
|
@ -257,8 +258,7 @@ private:
|
|||
}
|
||||
virtual void visit(AstNodeAssign* nodep) VL_OVERRIDE {
|
||||
processAndIterate(nodep);
|
||||
UASSERT_OBJ(!(v3Global.assertDTypesResolved()
|
||||
&& nodep->brokeLhsMustBeLvalue()
|
||||
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && nodep->brokeLhsMustBeLvalue()
|
||||
&& VN_IS(nodep->lhsp(), NodeVarRef)
|
||||
&& !VN_CAST(nodep->lhsp(), NodeVarRef)->lvalue()),
|
||||
nodep, "Assignment LHS is not an lvalue");
|
||||
|
|
@ -267,11 +267,10 @@ private:
|
|||
// Process not just iterate
|
||||
processAndIterate(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit BrokenCheckVisitor(AstNetlist* nodep) {
|
||||
iterate(nodep);
|
||||
}
|
||||
explicit BrokenCheckVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~BrokenCheckVisitor() {}
|
||||
};
|
||||
|
||||
|
|
@ -279,27 +278,21 @@ public:
|
|||
// Broken class functions
|
||||
|
||||
void V3Broken::brokenAll(AstNetlist* nodep) {
|
||||
//UINFO(9,__FUNCTION__<<": "<<endl);
|
||||
// UINFO(9, __FUNCTION__ << ": " << endl);
|
||||
static bool inBroken = false;
|
||||
if (VL_UNCOVERABLE(inBroken)) {
|
||||
// A error called by broken can recurse back into broken; avoid this
|
||||
UINFO(1,"Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE
|
||||
UINFO(1, "Broken called under broken, skipping recursion.\n"); // LCOV_EXCL_LINE
|
||||
} else {
|
||||
inBroken = true;
|
||||
BrokenTable::prepForTree();
|
||||
BrokenMarkVisitor mvisitor (nodep);
|
||||
BrokenCheckVisitor cvisitor (nodep);
|
||||
BrokenMarkVisitor mvisitor(nodep);
|
||||
BrokenCheckVisitor cvisitor(nodep);
|
||||
BrokenTable::doneWithTree();
|
||||
inBroken = false;
|
||||
}
|
||||
}
|
||||
|
||||
void V3Broken::addNewed(AstNode* nodep) {
|
||||
BrokenTable::addNewed(nodep);
|
||||
}
|
||||
void V3Broken::deleted(AstNode* nodep) {
|
||||
BrokenTable::deleted(nodep);
|
||||
}
|
||||
bool V3Broken::isAllocated(AstNode* nodep) {
|
||||
return BrokenTable::isAllocated(nodep);
|
||||
}
|
||||
void V3Broken::addNewed(AstNode* nodep) { BrokenTable::addNewed(nodep); }
|
||||
void V3Broken::deleted(AstNode* nodep) { BrokenTable::deleted(nodep); }
|
||||
bool V3Broken::isAllocated(AstNode* nodep) { return BrokenTable::isAllocated(nodep); }
|
||||
|
|
|
|||
|
|
@ -39,27 +39,27 @@
|
|||
|
||||
class V3CCtorsVisitor {
|
||||
private:
|
||||
string m_basename;
|
||||
string m_argsp;
|
||||
string m_callargsp;
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_tlFuncp; // Top level function being built
|
||||
AstCFunc* m_funcp; // Current function
|
||||
int m_numStmts; // Number of statements output
|
||||
int m_funcNum; // Function number being built
|
||||
string m_basename;
|
||||
string m_argsp;
|
||||
string m_callargsp;
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_tlFuncp; // Top level function being built
|
||||
AstCFunc* m_funcp; // Current function
|
||||
int m_numStmts; // Number of statements output
|
||||
int m_funcNum; // Function number being built
|
||||
|
||||
public:
|
||||
AstCFunc* builtFuncp() const { return m_tlFuncp; }
|
||||
void add(AstNode* nodep) {
|
||||
if (v3Global.opt.outputSplitCFuncs()
|
||||
&& v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||
if (v3Global.opt.outputSplitCFuncs() && v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||
m_funcp = NULL;
|
||||
}
|
||||
if (!m_funcp) {
|
||||
m_funcp = new AstCFunc(m_modp->fileline(),
|
||||
m_basename+"_"+cvtToStr(++m_funcNum), NULL, "void");
|
||||
m_funcp = new AstCFunc(m_modp->fileline(), m_basename + "_" + cvtToStr(++m_funcNum),
|
||||
NULL, "void");
|
||||
m_funcp->isStatic(false);
|
||||
m_funcp->declPrivate(true);
|
||||
m_funcp->slow(true);
|
||||
m_funcp->slow(!VN_IS(m_modp, Class)); // Only classes construct on fast path
|
||||
m_funcp->argTypes(m_argsp);
|
||||
m_modp->addStmtp(m_funcp);
|
||||
|
||||
|
|
@ -74,9 +74,8 @@ public:
|
|||
m_numStmts += 1;
|
||||
}
|
||||
|
||||
V3CCtorsVisitor(AstNodeModule* nodep, const string& basename,
|
||||
const string& argsp="", const string& callargsp="",
|
||||
const string& stmt="") {
|
||||
V3CCtorsVisitor(AstNodeModule* nodep, const string& basename, const string& argsp = "",
|
||||
const string& callargsp = "", const string& stmt = "") {
|
||||
m_basename = basename;
|
||||
m_argsp = argsp;
|
||||
m_callargsp = callargsp;
|
||||
|
|
@ -86,15 +85,14 @@ public:
|
|||
m_tlFuncp = new AstCFunc(nodep->fileline(), basename, NULL, "void");
|
||||
m_tlFuncp->declPrivate(true);
|
||||
m_tlFuncp->isStatic(false);
|
||||
m_tlFuncp->slow(true);
|
||||
m_tlFuncp->slow(!VN_IS(m_modp, Class)); // Only classes construct on fast path
|
||||
m_tlFuncp->argTypes(m_argsp);
|
||||
if (stmt != "") {
|
||||
m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
||||
}
|
||||
if (stmt != "") { m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); }
|
||||
m_funcp = m_tlFuncp;
|
||||
m_modp->addStmtp(m_tlFuncp);
|
||||
}
|
||||
~V3CCtorsVisitor() {}
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(V3CCtorsVisitor);
|
||||
};
|
||||
|
|
@ -121,15 +119,16 @@ void V3CCtors::evalAsserts() {
|
|||
if (varp->isWide()) {
|
||||
newp = new AstWordSel(
|
||||
varp->fileline(), newp,
|
||||
new AstConst(varp->fileline(), varp->widthWords()-1));
|
||||
new AstConst(varp->fileline(), varp->widthWords() - 1));
|
||||
}
|
||||
uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
|
||||
newp = new AstAnd(varp->fileline(), newp,
|
||||
new AstConst(varp->fileline(), AstConst::WidthedValue(),
|
||||
storedWidth, value));
|
||||
AstNodeIf* ifp = new AstIf(varp->fileline(), newp,
|
||||
new AstCStmt(varp->fileline(),
|
||||
"Verilated::overWidthError(\""+varp->prettyName()+"\");"));
|
||||
AstNodeIf* ifp = new AstIf(
|
||||
varp->fileline(), newp,
|
||||
new AstCStmt(varp->fileline(), "Verilated::overWidthError(\""
|
||||
+ varp->prettyName() + "\");"));
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
newp = ifp;
|
||||
funcp->addStmtsp(newp);
|
||||
|
|
@ -141,17 +140,23 @@ void V3CCtors::evalAsserts() {
|
|||
}
|
||||
|
||||
void V3CCtors::cctorsAll() {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
evalAsserts();
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp();
|
||||
modp; modp = VN_CAST(modp->nextp(), NodeModule)) {
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp;
|
||||
modp = VN_CAST(modp->nextp(), NodeModule)) {
|
||||
// Process each module in turn
|
||||
{
|
||||
V3CCtorsVisitor var_reset (modp, "_ctor_var_reset");
|
||||
AstCFunc* varResetFuncp;
|
||||
V3CCtorsVisitor var_reset(
|
||||
modp, "_ctor_var_reset",
|
||||
(VN_IS(modp, Class) ? EmitCBaseVisitor::symClassVar() : ""),
|
||||
(VN_IS(modp, Class) ? "vlSymsp" : ""),
|
||||
(VN_IS(modp, Class) ? "if (false && vlSymsp) {} // Prevent unused\n" : ""));
|
||||
varResetFuncp = var_reset.builtFuncp();
|
||||
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstVar* varp = VN_CAST(np, Var)) {
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef()
|
||||
&& !varp->noReset()) {
|
||||
if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) {
|
||||
var_reset.add(new AstCReset(varp->fileline(),
|
||||
new AstVarRef(varp->fileline(), varp, true)));
|
||||
}
|
||||
|
|
@ -159,10 +164,9 @@ void V3CCtors::cctorsAll() {
|
|||
}
|
||||
}
|
||||
if (v3Global.opt.coverage()) {
|
||||
V3CCtorsVisitor configure_coverage
|
||||
(modp, "_configure_coverage",
|
||||
EmitCBaseVisitor::symClassVar()+ ", bool first", "vlSymsp, first",
|
||||
"if (0 && vlSymsp && first) {} // Prevent unused\n");
|
||||
V3CCtorsVisitor configure_coverage(
|
||||
modp, "_configure_coverage", EmitCBaseVisitor::symClassVar() + ", bool first",
|
||||
"vlSymsp, first", "if (false && vlSymsp && first) {} // Prevent unused\n");
|
||||
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
||||
if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) {
|
||||
AstNode* backp = coverp->backp();
|
||||
|
|
@ -172,5 +176,12 @@ void V3CCtors::cctorsAll() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (VN_IS(modp, Class)) {
|
||||
AstCFunc* funcp = new AstCFunc(modp->fileline(), "~", NULL, "");
|
||||
funcp->isDestructor(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->slow(false);
|
||||
modp->addStmtp(funcp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
110
src/V3CUse.cpp
110
src/V3CUse.cpp
|
|
@ -18,6 +18,9 @@
|
|||
// Each module:
|
||||
// Each cell:
|
||||
// Create CUse for cell forward declaration
|
||||
// Each class:
|
||||
// Create string access functions
|
||||
// Search for dtypes referencing class, and create CUse for forward declaraion
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
|
|
@ -44,6 +47,8 @@ private:
|
|||
// Entire netlist:
|
||||
// AstClass::user1() -> bool. True if class needs to_string dumper
|
||||
AstUser1InUse m_inuser1;
|
||||
// AstClass::user2() -> bool. True if iterated
|
||||
AstUser2InUse m_inuser2;
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
|
@ -67,6 +72,49 @@ public:
|
|||
VL_UNCOPYABLE(CUseState);
|
||||
};
|
||||
|
||||
// Visit within a module all nodes and data types they reference, finding
|
||||
// any classes so we can make sure they are defined when Verilated code
|
||||
// compiles
|
||||
class CUseDTypeVisitor : public AstNVisitor {
|
||||
// MEMBERS
|
||||
CUseState& m_stater; // State for inserter
|
||||
bool m_impOnly; // In details needed only for implementation
|
||||
// METHODS
|
||||
virtual void visit(AstClassRefDType* nodep) VL_OVERRIDE {
|
||||
if (nodep->user2SetOnce()) return; // Process once
|
||||
if (!m_impOnly) m_stater.newUse(nodep, VUseType::INT_FWD_CLASS, nodep->classp()->name());
|
||||
// No class.h, it's inside the class package's h file
|
||||
m_stater.newUse(nodep, VUseType::IMP_INCLUDE, nodep->classp()->packagep()->name());
|
||||
// Need to include extends() when we implement, but no need for pointers to know
|
||||
bool oldImpOnly = m_impOnly;
|
||||
{
|
||||
m_impOnly = true;
|
||||
iterateChildren(nodep->classp()); // This also gets all extend classes
|
||||
}
|
||||
m_impOnly = oldImpOnly;
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep) VL_OVERRIDE {
|
||||
if (nodep->user2SetOnce()) return; // Process once
|
||||
if (nodep->virtRefDTypep()) iterate(nodep->virtRefDTypep());
|
||||
if (nodep->virtRefDType2p()) iterate(nodep->virtRefDType2p());
|
||||
}
|
||||
virtual void visit(AstNode* nodep) VL_OVERRIDE {
|
||||
if (nodep->user2SetOnce()) return; // Process once
|
||||
if (nodep->dtypep() && !nodep->dtypep()->user2()) iterate(nodep->dtypep());
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CUseDTypeVisitor(AstNodeModule* nodep, CUseState& stater)
|
||||
: m_stater(stater)
|
||||
, m_impOnly(false) {
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CUseDTypeVisitor() {}
|
||||
VL_UNCOPYABLE(CUseDTypeVisitor);
|
||||
};
|
||||
|
||||
class CUseVisitor : public AstNVisitor {
|
||||
// MEMBERS
|
||||
CUseState m_state; // Inserter state
|
||||
|
|
@ -83,6 +131,62 @@ class CUseVisitor : public AstNVisitor {
|
|||
}
|
||||
}
|
||||
}
|
||||
void makeVlToString(AstClass* nodep) {
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "VL_TO_STRING", NULL, "std::string");
|
||||
funcp->argTypes("const VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(nodep)
|
||||
+ ">& obj");
|
||||
funcp->isMethod(false);
|
||||
funcp->isConst(false);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
AstNode* exprp = new AstCMath(nodep->fileline(), "obj ? obj->to_string() : \"null\"", 0);
|
||||
exprp->dtypeSetString();
|
||||
funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp));
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
void makeToString(AstClass* nodep) {
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "to_string", NULL, "std::string");
|
||||
funcp->isConst(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
AstNode* exprp = new AstCMath(nodep->fileline(),
|
||||
"std::string(\"`{\") + to_string_middle() + \"}\"", 0);
|
||||
exprp->dtypeSetString();
|
||||
funcp->addStmtsp(new AstCReturn(nodep->fileline(), exprp));
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
void makeToStringMiddle(AstClass* nodep) {
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), "to_string_middle", NULL, "std::string");
|
||||
funcp->isConst(true);
|
||||
funcp->isStatic(false);
|
||||
funcp->protect(false);
|
||||
funcp->addStmtsp(new AstCStmt(nodep->fileline(), "std::string out;\n"));
|
||||
std::string comma;
|
||||
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
if (VN_IS(itemp, Var)) {
|
||||
string stmt = "out += \"";
|
||||
stmt += comma;
|
||||
comma = ", ";
|
||||
stmt += itemp->origNameProtect();
|
||||
stmt += ":\" + VL_TO_STRING(";
|
||||
stmt += itemp->nameProtect();
|
||||
stmt += ");\n";
|
||||
nodep->user1(true); // So what we extend dumps this
|
||||
funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
||||
}
|
||||
}
|
||||
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
|
||||
string stmt = "out += \"";
|
||||
if (!comma.empty()) stmt += "\", \"+ ";
|
||||
// comma = ", "; // Nothing further so not needed
|
||||
stmt += nodep->extendsp()->dtypep()->nameProtect();
|
||||
stmt += "::to_string_middle();\n";
|
||||
nodep->user1(true); // So what we extend dumps this
|
||||
funcp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
||||
}
|
||||
funcp->addStmtsp(new AstCStmt(nodep->fileline(), "return out;\n"));
|
||||
nodep->addStmtp(funcp);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
||||
|
|
@ -92,6 +196,12 @@ class CUseVisitor : public AstNVisitor {
|
|||
usep->protect(false);
|
||||
}
|
||||
makeUseCells(nodep);
|
||||
{ CUseDTypeVisitor dtypeVisitor(nodep, m_state); }
|
||||
if (AstClass* classp = VN_CAST(nodep, Class)) {
|
||||
makeVlToString(classp);
|
||||
makeToString(classp);
|
||||
makeToStringMiddle(classp);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode*) VL_OVERRIDE {} // All in AstNodeModule
|
||||
|
||||
|
|
|
|||
218
src/V3Case.cpp
218
src/V3Case.cpp
|
|
@ -45,9 +45,9 @@
|
|||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
|
||||
#define CASE_OVERLAP_WIDTH 16 // Maximum width we can check for overlaps in
|
||||
#define CASE_BARF 999999 // Magic width when non-constant
|
||||
#define CASE_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree
|
||||
#define CASE_OVERLAP_WIDTH 16 // Maximum width we can check for overlaps in
|
||||
#define CASE_BARF 999999 // Magic width when non-constant
|
||||
#define CASE_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
|
@ -64,8 +64,8 @@ private:
|
|||
}
|
||||
// Detect multiple defaults
|
||||
bool hitDefault = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp();
|
||||
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (itemp->isDefault()) {
|
||||
if (hitDefault) {
|
||||
itemp->v3error("Multiple default statements in case statement.");
|
||||
|
|
@ -78,8 +78,8 @@ private:
|
|||
{
|
||||
m_caseExprp = nodep;
|
||||
iterate(nodep->exprp());
|
||||
for (AstCaseItem* itemp = nodep->itemsp();
|
||||
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
iterateAndNextNull(itemp->condsp());
|
||||
}
|
||||
m_caseExprp = NULL;
|
||||
|
|
@ -89,16 +89,20 @@ private:
|
|||
// See also neverItem
|
||||
if (m_caseExprp && nodep->num().isFourState()) {
|
||||
if (VN_IS(m_caseExprp, GenCase)) {
|
||||
nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')");
|
||||
nodep->v3error("Use of x/? constant in generate case statement, "
|
||||
"(no such thing as 'generate casez')");
|
||||
} else if (VN_IS(m_caseExprp, Case) && VN_CAST(m_caseExprp, Case)->casex()) {
|
||||
// Don't sweat it, we already complained about casex in general
|
||||
} else if (VN_IS(m_caseExprp, Case) && (VN_CAST(m_caseExprp, Case)->casez()
|
||||
|| VN_CAST(m_caseExprp, Case)->caseInside())) {
|
||||
} else if (VN_IS(m_caseExprp, Case)
|
||||
&& (VN_CAST(m_caseExprp, Case)->casez()
|
||||
|| VN_CAST(m_caseExprp, Case)->caseInside())) {
|
||||
if (nodep->num().isUnknown()) {
|
||||
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)");
|
||||
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, "
|
||||
"(perhaps intended ?/z in constant)");
|
||||
}
|
||||
} else {
|
||||
nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, (perhaps intended casex/casez)");
|
||||
nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, "
|
||||
"(perhaps intended casex/casez)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -121,17 +125,18 @@ private:
|
|||
// NODE STATE
|
||||
// Cleared each Case
|
||||
// AstIf::user3() -> bool. Set true to indicate clone not needed
|
||||
AstUser3InUse m_inuser3;
|
||||
AstUser3InUse m_inuser3;
|
||||
|
||||
// STATE
|
||||
VDouble0 m_statCaseFast; // Statistic tracking
|
||||
VDouble0 m_statCaseSlow; // Statistic tracking
|
||||
|
||||
// Per-CASE
|
||||
int m_caseWidth; // Width of valueItems
|
||||
int m_caseItems; // Number of caseItem unique values
|
||||
bool m_caseNoOverlapsAllCovered; // Proven to be synopsys parallel_case compliant
|
||||
AstNode* m_valueItem[1<<CASE_OVERLAP_WIDTH]; // For each possible value, the case branch we need
|
||||
int m_caseWidth; // Width of valueItems
|
||||
int m_caseItems; // Number of caseItem unique values
|
||||
bool m_caseNoOverlapsAllCovered; // Proven to be synopsys parallel_case compliant
|
||||
// For each possible value, the case branch we need
|
||||
AstNode* m_valueItem[1 << CASE_OVERLAP_WIDTH];
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
|
@ -141,9 +146,9 @@ private:
|
|||
bool opaque = false;
|
||||
m_caseItems = 0;
|
||||
m_caseNoOverlapsAllCovered = true;
|
||||
for (AstCaseItem* itemp = nodep->itemsp();
|
||||
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondp->nextp()) {
|
||||
if (icondp->width() > width) width = icondp->width();
|
||||
if (icondp->isDouble()) opaque = true;
|
||||
if (!VN_IS(icondp, Const)) width = CASE_BARF; // Can't parse; not a constant
|
||||
|
|
@ -151,38 +156,39 @@ private:
|
|||
}
|
||||
}
|
||||
m_caseWidth = width;
|
||||
if (width==0 || width > CASE_OVERLAP_WIDTH || opaque) {
|
||||
if (width == 0 || width > CASE_OVERLAP_WIDTH || opaque) {
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false; // Too wide for analysis
|
||||
}
|
||||
UINFO(8,"Simple case statement: "<<nodep<<endl);
|
||||
UINFO(8, "Simple case statement: " << nodep << endl);
|
||||
// Zero list of items for each value
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) m_valueItem[i] = NULL;
|
||||
for (uint32_t i = 0; i < (1UL << m_caseWidth); ++i) m_valueItem[i] = NULL;
|
||||
// Now pick up the values for each assignment
|
||||
// We can cheat and use uint32_t's because we only support narrow case's
|
||||
bool bitched = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp();
|
||||
itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) {
|
||||
//if (debug()>=9) icondp->dumpTree(cout, " caseitem: ");
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondp->nextp()) {
|
||||
// if (debug() >= 9) icondp->dumpTree(cout, " caseitem: ");
|
||||
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||
UASSERT_OBJ(iconstp, nodep, "above 'can't parse' should have caught this");
|
||||
if (neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
} else {
|
||||
V3Number nummask (itemp, iconstp->width());
|
||||
V3Number nummask(itemp, iconstp->width());
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
uint32_t mask = nummask.toUInt();
|
||||
V3Number numval (itemp, iconstp->width());
|
||||
V3Number numval(itemp, iconstp->width());
|
||||
numval.opBitsOne(iconstp->num());
|
||||
uint32_t val = numval.toUInt();
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
uint32_t val = numval.toUInt();
|
||||
for (uint32_t i = 0; i < (1UL << m_caseWidth); ++i) {
|
||||
if ((i & mask) == val) {
|
||||
if (!m_valueItem[i]) {
|
||||
m_valueItem[i] = itemp;
|
||||
} else if (!itemp->ignoreOverlap() && !bitched) {
|
||||
icondp->v3warn(CASEOVERLAP, "Case values overlap (example pattern 0x"
|
||||
<<std::hex<<i<<")");
|
||||
icondp->v3warn(CASEOVERLAP,
|
||||
"Case values overlap (example pattern 0x"
|
||||
<< std::hex << i << ")");
|
||||
bitched = true;
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
}
|
||||
|
|
@ -192,15 +198,16 @@ private:
|
|||
}
|
||||
// Defaults were moved to last in the caseitem list by V3LinkDot
|
||||
if (itemp->isDefault()) { // Case statement's default... Fill the table
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
for (uint32_t i = 0; i < (1UL << m_caseWidth); ++i) {
|
||||
if (!m_valueItem[i]) m_valueItem[i] = itemp;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
for (uint32_t i = 0; i < (1UL << m_caseWidth); ++i) {
|
||||
if (!m_valueItem[i]) {
|
||||
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered (example pattern 0x"
|
||||
<<std::hex<<i<<")");
|
||||
nodep->v3warn(CASEINCOMPLETE, "Case values incompletely covered "
|
||||
"(example pattern 0x"
|
||||
<< std::hex << i << ")");
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -213,24 +220,23 @@ private:
|
|||
|
||||
// Convert valueItem from AstCaseItem* to the expression
|
||||
// Not done earlier, as we may now have a NULL because it's just a ";" NOP branch
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
for (uint32_t i = 0; i < (1UL << m_caseWidth); ++i) {
|
||||
m_valueItem[i] = VN_CAST(m_valueItem[i], CaseItem)->bodysp();
|
||||
}
|
||||
return true; // All is fine
|
||||
}
|
||||
|
||||
AstNode* replaceCaseFastRecurse(AstNode* cexprp, int msb, uint32_t upperValue) {
|
||||
if (msb<0) {
|
||||
if (msb < 0) {
|
||||
// There's no space for a IF. We know upperValue is thus down to a specific
|
||||
// exact value, so just return the tree value
|
||||
// Note can't clone here, as we're going to check for equivalence above
|
||||
return m_valueItem[upperValue];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Make left and right subtrees
|
||||
// cexpr[msb:lsb] == 1
|
||||
AstNode* tree0p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | 0);
|
||||
AstNode* tree1p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | (1UL<<msb));
|
||||
AstNode* tree0p = replaceCaseFastRecurse(cexprp, msb - 1, upperValue | 0);
|
||||
AstNode* tree1p = replaceCaseFastRecurse(cexprp, msb - 1, upperValue | (1UL << msb));
|
||||
|
||||
if (tree0p == tree1p) {
|
||||
// Same logic on both sides, so we can just return one of 'em
|
||||
|
|
@ -238,11 +244,12 @@ private:
|
|||
}
|
||||
// We could have a "checkerboard" with A B A B, we can use the same IF on both edges
|
||||
bool same = true;
|
||||
for (uint32_t a=upperValue,
|
||||
b=(upperValue|(1UL<<msb));
|
||||
a < (upperValue|(1UL<<msb));
|
||||
a++, b++) {
|
||||
if (m_valueItem[a] != m_valueItem[b]) { same = false; break; }
|
||||
for (uint32_t a = upperValue, b = (upperValue | (1UL << msb));
|
||||
a < (upperValue | (1UL << msb)); a++, b++) {
|
||||
if (m_valueItem[a] != m_valueItem[b]) {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (same) {
|
||||
VL_DO_DANGLING(tree1p->deleteTree(), tree1p);
|
||||
|
|
@ -256,14 +263,12 @@ private:
|
|||
if (tree1p && !tree1p->user3()) tree1p = tree1p->cloneTree(true);
|
||||
|
||||
// Alternate scheme if we ever do multiple bits at a time:
|
||||
//V3Number nummask (cexprp, cexprp->width(), (1UL<<msb));
|
||||
//AstNode* and1p = new AstAnd(cexprp->fileline(), cexprp->cloneTree(false),
|
||||
// V3Number nummask (cexprp, cexprp->width(), (1UL<<msb));
|
||||
// AstNode* and1p = new AstAnd(cexprp->fileline(), cexprp->cloneTree(false),
|
||||
// new AstConst(cexprp->fileline(), nummask));
|
||||
AstNode* and1p = new AstSel(cexprp->fileline(), cexprp->cloneTree(false),
|
||||
msb, 1);
|
||||
AstNode* eqp = new AstNeq(cexprp->fileline(),
|
||||
new AstConst(cexprp->fileline(), 0),
|
||||
and1p);
|
||||
AstNode* and1p = new AstSel(cexprp->fileline(), cexprp->cloneTree(false), msb, 1);
|
||||
AstNode* eqp
|
||||
= new AstNeq(cexprp->fileline(), new AstConst(cexprp->fileline(), 0), and1p);
|
||||
AstIf* ifp = new AstIf(cexprp->fileline(), eqp, tree1p, tree0p);
|
||||
ifp->user3(1); // So we don't bother to clone it
|
||||
return ifp;
|
||||
|
|
@ -276,10 +281,10 @@ private:
|
|||
// IF(msb-1, 01, 00))
|
||||
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
||||
|
||||
if (debug()>=9) {
|
||||
for (uint32_t i=0; i<(1UL<<m_caseWidth); ++i) {
|
||||
if (debug() >= 9) {
|
||||
for (uint32_t i = 0; i < (1UL << m_caseWidth); ++i) {
|
||||
if (AstNode* itemp = m_valueItem[i]) {
|
||||
UINFO(9,"Value "<<std::hex<<i<<" "<<itemp<<endl);
|
||||
UINFO(9, "Value " << std::hex << i << " " << itemp << endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -288,15 +293,18 @@ private:
|
|||
replaceCaseParallel(nodep, m_caseNoOverlapsAllCovered);
|
||||
|
||||
AstNode::user3ClearTree();
|
||||
AstNode* ifrootp = replaceCaseFastRecurse(cexprp, m_caseWidth-1, 0UL);
|
||||
AstNode* ifrootp = replaceCaseFastRecurse(cexprp, m_caseWidth - 1, 0UL);
|
||||
// Case expressions can't be linked twice, so clone them
|
||||
if (ifrootp && !ifrootp->user3()) ifrootp = ifrootp->cloneTree(true);
|
||||
|
||||
if (ifrootp) nodep->replaceWith(ifrootp);
|
||||
else nodep->unlinkFrBack();
|
||||
if (ifrootp) {
|
||||
nodep->replaceWith(ifrootp);
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(cexprp->deleteTree(), cexprp);
|
||||
if (debug()>=9) ifrootp->dumpTree(cout, " _simp: ");
|
||||
if (debug() >= 9) ifrootp->dumpTree(cout, " _simp: ");
|
||||
}
|
||||
|
||||
void replaceCaseComplicated(AstCase* nodep) {
|
||||
|
|
@ -307,9 +315,10 @@ private:
|
|||
AstNode* cexprp = nodep->exprp()->unlinkFrBack();
|
||||
// We'll do this in two stages. First stage, convert the conditions to
|
||||
// the appropriate IF AND terms.
|
||||
if (debug()>=9) nodep->dumpTree(cout, " _comp_IN: ");
|
||||
if (debug() >= 9) nodep->dumpTree(cout, " _comp_IN: ");
|
||||
bool hadDefault = false;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
if (!itemp->condsp()) {
|
||||
// Default clause. Just make true, we'll optimize it away later
|
||||
itemp->condsp(new AstConst(itemp->fileline(), AstConst::LogicTrue()));
|
||||
|
|
@ -318,7 +327,7 @@ private:
|
|||
// Expressioned clause
|
||||
AstNode* icondNextp = NULL;
|
||||
AstNode* ifexprp = NULL; // If expression to test
|
||||
for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondNextp) {
|
||||
for (AstNode* icondp = itemp->condsp(); icondp; icondp = icondNextp) {
|
||||
icondNextp = icondp->nextp();
|
||||
icondp->unlinkFrBack();
|
||||
|
||||
|
|
@ -326,7 +335,8 @@ private:
|
|||
AstConst* iconstp = VN_CAST(icondp, Const);
|
||||
if (iconstp && neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
VL_DO_DANGLING(icondp->deleteTree(), icondp); VL_DANGLING(iconstp);
|
||||
VL_DO_DANGLING(icondp->deleteTree(), icondp);
|
||||
VL_DANGLING(iconstp);
|
||||
// For simplicity, make expression that is not equal, and let later
|
||||
// optimizations remove it
|
||||
condp = new AstConst(itemp->fileline(), AstConst::LogicFalse());
|
||||
|
|
@ -336,16 +346,17 @@ private:
|
|||
irangep->rhsp()->unlinkFrBack());
|
||||
} else if (iconstp && iconstp->num().isFourState()
|
||||
&& (nodep->casex() || nodep->casez() || nodep->caseInside())) {
|
||||
V3Number nummask (itemp, iconstp->width());
|
||||
V3Number nummask(itemp, iconstp->width());
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
V3Number numval (itemp, iconstp->width());
|
||||
V3Number numval(itemp, iconstp->width());
|
||||
numval.opBitsOne(iconstp->num());
|
||||
AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
AstNode* and2p = new AstAnd(itemp->fileline(),
|
||||
new AstConst(itemp->fileline(), numval),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
VL_DO_DANGLING(icondp->deleteTree(), icondp); VL_DANGLING(iconstp);
|
||||
VL_DO_DANGLING(icondp->deleteTree(), icondp);
|
||||
VL_DANGLING(iconstp);
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
} else {
|
||||
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
|
||||
|
|
@ -367,11 +378,10 @@ private:
|
|||
if (!hadDefault) {
|
||||
// If there was no default, add a empty one, this greatly simplifies below code
|
||||
// and constant propagation will just eliminate it for us later.
|
||||
nodep->addItemsp(new AstCaseItem(nodep->fileline(),
|
||||
new AstConst(nodep->fileline(), AstConst::LogicTrue()),
|
||||
NULL));
|
||||
nodep->addItemsp(new AstCaseItem(
|
||||
nodep->fileline(), new AstConst(nodep->fileline(), AstConst::LogicTrue()), NULL));
|
||||
}
|
||||
if (debug()>=9) nodep->dumpTree(cout, " _comp_COND: ");
|
||||
if (debug() >= 9) nodep->dumpTree(cout, " _comp_COND: ");
|
||||
// Now build the IF statement tree
|
||||
// The tree can be quite huge. Pull ever group of 8 out, and make a OR tree.
|
||||
// This reduces the depth for the bottom elements, at the cost of
|
||||
|
|
@ -382,46 +392,56 @@ private:
|
|||
AstNode* grouprootp = NULL;
|
||||
AstIf* groupnextp = NULL;
|
||||
AstIf* itemnextp = NULL;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), CaseItem)) {
|
||||
AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action.
|
||||
if (istmtsp) istmtsp->unlinkFrBackWithNext();
|
||||
// Expressioned clause
|
||||
AstNode* ifexprp = itemp->condsp()->unlinkFrBack();
|
||||
{ // Prepare for next group
|
||||
{ // Prepare for next group
|
||||
if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1;
|
||||
if (depth == 1) { // First group or starting new group
|
||||
itemnextp = NULL;
|
||||
AstIf* newp = new AstIf(itemp->fileline(),
|
||||
ifexprp->cloneTree(true), NULL, NULL);
|
||||
if (groupnextp) groupnextp->addElsesp(newp);
|
||||
else grouprootp = newp;
|
||||
AstIf* newp
|
||||
= new AstIf(itemp->fileline(), ifexprp->cloneTree(true), NULL, NULL);
|
||||
if (groupnextp) {
|
||||
groupnextp->addElsesp(newp);
|
||||
} else {
|
||||
grouprootp = newp;
|
||||
}
|
||||
groupnextp = newp;
|
||||
} else { // Continue group, modify if condition to OR in this new condition
|
||||
AstNode* condp = groupnextp->condp()->unlinkFrBack();
|
||||
groupnextp->condp(new AstOr(ifexprp->fileline(),
|
||||
condp,
|
||||
ifexprp->cloneTree(true)));
|
||||
groupnextp->condp(
|
||||
new AstOr(ifexprp->fileline(), condp, ifexprp->cloneTree(true)));
|
||||
}
|
||||
}
|
||||
{ // Make the new lower IF and attach in the tree
|
||||
AstNode* itemexprp = ifexprp; VL_DANGLING(ifexprp);
|
||||
if (depth == (CASE_ENCODER_GROUP_DEPTH)) { // End of group - can skip the condition
|
||||
{ // Make the new lower IF and attach in the tree
|
||||
AstNode* itemexprp = ifexprp;
|
||||
VL_DANGLING(ifexprp);
|
||||
if (depth == CASE_ENCODER_GROUP_DEPTH) { // End of group - can skip the condition
|
||||
VL_DO_DANGLING(itemexprp->deleteTree(), itemexprp);
|
||||
itemexprp = new AstConst(itemp->fileline(), AstConst::LogicTrue());
|
||||
}
|
||||
AstIf* newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, NULL);
|
||||
if (itemnextp) itemnextp->addElsesp(newp);
|
||||
else groupnextp->addIfsp(newp); // First in a new group
|
||||
if (itemnextp) {
|
||||
itemnextp->addElsesp(newp);
|
||||
} else {
|
||||
groupnextp->addIfsp(newp); // First in a new group
|
||||
}
|
||||
itemnextp = newp;
|
||||
}
|
||||
}
|
||||
if (debug()>=9) nodep->dumpTree(cout, " _comp_TREE: ");
|
||||
if (debug() >= 9) nodep->dumpTree(cout, " _comp_TREE: ");
|
||||
// Handle any assertions
|
||||
replaceCaseParallel(nodep, false);
|
||||
// Replace the CASE... with IF...
|
||||
if (debug()>=9 && grouprootp) grouprootp->dumpTree(cout, " _new: ");
|
||||
if (grouprootp) nodep->replaceWith(grouprootp);
|
||||
else nodep->unlinkFrBack();
|
||||
if (debug() >= 9 && grouprootp) grouprootp->dumpTree(cout, " _new: ");
|
||||
if (grouprootp) {
|
||||
nodep->replaceWith(grouprootp);
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
|
||||
|
|
@ -451,7 +471,7 @@ private:
|
|||
virtual void visit(AstCase* nodep) VL_OVERRIDE {
|
||||
V3Case::caseLint(nodep);
|
||||
iterateChildren(nodep);
|
||||
if (debug()>=9) nodep->dumpTree(cout, " case_old: ");
|
||||
if (debug() >= 9) nodep->dumpTree(cout, " case_old: ");
|
||||
if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) {
|
||||
// It's a simple priority encoder or complete statement
|
||||
// we can make a tree of statements to avoid extra comparisons
|
||||
|
|
@ -471,9 +491,7 @@ public:
|
|||
m_caseWidth = 0;
|
||||
m_caseItems = 0;
|
||||
m_caseNoOverlapsAllCovered = false;
|
||||
for (uint32_t i=0; i<(1UL<<CASE_OVERLAP_WIDTH); ++i) {
|
||||
m_valueItem[i] = NULL;
|
||||
}
|
||||
for (uint32_t i = 0; i < (1UL << CASE_OVERLAP_WIDTH); ++i) m_valueItem[i] = NULL;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CaseVisitor() {
|
||||
|
|
@ -486,13 +504,11 @@ public:
|
|||
// Case class functions
|
||||
|
||||
void V3Case::caseAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
CaseVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CaseVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("case", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
void V3Case::caseLint(AstNodeCase* nodep) {
|
||||
UINFO(4,__FUNCTION__<<": "<<endl);
|
||||
CaseLintVisitor visitor (nodep);
|
||||
UINFO(4, __FUNCTION__ << ": " << endl);
|
||||
CaseLintVisitor visitor(nodep);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ private:
|
|||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstNode::user() // bool. Indicates node is of known size
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
|
||||
|
|
@ -63,26 +63,30 @@ private:
|
|||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node
|
||||
UINFO(4," NeedCast "<<nodep<<endl);
|
||||
UINFO(4, " NeedCast " << nodep << endl);
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
//
|
||||
AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin());
|
||||
relinkHandle.relink(castp);
|
||||
//if (debug()>8) castp->dumpTree(cout, "-castins: ");
|
||||
// if (debug() > 8) castp->dumpTree(cout, "-castins: ");
|
||||
//
|
||||
ensureLower32Cast(castp);
|
||||
nodep->user1(1); // Now must be of known size
|
||||
}
|
||||
int castSize(AstNode* nodep) {
|
||||
if (nodep->isQuad()) return VL_QUADSIZE;
|
||||
else if (nodep->width() <= 8) return 8;
|
||||
else if (nodep->width() <= 16) return 16;
|
||||
else return VL_IDATASIZE;
|
||||
if (nodep->isQuad()) {
|
||||
return VL_QUADSIZE;
|
||||
} else if (nodep->width() <= 8) {
|
||||
return 8;
|
||||
} else if (nodep->width() <= 16) {
|
||||
return 16;
|
||||
} else {
|
||||
return VL_IDATASIZE;
|
||||
}
|
||||
}
|
||||
void ensureCast(AstNode* nodep) {
|
||||
if (castSize(nodep->backp()) != castSize(nodep)
|
||||
|| !nodep->user1()) {
|
||||
if (castSize(nodep->backp()) != castSize(nodep) || !nodep->user1()) {
|
||||
insertCast(nodep, castSize(nodep->backp()));
|
||||
}
|
||||
}
|
||||
|
|
@ -91,11 +95,19 @@ private:
|
|||
// really needs to be CAST(uint64(CAST(uint32(x))).
|
||||
// Otherwise a (uint64)(a>b) would return wrong value, as
|
||||
// less than has nondeterministic signedness.
|
||||
if (nodep->isQuad() && !nodep->lhsp()->isQuad()
|
||||
&& !VN_IS(nodep->lhsp(), CCast)) {
|
||||
if (nodep->isQuad() && !nodep->lhsp()->isQuad() && !VN_IS(nodep->lhsp(), CCast)) {
|
||||
insertCast(nodep->lhsp(), VL_IDATASIZE);
|
||||
}
|
||||
}
|
||||
void ensureNullChecked(AstNode* nodep) {
|
||||
// TODO optimize to track null checked values and avoid where possible
|
||||
if (!VN_IS(nodep->backp(), NullCheck)) {
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
AstNode* newp = new AstNullCheck(nodep->fileline(), nodep);
|
||||
relinkHandle.relink(newp);
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeUniop* nodep) VL_OVERRIDE {
|
||||
|
|
@ -105,16 +117,13 @@ private:
|
|||
}
|
||||
virtual void visit(AstNodeBiop* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1()
|
||||
| nodep->rhsp()->user1());
|
||||
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
|
||||
}
|
||||
virtual void visit(AstNodeTriop* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1()
|
||||
| nodep->rhsp()->user1()
|
||||
| nodep->thsp()->user1());
|
||||
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1());
|
||||
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
|
||||
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
|
||||
if (nodep->sizeMattersThs()) ensureCast(nodep->thsp());
|
||||
|
|
@ -127,7 +136,7 @@ private:
|
|||
virtual void visit(AstNegate* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
if (nodep->lhsp()->widthMin()==1) {
|
||||
if (nodep->lhsp()->widthMin() == 1) {
|
||||
// We want to avoid a GCC "converting of negative value" warning
|
||||
// from our expansion of
|
||||
// out = {32{a<b}} => out = - (a<b)
|
||||
|
|
@ -137,11 +146,8 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstVarRef* nodep) VL_OVERRIDE {
|
||||
if (!nodep->lvalue()
|
||||
&& !VN_IS(nodep->backp(), CCast)
|
||||
&& VN_IS(nodep->backp(), NodeMath)
|
||||
&& !VN_IS(nodep->backp(), ArraySel)
|
||||
&& nodep->backp()->width()
|
||||
if (!nodep->lvalue() && !VN_IS(nodep->backp(), CCast) && VN_IS(nodep->backp(), NodeMath)
|
||||
&& !VN_IS(nodep->backp(), ArraySel) && nodep->backp()->width()
|
||||
&& castSize(nodep) != castSize(nodep->varp())) {
|
||||
// Cast vars to IData first, else below has upper bits wrongly set
|
||||
// CData x=3; out = (QData)(x<<30);
|
||||
|
|
@ -156,6 +162,20 @@ private:
|
|||
nodep->user1(nodep->isQuad() || nodep->isWide());
|
||||
}
|
||||
|
||||
// Null dereference protection
|
||||
virtual void visit(AstNullCheck* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
nodep->user1(nodep->lhsp()->user1());
|
||||
}
|
||||
virtual void visit(AstCMethodCall* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
ensureNullChecked(nodep->fromp());
|
||||
}
|
||||
virtual void visit(AstMemberSel* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
ensureNullChecked(nodep->fromp());
|
||||
}
|
||||
|
||||
// NOPs
|
||||
virtual void visit(AstVar*) VL_OVERRIDE {}
|
||||
|
||||
|
|
@ -164,9 +184,7 @@ private:
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CastVisitor(AstNetlist* nodep) {
|
||||
iterate(nodep);
|
||||
}
|
||||
explicit CastVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~CastVisitor() {}
|
||||
};
|
||||
|
||||
|
|
@ -174,9 +192,7 @@ public:
|
|||
// Cast class functions
|
||||
|
||||
void V3Cast::castAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
CastVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CastVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("cast", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
368
src/V3Cdc.cpp
368
src/V3Cdc.cpp
|
|
@ -40,7 +40,7 @@
|
|||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#define CDC_WEIGHT_ASYNC 0x1000 // Weight for edges that feed async logic
|
||||
#define CDC_WEIGHT_ASYNC 0x1000 // Weight for edges that feed async logic
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
|
@ -53,18 +53,23 @@ public:
|
|||
// Graph support classes
|
||||
|
||||
class CdcEitherVertex : public V3GraphVertex {
|
||||
AstScope* m_scopep;
|
||||
AstNode* m_nodep;
|
||||
AstScope* m_scopep;
|
||||
AstNode* m_nodep;
|
||||
AstSenTree* m_srcDomainp;
|
||||
AstSenTree* m_dstDomainp;
|
||||
bool m_srcDomainSet:1;
|
||||
bool m_dstDomainSet:1;
|
||||
bool m_asyncPath:1;
|
||||
bool m_srcDomainSet : 1;
|
||||
bool m_dstDomainSet : 1;
|
||||
bool m_asyncPath : 1;
|
||||
|
||||
public:
|
||||
CdcEitherVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep)
|
||||
: V3GraphVertex(graphp), m_scopep(scopep), m_nodep(nodep)
|
||||
, m_srcDomainp(NULL), m_dstDomainp(NULL)
|
||||
, m_srcDomainSet(false), m_dstDomainSet(false)
|
||||
: V3GraphVertex(graphp)
|
||||
, m_scopep(scopep)
|
||||
, m_nodep(nodep)
|
||||
, m_srcDomainp(NULL)
|
||||
, m_dstDomainp(NULL)
|
||||
, m_srcDomainSet(false)
|
||||
, m_dstDomainSet(false)
|
||||
, m_asyncPath(false) {}
|
||||
virtual ~CdcEitherVertex() {}
|
||||
// ACCESSORS
|
||||
|
|
@ -85,18 +90,22 @@ public:
|
|||
|
||||
class CdcVarVertex : public CdcEitherVertex {
|
||||
AstVarScope* m_varScp;
|
||||
int m_cntAsyncRst;
|
||||
bool m_fromFlop;
|
||||
int m_cntAsyncRst;
|
||||
bool m_fromFlop;
|
||||
|
||||
public:
|
||||
CdcVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||
: CdcEitherVertex(graphp, scopep, varScp)
|
||||
, m_varScp(varScp), m_cntAsyncRst(0), m_fromFlop(false) {}
|
||||
, m_varScp(varScp)
|
||||
, m_cntAsyncRst(0)
|
||||
, m_fromFlop(false) {}
|
||||
virtual ~CdcVarVertex() {}
|
||||
// ACCESSORS
|
||||
AstVarScope* varScp() const { return m_varScp; }
|
||||
virtual string name() const { return (cvtToHex(m_varScp)+" "+varScp()->name()); }
|
||||
virtual string name() const { return (cvtToHex(m_varScp) + " " + varScp()->name()); }
|
||||
virtual string dotColor() const {
|
||||
return fromFlop() ? "green" : cntAsyncRst() ? "red" : "blue"; }
|
||||
return fromFlop() ? "green" : cntAsyncRst() ? "red" : "blue";
|
||||
}
|
||||
int cntAsyncRst() const { return m_cntAsyncRst; }
|
||||
void cntAsyncRst(int flag) { m_cntAsyncRst = flag; }
|
||||
bool fromFlop() const { return m_fromFlop; }
|
||||
|
|
@ -104,19 +113,26 @@ public:
|
|||
};
|
||||
|
||||
class CdcLogicVertex : public CdcEitherVertex {
|
||||
bool m_hazard:1;
|
||||
bool m_isFlop:1;
|
||||
bool m_hazard : 1;
|
||||
bool m_isFlop : 1;
|
||||
|
||||
public:
|
||||
CdcLogicVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep, AstSenTree* sensenodep)
|
||||
: CdcEitherVertex(graphp, scopep, nodep)
|
||||
, m_hazard(false), m_isFlop(false)
|
||||
{ srcDomainp(sensenodep); dstDomainp(sensenodep); }
|
||||
, m_hazard(false)
|
||||
, m_isFlop(false) {
|
||||
srcDomainp(sensenodep);
|
||||
dstDomainp(sensenodep);
|
||||
}
|
||||
virtual ~CdcLogicVertex() {}
|
||||
// ACCESSORS
|
||||
virtual string name() const { return (cvtToHex(nodep())+"@"+scopep()->prettyName()); }
|
||||
virtual string name() const { return (cvtToHex(nodep()) + "@" + scopep()->prettyName()); }
|
||||
virtual string dotColor() const { return hazard() ? "black" : "yellow"; }
|
||||
bool hazard() const { return m_hazard; }
|
||||
void setHazard(AstNode* nodep) { m_hazard = true; nodep->user3(true); }
|
||||
void setHazard(AstNode* nodep) {
|
||||
m_hazard = true;
|
||||
nodep->user3(true);
|
||||
}
|
||||
void clearHazard() { m_hazard = false; }
|
||||
bool isFlop() const { return m_isFlop; }
|
||||
void isFlop(bool flag) { m_isFlop = flag; }
|
||||
|
|
@ -127,16 +143,19 @@ public:
|
|||
class CdcDumpVisitor : public CdcBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
//Entire netlist:
|
||||
// Entire netlist:
|
||||
// {statement}Node::user3 -> bool, indicating not hazard
|
||||
std::ofstream* m_ofp; // Output file
|
||||
string m_prefix;
|
||||
string m_prefix;
|
||||
|
||||
virtual void visit(AstNode* nodep) VL_OVERRIDE {
|
||||
*m_ofp<<m_prefix;
|
||||
if (nodep->user3()) *m_ofp<<" %%";
|
||||
else *m_ofp<<" ";
|
||||
*m_ofp<<nodep->prettyTypeName()<<" "<<endl;
|
||||
*m_ofp << m_prefix;
|
||||
if (nodep->user3()) {
|
||||
*m_ofp << " %%";
|
||||
} else {
|
||||
*m_ofp << " ";
|
||||
}
|
||||
*m_ofp << nodep->prettyTypeName() << " " << endl;
|
||||
string lastPrefix = m_prefix;
|
||||
m_prefix = lastPrefix + "1:";
|
||||
iterateAndNextNull(nodep->op1p());
|
||||
|
|
@ -163,19 +182,20 @@ public:
|
|||
|
||||
class CdcWidthVisitor : public CdcBaseVisitor {
|
||||
private:
|
||||
int m_maxLineno;
|
||||
size_t m_maxFilenameLen;
|
||||
int m_maxLineno;
|
||||
size_t m_maxFilenameLen;
|
||||
|
||||
virtual void visit(AstNode* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
// Keeping line+filename lengths separate is much faster than calling ascii().length()
|
||||
if (nodep->fileline()->lineno() >= m_maxLineno) {
|
||||
m_maxLineno = nodep->fileline()->lineno()+1;
|
||||
m_maxLineno = nodep->fileline()->lineno() + 1;
|
||||
}
|
||||
if (nodep->fileline()->filename().length() >= m_maxFilenameLen) {
|
||||
m_maxFilenameLen = nodep->fileline()->filename().length()+1;
|
||||
m_maxFilenameLen = nodep->fileline()->filename().length() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CdcWidthVisitor(AstNode* nodep) {
|
||||
|
|
@ -201,32 +221,32 @@ public:
|
|||
class CdcVisitor : public CdcBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
//Entire netlist:
|
||||
// Entire netlist:
|
||||
// AstVarScope::user1p -> CdcVarVertex* for usage var, 0=not set yet
|
||||
// AstVarScope::user2 -> bool Used in sensitivity list
|
||||
// {statement}Node::user1p -> CdcLogicVertex* for this statement
|
||||
// AstNode::user3 -> bool True indicates to print %% (via V3EmitV)
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
|
||||
// STATE
|
||||
V3Graph m_graph; // Scoreboard of var usages/dependencies
|
||||
CdcLogicVertex* m_logicVertexp; // Current statement being tracked, NULL=ignored
|
||||
AstScope* m_scopep; // Current scope being processed
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstSenTree* m_domainp; // Current sentree
|
||||
bool m_inDly; // In delayed assign
|
||||
int m_inSenItem; // Number of senitems
|
||||
string m_ofFilename; // Output filename
|
||||
std::ofstream* m_ofp; // Output file
|
||||
uint32_t m_userGeneration; // Generation count to avoid slow userClearVertices
|
||||
int m_filelineWidth; // Characters in longest fileline
|
||||
V3Graph m_graph; // Scoreboard of var usages/dependencies
|
||||
CdcLogicVertex* m_logicVertexp; // Current statement being tracked, NULL=ignored
|
||||
AstScope* m_scopep; // Current scope being processed
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstSenTree* m_domainp; // Current sentree
|
||||
bool m_inDly; // In delayed assign
|
||||
int m_inSenItem; // Number of senitems
|
||||
string m_ofFilename; // Output filename
|
||||
std::ofstream* m_ofp; // Output file
|
||||
uint32_t m_userGeneration; // Generation count to avoid slow userClearVertices
|
||||
int m_filelineWidth; // Characters in longest fileline
|
||||
|
||||
// METHODS
|
||||
void iterateNewStmt(AstNode* nodep) {
|
||||
if (m_scopep) {
|
||||
UINFO(4," STMT "<<nodep<<endl);
|
||||
UINFO(4, " STMT " << nodep << endl);
|
||||
m_logicVertexp = new CdcLogicVertex(&m_graph, m_scopep, nodep, m_domainp);
|
||||
if (m_domainp && m_domainp->hasClocked()) { // To/from a flop
|
||||
m_logicVertexp->isFlop(true);
|
||||
|
|
@ -238,7 +258,7 @@ private:
|
|||
iterateChildren(nodep);
|
||||
m_logicVertexp = NULL;
|
||||
|
||||
if (0 && debug()>=9) {
|
||||
if (0 && debug() >= 9) {
|
||||
UINFO(9, "Trace Logic:\n");
|
||||
nodep->dumpTree(cout, "-log1: ");
|
||||
}
|
||||
|
|
@ -248,15 +268,15 @@ private:
|
|||
CdcVarVertex* makeVarVertex(AstVarScope* varscp) {
|
||||
CdcVarVertex* vertexp = reinterpret_cast<CdcVarVertex*>(varscp->user1p());
|
||||
if (!vertexp) {
|
||||
UINFO(6,"New vertex "<<varscp<<endl);
|
||||
UINFO(6, "New vertex " << varscp << endl);
|
||||
vertexp = new CdcVarVertex(&m_graph, m_scopep, varscp);
|
||||
varscp->user1p(vertexp);
|
||||
if (varscp->varp()->isUsedClock()) {}
|
||||
if (varscp->varp()->isPrimaryIO()) {
|
||||
// Create IO vertex - note it's relative to the pointed to var, not where we are now
|
||||
// This allows reporting to easily print the input statement
|
||||
CdcLogicVertex* ioVertexp = new CdcLogicVertex(&m_graph, varscp->scopep(),
|
||||
varscp->varp(), NULL);
|
||||
// Create IO vertex - note it's relative to the pointed to var, not where we are
|
||||
// now This allows reporting to easily print the input statement
|
||||
CdcLogicVertex* ioVertexp
|
||||
= new CdcLogicVertex(&m_graph, varscp->scopep(), varscp->varp(), NULL);
|
||||
if (varscp->varp()->isWritable()) {
|
||||
new V3GraphEdge(&m_graph, vertexp, ioVertexp, 1);
|
||||
} else {
|
||||
|
|
@ -266,10 +286,10 @@ private:
|
|||
}
|
||||
if (m_inSenItem) {
|
||||
varscp->user2(true); // It's like a clock...
|
||||
// TODO: In the future we could mark it here and do normal clock tree glitch checks also
|
||||
// TODO: In the future mark it here and do normal clock tree glitch checks also
|
||||
} else if (varscp->user2()) { // It was detected in a sensitivity list earlier
|
||||
// And now it's used as logic. So must be a reset.
|
||||
vertexp->cntAsyncRst(vertexp->cntAsyncRst()+1);
|
||||
vertexp->cntAsyncRst(vertexp->cntAsyncRst() + 1);
|
||||
}
|
||||
return vertexp;
|
||||
}
|
||||
|
|
@ -279,9 +299,9 @@ private:
|
|||
nodep->v3warnCode(code, msg);
|
||||
if (!told_file) {
|
||||
told_file = 1;
|
||||
std::cerr<<V3Error::msgPrefix()<<" See details in "<<m_ofFilename<<endl;
|
||||
std::cerr << V3Error::msgPrefix() << " See details in " << m_ofFilename << endl;
|
||||
}
|
||||
*m_ofp<<"%Warning-"<<code.ascii()<<": "<<nodep->fileline()<<" "<<msg<<endl;
|
||||
*m_ofp << "%Warning-" << code.ascii() << ": " << nodep->fileline() << " " << msg << endl;
|
||||
}
|
||||
|
||||
void setNodeHazard(AstNode* nodep) {
|
||||
|
|
@ -291,28 +311,34 @@ private:
|
|||
// an issue until we find a hitting flop.
|
||||
// Furthermore, a module like a "Or" module would only get flagged
|
||||
// once, even though the signals feeding it are radically different.
|
||||
if (!m_domainp || m_domainp->hasCombo()) { // Source flop logic in a posedge block is OK for reset (not async though)
|
||||
if (!m_domainp || m_domainp->hasCombo()) {
|
||||
// Source flop logic in a posedge block is OK for reset (not async though)
|
||||
if (m_logicVertexp && !nodep->fileline()->warnIsOff(V3ErrorCode::CDCRSTLOGIC)) {
|
||||
UINFO(8,"Set hazard "<<nodep<<endl);
|
||||
UINFO(8, "Set hazard " << nodep << endl);
|
||||
m_logicVertexp->setHazard(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string spaces(int level) { string out; while (level--) out += " "; return out; } // LCOV_EXCL_LINE
|
||||
string spaces(int level) {
|
||||
string out;
|
||||
while (level--) out += " ";
|
||||
return out;
|
||||
} // LCOV_EXCL_LINE
|
||||
|
||||
string pad(unsigned column, const string& in) {
|
||||
string out = in;
|
||||
while (out.length()<column) out += ' ';
|
||||
while (out.length() < column) out += ' ';
|
||||
return out;
|
||||
}
|
||||
|
||||
void analyze() {
|
||||
UINFO(3,__FUNCTION__<<": "<<endl);
|
||||
//if (debug()>6) m_graph.dump();
|
||||
if (debug()>6) m_graph.dumpDotFilePrefixed("cdc_pre");
|
||||
UINFO(3, __FUNCTION__ << ": " << endl);
|
||||
// if (debug() > 6) m_graph.dump();
|
||||
if (debug() > 6) m_graph.dumpDotFilePrefixed("cdc_pre");
|
||||
//
|
||||
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); // This will MAX across edge weights
|
||||
m_graph.removeRedundantEdges(
|
||||
&V3GraphEdge::followAlwaysTrue); // This will MAX across edge weights
|
||||
//
|
||||
m_graph.dumpDotFilePrefixed("cdc_simp");
|
||||
//
|
||||
|
|
@ -321,7 +347,7 @@ private:
|
|||
|
||||
int filelineWidth() {
|
||||
if (!m_filelineWidth) {
|
||||
CdcWidthVisitor visitor (v3Global.rootp());
|
||||
CdcWidthVisitor visitor(v3Global.rootp());
|
||||
m_filelineWidth = visitor.maxWidth();
|
||||
}
|
||||
return m_filelineWidth;
|
||||
|
|
@ -334,15 +360,15 @@ private:
|
|||
// Find all async reset wires, and trace backwards
|
||||
// userClearVertices is very slow, so we use a generation count instead
|
||||
m_graph.userClearVertices(); // user1: uint32_t - was analyzed generation
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
if (vvertexp->cntAsyncRst()) {
|
||||
m_userGeneration++; // Effectively a userClearVertices()
|
||||
UINFO(8, " Trace One async: "<<vvertexp<<endl);
|
||||
UINFO(8, " Trace One async: " << vvertexp << endl);
|
||||
// Twice, as we need to detect, then propagate
|
||||
CdcEitherVertex* markp = traceAsyncRecurse(vvertexp, false);
|
||||
if (markp) { // Mark is non-NULL if something bad on this path
|
||||
UINFO(9, " Trace One bad! "<<vvertexp<<endl);
|
||||
UINFO(9, " Trace One bad! " << vvertexp << endl);
|
||||
m_userGeneration++; // Effectively a userClearVertices()
|
||||
traceAsyncRecurse(vvertexp, true);
|
||||
m_userGeneration++; // Effectively a userClearVertices()
|
||||
|
|
@ -356,11 +382,11 @@ private:
|
|||
CdcEitherVertex* traceAsyncRecurse(CdcEitherVertex* vertexp, bool mark) {
|
||||
// First pass: Return vertex of any hazardous stuff attached, or NULL if OK
|
||||
// If first pass returns true, second pass calls asyncPath() on appropriate nodes
|
||||
if (vertexp->user()>=m_userGeneration) return NULL; // Processed - prevent loop
|
||||
if (vertexp->user() >= m_userGeneration) return NULL; // Processed - prevent loop
|
||||
vertexp->user(m_userGeneration);
|
||||
|
||||
CdcEitherVertex* mark_outp = NULL;
|
||||
UINFO(9," Trace: "<<vertexp<<endl);
|
||||
UINFO(9, " Trace: " << vertexp << endl);
|
||||
|
||||
// Clear out in prep for marking next path
|
||||
if (!mark) vertexp->asyncPath(false);
|
||||
|
|
@ -369,8 +395,7 @@ private:
|
|||
// Any logic considered bad, at the moment, anyhow
|
||||
if (vvertexp->hazard() && !mark_outp) mark_outp = vvertexp;
|
||||
// And keep tracing back so the user can understand what's up
|
||||
}
|
||||
else if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
||||
} else if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
||||
if (mark) vvertexp->asyncPath(true);
|
||||
// If primary I/O, it's ok here back
|
||||
if (vvertexp->varScp()->varp()->isPrimaryInish()) {
|
||||
|
|
@ -403,62 +428,60 @@ private:
|
|||
|
||||
void dumpAsync(CdcVarVertex* vertexp, CdcEitherVertex* markp) {
|
||||
AstNode* nodep = vertexp->varScp();
|
||||
*m_ofp<<"\n";
|
||||
*m_ofp<<"\n";
|
||||
*m_ofp << "\n";
|
||||
*m_ofp << "\n";
|
||||
CdcEitherVertex* targetp = vertexp; // One example destination flop (of possibly many)
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
CdcEitherVertex* eToVertexp = static_cast<CdcEitherVertex*>(edgep->top());
|
||||
if (!eToVertexp) targetp = eToVertexp;
|
||||
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(eToVertexp)) {
|
||||
if (vvertexp->isFlop() // IE the target flop that is upsetting us
|
||||
&& edgep->weight() >= CDC_WEIGHT_ASYNC) { // And this signal feeds an async reset line
|
||||
&& edgep->weight() >= CDC_WEIGHT_ASYNC) { // And var feeds an async reset line
|
||||
targetp = eToVertexp;
|
||||
//UINFO(9," targetasync "<<targetp->name()<<" "<<" from "<<vertexp->name()<<endl);
|
||||
// UINFO(9," targetasync "<<targetp->name()<<" "<<" from
|
||||
// "<<vertexp->name()<<endl);
|
||||
break;
|
||||
}
|
||||
} // else it might be random logic that's not relevant
|
||||
}
|
||||
//UINFO(9," finalflop "<<targetp->name()<<" "<<targetp->nodep()->fileline()<<endl);
|
||||
// UINFO(9," finalflop "<<targetp->name()<<" "<<targetp->nodep()->fileline()<<endl);
|
||||
warnAndFile(markp->nodep(), V3ErrorCode::CDCRSTLOGIC,
|
||||
"Logic in path that feeds async reset, via signal: "+nodep->prettyNameQ());
|
||||
"Logic in path that feeds async reset, via signal: " + nodep->prettyNameQ());
|
||||
dumpAsyncRecurse(targetp, "", " ", 0);
|
||||
}
|
||||
bool dumpAsyncRecurse(CdcEitherVertex* vertexp, const string& prefix,
|
||||
const string& sep, int level) {
|
||||
bool dumpAsyncRecurse(CdcEitherVertex* vertexp, const string& prefix, const string& sep,
|
||||
int level) {
|
||||
// level=0 is special, indicates to dump destination flop
|
||||
// Return true if printed anything
|
||||
// If mark, also mark the output even if nothing hazardous below
|
||||
if (vertexp->user()>=m_userGeneration) return false; // Processed - prevent loop
|
||||
if (vertexp->user() >= m_userGeneration) return false; // Processed - prevent loop
|
||||
vertexp->user(m_userGeneration);
|
||||
if (!vertexp->asyncPath() && level!=0) return false; // Not part of path
|
||||
if (!vertexp->asyncPath() && level != 0) return false; // Not part of path
|
||||
|
||||
// Other logic in the path
|
||||
string cont = prefix+sep;
|
||||
string cont = prefix + sep;
|
||||
string nextsep = " ";
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
if (dumpAsyncRecurse(eFromVertexp, cont, nextsep, level+1)) {
|
||||
nextsep = " | ";
|
||||
}
|
||||
if (dumpAsyncRecurse(eFromVertexp, cont, nextsep, level + 1)) nextsep = " | ";
|
||||
}
|
||||
|
||||
// Dump single variable/logic block
|
||||
// See also OrderGraph::loopsVertexCb(V3GraphVertex* vertexp)
|
||||
AstNode* nodep = vertexp->nodep();
|
||||
string front = pad(filelineWidth(), nodep->fileline()->ascii()+":")+" "+prefix+" +- ";
|
||||
string front
|
||||
= pad(filelineWidth(), nodep->fileline()->ascii() + ":") + " " + prefix + " +- ";
|
||||
if (VN_IS(nodep, VarScope)) {
|
||||
*m_ofp<<front<<"Variable: "<<nodep->prettyName()<<endl;
|
||||
}
|
||||
else {
|
||||
V3EmitV::verilogPrefixedTree(nodep, *m_ofp, prefix+" +- ", filelineWidth(),
|
||||
*m_ofp << front << "Variable: " << nodep->prettyName() << endl;
|
||||
} else {
|
||||
V3EmitV::verilogPrefixedTree(nodep, *m_ofp, prefix + " +- ", filelineWidth(),
|
||||
vertexp->srcDomainp(), true);
|
||||
if (debug()) {
|
||||
CdcDumpVisitor visitor (nodep, m_ofp, front+"DBG: ");
|
||||
}
|
||||
if (debug()) { CdcDumpVisitor visitor(nodep, m_ofp, front + "DBG: "); }
|
||||
}
|
||||
|
||||
nextsep = " | ";
|
||||
if (level) *m_ofp<<V3OutFile::indentSpaces(filelineWidth())<<" "<<prefix<<nextsep<<"\n";
|
||||
if (level)
|
||||
*m_ofp << V3OutFile::indentSpaces(filelineWidth()) << " " << prefix << nextsep << "\n";
|
||||
|
||||
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
// Now that we've printed a path with this hazard, don't bother to print any more
|
||||
|
|
@ -479,27 +502,27 @@ private:
|
|||
// module. Disabling flattening though makes us consider each
|
||||
// signal in it's own unique clock domain.
|
||||
|
||||
UINFO(3,__FUNCTION__<<": "<<endl);
|
||||
UINFO(3, __FUNCTION__ << ": " << endl);
|
||||
|
||||
// Trace all sources and sinks
|
||||
for (int traceDests=0; traceDests<2; ++traceDests) {
|
||||
UINFO(9, " Trace Direction "<<(traceDests?"dst":"src")<<endl);
|
||||
for (int traceDests = 0; traceDests < 2; ++traceDests) {
|
||||
UINFO(9, " Trace Direction " << (traceDests ? "dst" : "src") << endl);
|
||||
m_graph.userClearVertices(); // user1: bool - was analyzed
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
UINFO(9, " Trace One edge: "<<vvertexp<<endl);
|
||||
UINFO(9, " Trace One edge: " << vvertexp << endl);
|
||||
edgeDomainRecurse(vvertexp, traceDests, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string filename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__cdc_edges.txt";
|
||||
const vl_unique_ptr<std::ofstream> ofp (V3File::new_ofstream(filename));
|
||||
if (ofp->fail()) v3fatal("Can't write "<<filename);
|
||||
*ofp<<"Edge Report for "<<v3Global.opt.prefix()<<endl;
|
||||
string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt";
|
||||
const vl_unique_ptr<std::ofstream> ofp(V3File::new_ofstream(filename));
|
||||
if (ofp->fail()) v3fatal("Can't write " << filename);
|
||||
*ofp << "Edge Report for " << v3Global.opt.prefix() << endl;
|
||||
|
||||
std::deque<string> report; // Sort output by name
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
AstVar* varp = vvertexp->varScp()->varp();
|
||||
{
|
||||
|
|
@ -511,46 +534,50 @@ private:
|
|||
// Module name - doesn't work due to flattening having lost the original
|
||||
// so we assume the modulename matches the filebasename
|
||||
string fname = vvertexp->varScp()->fileline()->filebasename() + ":";
|
||||
os<<" "<<std::setw(20)<<fname;
|
||||
os<<" "<<std::setw(8)<<what;
|
||||
os<<" "<<std::setw(40)<<vvertexp->varScp()->prettyName();
|
||||
os<<" SRC=";
|
||||
if (vvertexp->srcDomainp()) V3EmitV::verilogForTree(vvertexp->srcDomainp(), os);
|
||||
os<<" DST=";
|
||||
if (vvertexp->dstDomainp()) V3EmitV::verilogForTree(vvertexp->dstDomainp(), os);
|
||||
os<<std::setw(0);
|
||||
os<<endl;
|
||||
os << " " << std::setw(20) << fname;
|
||||
os << " " << std::setw(8) << what;
|
||||
os << " " << std::setw(40) << vvertexp->varScp()->prettyName();
|
||||
os << " SRC=";
|
||||
if (vvertexp->srcDomainp()) {
|
||||
V3EmitV::verilogForTree(vvertexp->srcDomainp(), os);
|
||||
}
|
||||
os << " DST=";
|
||||
if (vvertexp->dstDomainp()) {
|
||||
V3EmitV::verilogForTree(vvertexp->dstDomainp(), os);
|
||||
}
|
||||
os << std::setw(0);
|
||||
os << endl;
|
||||
report.push_back(os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
stable_sort(report.begin(), report.end());
|
||||
for (std::deque<string>::iterator it = report.begin(); it!=report.end(); ++it) {
|
||||
for (std::deque<string>::iterator it = report.begin(); it != report.end(); ++it) {
|
||||
*ofp << *it;
|
||||
}
|
||||
}
|
||||
|
||||
void edgeDomainRecurse(CdcEitherVertex* vertexp, bool traceDests, int level) {
|
||||
// Scan back to inputs/outputs, flops, and compute clock domain information
|
||||
UINFO(8,spaces(level)<<" Tracein "<<vertexp<<endl);
|
||||
if (vertexp->user()>=m_userGeneration) return; // Mid-Processed - prevent loop
|
||||
UINFO(8, spaces(level) << " Tracein " << vertexp << endl);
|
||||
if (vertexp->user() >= m_userGeneration) return; // Mid-Processed - prevent loop
|
||||
vertexp->user(m_userGeneration);
|
||||
|
||||
// Variables from flops already are domained
|
||||
if (traceDests ? vertexp->dstDomainSet() : vertexp->srcDomainSet()) return; // Fully computed
|
||||
if (traceDests ? vertexp->dstDomainSet() : vertexp->srcDomainSet()) {
|
||||
return;
|
||||
} // Fully computed
|
||||
|
||||
typedef std::set<AstSenTree*> SenSet;
|
||||
SenSet senouts; // List of all sensitivities for new signal
|
||||
if (CdcLogicVertex* vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
if (vvertexp) {} // Unused
|
||||
}
|
||||
else if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
||||
} else if (CdcVarVertex* vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
||||
// If primary I/O, give it domain of the input
|
||||
AstVar* varp = vvertexp->varScp()->varp();
|
||||
if (varp->isPrimaryIO() && varp->isNonOutput() && !traceDests) {
|
||||
senouts.insert(
|
||||
new AstSenTree(varp->fileline(),
|
||||
new AstSenItem(varp->fileline(), AstSenItem::Combo())));
|
||||
senouts.insert(new AstSenTree(
|
||||
varp->fileline(), new AstSenItem(varp->fileline(), AstSenItem::Combo())));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -558,23 +585,24 @@ private:
|
|||
if (traceDests) {
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
CdcEitherVertex* eToVertexp = static_cast<CdcEitherVertex*>(edgep->top());
|
||||
edgeDomainRecurse(eToVertexp, traceDests, level+1);
|
||||
edgeDomainRecurse(eToVertexp, traceDests, level + 1);
|
||||
if (eToVertexp->dstDomainp()) senouts.insert(eToVertexp->dstDomainp());
|
||||
}
|
||||
} else {
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
edgeDomainRecurse(eFromVertexp, traceDests, level+1);
|
||||
edgeDomainRecurse(eFromVertexp, traceDests, level + 1);
|
||||
if (eFromVertexp->srcDomainp()) senouts.insert(eFromVertexp->srcDomainp());
|
||||
}
|
||||
}
|
||||
|
||||
// Convert list of senses into one sense node
|
||||
AstSenTree* senoutp = NULL;
|
||||
bool senedited = false;
|
||||
for (SenSet::iterator it=senouts.begin(); it!=senouts.end(); ++it) {
|
||||
if (!senoutp) senoutp = *it;
|
||||
else {
|
||||
bool senedited = false;
|
||||
for (SenSet::iterator it = senouts.begin(); it != senouts.end(); ++it) {
|
||||
if (!senoutp) {
|
||||
senoutp = *it;
|
||||
} else {
|
||||
if (!senedited) {
|
||||
senedited = true;
|
||||
senoutp = senoutp->cloneTree(true);
|
||||
|
|
@ -583,22 +611,26 @@ private:
|
|||
}
|
||||
}
|
||||
// If multiple domains need to do complicated optimizations
|
||||
if (senedited) {
|
||||
senoutp = VN_CAST(V3Const::constifyExpensiveEdit(senoutp), SenTree);
|
||||
}
|
||||
if (senedited) { senoutp = VN_CAST(V3Const::constifyExpensiveEdit(senoutp), SenTree); }
|
||||
if (traceDests) {
|
||||
vertexp->dstDomainSet(true); // Note it's set - domainp may be null, so can't use that
|
||||
vertexp->dstDomainp(senoutp);
|
||||
if (debug()>=9) {
|
||||
UINFO(9,spaces(level)+" Tracedst "<<vertexp);
|
||||
if (senoutp) { V3EmitV::verilogForTree(senoutp, cout); cout<<endl; }
|
||||
if (debug() >= 9) {
|
||||
UINFO(9, spaces(level) + " Tracedst " << vertexp);
|
||||
if (senoutp) {
|
||||
V3EmitV::verilogForTree(senoutp, cout);
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vertexp->srcDomainSet(true); // Note it's set - domainp may be null, so can't use that
|
||||
vertexp->srcDomainp(senoutp);
|
||||
if (debug()>=9) {
|
||||
UINFO(9,spaces(level)+" Tracesrc "<<vertexp);
|
||||
if (senoutp) { V3EmitV::verilogForTree(senoutp, cout); cout<<endl; }
|
||||
if (debug() >= 9) {
|
||||
UINFO(9, spaces(level) + " Tracesrc " << vertexp);
|
||||
if (senoutp) {
|
||||
V3EmitV::verilogForTree(senoutp, cout);
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -613,7 +645,7 @@ private:
|
|||
m_modp = origModp;
|
||||
}
|
||||
virtual void visit(AstScope* nodep) VL_OVERRIDE {
|
||||
UINFO(4," SCOPE "<<nodep<<endl);
|
||||
UINFO(4, " SCOPE " << nodep << endl);
|
||||
m_scopep = nodep;
|
||||
m_logicVertexp = NULL;
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -621,10 +653,11 @@ private:
|
|||
}
|
||||
virtual void visit(AstActive* nodep) VL_OVERRIDE {
|
||||
// Create required blocks and add to module
|
||||
UINFO(4," BLOCK "<<nodep<<endl);
|
||||
UINFO(4, " BLOCK " << nodep << endl);
|
||||
AstNode::user2ClearTree();
|
||||
m_domainp = nodep->sensesp();
|
||||
if (!m_domainp || m_domainp->hasCombo() || m_domainp->hasClocked()) { // IE not hasSettle/hasInitial
|
||||
if (!m_domainp || m_domainp->hasCombo()
|
||||
|| m_domainp->hasClocked()) { // IE not hasSettle/hasInitial
|
||||
iterateNewStmt(nodep);
|
||||
}
|
||||
m_domainp = NULL;
|
||||
|
|
@ -636,7 +669,7 @@ private:
|
|||
AstVarScope* varscp = nodep->varScopep();
|
||||
UASSERT_OBJ(varscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
|
||||
CdcVarVertex* varvertexp = makeVarVertex(varscp);
|
||||
UINFO(5," VARREF to "<<varscp<<endl);
|
||||
UINFO(5, " VARREF to " << varscp << endl);
|
||||
// We use weight of one for normal edges,
|
||||
// Weight of CDC_WEIGHT_ASYNC to indicate feeds async (for reporting)
|
||||
// When simplify we'll take the MAX weight
|
||||
|
|
@ -649,10 +682,10 @@ private:
|
|||
}
|
||||
} else {
|
||||
if (varvertexp->cntAsyncRst()) {
|
||||
//UINFO(9," edgeasync "<<varvertexp->name()<<" to "<<m_logicVertexp<<endl);
|
||||
// UINFO(9," edgeasync "<<varvertexp->name()<<" to "<<m_logicVertexp<<endl);
|
||||
new V3GraphEdge(&m_graph, varvertexp, m_logicVertexp, CDC_WEIGHT_ASYNC);
|
||||
} else {
|
||||
//UINFO(9," edgena "<<varvertexp->name()<<" to "<<m_logicVertexp<<endl);
|
||||
// UINFO(9," edgena "<<varvertexp->name()<<" to "<<m_logicVertexp<<endl);
|
||||
new V3GraphEdge(&m_graph, varvertexp, m_logicVertexp, 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -670,38 +703,24 @@ private:
|
|||
iterateChildren(nodep);
|
||||
m_inSenItem = false;
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) VL_OVERRIDE {
|
||||
iterateNewStmt(nodep);
|
||||
}
|
||||
virtual void visit(AstAlways* nodep) VL_OVERRIDE { iterateNewStmt(nodep); }
|
||||
virtual void visit(AstAlwaysPublic* nodep) VL_OVERRIDE {
|
||||
// CDC doesn't care about public variables
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) VL_OVERRIDE {
|
||||
iterateNewStmt(nodep);
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) VL_OVERRIDE { iterateNewStmt(nodep); }
|
||||
virtual void visit(AstSenGate* nodep) VL_OVERRIDE {
|
||||
// First handle the clock part will be handled in a minute by visit AstSenItem
|
||||
// The logic gating term is dealt with as logic
|
||||
iterateNewStmt(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) VL_OVERRIDE {
|
||||
iterateNewStmt(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep) VL_OVERRIDE {
|
||||
iterateNewStmt(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignAlias* nodep) VL_OVERRIDE { iterateNewStmt(nodep); }
|
||||
virtual void visit(AstAssignW* nodep) VL_OVERRIDE { iterateNewStmt(nodep); }
|
||||
|
||||
// Math that shouldn't cause us to clear hazard
|
||||
virtual void visit(AstConst*) VL_OVERRIDE {}
|
||||
virtual void visit(AstReplicate* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstConcat* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNot* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstReplicate* nodep) VL_OVERRIDE { iterateChildren(nodep); }
|
||||
virtual void visit(AstConcat* nodep) VL_OVERRIDE { iterateChildren(nodep); }
|
||||
virtual void visit(AstNot* nodep) VL_OVERRIDE { iterateChildren(nodep); }
|
||||
virtual void visit(AstSel* nodep) VL_OVERRIDE {
|
||||
if (!VN_IS(nodep->lsbp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -738,15 +757,16 @@ public:
|
|||
m_filelineWidth = 0;
|
||||
|
||||
// Make report of all signal names and what clock edges they have
|
||||
string filename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__cdc.txt";
|
||||
string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc.txt";
|
||||
m_ofp = V3File::new_ofstream(filename);
|
||||
if (m_ofp->fail()) v3fatal("Can't write "<<filename);
|
||||
if (m_ofp->fail()) v3fatal("Can't write " << filename);
|
||||
m_ofFilename = filename;
|
||||
*m_ofp<<"CDC Report for "<<v3Global.opt.prefix()<<endl;
|
||||
*m_ofp<<"Each dump below traces logic from inputs/source flops to destination flop(s).\n";
|
||||
*m_ofp<<"First source logic is listed, then a variable that logic generates,\n";
|
||||
*m_ofp<<"repeating recursively forwards to the destination flop(s).\n";
|
||||
*m_ofp<<"%% Indicates the operator considered potentially hazardous.\n";
|
||||
*m_ofp << "CDC Report for " << v3Global.opt.prefix() << endl;
|
||||
*m_ofp
|
||||
<< "Each dump below traces logic from inputs/source flops to destination flop(s).\n";
|
||||
*m_ofp << "First source logic is listed, then a variable that logic generates,\n";
|
||||
*m_ofp << "repeating recursively forwards to the destination flop(s).\n";
|
||||
*m_ofp << "%% Indicates the operator considered potentially hazardous.\n";
|
||||
|
||||
iterate(nodep);
|
||||
analyze();
|
||||
|
|
@ -766,6 +786,6 @@ public:
|
|||
// Cdc class functions
|
||||
|
||||
void V3Cdc::cdcAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
CdcVisitor visitor (nodep);
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
CdcVisitor visitor(nodep);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,12 +43,12 @@
|
|||
class ChangedState {
|
||||
public:
|
||||
// STATE
|
||||
AstNodeModule* m_topModp; // Top module
|
||||
AstScope* m_scopetopp; // Scope under TOPSCOPE
|
||||
AstCFunc* m_chgFuncp; // Change function we're building
|
||||
AstCFunc* m_tlChgFuncp; // Top level change function we're building
|
||||
int m_numStmts; // Number of statements added to m_chgFuncp
|
||||
int m_funcNum; // Number of change functions emitted
|
||||
AstNodeModule* m_topModp; // Top module
|
||||
AstScope* m_scopetopp; // Scope under TOPSCOPE
|
||||
AstCFunc* m_chgFuncp; // Change function we're building
|
||||
AstCFunc* m_tlChgFuncp; // Top level change function we're building
|
||||
int m_numStmts; // Number of statements added to m_chgFuncp
|
||||
int m_funcNum; // Number of change functions emitted
|
||||
|
||||
ChangedState() {
|
||||
m_topModp = NULL;
|
||||
|
|
@ -67,9 +67,9 @@ public:
|
|||
return;
|
||||
}
|
||||
if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) {
|
||||
m_chgFuncp = new AstCFunc(m_scopetopp->fileline(),
|
||||
"_change_request_" + cvtToStr(++m_funcNum),
|
||||
m_scopetopp, "QData");
|
||||
m_chgFuncp
|
||||
= new AstCFunc(m_scopetopp->fileline(), "_change_request_" + cvtToStr(++m_funcNum),
|
||||
m_scopetopp, "QData");
|
||||
m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_chgFuncp->symProlog(true);
|
||||
m_chgFuncp->declPrivate(true);
|
||||
|
|
@ -89,9 +89,9 @@ public:
|
|||
// we want and is similar to the logic already in use inside
|
||||
// V3EmitC, however, it also means that verbose logging may
|
||||
// miss to print change detect variables.
|
||||
AstNode* newp = new AstCReturn(m_scopetopp->fileline(),
|
||||
new AstLogOr(m_scopetopp->fileline(), callp,
|
||||
returnp->lhsp()->unlinkFrBack()));
|
||||
AstNode* newp = new AstCReturn(
|
||||
m_scopetopp->fileline(),
|
||||
new AstLogOr(m_scopetopp->fileline(), callp, returnp->lhsp()->unlinkFrBack()));
|
||||
returnp->replaceWith(newp);
|
||||
VL_DO_DANGLING(returnp->deleteTree(), returnp);
|
||||
}
|
||||
|
|
@ -106,57 +106,57 @@ public:
|
|||
class ChangedInsertVisitor : public AstNVisitor {
|
||||
private:
|
||||
// STATE
|
||||
ChangedState* m_statep; // Shared state across visitors
|
||||
AstVarScope* m_vscp; // Original (non-change) variable we're change-detecting
|
||||
AstVarScope* m_newvscp; // New (change detect) variable we're change-detecting
|
||||
AstNode* m_varEqnp; // Original var's equation to get var value
|
||||
AstNode* m_newLvEqnp; // New var's equation to read value
|
||||
AstNode* m_newRvEqnp; // New var's equation to set value
|
||||
uint32_t m_detects; // # detects created
|
||||
ChangedState* m_statep; // Shared state across visitors
|
||||
AstVarScope* m_vscp; // Original (non-change) variable we're change-detecting
|
||||
AstVarScope* m_newvscp; // New (change detect) variable we're change-detecting
|
||||
AstNode* m_varEqnp; // Original var's equation to get var value
|
||||
AstNode* m_newLvEqnp; // New var's equation to read value
|
||||
AstNode* m_newRvEqnp; // New var's equation to set value
|
||||
uint32_t m_detects; // # detects created
|
||||
|
||||
// CONSTANTS
|
||||
enum MiscConsts {
|
||||
DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error
|
||||
DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error
|
||||
// Ok to increase this, but may result in much slower model
|
||||
};
|
||||
|
||||
void newChangeDet() {
|
||||
if (++m_detects > DETECTARRAY_MAX_INDEXES) {
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect more than "
|
||||
<<cvtToStr(DETECTARRAY_MAX_INDEXES)
|
||||
<<" array indexes (probably with UNOPTFLAT warning suppressed): "
|
||||
<<m_vscp->prettyName()<<endl
|
||||
<<m_vscp->warnMore()
|
||||
<<"... Could recompile with DETECTARRAY_MAX_INDEXES increased"<<endl);
|
||||
m_vscp->v3warn(E_DETECTARRAY,
|
||||
"Unsupported: Can't detect more than "
|
||||
<< cvtToStr(DETECTARRAY_MAX_INDEXES)
|
||||
<< " array indexes (probably with UNOPTFLAT warning suppressed): "
|
||||
<< m_vscp->prettyName() << endl
|
||||
<< m_vscp->warnMore()
|
||||
<< "... Could recompile with DETECTARRAY_MAX_INDEXES increased"
|
||||
<< endl);
|
||||
return;
|
||||
}
|
||||
m_statep->maybeCreateChgFuncp();
|
||||
|
||||
AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(),
|
||||
m_varEqnp->cloneTree(true),
|
||||
AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(), m_varEqnp->cloneTree(true),
|
||||
m_newRvEqnp->cloneTree(true), false);
|
||||
m_statep->m_chgFuncp->addStmtsp(changep);
|
||||
AstAssign* initp = new AstAssign(m_vscp->fileline(),
|
||||
m_newLvEqnp->cloneTree(true),
|
||||
AstAssign* initp = new AstAssign(m_vscp->fileline(), m_newLvEqnp->cloneTree(true),
|
||||
m_varEqnp->cloneTree(true));
|
||||
m_statep->m_chgFuncp->addFinalsp(initp);
|
||||
EmitCBaseCounterVisitor visitor(initp);
|
||||
m_statep->m_numStmts += visitor.count();
|
||||
}
|
||||
|
||||
virtual void visit(AstBasicDType* nodep) VL_OVERRIDE {
|
||||
virtual void visit(AstBasicDType* nodep) VL_OVERRIDE { //
|
||||
newChangeDet();
|
||||
}
|
||||
virtual void visit(AstPackArrayDType* nodep) VL_OVERRIDE {
|
||||
virtual void visit(AstPackArrayDType* nodep) VL_OVERRIDE { //
|
||||
newChangeDet();
|
||||
}
|
||||
virtual void visit(AstUnpackArrayDType* nodep) VL_OVERRIDE {
|
||||
for (int index=0; index < nodep->elementsConst(); ++index) {
|
||||
for (int index = 0; index < nodep->elementsConst(); ++index) {
|
||||
AstNode* origVEp = m_varEqnp;
|
||||
AstNode* origNLEp = m_newLvEqnp;
|
||||
AstNode* origNREp = m_newRvEqnp;
|
||||
|
||||
m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index);
|
||||
m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index);
|
||||
m_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index);
|
||||
m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index);
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ private:
|
|||
m_newLvEqnp->deleteTree();
|
||||
m_newRvEqnp->deleteTree();
|
||||
|
||||
m_varEqnp = origVEp;
|
||||
m_varEqnp = origVEp;
|
||||
m_newLvEqnp = origNLEp;
|
||||
m_newRvEqnp = origNREp;
|
||||
}
|
||||
|
|
@ -177,17 +177,18 @@ private:
|
|||
} else {
|
||||
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-class-");
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
||||
" (probably with UNOPTFLAT warning suppressed): "
|
||||
<<m_vscp->varp()->prettyNameQ());
|
||||
" (probably with UNOPTFLAT warning suppressed): "
|
||||
<< m_vscp->varp()->prettyNameQ());
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-general-");
|
||||
m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable"
|
||||
" (probably with UNOPTFLAT warning suppressed): "
|
||||
<<m_vscp->varp()->prettyNameQ());
|
||||
" (probably with UNOPTFLAT warning suppressed): "
|
||||
<< m_vscp->varp()->prettyNameQ());
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) {
|
||||
|
|
@ -196,19 +197,19 @@ public:
|
|||
m_detects = 0;
|
||||
{
|
||||
AstVar* varp = m_vscp->varp();
|
||||
string newvarname = ("__Vchglast__"+m_vscp->scopep()->nameDotless()
|
||||
+"__"+varp->shortName());
|
||||
string newvarname
|
||||
= ("__Vchglast__" + m_vscp->scopep()->nameDotless() + "__" + varp->shortName());
|
||||
// Create: VARREF(_last)
|
||||
// ASSIGN(VARREF(_last), VARREF(var))
|
||||
// ...
|
||||
// CHANGEDET(VARREF(_last), VARREF(var))
|
||||
AstVar* newvarp = new AstVar(varp->fileline(), AstVarType::MODULETEMP,
|
||||
newvarname, varp);
|
||||
AstVar* newvarp
|
||||
= new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp);
|
||||
m_statep->m_topModp->addStmtp(newvarp);
|
||||
m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp);
|
||||
m_statep->m_scopetopp->addVarp(m_newvscp);
|
||||
|
||||
m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, false);
|
||||
m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, false);
|
||||
m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true);
|
||||
m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false);
|
||||
}
|
||||
|
|
@ -229,41 +230,38 @@ private:
|
|||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstVarScope::user1() -> bool. True indicates processed
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
ChangedState* m_statep; // Shared state across visitors
|
||||
ChangedState* m_statep; // Shared state across visitors
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void genChangeDet(AstVarScope* vscp) {
|
||||
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: "<<vscp->prettyNameQ());
|
||||
ChangedInsertVisitor visitor (vscp, m_statep);
|
||||
vscp->v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: " << vscp->prettyNameQ());
|
||||
ChangedInsertVisitor visitor(vscp, m_statep);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
if (nodep->isTop()) {
|
||||
m_statep->m_topModp = nodep;
|
||||
}
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
if (nodep->isTop()) m_statep->m_topModp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstTopScope* nodep) VL_OVERRIDE {
|
||||
UINFO(4," TS "<<nodep<<endl);
|
||||
UINFO(4, " TS " << nodep << endl);
|
||||
// Clearing
|
||||
AstNode::user1ClearTree();
|
||||
// Create the change detection function
|
||||
AstScope* scopep = nodep->scopep();
|
||||
UASSERT_OBJ(scopep, nodep,
|
||||
"No scope found on top level, perhaps you have no statements?");
|
||||
UASSERT_OBJ(scopep, nodep, "No scope found on top level, perhaps you have no statements?");
|
||||
m_statep->m_scopetopp = scopep;
|
||||
|
||||
// Create a wrapper change detection function that calls each change detection function
|
||||
m_statep->m_tlChgFuncp = new AstCFunc(nodep->fileline(),
|
||||
"_change_request", scopep, "QData");
|
||||
m_statep->m_tlChgFuncp
|
||||
= new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData");
|
||||
m_statep->m_tlChgFuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
m_statep->m_tlChgFuncp->symProlog(true);
|
||||
m_statep->m_tlChgFuncp->declPrivate(true);
|
||||
|
|
@ -277,10 +275,8 @@ private:
|
|||
}
|
||||
virtual void visit(AstVarScope* nodep) VL_OVERRIDE {
|
||||
if (nodep->isCircular()) {
|
||||
UINFO(8," CIRC "<<nodep<<endl);
|
||||
if (!nodep->user1SetOnce()) {
|
||||
genChangeDet(nodep);
|
||||
}
|
||||
UINFO(8, " CIRC " << nodep << endl);
|
||||
if (!nodep->user1SetOnce()) genChangeDet(nodep);
|
||||
}
|
||||
}
|
||||
//--------------------
|
||||
|
|
@ -300,10 +296,10 @@ public:
|
|||
// Changed class functions
|
||||
|
||||
void V3Changed::changedAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{
|
||||
ChangedState state;
|
||||
ChangedVisitor visitor (nodep, &state);
|
||||
ChangedVisitor visitor(nodep, &state);
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("changed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,137 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Handle SV classes
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2020 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Class's Transformations:
|
||||
//
|
||||
// Each class:
|
||||
// Move to be modules under AstNetlist
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Class.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
//######################################################################
|
||||
|
||||
class ClassVisitor : public AstNVisitor {
|
||||
private:
|
||||
// MEMBERS
|
||||
AstUser1InUse m_inuser1;
|
||||
string m_prefix; // String prefix to add to name based on hier
|
||||
AstScope* m_classScopep; // Package moving scopes into
|
||||
typedef std::vector<std::pair<AstNode*, AstScope*> > MoveVector;
|
||||
MoveVector m_moves;
|
||||
|
||||
// NODE STATE
|
||||
// AstClass::user1() -> bool. True if iterated already
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
virtual void visit(AstClass* nodep) VL_OVERRIDE {
|
||||
if (nodep->user1SetOnce()) return;
|
||||
// Move this class
|
||||
nodep->name(m_prefix + nodep->name());
|
||||
nodep->unlinkFrBack();
|
||||
v3Global.rootp()->addModulep(nodep);
|
||||
// Make containing package
|
||||
// Note origName is the same as the class origName so errors look correct
|
||||
AstClassPackage* packagep = new AstClassPackage(nodep->fileline(), nodep->origName());
|
||||
packagep->name(nodep->name() + "__Vclpkg");
|
||||
nodep->packagep(packagep);
|
||||
packagep->classp(nodep);
|
||||
v3Global.rootp()->addModulep(packagep);
|
||||
// Add package to hierarchy
|
||||
AstCell* cellp = new AstCell(packagep->fileline(), packagep->fileline(), packagep->name(),
|
||||
packagep->name(), NULL, NULL, NULL);
|
||||
cellp->modp(packagep);
|
||||
v3Global.rootp()->topModulep()->addStmtp(cellp);
|
||||
// Find class's scope
|
||||
// Alternative would be to move this and related to V3Scope
|
||||
AstScope* classScopep = NULL;
|
||||
for (AstNode* itp = nodep->stmtsp(); itp; itp = itp->nextp()) {
|
||||
if ((classScopep = VN_CAST(itp, Scope))) break;
|
||||
}
|
||||
UASSERT_OBJ(classScopep, nodep, "No scope under class");
|
||||
|
||||
// Add scope
|
||||
AstScope* scopep = new AstScope(nodep->fileline(), packagep, classScopep->name(),
|
||||
classScopep->aboveScopep(), classScopep->aboveCellp());
|
||||
packagep->addStmtp(scopep);
|
||||
// Iterate
|
||||
string prevPrefix = m_prefix;
|
||||
{
|
||||
m_classScopep = classScopep;
|
||||
m_prefix = nodep->name() + "__02e"; // .
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_prefix = prevPrefix;
|
||||
m_classScopep = NULL;
|
||||
}
|
||||
virtual void visit(AstPackage* nodep) VL_OVERRIDE {
|
||||
string prevPrefix = m_prefix;
|
||||
{
|
||||
m_prefix = nodep->name() + "__03a__03a"; // ::
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_prefix = prevPrefix;
|
||||
}
|
||||
|
||||
virtual void visit(AstVar* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
// Don't move now, or wouldn't keep interating the class
|
||||
// TODO move class statics only
|
||||
// if (m_classScopep) {
|
||||
// m_moves.push_back(make_pair(nodep, m_classScopep));
|
||||
//}
|
||||
}
|
||||
virtual void visit(AstCFunc* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
// Don't move now, or wouldn't keep interating the class
|
||||
// TODO move function statics only
|
||||
// if (m_classScopep) {
|
||||
// m_moves.push_back(make_pair(nodep, m_classScopep));
|
||||
//}
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeMath* nodep) VL_OVERRIDE {} // Short circuit
|
||||
virtual void visit(AstNodeStmt* nodep) VL_OVERRIDE {} // Short circuit
|
||||
virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit ClassVisitor(AstNetlist* nodep)
|
||||
: m_classScopep(NULL) {
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~ClassVisitor() {
|
||||
for (MoveVector::iterator it = m_moves.begin(); it != m_moves.end(); ++it) {
|
||||
it->second->addVarp(it->first->unlinkFrBack());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Class class functions
|
||||
|
||||
void V3Class::classAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ClassVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("class", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Pre C-Emit stage changes
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2020 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _V3CLASS_H_
|
||||
#define _V3CLASS_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Class {
|
||||
public:
|
||||
static void classAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -43,9 +43,9 @@ private:
|
|||
// AstNode::user() -> CleanState. For this node, 0==UNKNOWN
|
||||
// AstNode::user2() -> bool. True indicates widthMin has been propagated
|
||||
// AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
|
||||
// TYPES
|
||||
enum CleanState { CS_UNKNOWN, CS_CLEAN, CS_DIRTY };
|
||||
|
|
@ -57,10 +57,14 @@ private:
|
|||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
// Width resetting
|
||||
int cppWidth(AstNode* nodep) {
|
||||
if (nodep->width() <= VL_IDATASIZE) return VL_IDATASIZE;
|
||||
else if (nodep->width() <= VL_QUADSIZE) return VL_QUADSIZE;
|
||||
else return nodep->widthWords() * VL_EDATASIZE;
|
||||
int cppWidth(AstNode* nodep) {
|
||||
if (nodep->width() <= VL_IDATASIZE) {
|
||||
return VL_IDATASIZE;
|
||||
} else if (nodep->width() <= VL_QUADSIZE) {
|
||||
return VL_QUADSIZE;
|
||||
} else {
|
||||
return nodep->widthWords() * VL_EDATASIZE;
|
||||
}
|
||||
}
|
||||
void setCppWidth(AstNode* nodep) {
|
||||
nodep->user2(true); // Don't resize it again
|
||||
|
|
@ -82,7 +86,8 @@ private:
|
|||
}
|
||||
void computeCppWidth(AstNode* nodep) {
|
||||
if (!nodep->user2() && nodep->hasDType()) {
|
||||
if (VN_IS(nodep, Var) || VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|
||||
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(), DynArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|
||||
|
|
@ -96,39 +101,33 @@ private:
|
|||
}
|
||||
|
||||
// Store the clean state in the userp on each node
|
||||
void setCleanState(AstNode* nodep, CleanState clean) {
|
||||
nodep->user1(clean);
|
||||
}
|
||||
CleanState getCleanState(AstNode* nodep) {
|
||||
return static_cast<CleanState>(nodep->user1());
|
||||
}
|
||||
void setCleanState(AstNode* nodep, CleanState clean) { nodep->user1(clean); }
|
||||
CleanState getCleanState(AstNode* nodep) { return static_cast<CleanState>(nodep->user1()); }
|
||||
bool isClean(AstNode* nodep) {
|
||||
CleanState clstate = getCleanState(nodep);
|
||||
if (clstate==CS_CLEAN) return true;
|
||||
if (clstate==CS_DIRTY) return false;
|
||||
nodep->v3fatalSrc("Unknown clean state on node: "+nodep->prettyTypeName());
|
||||
if (clstate == CS_CLEAN) return true;
|
||||
if (clstate == CS_DIRTY) return false;
|
||||
nodep->v3fatalSrc("Unknown clean state on node: " + nodep->prettyTypeName());
|
||||
return false;
|
||||
}
|
||||
void setClean(AstNode* nodep, bool isClean) {
|
||||
computeCppWidth(nodep); // Just to be sure it's in widthMin
|
||||
bool wholeUint = (nodep->widthMin() == VL_IDATASIZE
|
||||
|| nodep->widthMin() == VL_QUADSIZE
|
||||
bool wholeUint = (nodep->widthMin() == VL_IDATASIZE || nodep->widthMin() == VL_QUADSIZE
|
||||
|| (nodep->widthMin() % VL_EDATASIZE) == 0);
|
||||
setCleanState(nodep, ((isClean || wholeUint) ? CS_CLEAN : CS_DIRTY));
|
||||
}
|
||||
|
||||
// Operate on nodes
|
||||
void insertClean(AstNode* nodep) { // We'll insert ABOVE passed node
|
||||
UINFO(4," NeedClean "<<nodep<<endl);
|
||||
UINFO(4, " NeedClean " << nodep << endl);
|
||||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
//
|
||||
computeCppWidth(nodep);
|
||||
V3Number mask (nodep, cppWidth(nodep));
|
||||
V3Number mask(nodep, cppWidth(nodep));
|
||||
mask.setMask(nodep->widthMin());
|
||||
AstNode* cleanp = new AstAnd(nodep->fileline(),
|
||||
new AstConst(nodep->fileline(), mask),
|
||||
nodep);
|
||||
AstNode* cleanp
|
||||
= new AstAnd(nodep->fileline(), new AstConst(nodep->fileline(), mask), nodep);
|
||||
cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS
|
||||
relinkHandle.relink(cleanp);
|
||||
}
|
||||
|
|
@ -138,7 +137,7 @@ private:
|
|||
}
|
||||
void ensureCleanAndNext(AstNode* nodep) {
|
||||
// Editing list, careful looping!
|
||||
for (AstNode* exprp = nodep; exprp; ) {
|
||||
for (AstNode* exprp = nodep; exprp;) {
|
||||
AstNode* nextp = exprp->nextp();
|
||||
ensureClean(exprp);
|
||||
exprp = nextp;
|
||||
|
|
@ -149,27 +148,17 @@ private:
|
|||
void operandBiop(AstNodeBiop* nodep) {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
ensureClean(nodep->lhsp());
|
||||
}
|
||||
if (nodep->cleanRhs()) {
|
||||
ensureClean(nodep->rhsp());
|
||||
}
|
||||
//no setClean.. must do it in each user routine.
|
||||
if (nodep->cleanLhs()) ensureClean(nodep->lhsp());
|
||||
if (nodep->cleanRhs()) ensureClean(nodep->rhsp());
|
||||
// no setClean.. must do it in each user routine.
|
||||
}
|
||||
void operandTriop(AstNodeTriop* nodep) {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
ensureClean(nodep->lhsp());
|
||||
}
|
||||
if (nodep->cleanRhs()) {
|
||||
ensureClean(nodep->rhsp());
|
||||
}
|
||||
if (nodep->cleanThs()) {
|
||||
ensureClean(nodep->thsp());
|
||||
}
|
||||
//no setClean.. must do it in each user routine.
|
||||
if (nodep->cleanLhs()) ensureClean(nodep->lhsp());
|
||||
if (nodep->cleanRhs()) ensureClean(nodep->rhsp());
|
||||
if (nodep->cleanThs()) ensureClean(nodep->thsp());
|
||||
// no setClean.. must do it in each user routine.
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
|
|
@ -184,9 +173,7 @@ private:
|
|||
virtual void visit(AstNodeUniop* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanLhs()) {
|
||||
ensureClean(nodep->lhsp());
|
||||
}
|
||||
if (nodep->cleanLhs()) ensureClean(nodep->lhsp());
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
}
|
||||
virtual void visit(AstNodeBiop* nodep) VL_OVERRIDE {
|
||||
|
|
@ -213,14 +200,16 @@ private:
|
|||
virtual void visit(AstNodeAssign* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
computeCppWidth(nodep);
|
||||
if (nodep->cleanRhs()) {
|
||||
ensureClean(nodep->rhsp());
|
||||
}
|
||||
if (nodep->cleanRhs()) ensureClean(nodep->rhsp());
|
||||
}
|
||||
virtual void visit(AstText* nodep) VL_OVERRIDE {
|
||||
virtual void visit(AstText* nodep) VL_OVERRIDE { //
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstScopeName* nodep) VL_OVERRIDE {
|
||||
virtual void visit(AstScopeName* nodep) VL_OVERRIDE { //
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstCNew* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstSel* nodep) VL_OVERRIDE {
|
||||
|
|
@ -232,9 +221,7 @@ private:
|
|||
computeCppWidth(nodep);
|
||||
setClean(nodep, false);
|
||||
// We always clean, as we don't trust those pesky users.
|
||||
if (!VN_IS(nodep->backp(), And)) {
|
||||
insertClean(nodep);
|
||||
}
|
||||
if (!VN_IS(nodep->backp(), And)) insertClean(nodep);
|
||||
ensureCleanAndNext(nodep->bodysp());
|
||||
}
|
||||
virtual void visit(AstTraceDecl* nodep) VL_OVERRIDE {
|
||||
|
|
@ -312,9 +299,7 @@ public:
|
|||
// Clean class functions
|
||||
|
||||
void V3Clean::cleanAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
CleanVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CleanVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("clean", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
150
src/V3Clock.cpp
150
src/V3Clock.cpp
|
|
@ -46,22 +46,22 @@ private:
|
|||
// NODE STATE
|
||||
// Cleared each Module:
|
||||
// AstVarScope::user1p() -> AstVarScope*. Temporary signal that was created.
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// TYPES
|
||||
enum { DOUBLE_OR_RATE = 10 }; // How many | per ||, Determined experimentally as best
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstTopScope* m_topScopep; // Current top scope
|
||||
AstScope* m_scopep; // Current scope
|
||||
AstCFunc* m_evalFuncp; // Top eval function we are creating
|
||||
AstCFunc* m_initFuncp; // Top initial function we are creating
|
||||
AstCFunc* m_finalFuncp; // Top final function we are creating
|
||||
AstCFunc* m_settleFuncp; // Top settlement function we are creating
|
||||
AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates.
|
||||
AstIf* m_lastIfp; // Last sensitivity if active to add more under
|
||||
AstMTaskBody* m_mtaskBodyp; // Current mtask body
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstTopScope* m_topScopep; // Current top scope
|
||||
AstScope* m_scopep; // Current scope
|
||||
AstCFunc* m_evalFuncp; // Top eval function we are creating
|
||||
AstCFunc* m_initFuncp; // Top initial function we are creating
|
||||
AstCFunc* m_finalFuncp; // Top final function we are creating
|
||||
AstCFunc* m_settleFuncp; // Top settlement function we are creating
|
||||
AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates.
|
||||
AstIf* m_lastIfp; // Last sensitivity if active to add more under
|
||||
AstMTaskBody* m_mtaskBodyp; // Current mtask body
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
|
@ -69,12 +69,14 @@ private:
|
|||
AstVarScope* getCreateLastClk(AstVarScope* vscp) {
|
||||
if (vscp->user1p()) return static_cast<AstVarScope*>(vscp->user1p());
|
||||
AstVar* varp = vscp->varp();
|
||||
if (!varp->width1()) varp->v3error("Unsupported: Clock edge on non-single bit signal: "
|
||||
<<varp->prettyNameQ());
|
||||
string newvarname = (string("__Vclklast__")
|
||||
+vscp->scopep()->nameDotless()+"__"+varp->name());
|
||||
AstVar* newvarp = new AstVar(vscp->fileline(),
|
||||
AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1);
|
||||
if (!varp->width1()) {
|
||||
varp->v3error(
|
||||
"Unsupported: Clock edge on non-single bit signal: " << varp->prettyNameQ());
|
||||
}
|
||||
string newvarname
|
||||
= (string("__Vclklast__") + vscp->scopep()->nameDotless() + "__" + varp->name());
|
||||
AstVar* newvarp = new AstVar(vscp->fileline(), AstVarType::MODULETEMP, newvarname,
|
||||
VFlagLogicPacked(), 1);
|
||||
newvarp->noReset(true); // Reset by below assign
|
||||
m_modp->addStmtp(newvarp);
|
||||
AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp);
|
||||
|
|
@ -83,18 +85,16 @@ private:
|
|||
// Add init
|
||||
AstNode* fromp = new AstVarRef(newvarp->fileline(), vscp, false);
|
||||
if (v3Global.opt.xInitialEdge()) fromp = new AstNot(fromp->fileline(), fromp);
|
||||
AstNode* newinitp = new AstAssign(vscp->fileline(),
|
||||
new AstVarRef(newvarp->fileline(), newvscp, true),
|
||||
fromp);
|
||||
AstNode* newinitp = new AstAssign(
|
||||
vscp->fileline(), new AstVarRef(newvarp->fileline(), newvscp, true), fromp);
|
||||
addToInitial(newinitp);
|
||||
// At bottom, assign them
|
||||
AstAssign* finalp
|
||||
= new AstAssign(vscp->fileline(),
|
||||
new AstVarRef(vscp->fileline(), newvscp, true),
|
||||
= new AstAssign(vscp->fileline(), new AstVarRef(vscp->fileline(), newvscp, true),
|
||||
new AstVarRef(vscp->fileline(), vscp, false));
|
||||
m_evalFuncp->addFinalsp(finalp);
|
||||
//
|
||||
UINFO(4,"New Last: "<<newvscp<<endl);
|
||||
UINFO(4, "New Last: " << newvscp << endl);
|
||||
return newvscp;
|
||||
}
|
||||
AstNode* createSenItemEquation(AstSenItem* nodep) {
|
||||
|
|
@ -106,49 +106,44 @@ private:
|
|||
// HIGHEDGE: var
|
||||
// LOWEDGE: ~var
|
||||
AstNode* newp = NULL;
|
||||
if (nodep->edgeType()==VEdgeType::ET_ILLEGAL) {
|
||||
if (nodep->edgeType() == VEdgeType::ET_ILLEGAL) {
|
||||
if (!v3Global.opt.bboxUnsup()) {
|
||||
nodep->v3error("Unsupported: Complicated event expression in sensitive activity list");
|
||||
nodep->v3error(
|
||||
"Unsupported: Complicated event expression in sensitive activity list");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
AstVarScope* clkvscp = nodep->varrefp()->varScopep();
|
||||
if (nodep->edgeType() == VEdgeType::ET_POSEDGE) {
|
||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||
newp = new AstAnd(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
nodep->varrefp()->varScopep(), false),
|
||||
new AstNot(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
lastVscp, false)));
|
||||
newp = new AstAnd(
|
||||
nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), nodep->varrefp()->varScopep(), false),
|
||||
new AstNot(nodep->fileline(), new AstVarRef(nodep->fileline(), lastVscp, false)));
|
||||
} else if (nodep->edgeType() == VEdgeType::ET_NEGEDGE) {
|
||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||
newp = new AstAnd(nodep->fileline(),
|
||||
new AstNot(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
nodep->varrefp()->varScopep(), false)),
|
||||
new AstVarRef(nodep->fileline(), lastVscp, false));
|
||||
newp = new AstAnd(
|
||||
nodep->fileline(),
|
||||
new AstNot(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), nodep->varrefp()->varScopep(), false)),
|
||||
new AstVarRef(nodep->fileline(), lastVscp, false));
|
||||
} else if (nodep->edgeType() == VEdgeType::ET_BOTHEDGE) {
|
||||
AstVarScope* lastVscp = getCreateLastClk(clkvscp);
|
||||
newp = new AstXor(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
nodep->varrefp()->varScopep(), false),
|
||||
new AstVarRef(nodep->fileline(), lastVscp, false));
|
||||
newp = new AstXor(
|
||||
nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), nodep->varrefp()->varScopep(), false),
|
||||
new AstVarRef(nodep->fileline(), lastVscp, false));
|
||||
} else if (nodep->edgeType() == VEdgeType::ET_HIGHEDGE) {
|
||||
newp = new AstVarRef(nodep->fileline(),
|
||||
clkvscp, false);
|
||||
newp = new AstVarRef(nodep->fileline(), clkvscp, false);
|
||||
} else if (nodep->edgeType() == VEdgeType::ET_LOWEDGE) {
|
||||
newp = new AstNot(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(),
|
||||
clkvscp, false));
|
||||
newp = new AstNot(nodep->fileline(), new AstVarRef(nodep->fileline(), clkvscp, false));
|
||||
} else {
|
||||
nodep->v3fatalSrc("Bad edge type");
|
||||
}
|
||||
return newp;
|
||||
}
|
||||
AstNode* createSenGateEquation(AstSenGate* nodep) {
|
||||
AstNode* newp = new AstAnd(nodep->fileline(),
|
||||
createSenseEquation(nodep->sensesp()),
|
||||
AstNode* newp = new AstAnd(nodep->fileline(), createSenseEquation(nodep->sensesp()),
|
||||
nodep->rhsp()->cloneTree(true));
|
||||
return newp;
|
||||
}
|
||||
|
|
@ -186,12 +181,12 @@ private:
|
|||
|
||||
// VISITORS
|
||||
virtual void visit(AstTopScope* nodep) VL_OVERRIDE {
|
||||
UINFO(4," TOPSCOPE "<<nodep<<endl);
|
||||
UINFO(4, " TOPSCOPE " << nodep << endl);
|
||||
m_topScopep = nodep;
|
||||
m_scopep = nodep->scopep();
|
||||
UASSERT_OBJ(m_scopep, nodep,
|
||||
"No scope found on top level, perhaps you have no statements?");
|
||||
//VV***** We reset all user1p()
|
||||
// VV***** We reset all user1p()
|
||||
AstNode::user1ClearTree();
|
||||
// Make top functions
|
||||
{
|
||||
|
|
@ -223,11 +218,10 @@ private:
|
|||
funcp->isStatic(false);
|
||||
funcp->entryPoint(true);
|
||||
funcp->protect(false);
|
||||
funcp->addInitsp(new AstCStmt
|
||||
(nodep->fileline(),
|
||||
EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n"));
|
||||
funcp->addInitsp(new AstCStmt(nodep->fileline(),
|
||||
EmitCBaseVisitor::symTopAssign()+"\n"));
|
||||
funcp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symClassVar()
|
||||
+ " = this->__VlSymsp;\n"));
|
||||
funcp->addInitsp(
|
||||
new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign() + "\n"));
|
||||
m_scopep->addActivep(funcp);
|
||||
m_finalFuncp = funcp;
|
||||
}
|
||||
|
|
@ -245,13 +239,13 @@ private:
|
|||
// Process the activates
|
||||
iterateChildren(nodep);
|
||||
// Done, clear so we can detect errors
|
||||
UINFO(4," TOPSCOPEDONE "<<nodep<<endl);
|
||||
UINFO(4, " TOPSCOPEDONE " << nodep << endl);
|
||||
clearLastSen();
|
||||
m_topScopep = NULL;
|
||||
m_scopep = NULL;
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
||||
//UINFO(4," MOD "<<nodep<<endl);
|
||||
// UINFO(4, " MOD " << nodep << endl);
|
||||
AstNodeModule* origModp = m_modp;
|
||||
{
|
||||
m_modp = nodep;
|
||||
|
|
@ -260,7 +254,7 @@ private:
|
|||
m_modp = origModp;
|
||||
}
|
||||
virtual void visit(AstScope* nodep) VL_OVERRIDE {
|
||||
//UINFO(4," SCOPE "<<nodep<<endl);
|
||||
// UINFO(4, " SCOPE " << nodep << endl);
|
||||
m_scopep = nodep;
|
||||
iterateChildren(nodep);
|
||||
if (AstNode* movep = nodep->finalClksp()) {
|
||||
|
|
@ -289,24 +283,21 @@ private:
|
|||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
virtual void visit(AstCoverToggle* nodep) VL_OVERRIDE {
|
||||
//nodep->dumpTree(cout, "ct:");
|
||||
//COVERTOGGLE(INC, ORIG, CHANGE) ->
|
||||
// nodep->dumpTree(cout, "ct:");
|
||||
// COVERTOGGLE(INC, ORIG, CHANGE) ->
|
||||
// IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; }
|
||||
AstNode* incp = nodep->incp()->unlinkFrBack();
|
||||
AstNode* origp = nodep->origp()->unlinkFrBack();
|
||||
AstNode* changep = nodep->changep()->unlinkFrBack();
|
||||
AstIf* newp = new AstIf(nodep->fileline(),
|
||||
new AstXor(nodep->fileline(),
|
||||
origp,
|
||||
changep),
|
||||
AstIf* newp = new AstIf(nodep->fileline(), new AstXor(nodep->fileline(), origp, changep),
|
||||
incp, NULL);
|
||||
// We could add another IF to detect posedges, and only increment if so.
|
||||
// It's another whole branch though versus a potential memory miss.
|
||||
// We'll go with the miss.
|
||||
newp->addIfsp(new AstAssign(nodep->fileline(),
|
||||
changep->cloneTree(false),
|
||||
origp->cloneTree(false)));
|
||||
nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
newp->addIfsp(
|
||||
new AstAssign(nodep->fileline(), changep->cloneTree(false), origp->cloneTree(false)));
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
virtual void visit(AstInitial* nodep) VL_OVERRIDE {
|
||||
AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName(), true);
|
||||
|
|
@ -321,7 +312,7 @@ private:
|
|||
iterateChildren(nodep);
|
||||
// Link to global function
|
||||
if (nodep->formCallTree()) {
|
||||
UINFO(4, " formCallTree "<<nodep<<endl);
|
||||
UINFO(4, " formCallTree " << nodep << endl);
|
||||
AstCCall* callp = new AstCCall(nodep->fileline(), nodep);
|
||||
callp->argTypes("vlSymsp");
|
||||
m_finalFuncp->addStmtsp(callp);
|
||||
|
|
@ -333,13 +324,13 @@ private:
|
|||
pushDeletep(nodep);
|
||||
}
|
||||
void addToEvalLoop(AstNode* stmtsp) {
|
||||
m_evalFuncp->addStmtsp(stmtsp); // add to top level function
|
||||
m_evalFuncp->addStmtsp(stmtsp); // add to top level function
|
||||
}
|
||||
void addToSettleLoop(AstNode* stmtsp) {
|
||||
m_settleFuncp->addStmtsp(stmtsp); // add to top level function
|
||||
m_settleFuncp->addStmtsp(stmtsp); // add to top level function
|
||||
}
|
||||
void addToInitial(AstNode* stmtsp) {
|
||||
m_initFuncp->addStmtsp(stmtsp); // add to top level function
|
||||
m_initFuncp->addStmtsp(stmtsp); // add to top level function
|
||||
}
|
||||
virtual void visit(AstActive* nodep) VL_OVERRIDE {
|
||||
// Careful if adding variables here, ACTIVES can be under other ACTIVES
|
||||
|
|
@ -350,13 +341,13 @@ private:
|
|||
UASSERT_OBJ(!nodep->stmtsp(), nodep, "Non-empty lower active");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
} else if (m_mtaskBodyp) {
|
||||
UINFO(4," TR ACTIVE "<<nodep<<endl);
|
||||
UINFO(4, " TR ACTIVE " << nodep << endl);
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
if (nodep->hasClocked()) {
|
||||
UASSERT_OBJ(!nodep->hasInitial(), nodep,
|
||||
"Initial block should not have clock sensitivity");
|
||||
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
|
||||
UINFO(4," sameSenseTree\n");
|
||||
UINFO(4, " sameSenseTree\n");
|
||||
} else {
|
||||
clearLastSen();
|
||||
m_lastSenp = nodep->sensesp();
|
||||
|
|
@ -375,14 +366,14 @@ private:
|
|||
}
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
} else {
|
||||
UINFO(4," ACTIVE "<<nodep<<endl);
|
||||
UINFO(4, " ACTIVE " << nodep << endl);
|
||||
AstNode* stmtsp = nodep->stmtsp()->unlinkFrBackWithNext();
|
||||
if (nodep->hasClocked()) {
|
||||
// Remember the latest sensitivity so we can compare it next time
|
||||
UASSERT_OBJ(!nodep->hasInitial(), nodep,
|
||||
"Initial block should not have clock sensitivity");
|
||||
if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) {
|
||||
UINFO(4," sameSenseTree\n");
|
||||
UINFO(4, " sameSenseTree\n");
|
||||
} else {
|
||||
clearLastSen();
|
||||
m_lastSenp = nodep->sensesp();
|
||||
|
|
@ -410,8 +401,7 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstExecGraph* nodep) VL_OVERRIDE {
|
||||
for (m_mtaskBodyp = VN_CAST(nodep->op1p(), MTaskBody);
|
||||
m_mtaskBodyp;
|
||||
for (m_mtaskBodyp = VN_CAST(nodep->op1p(), MTaskBody); m_mtaskBodyp;
|
||||
m_mtaskBodyp = VN_CAST(m_mtaskBodyp->nextp(), MTaskBody)) {
|
||||
clearLastSen();
|
||||
iterate(m_mtaskBodyp);
|
||||
|
|
@ -453,9 +443,7 @@ public:
|
|||
// Clock class functions
|
||||
|
||||
void V3Clock::clockAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
ClockVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ ClockVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("clock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,23 +75,24 @@ class CombCallVisitor : CombBaseVisitor {
|
|||
// Find all CCALLS of each CFUNC, so that we can later rename them
|
||||
private:
|
||||
// NODE STATE
|
||||
bool m_find; // Find mode vs. delete mode
|
||||
typedef std::multimap<AstCFunc*,AstCCall*> CallMmap;
|
||||
CallMmap m_callMmap; // Associative array of {function}{call}
|
||||
typedef std::multimap<AstCFunc*, AstCCall*> CallMmap;
|
||||
CallMmap m_callMmap; // Associative array of {function}{call}
|
||||
// METHODS
|
||||
public:
|
||||
void replaceFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
if (oldfuncp==newfuncp) return;
|
||||
if (oldfuncp == newfuncp) return;
|
||||
if (newfuncp) {
|
||||
UINFO(4, " Replace "<<oldfuncp<<" -WITH-> "<<newfuncp<<endl);
|
||||
} else UINFO(4, " Remove "<<oldfuncp<<endl);
|
||||
std::pair <CallMmap::iterator,CallMmap::iterator> eqrange
|
||||
UINFO(4, " Replace " << oldfuncp << " -WITH-> " << newfuncp << endl);
|
||||
} else {
|
||||
UINFO(4, " Remove " << oldfuncp << endl);
|
||||
}
|
||||
std::pair<CallMmap::iterator, CallMmap::iterator> eqrange
|
||||
= m_callMmap.equal_range(oldfuncp);
|
||||
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
||||
CallMmap::iterator eqit = nextit++;
|
||||
AstCCall* callp = eqit->second;
|
||||
if (!callp->user3()) { // !already done
|
||||
UINFO(4, " Called "<<callp<<endl);
|
||||
UINFO(4, " Called " << callp << endl);
|
||||
UASSERT_OBJ(callp->funcp() == oldfuncp, callp,
|
||||
"Call list broken, points to call w/different func");
|
||||
if (newfuncp) {
|
||||
|
|
@ -109,27 +110,24 @@ public:
|
|||
}
|
||||
}
|
||||
// METHODS
|
||||
void addCall(AstCCall* nodep) {
|
||||
m_callMmap.insert(make_pair(nodep->funcp(), nodep));
|
||||
}
|
||||
void addCall(AstCCall* nodep) { m_callMmap.insert(make_pair(nodep->funcp(), nodep)); }
|
||||
void deleteCall(AstCCall* nodep) {
|
||||
std::pair<CallMmap::iterator,CallMmap::iterator> eqrange
|
||||
std::pair<CallMmap::iterator, CallMmap::iterator> eqrange
|
||||
= m_callMmap.equal_range(nodep->funcp());
|
||||
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
||||
CallMmap::iterator eqit = nextit++;
|
||||
AstCCall* callp = eqit->second;
|
||||
if (callp==nodep) {
|
||||
if (callp == nodep) {
|
||||
m_callMmap.erase(eqit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
nodep->v3fatalSrc("deleteCall node not found in table");
|
||||
}
|
||||
|
||||
private:
|
||||
// VISITORS
|
||||
virtual void visit(AstCCall* nodep) VL_OVERRIDE {
|
||||
addCall(nodep);
|
||||
}
|
||||
virtual void visit(AstCCall* nodep) VL_OVERRIDE { addCall(nodep); }
|
||||
// Speed things up
|
||||
virtual void visit(AstNodeAssign*) VL_OVERRIDE {}
|
||||
virtual void visit(AstNodeMath*) VL_OVERRIDE {}
|
||||
|
|
@ -137,8 +135,7 @@ private:
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
CombCallVisitor()
|
||||
: m_find(false) {}
|
||||
CombCallVisitor() {}
|
||||
virtual ~CombCallVisitor() {}
|
||||
void main(AstNetlist* nodep) { iterate(nodep); }
|
||||
};
|
||||
|
|
@ -156,11 +153,10 @@ private:
|
|||
nodep->user3(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CombMarkVisitor(AstNode* nodep) {
|
||||
iterate(nodep);
|
||||
}
|
||||
explicit CombMarkVisitor(AstNode* nodep) { iterate(nodep); }
|
||||
virtual ~CombMarkVisitor() {}
|
||||
};
|
||||
|
||||
|
|
@ -174,29 +170,30 @@ private:
|
|||
// AstNodeStmt::user() -> bool. True if iterated already
|
||||
// AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func
|
||||
// AstNodeStmt::user3() -> AstNode*. True if to ignore this cell
|
||||
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is illegal)
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser3InUse m_inuser3;
|
||||
//AstUser4InUse part of V3Hashed
|
||||
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is
|
||||
// illegal)
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser3InUse m_inuser3;
|
||||
// AstUser4InUse part of V3Hashed
|
||||
|
||||
// STATE
|
||||
typedef enum {STATE_IDLE, STATE_HASH, STATE_DUP} CombineState;
|
||||
VDouble0 m_statCombs; // Statistic tracking
|
||||
CombineState m_state; // Major state
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_funcp; // Current function
|
||||
V3Hash m_lowerHash; // Hash of the statement we're building
|
||||
CombCallVisitor m_call; // Tracking of function call users
|
||||
int m_modNFuncs; // Number of functions made
|
||||
AstNode* m_walkLast1p; // Final node that is the same in duplicate list
|
||||
AstNode* m_walkLast2p; // Final node that is the same in duplicate list
|
||||
V3Hashed m_hashed; // Hash for every node in module
|
||||
typedef enum { STATE_IDLE, STATE_HASH, STATE_DUP } CombineState;
|
||||
VDouble0 m_statCombs; // Statistic tracking
|
||||
CombineState m_state; // Major state
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_funcp; // Current function
|
||||
V3Hash m_lowerHash; // Hash of the statement we're building
|
||||
CombCallVisitor m_call; // Tracking of function call users
|
||||
int m_modNFuncs; // Number of functions made
|
||||
AstNode* m_walkLast1p; // Final node that is the same in duplicate list
|
||||
AstNode* m_walkLast2p; // Final node that is the same in duplicate list
|
||||
V3Hashed m_hashed; // Hash for every node in module
|
||||
|
||||
// METHODS
|
||||
void hashStatement(AstNode* nodep) {
|
||||
// Compute hash on entire tree of this statement
|
||||
m_hashed.hashAndInsert(nodep);
|
||||
//UINFO(9," stmthash "<<hex<<nodep->user4()<<" "<<nodep<<endl);
|
||||
// UINFO(9, " stmthash " << hex << nodep->user4() << " " << nodep << endl);
|
||||
}
|
||||
void hashFunctions(AstCFunc* nodep) {
|
||||
// Compute hash of all statement trees in the function
|
||||
|
|
@ -211,11 +208,9 @@ private:
|
|||
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
||||
AstNode* node1p = it->second;
|
||||
AstCFunc* oldfuncp = VN_CAST(node1p, CFunc);
|
||||
if (oldfuncp
|
||||
&& oldfuncp->emptyBody()
|
||||
&& !oldfuncp->dontCombine()) {
|
||||
UINFO(5," EmptyFunc "<<std::hex<<V3Hash(oldfuncp->user4p())
|
||||
<<" "<<oldfuncp<<endl);
|
||||
if (oldfuncp && oldfuncp->emptyBody() && !oldfuncp->dontCombine()) {
|
||||
UINFO(5, " EmptyFunc " << std::hex << V3Hash(oldfuncp->user4p()) << " "
|
||||
<< oldfuncp << endl);
|
||||
// Mark user3p on entire old tree, so we don't process it more
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
m_call.replaceFunc(oldfuncp, NULL);
|
||||
|
|
@ -233,7 +228,7 @@ private:
|
|||
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (!(eqit->first == hashval)) break;
|
||||
if (node1p==node2p) continue; // Identical iterator
|
||||
if (node1p == node2p) continue; // Identical iterator
|
||||
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
|
||||
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
|
||||
// Replace AstCCall's that point here
|
||||
|
|
@ -245,8 +240,10 @@ private:
|
|||
}
|
||||
}
|
||||
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
||||
UINFO(5," DupFunc "<<std::hex<<V3Hash(newfuncp->user4p())<<" "<<newfuncp<<endl);
|
||||
UINFO(5," and "<<std::hex<<V3Hash(oldfuncp->user4p())<<" "<<oldfuncp<<endl);
|
||||
UINFO(5, " DupFunc " << std::hex << V3Hash(newfuncp->user4p()) << " " << newfuncp
|
||||
<< endl);
|
||||
UINFO(5, " and " << std::hex << V3Hash(oldfuncp->user4p()) << " " << oldfuncp
|
||||
<< endl);
|
||||
// Mark user3p on entire old tree, so we don't process it more
|
||||
++m_statCombs;
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
|
|
@ -256,12 +253,10 @@ private:
|
|||
}
|
||||
void replaceOnlyCallFunc(AstCCall* nodep) {
|
||||
if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) {
|
||||
//oldfuncp->dumpTree(cout, "MAYDEL: ");
|
||||
if (nodep->nextp()==NULL
|
||||
&& oldfuncp->initsp()==NULL
|
||||
&& oldfuncp->stmtsp()==nodep
|
||||
&& oldfuncp->finalsp()==NULL) {
|
||||
UINFO(9," Function only has call "<<oldfuncp<<endl);
|
||||
// oldfuncp->dumpTree(cout, "MAYDEL: ");
|
||||
if (nodep->nextp() == NULL && oldfuncp->initsp() == NULL && oldfuncp->stmtsp() == nodep
|
||||
&& oldfuncp->finalsp() == NULL) {
|
||||
UINFO(9, " Function only has call " << oldfuncp << endl);
|
||||
m_call.deleteCall(nodep);
|
||||
CombMarkVisitor visitor(oldfuncp);
|
||||
VL_DO_DANGLING(replaceFuncWFunc(oldfuncp, nodep->funcp()), nodep);
|
||||
|
|
@ -271,18 +266,18 @@ private:
|
|||
|
||||
void walkDupCodeStart(AstNode* node1p) {
|
||||
V3Hash hashval(node1p->user4p());
|
||||
//UINFO(4," STMT "<<hashval<<" "<<node1p<<endl);
|
||||
// UINFO(4," STMT " << hashval << " " << node1p << endl);
|
||||
//
|
||||
int bestDepth = 0; // Best substitution found in the search
|
||||
AstNode* bestNode2p = NULL;
|
||||
AstNode* bestLast1p = NULL;
|
||||
AstNode* bestLast2p = NULL;
|
||||
//
|
||||
std::pair<V3Hashed::iterator,V3Hashed::iterator> eqrange
|
||||
std::pair<V3Hashed::iterator, V3Hashed::iterator> eqrange
|
||||
= m_hashed.mmap().equal_range(hashval);
|
||||
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
||||
AstNode* node2p = eqit->second;
|
||||
if (node1p==node2p) continue;
|
||||
if (node1p == node2p) continue;
|
||||
//
|
||||
// We need to mark iteration to prevent matching code inside
|
||||
// code (abab matching in ababab)
|
||||
|
|
@ -290,8 +285,7 @@ private:
|
|||
m_walkLast1p = NULL;
|
||||
m_walkLast2p = NULL;
|
||||
int depth = walkDupCodeNext(node1p, node2p, 1);
|
||||
if (depth>COMBINE_MIN_STATEMENTS
|
||||
&& depth>bestDepth) {
|
||||
if (depth > COMBINE_MIN_STATEMENTS && depth > bestDepth) {
|
||||
bestDepth = depth;
|
||||
bestNode2p = node2p;
|
||||
bestLast1p = m_walkLast1p;
|
||||
|
|
@ -300,11 +294,15 @@ private:
|
|||
}
|
||||
if (bestDepth) {
|
||||
// Found a replacement
|
||||
UINFO(5," Duplicate of depth "<<bestDepth<<endl);
|
||||
UINFO(5," DupFunc "<<" "<<node1p<<endl);
|
||||
UINFO(5," and "<<" "<<bestNode2p<<endl);
|
||||
UINFO(5," Through "<<" "<<bestLast1p<<endl);
|
||||
UINFO(5," and "<<" "<<bestLast2p<<endl);
|
||||
UINFO(5, " Duplicate of depth " << bestDepth << endl);
|
||||
UINFO(5, " DupFunc "
|
||||
<< " " << node1p << endl);
|
||||
UINFO(5, " and "
|
||||
<< " " << bestNode2p << endl);
|
||||
UINFO(5, " Through "
|
||||
<< " " << bestLast1p << endl);
|
||||
UINFO(5, " and "
|
||||
<< " " << bestLast2p << endl);
|
||||
//
|
||||
walkReplace(node1p, bestNode2p, bestLast1p, bestLast2p);
|
||||
}
|
||||
|
|
@ -316,32 +314,28 @@ private:
|
|||
if (node1p->user3p() || node2p->user3p()) return 0; // Already merged
|
||||
if (!m_hashed.sameNodes(node1p, node2p)) return 0; // walk of tree has same comparison
|
||||
V3Hash hashval(node1p->user4p());
|
||||
//UINFO(9," wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
|
||||
//UINFO(9," wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
|
||||
// UINFO(9, " wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
|
||||
// UINFO(9, " wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
|
||||
m_walkLast1p = node1p;
|
||||
m_walkLast2p = node2p;
|
||||
node1p->user1(true);
|
||||
node2p->user1(true);
|
||||
if (node1p->nextp() && node2p->nextp()) {
|
||||
return hashval.depth()+walkDupCodeNext(node1p->nextp(), node2p->nextp(), level+1);
|
||||
return hashval.depth() + walkDupCodeNext(node1p->nextp(), node2p->nextp(), level + 1);
|
||||
}
|
||||
return hashval.depth();
|
||||
}
|
||||
|
||||
void walkReplace(AstNode* node1p, AstNode* node2p,
|
||||
AstNode* last1p, AstNode* last2p) { // Final node in linked list, maybe null if all statements to be grabbed
|
||||
void walkReplace(AstNode* node1p, AstNode* node2p, AstNode* last1p,
|
||||
AstNode* last2p) { // Final node in linked list, maybe null if all statements
|
||||
// to be grabbed
|
||||
// Make new function
|
||||
string oldname = m_funcp->name();
|
||||
string::size_type pos;
|
||||
if ((pos = oldname.find("_common")) != string::npos) {
|
||||
oldname.erase(pos);
|
||||
}
|
||||
if ((pos = oldname.find("__")) != string::npos) {
|
||||
oldname.erase(pos);
|
||||
}
|
||||
if ((pos = oldname.find("_common")) != string::npos) oldname.erase(pos);
|
||||
if ((pos = oldname.find("__")) != string::npos) oldname.erase(pos);
|
||||
AstCFunc* newfuncp = new AstCFunc(node1p->fileline(),
|
||||
oldname+"_common"+cvtToStr(++m_modNFuncs),
|
||||
NULL);
|
||||
oldname + "_common" + cvtToStr(++m_modNFuncs), NULL);
|
||||
m_modp->addStmtp(newfuncp);
|
||||
// Create calls
|
||||
AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp);
|
||||
|
|
@ -349,21 +343,29 @@ private:
|
|||
// Grab statement bodies
|
||||
AstNRelinker relink1Handle;
|
||||
AstNRelinker relink2Handle;
|
||||
for (AstNode* nextp, *walkp = node1p; 1; walkp = nextp) {
|
||||
for (AstNode *nextp, *walkp = node1p; 1; walkp = nextp) {
|
||||
nextp = walkp->nextp();
|
||||
if (walkp==node1p) walkp->unlinkFrBack(&relink1Handle);
|
||||
else { walkp->unlinkFrBack(); node1p->addNext(walkp); }
|
||||
if (walkp==last1p) break;
|
||||
if (walkp == node1p) {
|
||||
walkp->unlinkFrBack(&relink1Handle);
|
||||
} else {
|
||||
walkp->unlinkFrBack();
|
||||
node1p->addNext(walkp);
|
||||
}
|
||||
if (walkp == last1p) break;
|
||||
}
|
||||
for (AstNode* nextp, *walkp = node2p; 1; walkp = nextp) {
|
||||
for (AstNode *nextp, *walkp = node2p; 1; walkp = nextp) {
|
||||
nextp = walkp->nextp();
|
||||
if (walkp==node2p) walkp->unlinkFrBack(&relink2Handle);
|
||||
else { walkp->unlinkFrBack(); node2p->addNext(walkp); }
|
||||
if (walkp==last2p) break;
|
||||
if (walkp == node2p) {
|
||||
walkp->unlinkFrBack(&relink2Handle);
|
||||
} else {
|
||||
walkp->unlinkFrBack();
|
||||
node2p->addNext(walkp);
|
||||
}
|
||||
if (walkp == last2p) break;
|
||||
}
|
||||
// Move node1 statements to new function
|
||||
newfuncp->addStmtsp(node1p);
|
||||
//newfuncp->dumpTree(cout, " newfunctree: ");
|
||||
// newfuncp->dumpTree(cout, " newfunctree: ");
|
||||
// Mark node2 statements as dead
|
||||
CombMarkVisitor visitor(node2p);
|
||||
pushDeletep(node2p); // Delete later
|
||||
|
|
@ -385,13 +387,13 @@ private:
|
|||
// Track all callers of each function
|
||||
m_call.main(nodep);
|
||||
//
|
||||
//In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
|
||||
// In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
|
||||
// Iterate modules backwards, in bottom-up order.
|
||||
// Required so that a module instantiating another can benefit from collapsing.
|
||||
iterateChildrenBackwards(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
m_modp = nodep;
|
||||
m_modNFuncs = 0;
|
||||
m_walkLast2p = NULL;
|
||||
|
|
@ -400,17 +402,11 @@ private:
|
|||
m_state = STATE_HASH;
|
||||
iterateChildren(nodep);
|
||||
m_state = STATE_IDLE;
|
||||
if (debug()>=9) {
|
||||
m_hashed.dumpFilePrefixed("combine");
|
||||
}
|
||||
if (debug() >= 9) m_hashed.dumpFilePrefixed("combine");
|
||||
// Walk the hashes removing empty functions
|
||||
if (emptyFunctionDeletion()) {
|
||||
walkEmptyFuncs();
|
||||
}
|
||||
if (emptyFunctionDeletion()) walkEmptyFuncs();
|
||||
// Walk the hashes looking for duplicate functions
|
||||
if (duplicateFunctionCombine()) {
|
||||
walkDupFuncs();
|
||||
}
|
||||
if (duplicateFunctionCombine()) walkDupFuncs();
|
||||
// Walk the statements looking for large replicated code sections
|
||||
if (statementCombine()) {
|
||||
m_state = STATE_DUP;
|
||||
|
|
@ -437,8 +433,7 @@ private:
|
|||
}
|
||||
if (m_state == STATE_HASH && m_funcp) {
|
||||
hashStatement(nodep);
|
||||
}
|
||||
else if (m_state == STATE_DUP && m_funcp) {
|
||||
} else if (m_state == STATE_DUP && m_funcp) {
|
||||
walkDupCodeStart(nodep);
|
||||
}
|
||||
}
|
||||
|
|
@ -461,7 +456,7 @@ public:
|
|||
m_walkLast2p = NULL;
|
||||
iterate(nodep);
|
||||
}
|
||||
virtual ~CombineVisitor() {
|
||||
virtual ~CombineVisitor() { //
|
||||
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
|
||||
}
|
||||
};
|
||||
|
|
@ -470,9 +465,7 @@ public:
|
|||
// Combine class functions
|
||||
|
||||
void V3Combine::combineAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
CombineVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CombineVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("combine", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void applyBlock(AstBegin* nodep) {
|
||||
void applyBlock(AstNodeBlock* nodep) {
|
||||
AstPragmaType pragma = AstPragmaType::COVERAGE_BLOCK_OFF;
|
||||
if (!nodep->unnamed()) {
|
||||
for (StringSet::const_iterator it = m_coverageOffBlocks.begin();
|
||||
|
|
@ -250,7 +250,7 @@ public:
|
|||
return (m_on > rh.m_on);
|
||||
}
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, V3ConfigIgnoresLine rhs) {
|
||||
std::ostream& operator<<(std::ostream& os, const V3ConfigIgnoresLine& rhs) {
|
||||
return os << rhs.m_lineno << ", " << rhs.m_code << ", " << rhs.m_on;
|
||||
}
|
||||
|
||||
|
|
@ -267,7 +267,7 @@ class V3ConfigFile {
|
|||
|
||||
LineAttrMap m_lineAttrs; // Atributes to line mapping
|
||||
IgnLines m_ignLines; // Ignore line settings
|
||||
Waivers m_waivers; // Waive messages
|
||||
Waivers m_waivers; // Waive messages
|
||||
|
||||
struct {
|
||||
int lineno; // Last line number
|
||||
|
|
@ -311,7 +311,7 @@ public:
|
|||
m_waivers.push_back(make_pair(code, match));
|
||||
}
|
||||
|
||||
void applyBlock(AstBegin* nodep) {
|
||||
void applyBlock(AstNodeBlock* nodep) {
|
||||
// Apply to block at this line
|
||||
AstPragmaType pragma = AstPragmaType::COVERAGE_BLOCK_OFF;
|
||||
if (lineMatch(nodep->fileline()->lineno(), pragma)) {
|
||||
|
|
@ -327,12 +327,12 @@ public:
|
|||
inline void applyIgnores(FileLine* filelinep) {
|
||||
// HOT routine, called each parsed token line of this filename
|
||||
if (m_lastIgnore.lineno != filelinep->lineno()) {
|
||||
// UINFO(9," ApplyIgnores for "<<filelinep->ascii()<<endl);
|
||||
// UINFO(9, " ApplyIgnores for " << filelinep->ascii() << endl);
|
||||
// Process all on/offs for lines up to and including the current line
|
||||
int curlineno = filelinep->lastLineno();
|
||||
for (; m_lastIgnore.it != m_ignLines.end(); ++m_lastIgnore.it) {
|
||||
if (m_lastIgnore.it->m_lineno > curlineno) break;
|
||||
// UINFO(9," Hit "<<*m_lastIt<<endl);
|
||||
// UINFO(9, " Hit " << *m_lastIt << endl);
|
||||
filelinep->warnOn(m_lastIgnore.it->m_code, m_lastIgnore.it->m_on);
|
||||
}
|
||||
if (0 && debug() >= 9) {
|
||||
|
|
@ -346,7 +346,9 @@ public:
|
|||
bool waive(V3ErrorCode code, const string& match) {
|
||||
for (Waivers::const_iterator it = m_waivers.begin(); it != m_waivers.end(); ++it) {
|
||||
if (((it->first == code) || (it->first == V3ErrorCode::I_LINT))
|
||||
&& VString::wildmatch(match, it->second)) return true;
|
||||
&& VString::wildmatch(match, it->second)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -454,8 +456,8 @@ void V3Config::addVarAttr(FileLine* fl, const string& module, const string& ftas
|
|||
}
|
||||
}
|
||||
|
||||
void V3Config::addWaiver(V3ErrorCode code, const string& filename, const string& match) {
|
||||
V3ConfigResolver::s().files().at(filename).addWaiver(code, match);
|
||||
void V3Config::addWaiver(V3ErrorCode code, const string& filename, const string& message) {
|
||||
V3ConfigResolver::s().files().at(filename).addWaiver(code, message);
|
||||
}
|
||||
|
||||
void V3Config::applyCase(AstCase* nodep) {
|
||||
|
|
|
|||
|
|
@ -33,14 +33,14 @@ public:
|
|||
static void addCoverageBlockOff(const string& file, int lineno);
|
||||
static void addCoverageBlockOff(const string& module, const string& blockname);
|
||||
static void addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max);
|
||||
static void addWaiver(V3ErrorCode code, const string& filename, const string& msg);
|
||||
static void addWaiver(V3ErrorCode code, const string& filename, const string& message);
|
||||
static void addInline(FileLine* fl, const string& module, const string& ftask, bool on);
|
||||
static void addVarAttr(FileLine* fl, const string& module, const string& ftask,
|
||||
const string& signal, AstAttrType type, AstSenTree* nodep);
|
||||
static void applyCase(AstCase* nodep);
|
||||
static void applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep);
|
||||
static void applyIgnores(FileLine* filelinep);
|
||||
static void applyModule(AstNodeModule* nodep);
|
||||
static void applyModule(AstNodeModule* modulep);
|
||||
static void applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp);
|
||||
static void applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp);
|
||||
static bool waive(FileLine* filelinep, V3ErrorCode code, const string& match);
|
||||
|
|
|
|||
1068
src/V3Const.cpp
1068
src/V3Const.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -40,14 +40,16 @@
|
|||
class CoverageVisitor : public AstNVisitor {
|
||||
private:
|
||||
// TYPES
|
||||
typedef std::map<string,int> FileMap;
|
||||
typedef std::map<string, int> FileMap;
|
||||
|
||||
struct ToggleEnt {
|
||||
string m_comment; // Comment for coverage dump
|
||||
AstNode* m_varRefp; // How to get to this element
|
||||
AstNode* m_chgRefp; // How to get to this element
|
||||
string m_comment; // Comment for coverage dump
|
||||
AstNode* m_varRefp; // How to get to this element
|
||||
AstNode* m_chgRefp; // How to get to this element
|
||||
ToggleEnt(const string& comment, AstNode* vp, AstNode* cp)
|
||||
: m_comment(comment), m_varRefp(vp), m_chgRefp(cp) {}
|
||||
: m_comment(comment)
|
||||
, m_varRefp(vp)
|
||||
, m_chgRefp(cp) {}
|
||||
~ToggleEnt() {}
|
||||
void cleanup() {
|
||||
VL_DO_CLEAR(m_varRefp->deleteTree(), m_varRefp = NULL);
|
||||
|
|
@ -58,15 +60,15 @@ private:
|
|||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstIf::user1() -> bool. True indicates ifelse processed
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// STATE
|
||||
bool m_checkBlock; // Should this block get covered?
|
||||
AstNodeModule* m_modp; // Current module to add statement to
|
||||
bool m_inToggleOff; // In function/task etc
|
||||
bool m_inModOff; // In module with no coverage
|
||||
FileMap m_fileps; // Column counts for each fileline
|
||||
string m_beginHier; // AstBegin hier name for user coverage points
|
||||
bool m_checkBlock; // Should this block get covered?
|
||||
AstNodeModule* m_modp; // Current module to add statement to
|
||||
bool m_inToggleOff; // In function/task etc
|
||||
bool m_inModOff; // In module with no coverage
|
||||
FileMap m_fileps; // Column counts for each fileline
|
||||
string m_beginHier; // AstBegin hier name for user coverage points
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
|
@ -74,16 +76,13 @@ private:
|
|||
const char* varIgnoreToggle(AstVar* nodep) {
|
||||
// Return true if this shouldn't be traced
|
||||
// See also similar rule in V3TraceDecl::varIgnoreTrace
|
||||
if (!nodep->isToggleCoverable())
|
||||
return "Not relevant signal type";
|
||||
if (!nodep->isToggleCoverable()) return "Not relevant signal type";
|
||||
if (!v3Global.opt.coverageUnderscore()) {
|
||||
string prettyName = nodep->prettyName();
|
||||
if (prettyName[0] == '_')
|
||||
return "Leading underscore";
|
||||
if (prettyName.find("._") != string::npos)
|
||||
return "Inlined leading underscore";
|
||||
if (prettyName[0] == '_') return "Leading underscore";
|
||||
if (prettyName.find("._") != string::npos) return "Inlined leading underscore";
|
||||
}
|
||||
if ((nodep->width()*nodep->dtypep()->arrayUnpackedElements()) > 256) {
|
||||
if ((nodep->width() * nodep->dtypep()->arrayUnpackedElements()) > 256) {
|
||||
return "Wide bus/array > 256 bits";
|
||||
}
|
||||
// We allow this, though tracing doesn't
|
||||
|
|
@ -91,19 +90,18 @@ private:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
AstCoverInc* newCoverInc(FileLine* fl, const string& hier,
|
||||
const string& page_prefix, const string& comment,
|
||||
const string& trace_var_name) {
|
||||
AstCoverInc* newCoverInc(FileLine* fl, const string& hier, const string& page_prefix,
|
||||
const string& comment, const string& trace_var_name) {
|
||||
// For line coverage, we may have multiple if's on one line, so disambiguate if
|
||||
// everything is otherwise identical
|
||||
// (Don't set column otherwise as it may result in making bins not match up with
|
||||
// different types of coverage enabled.)
|
||||
string key = fl->filename()+"\001"+cvtToStr(fl->lineno())
|
||||
+"\001"+hier+"\001"+page_prefix+"\001"+comment;
|
||||
string key = fl->filename() + "\001" + cvtToStr(fl->lineno()) + "\001" + hier + "\001"
|
||||
+ page_prefix + "\001" + comment;
|
||||
int column = 0;
|
||||
FileMap::iterator it = m_fileps.find(key);
|
||||
if (it == m_fileps.end()) {
|
||||
m_fileps.insert(make_pair(key, column+1));
|
||||
m_fileps.insert(make_pair(key, column + 1));
|
||||
} else {
|
||||
column = (it->second)++;
|
||||
}
|
||||
|
|
@ -122,27 +120,23 @@ private:
|
|||
|
||||
AstCoverInc* incp = new AstCoverInc(fl, declp);
|
||||
if (!trace_var_name.empty() && v3Global.opt.traceCoverage()) {
|
||||
AstVar* varp = new AstVar(incp->fileline(),
|
||||
AstVarType::MODULETEMP, trace_var_name,
|
||||
AstVar* varp = new AstVar(incp->fileline(), AstVarType::MODULETEMP, trace_var_name,
|
||||
incp->findUInt32DType());
|
||||
varp->trace(true);
|
||||
varp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
|
||||
m_modp->addStmtp(varp);
|
||||
UINFO(5, "New coverage trace: "<<varp<<endl);
|
||||
AstAssign* assp = new AstAssign(
|
||||
incp->fileline(),
|
||||
new AstVarRef(incp->fileline(), varp, true),
|
||||
new AstAdd(incp->fileline(),
|
||||
new AstVarRef(incp->fileline(), varp, false),
|
||||
UINFO(5, "New coverage trace: " << varp << endl);
|
||||
AstAssign* assp = new AstAssign(
|
||||
incp->fileline(), new AstVarRef(incp->fileline(), varp, true),
|
||||
new AstAdd(incp->fileline(), new AstVarRef(incp->fileline(), varp, false),
|
||||
new AstConst(incp->fileline(), AstConst::WidthedValue(), 32, 1)));
|
||||
incp->addNext(assp);
|
||||
}
|
||||
return incp;
|
||||
}
|
||||
string traceNameForLine(AstNode* nodep, const string& type) {
|
||||
return "vlCoverageLineTrace_"+nodep->fileline()->filebasenameNoExt()
|
||||
+"__"+cvtToStr(nodep->fileline()->lineno())
|
||||
+"_"+type;
|
||||
return "vlCoverageLineTrace_" + nodep->fileline()->filebasenameNoExt() + "__"
|
||||
+ cvtToStr(nodep->fileline()->lineno()) + "_" + type;
|
||||
}
|
||||
// VISITORS - BOTH
|
||||
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
||||
|
|
@ -169,13 +163,13 @@ private:
|
|||
}
|
||||
virtual void visit(AstVar* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
if (m_modp && !m_inModOff && !m_inToggleOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageToggle()) {
|
||||
if (m_modp && !m_inModOff && !m_inToggleOff && nodep->fileline()->coverageOn()
|
||||
&& v3Global.opt.coverageToggle()) {
|
||||
const char* disablep = varIgnoreToggle(nodep);
|
||||
if (disablep) {
|
||||
UINFO(4, " Disable Toggle: "<<disablep<<" "<<nodep<<endl);
|
||||
UINFO(4, " Disable Toggle: " << disablep << " " << nodep << endl);
|
||||
} else {
|
||||
UINFO(4, " Toggle: "<<nodep<<endl);
|
||||
UINFO(4, " Toggle: " << nodep << endl);
|
||||
// There's several overall ways to approach this
|
||||
// Treat like tracing, where a end-of-timestamp action sees all changes
|
||||
// Works ok, but would be quite slow as need to reform
|
||||
|
|
@ -188,9 +182,9 @@ private:
|
|||
// We'll do this, and make the if(...) coverinc later.
|
||||
|
||||
// Add signal to hold the old value
|
||||
string newvarname = string("__Vtogcov__")+nodep->shortName();
|
||||
AstVar* chgVarp = new AstVar(nodep->fileline(),
|
||||
AstVarType::MODULETEMP, newvarname, nodep);
|
||||
string newvarname = string("__Vtogcov__") + nodep->shortName();
|
||||
AstVar* chgVarp
|
||||
= new AstVar(nodep->fileline(), AstVarType::MODULETEMP, newvarname, nodep);
|
||||
chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true);
|
||||
m_modp->addStmtp(chgVarp);
|
||||
|
||||
|
|
@ -198,123 +192,108 @@ 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, false),
|
||||
new AstVarRef(nodep->fileline(), chgVarp, true));
|
||||
toggleVarRecurse(nodep->dtypeSkipRefp(), 0, newvec,
|
||||
nodep, chgVarp);
|
||||
ToggleEnt newvec(string(""), new AstVarRef(nodep->fileline(), nodep, false),
|
||||
new AstVarRef(nodep->fileline(), chgVarp, true));
|
||||
toggleVarRecurse(nodep->dtypeSkipRefp(), 0, newvec, nodep, chgVarp);
|
||||
newvec.cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void toggleVarBottom(const ToggleEnt& above, const AstVar* varp) {
|
||||
AstCoverToggle* newp
|
||||
= new AstCoverToggle(varp->fileline(),
|
||||
newCoverInc(varp->fileline(), "", "v_toggle",
|
||||
varp->name()+above.m_comment, ""),
|
||||
above.m_varRefp->cloneTree(true),
|
||||
above.m_chgRefp->cloneTree(true));
|
||||
AstCoverToggle* newp = new AstCoverToggle(
|
||||
varp->fileline(),
|
||||
newCoverInc(varp->fileline(), "", "v_toggle", varp->name() + above.m_comment, ""),
|
||||
above.m_varRefp->cloneTree(true), above.m_chgRefp->cloneTree(true));
|
||||
m_modp->addStmtp(newp);
|
||||
}
|
||||
|
||||
void toggleVarRecurse(AstNodeDType* dtypep, int depth, // per-iteration
|
||||
const ToggleEnt& above,
|
||||
AstVar* varp, AstVar* chgVarp) { // Constant
|
||||
const ToggleEnt& above, AstVar* varp, AstVar* chgVarp) { // Constant
|
||||
if (const AstBasicDType* bdtypep = VN_CAST(dtypep, BasicDType)) {
|
||||
if (bdtypep->isRanged()) {
|
||||
for (int index_docs=bdtypep->lsb(); index_docs<bdtypep->msb()+1; index_docs++) {
|
||||
for (int index_docs = bdtypep->lsb(); index_docs < bdtypep->msb() + 1;
|
||||
index_docs++) {
|
||||
int index_code = index_docs - bdtypep->lsb();
|
||||
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 + 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();
|
||||
}
|
||||
} else {
|
||||
toggleVarBottom(above, varp);
|
||||
}
|
||||
}
|
||||
else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) {
|
||||
} else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
for (int index_docs = adtypep->lsb(); index_docs <= adtypep->msb(); ++index_docs) {
|
||||
int index_code = index_docs - adtypep->lsb();
|
||||
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));
|
||||
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1,
|
||||
newent,
|
||||
varp, chgVarp);
|
||||
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));
|
||||
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth + 1, newent, varp,
|
||||
chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
else if (AstPackArrayDType* adtypep = VN_CAST(dtypep, PackArrayDType)) {
|
||||
for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) {
|
||||
} else if (AstPackArrayDType* adtypep = VN_CAST(dtypep, PackArrayDType)) {
|
||||
for (int index_docs = adtypep->lsb(); index_docs <= adtypep->msb(); ++index_docs) {
|
||||
AstNodeDType* subtypep = adtypep->subDTypep()->skipRefp();
|
||||
int index_code = index_docs - adtypep->lsb();
|
||||
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()));
|
||||
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1,
|
||||
newent,
|
||||
varp, chgVarp);
|
||||
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()));
|
||||
toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth + 1, newent, varp,
|
||||
chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
else if (AstStructDType* adtypep = VN_CAST(dtypep, StructDType)) {
|
||||
} else if (AstStructDType* adtypep = VN_CAST(dtypep, StructDType)) {
|
||||
// For now it's packed, so similar to array
|
||||
for (AstMemberDType* itemp = adtypep->membersp();
|
||||
itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
for (AstMemberDType* itemp = adtypep->membersp(); itemp;
|
||||
itemp = VN_CAST(itemp->nextp(), MemberDType)) {
|
||||
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
|
||||
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()));
|
||||
toggleVarRecurse(subtypep, depth+1,
|
||||
newent,
|
||||
varp, chgVarp);
|
||||
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()));
|
||||
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
else if (AstUnionDType* adtypep = VN_CAST(dtypep, UnionDType)) {
|
||||
} else if (AstUnionDType* adtypep = VN_CAST(dtypep, UnionDType)) {
|
||||
// Arbitrarily handle only the first member of the union
|
||||
if (AstMemberDType* itemp = adtypep->membersp()) {
|
||||
AstNodeDType* subtypep = itemp->subDTypep()->skipRefp();
|
||||
ToggleEnt newent (above.m_comment+string(".")+itemp->name(),
|
||||
above.m_varRefp->cloneTree(true),
|
||||
above.m_chgRefp->cloneTree(true));
|
||||
toggleVarRecurse(subtypep, depth+1,
|
||||
newent,
|
||||
varp, chgVarp);
|
||||
ToggleEnt newent(above.m_comment + string(".") + itemp->name(),
|
||||
above.m_varRefp->cloneTree(true),
|
||||
above.m_chgRefp->cloneTree(true));
|
||||
toggleVarRecurse(subtypep, depth + 1, newent, varp, chgVarp);
|
||||
newent.cleanup();
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
dtypep->v3fatalSrc("Unexpected node data type in toggle coverage generation: "
|
||||
<<dtypep->prettyTypeName());
|
||||
<< dtypep->prettyTypeName());
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS - LINE COVERAGE
|
||||
virtual void visit(AstIf* nodep) VL_OVERRIDE { // Note not AstNodeIf; other types don't get covered
|
||||
UINFO(4," IF: "<<nodep<<endl);
|
||||
virtual void
|
||||
visit(AstIf* nodep) VL_OVERRIDE { // Note not AstNodeIf; other types don't get covered
|
||||
UINFO(4, " IF: " << nodep << endl);
|
||||
if (m_checkBlock) {
|
||||
// An else-if. When we iterate the if, use "elsif" marking
|
||||
bool elsif = (VN_IS(nodep->elsesp(), If)
|
||||
&& !VN_CAST(nodep->elsesp(), If)->nextp());
|
||||
bool elsif = (VN_IS(nodep->elsesp(), If) && !VN_CAST(nodep->elsesp(), If)->nextp());
|
||||
if (elsif) VN_CAST(nodep->elsesp(), If)->user1(true);
|
||||
//
|
||||
iterateAndNextNull(nodep->ifsp());
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
if (m_checkBlock && !m_inModOff && nodep->fileline()->coverageOn()
|
||||
&& v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it
|
||||
UINFO(4, " COVER: " << nodep << endl);
|
||||
if (nodep->user1()) {
|
||||
nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "elsif",
|
||||
traceNameForLine(nodep, "elsif")));
|
||||
|
|
@ -327,13 +306,12 @@ private:
|
|||
if (nodep->elsesp()) {
|
||||
m_checkBlock = true;
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
if (m_checkBlock && !m_inModOff && nodep->fileline()->coverageOn()
|
||||
&& v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it
|
||||
UINFO(4, " COVER: " << nodep << endl);
|
||||
if (!elsif) { // elsif done inside if()
|
||||
nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(),
|
||||
"", "v_line", "else",
|
||||
traceNameForLine(nodep, "else")));
|
||||
nodep->addElsesp(newCoverInc(nodep->elsesp()->fileline(), "", "v_line",
|
||||
"else", traceNameForLine(nodep, "else")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -341,12 +319,12 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstCaseItem* nodep) VL_OVERRIDE {
|
||||
UINFO(4," CASEI: "<<nodep<<endl);
|
||||
if (m_checkBlock && !m_inModOff
|
||||
&& nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) {
|
||||
UINFO(4, " CASEI: " << nodep << endl);
|
||||
if (m_checkBlock && !m_inModOff && nodep->fileline()->coverageOn()
|
||||
&& v3Global.opt.coverageLine()) {
|
||||
iterateAndNextNull(nodep->bodysp());
|
||||
if (m_checkBlock) { // if the case body didn't disable it
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
UINFO(4, " COVER: " << nodep << endl);
|
||||
nodep->addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case",
|
||||
traceNameForLine(nodep, "case")));
|
||||
}
|
||||
|
|
@ -354,24 +332,24 @@ private:
|
|||
}
|
||||
}
|
||||
virtual void visit(AstCover* nodep) VL_OVERRIDE {
|
||||
UINFO(4," COVER: "<<nodep<<endl);
|
||||
UINFO(4, " COVER: " << nodep << endl);
|
||||
m_checkBlock = true; // Always do cover blocks, even if there's a $stop
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->coverincp()) {
|
||||
// Note the name may be overridden by V3Assert processing
|
||||
nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover",
|
||||
m_beginHier+"_vlCoverageUserTrace"));
|
||||
m_beginHier + "_vlCoverageUserTrace"));
|
||||
}
|
||||
m_checkBlock = true; // Reset as a child may have cleared it
|
||||
}
|
||||
virtual void visit(AstStop* nodep) VL_OVERRIDE {
|
||||
UINFO(4," STOP: "<<nodep<<endl);
|
||||
UINFO(4, " STOP: " << nodep << endl);
|
||||
m_checkBlock = false;
|
||||
}
|
||||
virtual void visit(AstPragma* nodep) VL_OVERRIDE {
|
||||
if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) {
|
||||
// Skip all NEXT nodes under this block, and skip this if/case branch
|
||||
UINFO(4," OFF: "<<nodep<<endl);
|
||||
UINFO(4, " OFF: " << nodep << endl);
|
||||
m_checkBlock = false;
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
} else {
|
||||
|
|
@ -388,8 +366,8 @@ private:
|
|||
bool oldtog = m_inToggleOff;
|
||||
{
|
||||
m_inToggleOff = true;
|
||||
if (nodep->name()!="") {
|
||||
m_beginHier = m_beginHier + (m_beginHier!=""?".":"") + nodep->name();
|
||||
if (nodep->name() != "") {
|
||||
m_beginHier = m_beginHier + (m_beginHier != "" ? "." : "") + nodep->name();
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
|
@ -423,9 +401,7 @@ public:
|
|||
// Coverage class functions
|
||||
|
||||
void V3Coverage::coverage(AstNetlist* rootp) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
CoverageVisitor visitor (rootp);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CoverageVisitor visitor(rootp); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("coverage", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
// If two COVERTOGGLEs have same VARSCOPE, combine them
|
||||
//*************************************************************************
|
||||
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
|
|
@ -38,23 +37,23 @@ private:
|
|||
// V3Hashed
|
||||
// AstCoverToggle->VarRef::user4() // V3Hashed calculation
|
||||
|
||||
//AstUser4InUse In V3Hashed
|
||||
// AstUser4InUse In V3Hashed
|
||||
|
||||
// TYPES
|
||||
typedef std::vector<AstCoverToggle*> ToggleList;
|
||||
|
||||
// STATE
|
||||
ToggleList m_toggleps; // List of of all AstCoverToggle's
|
||||
ToggleList m_toggleps; // List of of all AstCoverToggle's
|
||||
|
||||
VDouble0 m_statToggleJoins; // Statistic tracking
|
||||
VDouble0 m_statToggleJoins; // Statistic tracking
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void detectDuplicates() {
|
||||
UINFO(9,"Finding duplicates\n");
|
||||
UINFO(9, "Finding duplicates\n");
|
||||
// Note uses user4
|
||||
V3Hashed hashed; // Duplicate code detection
|
||||
V3Hashed hashed; // Duplicate code detection
|
||||
// Hash all of the original signals we toggle cover
|
||||
for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) {
|
||||
AstCoverToggle* nodep = *it;
|
||||
|
|
@ -78,17 +77,18 @@ private:
|
|||
// covertoggle which is immediately above, so:
|
||||
AstCoverToggle* removep = VN_CAST(duporigp->backp(), CoverToggle);
|
||||
UASSERT_OBJ(removep, nodep, "CoverageJoin duplicate of wrong type");
|
||||
UINFO(8," Orig "<<nodep<<" -->> "<<nodep->incp()->declp()<<endl);
|
||||
UINFO(8," dup "<<removep<<" -->> "<<removep->incp()->declp()<<endl);
|
||||
UINFO(8, " Orig " << nodep << " -->> " << nodep->incp()->declp() << endl);
|
||||
UINFO(8, " dup " << removep << " -->> " << removep->incp()->declp() << endl);
|
||||
// The CoverDecl the duplicate pointed to now needs to point to the
|
||||
// original's data. I.e. the duplicate will get the coverage number
|
||||
// from the non-duplicate
|
||||
AstCoverDecl* datadeclp = nodep->incp()->declp()->dataDeclThisp();
|
||||
removep->incp()->declp()->dataDeclp(datadeclp);
|
||||
UINFO(8," new "<<removep->incp()->declp()<<endl);
|
||||
UINFO(8, " new " << removep->incp()->declp() << endl);
|
||||
// Mark the found node as a duplicate of the first node
|
||||
// (Not vice-versa as we have the iterator for the found node)
|
||||
removep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(removep), removep);
|
||||
removep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(removep), removep);
|
||||
// Remove node from comparison so don't hit it again
|
||||
hashed.erase(dupit);
|
||||
++m_statToggleJoins;
|
||||
|
|
@ -114,9 +114,7 @@ private:
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CoverageJoinVisitor(AstNetlist* nodep) {
|
||||
iterate(nodep);
|
||||
}
|
||||
explicit CoverageJoinVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
virtual ~CoverageJoinVisitor() {
|
||||
V3Stats::addStat("Coverage, Toggle points joined", m_statToggleJoins);
|
||||
}
|
||||
|
|
@ -126,9 +124,7 @@ public:
|
|||
// Coverage class functions
|
||||
|
||||
void V3CoverageJoin::coverageJoin(AstNetlist* rootp) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
CoverageJoinVisitor visitor (rootp);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CoverageJoinVisitor visitor(rootp); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("coveragejoin", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
228
src/V3Dead.cpp
228
src/V3Dead.cpp
|
|
@ -63,9 +63,7 @@ private:
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit DeadModVisitor(AstNodeModule* nodep) {
|
||||
iterate(nodep);
|
||||
}
|
||||
explicit DeadModVisitor(AstNodeModule* nodep) { iterate(nodep); }
|
||||
virtual ~DeadModVisitor() {}
|
||||
};
|
||||
|
||||
|
|
@ -80,51 +78,46 @@ private:
|
|||
// AstVar::user1() -> int. Count of number of references
|
||||
// AstVarScope::user1() -> int. Count of number of references
|
||||
// AstNodeDType::user1() -> int. Count of number of references
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser1InUse m_inuser1;
|
||||
|
||||
// TYPES
|
||||
typedef std::multimap<AstVarScope*,AstNodeAssign*> AssignMap;
|
||||
typedef std::multimap<AstVarScope*, AstNodeAssign*> AssignMap;
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
std::vector<AstVar*> m_varsp; // List of all encountered to avoid another loop through tree
|
||||
std::vector<AstNode*> m_dtypesp; // List of all encountered to avoid another loop through tree
|
||||
std::vector<AstVarScope*> m_vscsp; // List of all encountered to avoid another loop through tree
|
||||
std::vector<AstScope*> m_scopesp; // List of all encountered to avoid another loop through tree
|
||||
std::vector<AstCell*> m_cellsp; // List of all encountered to avoid another loop through tree
|
||||
AssignMap m_assignMap; // List of all simple assignments for each variable
|
||||
bool m_elimUserVars; // Allow removal of user's vars
|
||||
bool m_elimDTypes; // Allow removal of DTypes
|
||||
bool m_elimScopes; // Allow removal of Scopes
|
||||
bool m_elimCells; // Allow removal of Cells
|
||||
bool m_sideEffect; // Side effects discovered in assign RHS
|
||||
AstNodeModule* m_modp; // Current module
|
||||
// List of all encountered to avoid another loop through tree
|
||||
std::vector<AstVar*> m_varsp;
|
||||
std::vector<AstNode*> m_dtypesp;
|
||||
std::vector<AstVarScope*> m_vscsp;
|
||||
std::vector<AstScope*> m_scopesp;
|
||||
std::vector<AstCell*> m_cellsp;
|
||||
std::vector<AstClass*> m_classesp;
|
||||
|
||||
AssignMap m_assignMap; // List of all simple assignments for each variable
|
||||
bool m_elimUserVars; // Allow removal of user's vars
|
||||
bool m_elimDTypes; // Allow removal of DTypes
|
||||
bool m_elimScopes; // Allow removal of Scopes
|
||||
bool m_elimCells; // Allow removal of Cells
|
||||
bool m_sideEffect; // Side effects discovered in assign RHS
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void checkAll(AstNode* nodep) {
|
||||
if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves
|
||||
if (AstNode* subnodep = nodep->dtypep()) {
|
||||
subnodep->user1Inc();
|
||||
}
|
||||
}
|
||||
if (AstNode* subnodep = nodep->getChildDTypep()) {
|
||||
subnodep->user1Inc();
|
||||
if (AstNode* subnodep = nodep->dtypep()) subnodep->user1Inc();
|
||||
}
|
||||
if (AstNode* subnodep = nodep->getChildDTypep()) subnodep->user1Inc();
|
||||
}
|
||||
void checkDType(AstNodeDType* nodep) {
|
||||
if (!nodep->generic() // Don't remove generic types
|
||||
&& m_elimDTypes // dtypes stick around until post-widthing
|
||||
&& !VN_IS(nodep, MemberDType) // Keep member names iff upper type exists
|
||||
) {
|
||||
) {
|
||||
m_dtypesp.push_back(nodep);
|
||||
}
|
||||
if (AstNode* subnodep = nodep->virtRefDTypep()) {
|
||||
subnodep->user1Inc();
|
||||
}
|
||||
if (AstNode* subnodep = nodep->virtRefDType2p()) {
|
||||
subnodep->user1Inc();
|
||||
}
|
||||
if (AstNode* subnodep = nodep->virtRefDTypep()) subnodep->user1Inc();
|
||||
if (AstNode* subnodep = nodep->virtRefDType2p()) subnodep->user1Inc();
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
|
|
@ -135,6 +128,13 @@ private:
|
|||
if (!nodep->dead()) {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (AstClass* classp = VN_CAST(nodep, Class)) {
|
||||
if (classp->extendsp()) classp->extendsp()->user1Inc();
|
||||
if (classp->packagep()) classp->packagep()->user1Inc();
|
||||
m_classesp.push_back(classp);
|
||||
// TODO we don't reclaim dead classes yet - graph implementation instead?
|
||||
classp->user1Inc();
|
||||
}
|
||||
}
|
||||
}
|
||||
m_modp = origModp;
|
||||
|
|
@ -148,7 +148,9 @@ private:
|
|||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->aboveScopep()) nodep->aboveScopep()->user1Inc();
|
||||
|
||||
// Class packages might have no children, but need to remain as
|
||||
// long as the class they refer to is needed
|
||||
if (VN_IS(m_modp, Class) || VN_IS(m_modp, ClassPackage)) nodep->user1Inc();
|
||||
if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp() && !nodep->finalClksp()) {
|
||||
m_scopesp.push_back(nodep);
|
||||
}
|
||||
|
|
@ -167,20 +169,24 @@ private:
|
|||
nodep->varScopep()->user1Inc();
|
||||
nodep->varScopep()->varp()->user1Inc();
|
||||
}
|
||||
if (nodep->varp()) {
|
||||
nodep->varp()->user1Inc();
|
||||
}
|
||||
if (nodep->varp()) nodep->varp()->user1Inc();
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
else nodep->packagep()->user1Inc();
|
||||
if (m_elimCells) {
|
||||
nodep->packagep(NULL);
|
||||
} else {
|
||||
nodep->packagep()->user1Inc();
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeFTaskRef* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
else nodep->packagep()->user1Inc();
|
||||
if (m_elimCells) {
|
||||
nodep->packagep(NULL);
|
||||
} else {
|
||||
nodep->packagep()->user1Inc();
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstMethodCall* nodep) VL_OVERRIDE {
|
||||
|
|
@ -192,10 +198,26 @@ private:
|
|||
checkDType(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
else nodep->packagep()->user1Inc();
|
||||
if (m_elimCells) {
|
||||
nodep->packagep(NULL);
|
||||
} else {
|
||||
nodep->packagep()->user1Inc();
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstClassRefDType* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
checkDType(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) {
|
||||
nodep->packagep(NULL);
|
||||
} else {
|
||||
nodep->packagep()->user1Inc();
|
||||
}
|
||||
}
|
||||
if (nodep->classp()) nodep->classp()->user1Inc();
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
checkDType(nodep);
|
||||
|
|
@ -205,8 +227,11 @@ private:
|
|||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->packagep()) {
|
||||
if (m_elimCells) nodep->packagep(NULL);
|
||||
else nodep->packagep()->user1Inc();
|
||||
if (m_elimCells) {
|
||||
nodep->packagep(NULL);
|
||||
} else {
|
||||
nodep->packagep()->user1Inc();
|
||||
}
|
||||
}
|
||||
checkAll(nodep);
|
||||
}
|
||||
|
|
@ -241,17 +266,13 @@ private:
|
|||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
||||
if (mightElimVar(nodep->varp())) {
|
||||
m_vscsp.push_back(nodep);
|
||||
}
|
||||
if (mightElimVar(nodep->varp())) m_vscsp.push_back(nodep);
|
||||
}
|
||||
virtual void visit(AstVar* nodep) VL_OVERRIDE {
|
||||
iterateChildren(nodep);
|
||||
checkAll(nodep);
|
||||
if (nodep->isSigPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc();
|
||||
if (mightElimVar(nodep)) {
|
||||
m_varsp.push_back(nodep);
|
||||
}
|
||||
if (mightElimVar(nodep)) m_varsp.push_back(nodep);
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) VL_OVERRIDE {
|
||||
// See if simple assignments to variables may be eliminated because
|
||||
|
|
@ -284,14 +305,15 @@ private:
|
|||
// Kill any unused modules
|
||||
// V3LinkCells has a graph that is capable of this too, but we need to do it
|
||||
// after we've done all the generate blocks
|
||||
for (bool retry=true; retry; ) {
|
||||
for (bool retry = true; retry;) {
|
||||
retry = false;
|
||||
AstNodeModule* nextmodp;
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
|
||||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp = nextmodp) {
|
||||
nextmodp = VN_CAST(modp->nextp(), NodeModule);
|
||||
if (modp->dead() || (modp->level()>2 && modp->user1()==0 && !modp->internal())) {
|
||||
if (modp->dead()
|
||||
|| (modp->level() > 2 && modp->user1() == 0 && !modp->internal())) {
|
||||
// > 2 because L1 is the wrapper, L2 is the top user module
|
||||
UINFO(4," Dead module "<<modp<<endl);
|
||||
UINFO(4, " Dead module " << modp << endl);
|
||||
// And its children may now be killable too; correct counts
|
||||
// Recurse, as cells may not be directly under the module but in a generate
|
||||
if (!modp->dead()) { // If was dead didn't increment user1's
|
||||
|
|
@ -305,26 +327,23 @@ private:
|
|||
}
|
||||
bool mightElimVar(AstVar* nodep) {
|
||||
return (!nodep->isSigPublic() // Can't elim publics!
|
||||
&& !nodep->isIO()
|
||||
&& !nodep->isIO() && !nodep->isClassMember()
|
||||
&& ((nodep->isTemp() && !nodep->isTrace())
|
||||
|| (nodep->isParam() && !nodep->isTrace() && !v3Global.opt.xmlOnly())
|
||||
|| m_elimUserVars)); // Post-Trace can kill most anything
|
||||
}
|
||||
|
||||
void deadCheckScope() {
|
||||
for (bool retry=true; retry; ) {
|
||||
for (bool retry = true; retry;) {
|
||||
retry = false;
|
||||
for (std::vector<AstScope*>::iterator it = m_scopesp.begin();
|
||||
it != m_scopesp.end();++it) {
|
||||
for (std::vector<AstScope*>::iterator it = m_scopesp.begin(); it != m_scopesp.end();
|
||||
++it) {
|
||||
AstScope* scp = *it;
|
||||
if (!scp)
|
||||
continue;
|
||||
if (!scp) continue;
|
||||
if (scp->user1() == 0) {
|
||||
UINFO(4, " Dead AstScope " << scp << endl);
|
||||
scp->aboveScopep()->user1Inc(-1);
|
||||
if (scp->dtypep()) {
|
||||
scp->dtypep()->user1Inc(-1);
|
||||
}
|
||||
if (scp->dtypep()) scp->dtypep()->user1Inc(-1);
|
||||
VL_DO_DANGLING(scp->unlinkFrBack()->deleteTree(), scp);
|
||||
*it = NULL;
|
||||
retry = true;
|
||||
|
|
@ -334,7 +353,7 @@ private:
|
|||
}
|
||||
|
||||
void deadCheckCells() {
|
||||
for (std::vector<AstCell*>::iterator it = m_cellsp.begin(); it!=m_cellsp.end(); ++it) {
|
||||
for (std::vector<AstCell*>::iterator it = m_cellsp.begin(); it != m_cellsp.end(); ++it) {
|
||||
AstCell* cellp = *it;
|
||||
if (cellp->user1() == 0 && !cellp->modp()->stmtsp()) {
|
||||
cellp->modp()->user1Inc(-1);
|
||||
|
|
@ -342,18 +361,35 @@ private:
|
|||
}
|
||||
}
|
||||
}
|
||||
void deadCheckClasses() {
|
||||
for (bool retry = true; retry;) {
|
||||
retry = false;
|
||||
for (std::vector<AstClass*>::iterator it = m_classesp.begin(); it != m_classesp.end();
|
||||
++it) {
|
||||
if (AstClass* nodep = *it) { // NULL if deleted earlier
|
||||
if (nodep->user1() == 0) {
|
||||
if (nodep->extendsp()) nodep->extendsp()->user1Inc(-1);
|
||||
if (nodep->packagep()) nodep->packagep()->user1Inc(-1);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
*it = NULL;
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deadCheckVar() {
|
||||
// Delete any unused varscopes
|
||||
for (std::vector<AstVarScope*>::iterator it = m_vscsp.begin(); it!=m_vscsp.end(); ++it) {
|
||||
for (std::vector<AstVarScope*>::iterator it = m_vscsp.begin(); it != m_vscsp.end(); ++it) {
|
||||
AstVarScope* vscp = *it;
|
||||
if (vscp->user1() == 0) {
|
||||
UINFO(4," Dead "<<vscp<<endl);
|
||||
std::pair<AssignMap::iterator,AssignMap::iterator> eqrange
|
||||
UINFO(4, " Dead " << vscp << endl);
|
||||
std::pair<AssignMap::iterator, AssignMap::iterator> eqrange
|
||||
= m_assignMap.equal_range(vscp);
|
||||
for (AssignMap::iterator itr = eqrange.first; itr != eqrange.second; ++itr) {
|
||||
AstNodeAssign* assp = itr->second;
|
||||
UINFO(4," Dead assign "<<assp<<endl);
|
||||
UINFO(4, " Dead assign " << assp << endl);
|
||||
assp->dtypep()->user1Inc(-1);
|
||||
VL_DO_DANGLING(assp->unlinkFrBack()->deleteTree(), assp);
|
||||
}
|
||||
|
|
@ -362,40 +398,36 @@ private:
|
|||
VL_DO_DANGLING(vscp->unlinkFrBack()->deleteTree(), vscp);
|
||||
}
|
||||
}
|
||||
for (bool retry=true; retry; ) {
|
||||
for (bool retry = true; retry;) {
|
||||
retry = false;
|
||||
for (std::vector<AstVar *>::iterator it = m_varsp.begin(); it != m_varsp.end();++it) {
|
||||
for (std::vector<AstVar*>::iterator it = m_varsp.begin(); it != m_varsp.end(); ++it) {
|
||||
AstVar* varp = *it;
|
||||
if (!varp)
|
||||
continue;
|
||||
if (!varp) continue;
|
||||
if (varp->user1() == 0) {
|
||||
UINFO(4, " Dead " << varp << endl);
|
||||
if (varp->dtypep()) {
|
||||
varp->dtypep()->user1Inc(-1);
|
||||
}
|
||||
if (varp->dtypep()) varp->dtypep()->user1Inc(-1);
|
||||
VL_DO_DANGLING(varp->unlinkFrBack()->deleteTree(), varp);
|
||||
*it = NULL;
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (std::vector<AstNode*>::iterator it = m_dtypesp.begin(); it != m_dtypesp.end();++it) {
|
||||
for (std::vector<AstNode*>::iterator it = m_dtypesp.begin(); it != m_dtypesp.end(); ++it) {
|
||||
if ((*it)->user1() == 0) {
|
||||
AstNodeUOrStructDType *classp;
|
||||
AstNodeUOrStructDType* classp;
|
||||
// It's possible that there if a reference to each individual member, but
|
||||
// not to the dtype itself. Check and don't remove the parent dtype if
|
||||
// members are still alive.
|
||||
if ((classp = VN_CAST((*it), NodeUOrStructDType))) {
|
||||
bool cont = true;
|
||||
for (AstMemberDType *memberp = classp->membersp();
|
||||
memberp; memberp = VN_CAST(memberp->nextp(), MemberDType)) {
|
||||
for (AstMemberDType* memberp = classp->membersp(); memberp;
|
||||
memberp = VN_CAST(memberp->nextp(), MemberDType)) {
|
||||
if (memberp->user1() != 0) {
|
||||
cont = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!cont)
|
||||
continue;
|
||||
if (!cont) continue;
|
||||
}
|
||||
VL_DO_DANGLING((*it)->unlinkFrBack()->deleteTree(), *it);
|
||||
}
|
||||
|
|
@ -404,8 +436,8 @@ private:
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes,
|
||||
bool elimScopes, bool elimCells) {
|
||||
DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, bool elimScopes,
|
||||
bool elimCells) {
|
||||
m_modp = NULL;
|
||||
m_elimCells = elimCells;
|
||||
m_elimUserVars = elimUserVars;
|
||||
|
|
@ -422,6 +454,7 @@ public:
|
|||
// Otherwise we have no easy way to know if a scope is used
|
||||
if (elimScopes) deadCheckScope();
|
||||
if (elimCells) deadCheckCells();
|
||||
deadCheckClasses();
|
||||
// Modules after vars, because might be vars we delete inside a mod we delete
|
||||
deadCheckMod();
|
||||
|
||||
|
|
@ -435,41 +468,32 @@ public:
|
|||
// Dead class functions
|
||||
|
||||
void V3Dead::deadifyModules(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
DeadVisitor visitor (nodep, false, false, false, false);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor visitor(nodep, false, false, false, false); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadModules", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyDTypes(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
DeadVisitor visitor (nodep, false, true, false, false);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor visitor(nodep, false, true, false, false); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadDtypes", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
DeadVisitor visitor (nodep, false, true, true, false);
|
||||
} // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor visitor(nodep, false, true, true, false); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0,
|
||||
v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
DeadVisitor visitor (nodep, true, true, false, true);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor visitor(nodep, true, true, false, true); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadAll", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
||||
void V3Dead::deadifyAllScoped(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
DeadVisitor visitor (nodep, true, true, true, true);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DeadVisitor visitor(nodep, true, true, true, true); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deadAllScoped", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,40 +81,41 @@ private:
|
|||
// Cleared each scope/active:
|
||||
// AstAssignDly::user3() -> AstVarScope*. __Vdlyvset__ created for this assign
|
||||
// AstAlwaysPost::user3() -> AstVarScope*. __Vdlyvset__ last referenced in IF
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
AstUser4InUse m_inuser4;
|
||||
AstUser5InUse m_inuser5;
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
AstUser4InUse m_inuser4;
|
||||
AstUser5InUse m_inuser5;
|
||||
|
||||
enum VarUsage { VU_NONE=0, VU_DLY=1, VU_NONDLY=2 };
|
||||
enum VarUsage { VU_NONE = 0, VU_DLY = 1, VU_NONDLY = 2 };
|
||||
|
||||
// STATE
|
||||
AstActive* m_activep; // Current activate
|
||||
AstCFunc* m_cfuncp; // Current public C Function
|
||||
AstAssignDly* m_nextDlyp; // Next delayed assignment in a list of assignments
|
||||
bool m_inDly; // True in delayed assignments
|
||||
bool m_inLoop; // True in for loops
|
||||
bool m_inInitial; // True in initial blocks
|
||||
typedef std::map<std::pair<AstNodeModule*,string>,AstVar*> VarMap;
|
||||
VarMap m_modVarMap; // Table of new var names created under module
|
||||
VDouble0 m_statSharedSet; // Statistic tracking
|
||||
typedef std::map<AstVarScope*,int> ScopeVecMap;
|
||||
AstActive* m_activep; // Current activate
|
||||
AstCFunc* m_cfuncp; // Current public C Function
|
||||
AstAssignDly* m_nextDlyp; // Next delayed assignment in a list of assignments
|
||||
bool m_inDly; // True in delayed assignments
|
||||
bool m_inLoop; // True in for loops
|
||||
bool m_inInitial; // True in initial blocks
|
||||
typedef std::map<std::pair<AstNodeModule*, string>, AstVar*> VarMap;
|
||||
VarMap m_modVarMap; // Table of new var names created under module
|
||||
VDouble0 m_statSharedSet; // Statistic tracking
|
||||
typedef std::map<AstVarScope*, int> ScopeVecMap;
|
||||
ScopeVecMap m_scopeVecMap; // Next var number for each scope
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void markVarUsage(AstVarScope* nodep, uint32_t flags) {
|
||||
//UINFO(4," MVU "<<flags<<" "<<nodep<<endl);
|
||||
nodep->user5( nodep->user5() | flags );
|
||||
// UINFO(4, " MVU " << flags << " " << nodep << endl);
|
||||
nodep->user5(nodep->user5() | flags);
|
||||
if ((nodep->user5() & VU_DLY) && (nodep->user5() & VU_NONDLY)) {
|
||||
nodep->v3warn(BLKANDNBLK, "Unsupported: Blocked and non-blocking assignments to same variable: "
|
||||
<<nodep->varp()->prettyNameQ());
|
||||
nodep->v3warn(BLKANDNBLK,
|
||||
"Unsupported: Blocked and non-blocking assignments to same variable: "
|
||||
<< nodep->varp()->prettyNameQ());
|
||||
}
|
||||
}
|
||||
AstVarScope* createVarSc(AstVarScope* oldvarscp, const string& name,
|
||||
int width/*0==fromoldvar*/, AstNodeDType* newdtypep) {
|
||||
int width /*0==fromoldvar*/, AstNodeDType* newdtypep) {
|
||||
// Because we've already scoped it, we may need to add both the AstVar and the AstVarScope
|
||||
UASSERT_OBJ(oldvarscp->scopep(), oldvarscp, "Var unscoped");
|
||||
AstVar* varp;
|
||||
|
|
@ -126,15 +127,14 @@ private:
|
|||
varp = it->second;
|
||||
} else {
|
||||
if (newdtypep) {
|
||||
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP,
|
||||
name, newdtypep);
|
||||
} else if (width==0) {
|
||||
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP,
|
||||
name, oldvarscp->varp());
|
||||
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, newdtypep);
|
||||
} else if (width == 0) {
|
||||
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name,
|
||||
oldvarscp->varp());
|
||||
varp->dtypeFrom(oldvarscp);
|
||||
} else { // Used for vset and dimensions, so can zero init
|
||||
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP,
|
||||
name, VFlagBitPacked(), width);
|
||||
varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name,
|
||||
VFlagBitPacked(), width);
|
||||
}
|
||||
addmodp->addStmtp(varp);
|
||||
m_modVarMap.insert(make_pair(make_pair(addmodp, name), varp));
|
||||
|
|
@ -146,8 +146,8 @@ private:
|
|||
}
|
||||
|
||||
AstActive* createActivePost(AstVarRef* varrefp) {
|
||||
AstActive* newactp = new AstActive(varrefp->fileline(), "sequentdly",
|
||||
m_activep->sensesp());
|
||||
AstActive* newactp
|
||||
= new AstActive(varrefp->fileline(), "sequentdly", m_activep->sensesp());
|
||||
// Was addNext(), but addNextHere() avoids a linear search.
|
||||
m_activep->addNextHere(newactp);
|
||||
return newactp;
|
||||
|
|
@ -159,18 +159,18 @@ private:
|
|||
if (!varrefp->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN)
|
||||
&& !varrefp->varp()->user2()) {
|
||||
varrefp->varp()->v3warn(
|
||||
MULTIDRIVEN, "Signal has multiple driving blocks with different clocking: "
|
||||
<<varrefp->varp()->prettyNameQ()<<endl
|
||||
<<varrefp->warnOther()<<"... Location of first driving block"<<endl
|
||||
<<varrefp->warnContextPrimary()<<endl
|
||||
<<oldactivep->warnOther()<<"... Location of other driving block"<<endl
|
||||
<<oldactivep->warnContextSecondary()
|
||||
);
|
||||
MULTIDRIVEN,
|
||||
"Signal has multiple driving blocks with different clocking: "
|
||||
<< varrefp->varp()->prettyNameQ() << endl
|
||||
<< varrefp->warnOther() << "... Location of first driving block" << endl
|
||||
<< varrefp->warnContextPrimary() << endl
|
||||
<< oldactivep->warnOther() << "... Location of other driving block" << endl
|
||||
<< oldactivep->warnContextSecondary());
|
||||
varrefp->varp()->user2(true);
|
||||
}
|
||||
UINFO(4,"AssignDupDlyVar: "<<varrefp<<endl);
|
||||
UINFO(4," Act: "<<m_activep<<endl);
|
||||
UINFO(4," Act: "<<oldactivep<<endl);
|
||||
UINFO(4, "AssignDupDlyVar: " << varrefp << endl);
|
||||
UINFO(4, " Act: " << m_activep << endl);
|
||||
UINFO(4, " Act: " << oldactivep << endl);
|
||||
// Make a new sensitivity list, which is the combination of both blocks
|
||||
AstNodeSenItem* sena = m_activep->sensesp()->sensesp()->cloneTree(true);
|
||||
AstNodeSenItem* senb = oldactivep->sensesp()->sensesp()->cloneTree(true);
|
||||
|
|
@ -191,8 +191,8 @@ private:
|
|||
// Return the new LHS for the assignment, Null = unlink
|
||||
// Find selects
|
||||
AstNode* newlhsp = NULL; // NULL = unlink old assign
|
||||
AstSel* bitselp = NULL;
|
||||
AstArraySel* arrayselp = NULL;
|
||||
AstSel* bitselp = NULL;
|
||||
AstArraySel* arrayselp = NULL;
|
||||
if (VN_IS(lhsp, Sel)) {
|
||||
bitselp = VN_CAST(lhsp, Sel);
|
||||
arrayselp = VN_CAST(bitselp->fromp(), ArraySel);
|
||||
|
|
@ -202,12 +202,12 @@ private:
|
|||
UASSERT_OBJ(arrayselp, nodep, "No arraysel under bitsel?");
|
||||
UASSERT_OBJ(!VN_IS(arrayselp->dtypep()->skipRefp(), UnpackArrayDType), nodep,
|
||||
"ArraySel with unpacked arrays should have been removed in V3Slice");
|
||||
UINFO(4,"AssignDlyArray: "<<nodep<<endl);
|
||||
UINFO(4, "AssignDlyArray: " << nodep << endl);
|
||||
//
|
||||
//=== Dimensions: __Vdlyvdim__
|
||||
std::deque<AstNode*> dimvalp; // Assignment value for each dimension of assignment
|
||||
AstNode* dimselp = arrayselp;
|
||||
for (; VN_IS(dimselp, ArraySel); dimselp=VN_CAST(dimselp, ArraySel)->fromp()) {
|
||||
for (; VN_IS(dimselp, ArraySel); dimselp = VN_CAST(dimselp, ArraySel)->fromp()) {
|
||||
AstNode* valp = VN_CAST(dimselp, ArraySel)->bitp()->unlinkFrBack();
|
||||
dimvalp.push_front(valp);
|
||||
}
|
||||
|
|
@ -219,19 +219,17 @@ private:
|
|||
int modVecNum = m_scopeVecMap[varrefp->varScopep()]++;
|
||||
//
|
||||
std::deque<AstNode*> dimreadps; // Read value for each dimension of assignment
|
||||
for (unsigned dimension=0; dimension<dimvalp.size(); dimension++) {
|
||||
for (unsigned dimension = 0; dimension < dimvalp.size(); dimension++) {
|
||||
AstNode* dimp = dimvalp[dimension];
|
||||
if (VN_IS(dimp, Const)) { // bit = const, can just use it
|
||||
dimreadps.push_front(dimp);
|
||||
} else {
|
||||
string bitvarname = (string("__Vdlyvdim")+cvtToStr(dimension)
|
||||
+"__"+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(),
|
||||
bitvarname, dimp->width(), NULL);
|
||||
AstAssign* bitassignp
|
||||
= new AstAssign(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), bitvscp, true),
|
||||
dimp);
|
||||
string bitvarname = (string("__Vdlyvdim") + cvtToStr(dimension) + "__"
|
||||
+ oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
|
||||
AstVarScope* bitvscp
|
||||
= createVarSc(varrefp->varScopep(), bitvarname, dimp->width(), NULL);
|
||||
AstAssign* bitassignp = new AstAssign(
|
||||
nodep->fileline(), new AstVarRef(nodep->fileline(), bitvscp, true), dimp);
|
||||
nodep->addNextHere(bitassignp);
|
||||
dimreadps.push_front(new AstVarRef(nodep->fileline(), bitvscp, false));
|
||||
}
|
||||
|
|
@ -241,15 +239,16 @@ private:
|
|||
AstNode* bitreadp = NULL; // Code to read Vdlyvlsb
|
||||
if (bitselp) {
|
||||
AstNode* lsbvaluep = bitselp->lsbp()->unlinkFrBack();
|
||||
if (VN_IS(bitselp->fromp(), Const)) { // vlsb = constant, can just push constant into where we use it
|
||||
if (VN_IS(bitselp->fromp(), Const)) {
|
||||
// vlsb = constant, can just push constant into where we use it
|
||||
bitreadp = lsbvaluep;
|
||||
} else {
|
||||
string bitvarname = (string("__Vdlyvlsb__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
AstVarScope* bitvscp = createVarSc(varrefp->varScopep(),
|
||||
bitvarname, lsbvaluep->width(), NULL);
|
||||
AstAssign* bitassignp = new AstAssign(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), bitvscp, true),
|
||||
lsbvaluep);
|
||||
string bitvarname = (string("__Vdlyvlsb__") + oldvarp->shortName() + "__v"
|
||||
+ cvtToStr(modVecNum));
|
||||
AstVarScope* bitvscp
|
||||
= createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width(), NULL);
|
||||
AstAssign* bitassignp = new AstAssign(
|
||||
nodep->fileline(), new AstVarRef(nodep->fileline(), bitvscp, true), lsbvaluep);
|
||||
nodep->addNextHere(bitassignp);
|
||||
bitreadp = new AstVarRef(nodep->fileline(), bitvscp, false);
|
||||
}
|
||||
|
|
@ -257,11 +256,14 @@ private:
|
|||
//
|
||||
//=== Value: __Vdlyvval__
|
||||
AstNode* valreadp; // Code to read Vdlyvval
|
||||
if (VN_IS(nodep->rhsp(), Const)) { // vval = constant, can just push constant into where we use it
|
||||
if (VN_IS(nodep->rhsp(), Const)) {
|
||||
// vval = constant, can just push constant into where we use it
|
||||
valreadp = nodep->rhsp()->unlinkFrBack();
|
||||
} else {
|
||||
string valvarname = (string("__Vdlyvval__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
AstVarScope* valvscp = createVarSc(varrefp->varScopep(), valvarname, 0, nodep->rhsp()->dtypep());
|
||||
string valvarname
|
||||
= (string("__Vdlyvval__") + oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
|
||||
AstVarScope* valvscp
|
||||
= createVarSc(varrefp->varScopep(), valvarname, 0, nodep->rhsp()->dtypep());
|
||||
newlhsp = new AstVarRef(nodep->fileline(), valvscp, true);
|
||||
valreadp = new AstVarRef(nodep->fileline(), valvscp, false);
|
||||
}
|
||||
|
|
@ -278,14 +280,14 @@ private:
|
|||
setvscp = VN_CAST(nodep->user3p(), VarScope);
|
||||
++m_statSharedSet;
|
||||
} else { // Create new one
|
||||
string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
|
||||
string setvarname
|
||||
= (string("__Vdlyvset__") + oldvarp->shortName() + "__v" + cvtToStr(modVecNum));
|
||||
setvscp = createVarSc(varrefp->varScopep(), setvarname, 1, NULL);
|
||||
setinitp = new AstAssignPre(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), setvscp, true),
|
||||
new AstConst(nodep->fileline(), 0));
|
||||
AstAssign* setassignp
|
||||
= new AstAssign(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), setvscp, true),
|
||||
= new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), setvscp, true),
|
||||
new AstConst(nodep->fileline(), AstConst::LogicTrue()));
|
||||
nodep->addNextHere(setassignp);
|
||||
}
|
||||
|
|
@ -299,7 +301,7 @@ private:
|
|||
// in correctly ordered code - the last assignment must be last.
|
||||
// It also has the nice side effect of assisting cache locality.
|
||||
AstNode* selectsp = varrefp;
|
||||
for (int dimension=int(dimreadps.size())-1; dimension>=0; --dimension) {
|
||||
for (int dimension = int(dimreadps.size()) - 1; dimension >= 0; --dimension) {
|
||||
selectsp = new AstArraySel(nodep->fileline(), selectsp, dimreadps[dimension]);
|
||||
}
|
||||
if (bitselp) {
|
||||
|
|
@ -307,16 +309,16 @@ private:
|
|||
bitselp->widthp()->cloneTree(false));
|
||||
}
|
||||
// Build "IF (changeit) ...
|
||||
UINFO(9," For "<<setvscp<<endl);
|
||||
UINFO(9," & "<<varrefp<<endl);
|
||||
UINFO(9, " For " << setvscp << endl);
|
||||
UINFO(9, " & " << varrefp << endl);
|
||||
AstAlwaysPost* finalp = VN_CAST(varrefp->varScopep()->user4p(), AlwaysPost);
|
||||
if (finalp) {
|
||||
AstActive* oldactivep = VN_CAST(finalp->user2p(), Active);
|
||||
checkActivePost(varrefp, oldactivep);
|
||||
if (setinitp) oldactivep->addStmtsp(setinitp);
|
||||
} else { // first time we've dealt with this memory
|
||||
finalp = new AstAlwaysPost(nodep->fileline(), NULL/*sens*/, NULL/*body*/);
|
||||
UINFO(9," Created "<<finalp<<endl);
|
||||
finalp = new AstAlwaysPost(nodep->fileline(), NULL /*sens*/, NULL /*body*/);
|
||||
UINFO(9, " Created " << finalp << endl);
|
||||
AstActive* newactp = createActivePost(varrefp);
|
||||
newactp->addStmtsp(finalp);
|
||||
varrefp->varScopep()->user4p(finalp);
|
||||
|
|
@ -332,9 +334,8 @@ private:
|
|||
"Delayed assignment misoptimized; prev var found w/o associated IF");
|
||||
} else {
|
||||
postLogicp = new AstIf(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), setvscp, false),
|
||||
NULL, NULL);
|
||||
UINFO(9," Created "<<postLogicp<<endl);
|
||||
new AstVarRef(nodep->fileline(), setvscp, false), NULL, NULL);
|
||||
UINFO(9, " Created " << postLogicp << endl);
|
||||
finalp->addBodysp(postLogicp);
|
||||
finalp->user3p(setvscp); // Remember IF's vset variable
|
||||
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
|
||||
|
|
@ -345,12 +346,12 @@ private:
|
|||
|
||||
// VISITORS
|
||||
virtual void visit(AstNetlist* nodep) VL_OVERRIDE {
|
||||
//VV***** We reset all userp() on the netlist
|
||||
// VV***** We reset all userp() on the netlist
|
||||
m_modVarMap.clear();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstScope* nodep) VL_OVERRIDE {
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
AstNode::user3ClearTree();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
|
@ -363,28 +364,36 @@ private:
|
|||
m_activep = nodep;
|
||||
bool oldinit = m_inInitial;
|
||||
m_inInitial = nodep->hasInitial();
|
||||
AstNode::user3ClearTree(); // Two sets to same variable in different actives must use different vars.
|
||||
AstNode::user3ClearTree(); // Two sets to same variable in different
|
||||
// actives must use different vars.
|
||||
iterateChildren(nodep);
|
||||
m_inInitial = oldinit;
|
||||
}
|
||||
virtual void visit(AstAssignDly* nodep) VL_OVERRIDE {
|
||||
m_inDly = true;
|
||||
m_nextDlyp = VN_CAST(nodep->nextp(), AssignDly); // Next assignment in same block, maybe NULL.
|
||||
if (m_cfuncp) nodep->v3error("Unsupported: Delayed assignment inside public function/task");
|
||||
m_nextDlyp
|
||||
= VN_CAST(nodep->nextp(), AssignDly); // Next assignment in same block, maybe NULL.
|
||||
if (m_cfuncp) {
|
||||
nodep->v3error("Unsupported: Delayed assignment inside public function/task");
|
||||
}
|
||||
if (VN_IS(nodep->lhsp(), ArraySel)
|
||||
|| (VN_IS(nodep->lhsp(), Sel)
|
||||
&& VN_IS(VN_CAST(nodep->lhsp(), Sel)->fromp(), ArraySel))) {
|
||||
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNode* newlhsp = createDlyArray(nodep, lhsp);
|
||||
if (m_inLoop) nodep->v3warn(BLKLOOPINIT, "Unsupported: Delayed assignment to array inside for loops (non-delayed is ok - see docs)");
|
||||
if (m_inLoop) {
|
||||
nodep->v3warn(BLKLOOPINIT, "Unsupported: Delayed assignment to array inside for "
|
||||
"loops (non-delayed is ok - see docs)");
|
||||
}
|
||||
AstBasicDType* basicp = lhsp->dtypep()->basicp();
|
||||
if (basicp && basicp->isEventValue()) nodep->v3error("Unsupported: event arrays");
|
||||
if (newlhsp) {
|
||||
nodep->lhsp(newlhsp);
|
||||
} else {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
VL_DO_DANGLING(lhsp->deleteTree(), lhsp);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
m_inDly = false;
|
||||
|
|
@ -394,11 +403,12 @@ private:
|
|||
virtual void visit(AstVarRef* nodep) VL_OVERRIDE {
|
||||
if (!nodep->user2Inc()) { // Not done yet
|
||||
if (m_inDly && nodep->lvalue()) {
|
||||
UINFO(4,"AssignDlyVar: "<<nodep<<endl);
|
||||
UINFO(4, "AssignDlyVar: " << nodep << endl);
|
||||
markVarUsage(nodep->varScopep(), VU_DLY);
|
||||
UASSERT_OBJ(m_activep, nodep, "<= not under sensitivity block");
|
||||
if (!m_activep->hasClocked()) {
|
||||
nodep->v3error("Internal: Blocking <= assignment in non-clocked block, should have converted in V3Active");
|
||||
nodep->v3error("Internal: Blocking <= assignment in non-clocked block, should "
|
||||
"have converted in V3Active");
|
||||
}
|
||||
AstVarScope* oldvscp = nodep->varScopep();
|
||||
UASSERT_OBJ(oldvscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
|
||||
|
|
@ -408,16 +418,23 @@ private:
|
|||
checkActivePost(nodep, oldactivep);
|
||||
}
|
||||
if (!dlyvscp) { // First use of this delayed variable
|
||||
string newvarname = (string("__Vdly__")+nodep->varp()->shortName());
|
||||
string newvarname = (string("__Vdly__") + nodep->varp()->shortName());
|
||||
dlyvscp = createVarSc(oldvscp, newvarname, 0, NULL);
|
||||
AstNodeAssign* prep
|
||||
= new AstAssignPre(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), dlyvscp, true),
|
||||
new AstVarRef(nodep->fileline(), oldvscp, false));
|
||||
AstNodeAssign* postp
|
||||
= new AstAssignPost(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), oldvscp, true),
|
||||
new AstVarRef(nodep->fileline(), dlyvscp, false));
|
||||
AstNodeAssign* prep;
|
||||
AstBasicDType* basicp = oldvscp->dtypep()->basicp();
|
||||
if (basicp && basicp->isEventValue()) {
|
||||
// Events go to zero on next timestep unless reactivated
|
||||
prep = new AstAssignPre(
|
||||
nodep->fileline(), new AstVarRef(nodep->fileline(), dlyvscp, true),
|
||||
new AstConst(nodep->fileline(), AstConst::LogicFalse()));
|
||||
} else {
|
||||
prep = new AstAssignPre(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), dlyvscp, true),
|
||||
new AstVarRef(nodep->fileline(), oldvscp, false));
|
||||
}
|
||||
AstNodeAssign* postp = new AstAssignPost(
|
||||
nodep->fileline(), new AstVarRef(nodep->fileline(), oldvscp, true),
|
||||
new AstVarRef(nodep->fileline(), dlyvscp, false));
|
||||
postp->lhsp()->user2(true); // Don't detect this assignment
|
||||
oldvscp->user1p(dlyvscp); // So we can find it later
|
||||
// Make new ACTIVE with identical sensitivity tree
|
||||
|
|
@ -428,12 +445,12 @@ private:
|
|||
}
|
||||
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), dlyvscp, true);
|
||||
newrefp->user2(true); // No reason to do it again
|
||||
nodep->replaceWith(newrefp); VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
else if (!m_inDly && nodep->lvalue()) {
|
||||
//UINFO(9,"NBA "<<nodep<<endl);
|
||||
nodep->replaceWith(newrefp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
} else if (!m_inDly && nodep->lvalue()) {
|
||||
// UINFO(9, "NBA " << nodep << endl);
|
||||
if (!m_inInitial) {
|
||||
UINFO(4,"AssignNDlyVar: "<<nodep<<endl);
|
||||
UINFO(4, "AssignNDlyVar: " << nodep << endl);
|
||||
markVarUsage(nodep->varScopep(), VU_NONDLY);
|
||||
}
|
||||
}
|
||||
|
|
@ -441,7 +458,8 @@ private:
|
|||
}
|
||||
|
||||
virtual void visit(AstNodeFor* nodep) VL_OVERRIDE {
|
||||
nodep->v3fatalSrc("For statements should have been converted to while statements in V3Begin");
|
||||
nodep->v3fatalSrc(
|
||||
"For statements should have been converted to while statements in V3Begin");
|
||||
}
|
||||
virtual void visit(AstWhile* nodep) VL_OVERRIDE {
|
||||
bool oldloop = m_inLoop;
|
||||
|
|
@ -474,9 +492,7 @@ public:
|
|||
// Delayed class functions
|
||||
|
||||
void V3Delayed::delayedAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
DelayedVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DelayedVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("delayed", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,20 +40,20 @@ private:
|
|||
// NODE STATE
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_funcp; // Current block
|
||||
AstNode* m_stmtp; // Current statement
|
||||
int m_depth; // How deep in an expression
|
||||
int m_maxdepth; // Maximum depth in an expression
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_funcp; // Current block
|
||||
AstNode* m_stmtp; // Current statement
|
||||
int m_depth; // How deep in an expression
|
||||
int m_maxdepth; // Maximum depth in an expression
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
||||
void createDeepTemp(AstNode* nodep) {
|
||||
UINFO(6," Deep "<<nodep<<endl);
|
||||
//if (debug()>=9) nodep->dumpTree(cout, "deep:");
|
||||
UINFO(6, " Deep " << nodep << endl);
|
||||
// if (debug() >= 9) nodep->dumpTree(cout, "deep:");
|
||||
|
||||
string newvarname = (string("__Vdeeptemp")+cvtToStr(m_modp->varNumGetInc()));
|
||||
string newvarname = (string("__Vdeeptemp") + cvtToStr(m_modp->varNumGetInc()));
|
||||
AstVar* varp = new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname,
|
||||
// Width, not widthMin, as we may be in
|
||||
// middle of BITSEL expression which though
|
||||
|
|
@ -69,8 +69,7 @@ private:
|
|||
nodep->replaceWith(newp);
|
||||
// Put assignment before the referencing statement
|
||||
AstAssign* assp = new AstAssign(nodep->fileline(),
|
||||
new AstVarRef(nodep->fileline(), varp, true),
|
||||
nodep);
|
||||
new AstVarRef(nodep->fileline(), varp, true), nodep);
|
||||
AstNRelinker linker2;
|
||||
m_stmtp->unlinkFrBack(&linker2);
|
||||
assp->addNext(m_stmtp);
|
||||
|
|
@ -79,7 +78,7 @@ private:
|
|||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
AstNodeModule* origModp = m_modp;
|
||||
{
|
||||
m_modp = nodep;
|
||||
|
|
@ -110,20 +109,18 @@ private:
|
|||
}
|
||||
}
|
||||
// Operators
|
||||
virtual void visit(AstNodeTermop* nodep) VL_OVERRIDE {
|
||||
}
|
||||
virtual void visit(AstNodeTermop* nodep) VL_OVERRIDE {}
|
||||
virtual void visit(AstNodeMath* nodep) VL_OVERRIDE {
|
||||
// We have some operator defines that use 2 parens, so += 2.
|
||||
m_depth += 2;
|
||||
if (m_depth>m_maxdepth) m_maxdepth = m_depth;
|
||||
if (m_depth > m_maxdepth) m_maxdepth = m_depth;
|
||||
iterateChildren(nodep);
|
||||
m_depth -= 2;
|
||||
|
||||
if (m_stmtp
|
||||
&& (v3Global.opt.compLimitParens() >= 1) // Else compiler doesn't need it
|
||||
&& (m_maxdepth-m_depth) > v3Global.opt.compLimitParens()
|
||||
if (m_stmtp && (v3Global.opt.compLimitParens() >= 1) // Else compiler doesn't need it
|
||||
&& (m_maxdepth - m_depth) > v3Global.opt.compLimitParens()
|
||||
&& !VN_IS(nodep->backp(), NodeStmt) // Not much point if we're about to use it
|
||||
) {
|
||||
) {
|
||||
m_maxdepth = m_depth;
|
||||
createDeepTemp(nodep);
|
||||
}
|
||||
|
|
@ -135,7 +132,7 @@ private:
|
|||
void needNonStaticFunc(AstNode* nodep) {
|
||||
UASSERT_OBJ(m_funcp, nodep, "Non-static accessor not under a function");
|
||||
if (m_funcp->isStatic().trueUnknown()) {
|
||||
UINFO(5,"Mark non-public due to "<<nodep<<endl);
|
||||
UINFO(5, "Mark non-public due to " << nodep << endl);
|
||||
m_funcp->isStatic(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -171,9 +168,7 @@ public:
|
|||
// Depth class functions
|
||||
|
||||
void V3Depth::depthAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
DepthVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DepthVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("depth", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@ private:
|
|||
// NODE STATE
|
||||
|
||||
// STATE
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_funcp; // Current function
|
||||
int m_depth; // How deep in an expression
|
||||
int m_deepNum; // How many functions made
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstCFunc* m_funcp; // Current function
|
||||
int m_depth; // How deep in an expression
|
||||
int m_deepNum; // How many functions made
|
||||
|
||||
// METHODS
|
||||
VL_DEBUG_FUNC; // Declare debug()
|
||||
|
|
@ -50,7 +50,7 @@ private:
|
|||
AstNRelinker relinkHandle;
|
||||
nodep->unlinkFrBack(&relinkHandle);
|
||||
// Create function
|
||||
string name = m_funcp->name()+"__deep"+cvtToStr(++m_deepNum);
|
||||
string name = m_funcp->name() + "__deep" + cvtToStr(++m_deepNum);
|
||||
AstCFunc* funcp = new AstCFunc(nodep->fileline(), name, NULL);
|
||||
funcp->argTypes(EmitCBaseVisitor::symClassVar());
|
||||
funcp->symProlog(true);
|
||||
|
|
@ -60,7 +60,7 @@ private:
|
|||
// Call it at the point where the body was removed from
|
||||
AstCCall* callp = new AstCCall(nodep->fileline(), funcp);
|
||||
callp->argTypes("vlSymsp");
|
||||
UINFO(6," New "<<callp<<endl);
|
||||
UINFO(6, " New " << callp << endl);
|
||||
//
|
||||
relinkHandle.relink(callp);
|
||||
return funcp;
|
||||
|
|
@ -68,7 +68,7 @@ private:
|
|||
|
||||
// VISITORS
|
||||
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
UINFO(4, " MOD " << nodep << endl);
|
||||
AstNodeModule* origModp = m_modp;
|
||||
{
|
||||
m_modp = nodep;
|
||||
|
|
@ -93,13 +93,13 @@ private:
|
|||
m_depth++;
|
||||
if (m_depth > v3Global.opt.compLimitBlocks()
|
||||
&& !VN_IS(nodep, NodeCCall)) { // Already done
|
||||
UINFO(4, "DeepBlocks "<<m_depth<<" "<<nodep<<endl);
|
||||
UINFO(4, "DeepBlocks " << m_depth << " " << nodep << endl);
|
||||
AstNode* backp = nodep->backp(); // Only for debug
|
||||
if (debug()>=9) backp->dumpTree(cout, "- pre : ");
|
||||
if (debug() >= 9) backp->dumpTree(cout, "- pre : ");
|
||||
AstCFunc* funcp = createDeepFunc(nodep);
|
||||
iterate(funcp);
|
||||
if (debug()>=9) backp->dumpTree(cout, "- post: ");
|
||||
if (debug()>=9) funcp->dumpTree(cout, "- func: ");
|
||||
if (debug() >= 9) backp->dumpTree(cout, "- post: ");
|
||||
if (debug() >= 9) funcp->dumpTree(cout, "- func: ");
|
||||
} else {
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
|
@ -135,9 +135,7 @@ public:
|
|||
// DepthBlock class functions
|
||||
|
||||
void V3DepthBlock::depthBlockAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
DepthBlockVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DepthBlockVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("deepblock", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,9 @@ private:
|
|||
// The risk that this prevents combining identical logic from differently-
|
||||
// named but identical modules seems low.
|
||||
if (m_modSingleton) relativeRefOk = false;
|
||||
//
|
||||
// Class methods need relative
|
||||
if (m_modp && VN_IS(m_modp, Class)) relativeRefOk = true;
|
||||
|
||||
if (varp && varp->isFuncLocal()) {
|
||||
hierThisr = true;
|
||||
|
|
@ -116,8 +119,7 @@ private:
|
|||
} else if (relativeRefOk && scopep == m_scopep) {
|
||||
m_needThis = true;
|
||||
return "this->";
|
||||
} else if (relativeRefOk && scopep->aboveScopep()
|
||||
&& scopep->aboveScopep()==m_scopep) {
|
||||
} else if (relativeRefOk && scopep->aboveScopep() && scopep->aboveScopep() == m_scopep) {
|
||||
// Reference to scope of cell directly under this module, can just "cell->"
|
||||
string name = scopep->name();
|
||||
string::size_type pos;
|
||||
|
|
@ -176,8 +178,8 @@ private:
|
|||
UASSERT_OBJ(funcp->scopep(), funcp, "Not scoped");
|
||||
|
||||
UINFO(6, " Wrapping " << name << " " << funcp << endl);
|
||||
UINFO(6, " at " << newfuncp->argTypes()
|
||||
<< " und " << funcp->argTypes() << endl);
|
||||
UINFO(6,
|
||||
" at " << newfuncp->argTypes() << " und " << funcp->argTypes() << endl);
|
||||
funcp->declPrivate(true);
|
||||
AstNode* argsp = NULL;
|
||||
for (AstNode* stmtp = newfuncp->argsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
|
|
@ -256,7 +258,7 @@ private:
|
|||
nodep->varScopep(NULL);
|
||||
}
|
||||
virtual void visit(AstNodeCCall* nodep) VL_OVERRIDE {
|
||||
// UINFO(9," "<<nodep<<endl);
|
||||
// UINFO(9, " " << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
// Convert the hierch name
|
||||
UASSERT_OBJ(m_scopep, nodep, "Node not under scope");
|
||||
|
|
@ -307,9 +309,7 @@ public:
|
|||
// Descope class functions
|
||||
|
||||
void V3Descope::descopeAll(AstNetlist* nodep) {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
{
|
||||
DescopeVisitor visitor (nodep);
|
||||
} // Destruct before checking
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ DescopeVisitor visitor(nodep); } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("descope", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
||||
}
|
||||
|
|
|
|||
1755
src/V3EmitC.cpp
1755
src/V3EmitC.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -39,23 +39,32 @@ public:
|
|||
V3OutCFile* ofp() const { return m_ofp; }
|
||||
void puts(const string& str) { ofp()->puts(str); }
|
||||
void putbs(const string& str) { ofp()->putbs(str); }
|
||||
void putsDecoration(const string& str) { if (v3Global.opt.decoration()) puts(str); }
|
||||
void putsDecoration(const string& str) {
|
||||
if (v3Global.opt.decoration()) puts(str);
|
||||
}
|
||||
void putsQuoted(const string& str) { ofp()->putsQuoted(str); }
|
||||
bool optSystemC() { return v3Global.opt.systemC(); }
|
||||
static string protect(const string& name) { return VIdProtect::protectIf(name, true); }
|
||||
static string protectIf(const string& name, bool doIt) {
|
||||
return VIdProtect::protectIf(name, doIt); }
|
||||
return VIdProtect::protectIf(name, doIt);
|
||||
}
|
||||
static string protectWordsIf(const string& name, bool doIt) {
|
||||
return VIdProtect::protectWordsIf(name, doIt); }
|
||||
return VIdProtect::protectWordsIf(name, doIt);
|
||||
}
|
||||
static string ifNoProtect(const string& in) { return v3Global.opt.protectIds() ? "" : in; }
|
||||
static string symClassName() { return v3Global.opt.prefix()+"_"+protect("_Syms"); }
|
||||
static string symClassVar() { return symClassName()+"* __restrict vlSymsp"; }
|
||||
static string symClassName() { return v3Global.opt.prefix() + "_" + protect("_Syms"); }
|
||||
static string symClassVar() { return symClassName() + "* __restrict vlSymsp"; }
|
||||
static string symTopAssign() {
|
||||
return v3Global.opt.prefix()+"* __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;"; }
|
||||
return v3Global.opt.prefix() + "* __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;";
|
||||
}
|
||||
static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp) {
|
||||
if (nodep->isConstructor()) return prefixNameProtect(modp);
|
||||
else if (nodep->isDestructor()) return string("~") + prefixNameProtect(modp);
|
||||
else return nodep->nameProtect();
|
||||
if (nodep->isConstructor()) {
|
||||
return prefixNameProtect(modp);
|
||||
} else if (nodep->isDestructor()) {
|
||||
return string("~") + prefixNameProtect(modp);
|
||||
} else {
|
||||
return nodep->nameProtect();
|
||||
}
|
||||
}
|
||||
static string prefixNameProtect(const AstNode* nodep) { // C++ name with prefix
|
||||
const AstNodeModule* modp = VN_CAST_CONST(nodep, NodeModule);
|
||||
|
|
@ -79,15 +88,17 @@ public:
|
|||
// Return argument list for given C function
|
||||
string args = nodep->argTypes();
|
||||
// Might be a user function with argument list.
|
||||
for (const AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) {
|
||||
for (const AstNode* stmtp = nodep->argsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (const AstVar* portp = VN_CAST_CONST(stmtp, Var)) {
|
||||
if (portp->isIO() && !portp->isFuncReturn()) {
|
||||
if (args != "") args+= ", ";
|
||||
if (nodep->dpiImport() || nodep->dpiExportWrapper())
|
||||
if (args != "") args += ", ";
|
||||
if (nodep->dpiImport() || nodep->dpiExportWrapper()) {
|
||||
args += portp->dpiArgType(true, false);
|
||||
else if (nodep->funcPublic())
|
||||
} else if (nodep->funcPublic()) {
|
||||
args += portp->cPubArgType(true, false);
|
||||
else args += portp->vlArgType(true, false, true);
|
||||
} else {
|
||||
args += portp->vlArgType(true, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -114,6 +125,7 @@ private:
|
|||
m_count++;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit EmitCBaseCounterVisitor(AstNode* nodep) {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@ class EmitCInlines : EmitCBaseVisitor {
|
|||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstClass* nodep) VL_OVERRIDE {
|
||||
v3Global.needC11(true);
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstDynArrayDType* nodep) VL_OVERRIDE {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -62,7 +67,7 @@ class EmitCInlines : EmitCBaseVisitor {
|
|||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstNew* nodep) VL_OVERRIDE {
|
||||
virtual void visit(AstCNew* nodep) VL_OVERRIDE {
|
||||
if (v3Global.opt.savable()) v3error("Unsupported: --savable with dynamic new");
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
|
@ -102,16 +107,14 @@ class EmitCInlines : EmitCBaseVisitor {
|
|||
public:
|
||||
explicit EmitCInlines(AstNetlist* nodep) {
|
||||
iterate(nodep);
|
||||
if (v3Global.needHInlines()) {
|
||||
emitInt();
|
||||
}
|
||||
if (v3Global.needHInlines()) emitInt();
|
||||
}
|
||||
};
|
||||
|
||||
void EmitCInlines::emitInt() {
|
||||
string filename = v3Global.opt.makeDir()+"/"+topClassName()+"__Inlines.h";
|
||||
newCFile(filename, false/*slow*/, false/*source*/);
|
||||
V3OutCFile hf (filename);
|
||||
string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Inlines.h";
|
||||
newCFile(filename, false /*slow*/, false /*source*/);
|
||||
V3OutCFile hf(filename);
|
||||
m_ofp = &hf;
|
||||
|
||||
ofp()->putsHeader();
|
||||
|
|
@ -132,6 +135,6 @@ void EmitCInlines::emitInt() {
|
|||
// EmitC class functions
|
||||
|
||||
void V3EmitC::emitcInlines() {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
EmitCInlines syms (v3Global.rootp());
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
EmitCInlines(v3Global.rootp());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Emit C++ for tree
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2020 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3EmitC.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3EmitCMain.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
//######################################################################
|
||||
|
||||
class EmitCMain : EmitCBaseVisitor {
|
||||
// METHODS
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstNode* nodep) { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit EmitCMain(AstNetlist* nodep) { emitInt(); }
|
||||
|
||||
private:
|
||||
// MAIN METHOD
|
||||
void emitInt() {
|
||||
string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__main.cpp";
|
||||
newCFile(filename, false /*slow*/, true /*source*/);
|
||||
V3OutCFile cf(filename);
|
||||
m_ofp = &cf;
|
||||
|
||||
// Heavly commented output, as users are likely to look at or copy this code
|
||||
ofp()->putsHeader();
|
||||
puts("// DESCRIPTION: main() calling loop, created with Verilator --main\n");
|
||||
puts("\n");
|
||||
|
||||
puts("#include \"verilated.h\"\n");
|
||||
puts("#include \"" + topClassName() + ".h\"\n");
|
||||
|
||||
puts("\n//======================\n\n");
|
||||
|
||||
puts(topClassName() + "* topp;\n");
|
||||
puts("\n");
|
||||
puts("// Requires -DVL_TIME_STAMP64\n");
|
||||
v3Global.opt.addCFlags("-DVL_TIME_STAMP64");
|
||||
puts("vluint64_t main_time = 0;\n");
|
||||
puts("vluint64_t vl_time_stamp64() { return main_time; }\n");
|
||||
puts("\n");
|
||||
|
||||
puts("int main(int argc, char** argv, char**) {\n");
|
||||
puts("// Setup defaults and parse command line\n");
|
||||
puts("Verilated::debug(0);\n");
|
||||
puts("Verilated::commandArgs(argc, argv);\n");
|
||||
puts("// Construct the Verilated model, from Vtop.h generated from Verilating\n");
|
||||
puts("topp = new " + topClassName() + "(\"top\");\n");
|
||||
puts("// Evaluate initials\n");
|
||||
puts("topp->eval(); // Evaluate\n");
|
||||
|
||||
puts("// Simulate until $finish\n");
|
||||
puts("while (!Verilated::gotFinish()) {\n");
|
||||
puts(/**/ "// Evaluate model\n");
|
||||
puts(/**/ "topp->eval();\n");
|
||||
puts(/**/ "// Advance time\n");
|
||||
puts(/**/ "++main_time;\n");
|
||||
puts("}\n");
|
||||
puts("\n");
|
||||
|
||||
puts("if (!Verilated::gotFinish()) {\n");
|
||||
puts(/**/ "VL_DEBUG_IF(VL_PRINTF(\"+ Exiting without $finish; no events left\\n\"););\n");
|
||||
puts("}\n");
|
||||
puts("\n");
|
||||
|
||||
puts("// Final model cleanup\n");
|
||||
puts("topp->final();\n");
|
||||
puts("VL_DO_DANGLING(delete topp, topp);\n");
|
||||
puts("exit(0);\n");
|
||||
puts("}\n");
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// EmitC class functions
|
||||
|
||||
void V3EmitCMain::emit() {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
EmitCMain(v3Global.rootp());
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Emit C main()
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2020 by Wilson Snyder. This program is free software; you
|
||||
// can redistribute it and/or modify it under the terms of either the GNU
|
||||
// Lesser General Public License Version 3 or the Perl Artistic License
|
||||
// Version 2.0.
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _V3EMITCMAIN_H_
|
||||
#define _V3EMITCMAIN_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3EmitCMain {
|
||||
public:
|
||||
static void emit();
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -35,8 +35,7 @@ class CMakeEmitter {
|
|||
// 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("\"");
|
||||
|
|
@ -56,16 +55,14 @@ class CMakeEmitter {
|
|||
// "BOOL", "FILEPATH", "PATH", "STRING" or "INTERNAL" for a CACHE variable
|
||||
// See https://cmake.org/cmake/help/latest/command/set.html
|
||||
static void cmake_set_raw(std::ofstream& of, const string& name, const string& raw_value,
|
||||
const string& cache_type = "", const string& docstring = "") {
|
||||
const string& cache_type = "", const string& docstring = "") {
|
||||
of << "set(" << name << " " << raw_value;
|
||||
if (!cache_type.empty()) {
|
||||
of << " CACHE " << cache_type << " \"" << docstring << "\"";
|
||||
}
|
||||
if (!cache_type.empty()) { of << " CACHE " << cache_type << " \"" << docstring << "\""; }
|
||||
of << ")\n";
|
||||
}
|
||||
|
||||
static void cmake_set(std::ofstream& of, const string& name, const string& value,
|
||||
const string& cache_type = "", const string& docstring = "") {
|
||||
const string& cache_type = "", const string& docstring = "") {
|
||||
string raw_value = "\"" + value + "\"";
|
||||
cmake_set_raw(of, name, raw_value, cache_type, docstring);
|
||||
}
|
||||
|
|
@ -80,22 +77,24 @@ class CMakeEmitter {
|
|||
}
|
||||
|
||||
static void emitOverallCMake() {
|
||||
const vl_unique_ptr<std::ofstream>
|
||||
of (V3File::new_ofstream(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+".cmake"));
|
||||
const vl_unique_ptr<std::ofstream> of(
|
||||
V3File::new_ofstream(v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".cmake"));
|
||||
string name = v3Global.opt.prefix();
|
||||
|
||||
*of << "# Verilated -*- CMake -*-\n";
|
||||
*of << "# DESCR" "IPTION: Verilator output: CMake include script with class lists\n";
|
||||
*of << "# DESCR"
|
||||
"IPTION: Verilator output: CMake include script with class lists\n";
|
||||
*of << "#\n";
|
||||
*of << "# This CMake script lists generated Verilated files, for including in higher level CMake scripts.\n";
|
||||
*of << "# This CMake script lists generated Verilated files, for "
|
||||
"including in higher level CMake scripts.\n";
|
||||
*of << "# This file is meant to be consumed by the verilate() function,\n";
|
||||
*of << "# which becomes available after executing `find_package(verilator).\n";
|
||||
|
||||
*of << "\n### Constants...\n";
|
||||
cmake_set(*of, "PERL", deslash(V3Options::getenvPERL()),
|
||||
"FILEPATH", "Perl executable (from $PERL)");
|
||||
cmake_set(*of, "VERILATOR_ROOT", deslash(V3Options::getenvVERILATOR_ROOT()),
|
||||
"PATH", "Path to Verilator kit (from $VERILATOR_ROOT)");
|
||||
cmake_set(*of, "PERL", deslash(V3Options::getenvPERL()), "FILEPATH",
|
||||
"Perl executable (from $PERL)");
|
||||
cmake_set(*of, "VERILATOR_ROOT", deslash(V3Options::getenvVERILATOR_ROOT()), "PATH",
|
||||
"Path to Verilator kit (from $VERILATOR_ROOT)");
|
||||
|
||||
*of << "\n### Compiler flags...\n";
|
||||
|
||||
|
|
@ -110,13 +109,19 @@ class CMakeEmitter {
|
|||
*of << "# SystemC output mode? 0/1 (from --sc)\n";
|
||||
cmake_set_raw(*of, name + "_SC", v3Global.opt.systemC() ? "1" : "0");
|
||||
*of << "# Coverage output mode? 0/1 (from --coverage)\n";
|
||||
cmake_set_raw(*of, name + "_COVERAGE", v3Global.opt.coverage()?"1":"0");
|
||||
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 << "# VCD Tracing output mode? 0/1 (from --trace)\n";
|
||||
cmake_set_raw(*of, name + "_TRACE_VCD", (v3Global.opt.trace() && (v3Global.opt.traceFormat() == TraceFormat::VCD))?"1":"0");
|
||||
cmake_set_raw(*of, name + "_TRACE_VCD",
|
||||
(v3Global.opt.trace() && (v3Global.opt.traceFormat() == TraceFormat::VCD))
|
||||
? "1"
|
||||
: "0");
|
||||
*of << "# FST Tracing output mode? 0/1 (from --fst-trace)\n";
|
||||
cmake_set_raw(*of, name + "_TRACE_FST", (v3Global.opt.trace() && (v3Global.opt.traceFormat() != TraceFormat::VCD)) ? "1":"0");
|
||||
cmake_set_raw(*of, name + "_TRACE_FST",
|
||||
(v3Global.opt.trace() && (v3Global.opt.traceFormat() != TraceFormat::VCD))
|
||||
? "1"
|
||||
: "0");
|
||||
|
||||
*of << "\n### Sources...\n";
|
||||
std::vector<string> classes_fast, classes_slow, support_fast, support_slow, global;
|
||||
|
|
@ -141,7 +146,7 @@ class CMakeEmitter {
|
|||
}
|
||||
|
||||
global.push_back("${VERILATOR_ROOT}/include/verilated.cpp");
|
||||
if (v3Global.dpi()) {
|
||||
if (v3Global.dpi()) { //
|
||||
global.push_back("${VERILATOR_ROOT}/include/verilated_dpi.cpp");
|
||||
}
|
||||
if (v3Global.opt.vpi()) {
|
||||
|
|
@ -154,21 +159,22 @@ class CMakeEmitter {
|
|||
global.push_back("${VERILATOR_ROOT}/include/verilated_cov.cpp");
|
||||
}
|
||||
if (v3Global.opt.trace()) {
|
||||
global.push_back("${VERILATOR_ROOT}/include/"
|
||||
+ v3Global.opt.traceSourceBase() + "_c.cpp");
|
||||
global.push_back("${VERILATOR_ROOT}/include/" + v3Global.opt.traceSourceBase()
|
||||
+ "_c.cpp");
|
||||
if (v3Global.opt.systemC()) {
|
||||
if (v3Global.opt.traceFormat() != TraceFormat::VCD) {
|
||||
v3error("Unsupported: This trace format is not supported in SystemC, use VCD format.");
|
||||
v3error("Unsupported: This trace format is not supported in SystemC, "
|
||||
"use VCD format.");
|
||||
}
|
||||
global.push_back("${VERILATOR_ROOT}/include/"
|
||||
+ v3Global.opt.traceSourceLang() + ".cpp");
|
||||
global.push_back("${VERILATOR_ROOT}/include/" + v3Global.opt.traceSourceLang()
|
||||
+ ".cpp");
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.mtasks()) {
|
||||
global.push_back("${VERILATOR_ROOT}/include/verilated_threads.cpp");
|
||||
}
|
||||
if (!v3Global.opt.protectLib().empty()) {
|
||||
global.push_back(v3Global.opt.makeDir()+"/"+v3Global.opt.protectLib()+".cpp");
|
||||
global.push_back(v3Global.opt.makeDir() + "/" + v3Global.opt.protectLib() + ".cpp");
|
||||
}
|
||||
|
||||
*of << "# Global classes, need linked once per executable\n";
|
||||
|
|
@ -177,7 +183,8 @@ class CMakeEmitter {
|
|||
cmake_set_raw(*of, name + "_CLASSES_SLOW", deslash(cmake_list(classes_slow)));
|
||||
*of << "# Generated module classes, fast-path, compile with highest optimization\n";
|
||||
cmake_set_raw(*of, name + "_CLASSES_FAST", deslash(cmake_list(classes_fast)));
|
||||
*of << "# Generated support classes, non-fast-path, compile with low/medium optimization\n";
|
||||
*of << "# Generated support classes, non-fast-path, compile with "
|
||||
"low/medium optimization\n";
|
||||
cmake_set_raw(*of, name + "_SUPPORT_SLOW", deslash(cmake_list(support_slow)));
|
||||
*of << "# Generated support classes, fast-path, compile with highest optimization\n";
|
||||
cmake_set_raw(*of, name + "_SUPPORT_FAST", deslash(cmake_list(support_fast)));
|
||||
|
|
@ -188,14 +195,13 @@ class CMakeEmitter {
|
|||
*of << "# User .cpp files (from .cpp's on Verilator command line)\n";
|
||||
cmake_set_raw(*of, name + "_USER_CLASSES", deslash(cmake_list(v3Global.opt.cppFiles())));
|
||||
}
|
||||
|
||||
public:
|
||||
explicit CMakeEmitter() {
|
||||
emitOverallCMake();
|
||||
}
|
||||
explicit CMakeEmitter() { emitOverallCMake(); }
|
||||
virtual ~CMakeEmitter() {}
|
||||
};
|
||||
|
||||
void V3EmitCMake::emit() {
|
||||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
CMakeEmitter emitter;
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue