'Merge from master for release.'

This commit is contained in:
Wilson Snyder 2020-05-03 11:15:21 -04:00
commit f83817510d
508 changed files with 40366 additions and 21909 deletions

View File

@ -13,7 +13,7 @@ AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: false
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false

View File

@ -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?

View File

@ -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
View File

@ -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.

View File

@ -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:

View File

@ -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/> +++ &bullet; Accepts synthesizable Verilog or SystemVerilog
+++ <br/> +++ &bullet; Performs lint code-quality checks
+++ <br/> +++ &bullet; 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

View File

@ -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:

View File

@ -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/*

View File

@ -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],

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>`::

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 --------------------"

View File

@ -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 --------------------"

View File

@ -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

View File

@ -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");

View File

@ -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);

View File

@ -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 ---------------------"

View File

@ -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);

View File

@ -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

View File

@ -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 ---------------------"

View File

@ -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;

View File

@ -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

View File

@ -8,7 +8,7 @@
module sub
#(parameter type TYPE_t = logic)
(
input TYPE_t in,
input TYPE_t in,
output TYPE_t out
);

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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@"

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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;

View File

@ -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;
}
//======================================================================

View File

@ -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);
}

View File

@ -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; };

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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);
};

388
include/verilated_trace.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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; }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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());
}
//######################################################################

File diff suppressed because it is too large Load Diff

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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); }

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

137
src/V3Class.cpp Normal file
View File

@ -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);
}

33
src/V3Class.h Normal file
View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

File diff suppressed because it is too large Load Diff

View File

@ -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) {

View File

@ -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());
}

105
src/V3EmitCMain.cpp Normal file
View File

@ -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());
}

30
src/V3EmitCMain.h Normal file
View File

@ -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

View File

@ -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